aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2009-04-24 14:04:26 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2009-04-24 14:04:26 +0000
commit89175b7e049f8203ece12ba74301d7ad9385c050 (patch)
tree1812569845aaf29df5f2a18285e73bc1fcc6268c
parenta8182a597e392aa831f2e6960a71d3554fc34aa9 (diff)
Convert the ast_channel data structure over to the astobj2 framework.
There is a lot that could be said about this, but the patch is a big improvement for performance, stability, code maintainability, and ease of future code development. The channel list is no longer an unsorted linked list. The main container for channels is an astobj2 hash table. All of the code related to searching for channels or iterating active channels has been rewritten. Let n be the number of active channels. Iterating the channel list has gone from O(n^2) to O(n). Searching for a channel by name went from O(n) to O(1). Searching for a channel by extension is still O(n), but uses a new method for doing so, which is more efficient. The ast_channel object is now a reference counted object. The benefits here are plentiful. Some benefits directly related to issues in the previous code include: 1) When threads other than the channel thread owning a channel wanted access to a channel, it had to hold the lock on it to ensure that it didn't go away. This is no longer a requirement. Holding a reference is sufficient. 2) There are places that now require less dealing with channel locks. 3) There are places where channel locks are held for much shorter periods of time. 4) There are places where dealing with more than one channel at a time becomes _MUCH_ easier. ChanSpy is a great example of this. Writing code in the future that deals with multiple channels will be much easier. Some additional information regarding channel locking and reference count handling can be found in channel.h, where a new section has been added that discusses some of the rules associated with it. Mark Michelson also assisted with the development of this patch. He did the conversion of ChanSpy and introduced a new API, ast_autochan, which makes it much easier to deal with holding on to a channel pointer for an extended period of time and having it get automatically updated if the channel gets masqueraded. Mark was also a huge help in the code review process. Thanks to David Vossel for his assistance with this branch, as well. David did the conversion of the DAHDIScan application by making it become a wrapper for ChanSpy internally. The changes come from the svn/asterisk/team/russell/ast_channel_ao2 branch. Review: http://reviewboard.digium.com/r/203/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@190423 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--CHANGES4
-rw-r--r--UPGRADE.txt3
-rw-r--r--apps/app_channelredirect.c8
-rw-r--r--apps/app_chanspy.c450
-rw-r--r--apps/app_dahdiscan.c378
-rw-r--r--apps/app_directed_pickup.c112
-rw-r--r--apps/app_minivm.c5
-rw-r--r--apps/app_mixmonitor.c100
-rw-r--r--apps/app_senddtmf.c16
-rw-r--r--apps/app_softhangup.c14
-rw-r--r--apps/app_voicemail.c12
-rw-r--r--build_tools/cflags.xml2
-rw-r--r--channels/chan_agent.c9
-rw-r--r--channels/chan_bridge.c2
-rw-r--r--channels/chan_dahdi.c10
-rw-r--r--channels/chan_gtalk.c2
-rw-r--r--channels/chan_iax2.c2
-rw-r--r--channels/chan_local.c9
-rw-r--r--channels/chan_mgcp.c1
-rw-r--r--channels/chan_misdn.c2
-rw-r--r--channels/chan_sip.c20
-rw-r--r--channels/chan_unistim.c6
-rw-r--r--funcs/func_channel.c17
-rw-r--r--funcs/func_global.c35
-rw-r--r--funcs/func_logic.c18
-rw-r--r--funcs/func_odbc.c20
-rw-r--r--include/asterisk/autochan.h112
-rw-r--r--include/asterisk/channel.h297
-rw-r--r--include/asterisk/lock.h35
-rw-r--r--main/Makefile2
-rw-r--r--main/autochan.c94
-rw-r--r--main/channel.c639
-rw-r--r--main/cli.c151
-rw-r--r--main/devicestate.c14
-rw-r--r--main/features.c201
-rw-r--r--main/logger.c2
-rw-r--r--main/manager.c168
-rw-r--r--main/pbx.c29
-rw-r--r--res/res_agi.c56
-rw-r--r--res/res_clioriginate.c5
-rw-r--r--res/res_monitor.c67
-rw-r--r--res/snmp/agent.c61
42 files changed, 1704 insertions, 1486 deletions
diff --git a/CHANGES b/CHANGES
index fe31769ad..c243eb2a2 100644
--- a/CHANGES
+++ b/CHANGES
@@ -28,6 +28,10 @@ Applications
regardless if the call has been answered or not.
* Added functionality to the app_dial F() option to continue with execution
at the current location when no parameters are provided.
+ * Added c() option to app_chanspy. This option allows custom DTMF to be set
+ to cycle through the next avaliable channel. By default this is still '*'.
+ * Added x() option to app_chanspy. This option allows DTMF to be set to
+ exit the application.
Dialplan Functions
------------------
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 35b0d455a..6272b9948 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -25,6 +25,9 @@ From 1.6.2 to 1.6.3:
If you are not using autoload=yes in modules.conf you will need to ensure
it is set to load. If not, then any module which uses RTP (such as chan_sip)
will not be able to send or receive calls.
+* The app_dahdiscan.c file has been removed, but the dialplan app DAHDIScan still
+ remains. It now exists within app_chanspy.c and retains the exact same
+ functionality as before.
From 1.6.1 to 1.6.2:
diff --git a/apps/app_channelredirect.c b/apps/app_channelredirect.c
index a20d20f43..f604151ee 100644
--- a/apps/app_channelredirect.c
+++ b/apps/app_channelredirect.c
@@ -86,8 +86,7 @@ static int asyncgoto_exec(struct ast_channel *chan, void *data)
return -1;
}
- chan2 = ast_get_channel_by_name_locked(args.channel);
- if (!chan2) {
+ if (!(chan2 = ast_channel_get_by_name(args.channel))) {
ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
pbx_builtin_setvar_helper(chan, "CHANNELREDIRECT_STATUS", "NOCHANNEL");
return 0;
@@ -96,9 +95,12 @@ static int asyncgoto_exec(struct ast_channel *chan, void *data)
if (chan2->pbx) {
ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
}
+
res = ast_async_parseable_goto(chan2, args.label);
+
+ chan2 = ast_channel_unref(chan2);
+
pbx_builtin_setvar_helper(chan, "CHANNELREDIRECT_STATUS", "SUCCESS");
- ast_channel_unlock(chan2);
return res;
}
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
index 9d087c4d1..07eac74a2 100644
--- a/apps/app_chanspy.c
+++ b/apps/app_chanspy.c
@@ -50,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/options.h"
+#include "asterisk/autochan.h"
#define AST_NAME_STRLEN 256
#define NUM_SPYGROUPS 128
@@ -141,6 +142,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
name of the last channel that was spied on will be stored
in the <variable>SPY_CHANNEL</variable> variable.</para>
</option>
+ <option name="x">
+ <argument name="digit" required="true">
+ <para>Specify a DTMF digit that can be used to exit the application.</para>
+ </argument>
+ </option>
+ <option name="c">
+ <argument name="digit" required="true">
+ <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
+ </argument>
+ </option>
<option name="e">
<argument name="ext" required="true" />
<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
@@ -262,6 +273,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
name of the last channel that was spied on will be stored
in the <variable>SPY_CHANNEL</variable> variable.</para>
</option>
+ <option name="x">
+ <argument name="digit" required="true">
+ <para>Specify a DTMF digit that can be used to exit the application.</para>
+ </argument>
+ </option>
+ <option name="c">
+ <argument name="digit" required="true">
+ <para>Specify a DTMF digit that can be used to spy on the next available channel.</para>
+ </argument>
+ </option>
<option name="e">
<argument name="ext" required="true" />
<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
@@ -287,12 +308,29 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<ref type="application">ChanSpy</ref>
</see-also>
</application>
-
+
+ <application name="DAHDIScan" language="en_US">
+ <synopsis>
+ Scan DAHDI channels to monitor calls.
+ </synopsis>
+ <syntax>
+ <parameter name="group">
+ <para>Limit scanning to a channel <replaceable>group</replaceable> by setting this option.</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Allows a call center manager to monitor DAHDI channels in a
+ convenient way. Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
+ </description>
+ </application>
***/
+
static const char *app_chan = "ChanSpy";
static const char *app_ext = "ExtenSpy";
+static const char *app_dahdiscan = "DAHDIScan";
+
enum {
OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
@@ -307,7 +345,10 @@ enum {
OPTION_NOTECH = (1 << 10), /* Skip technology name playback */
OPTION_BARGE = (1 << 11), /* Barge mode (whisper to both channels) */
OPTION_NAME = (1 << 12), /* Say the name of the person on whom we will spy */
- OPTION_DTMF_SWITCH_MODES = (1 << 13), /*Allow numeric DTMF to switch between chanspy modes */
+ OPTION_DTMF_SWITCH_MODES = (1 << 13), /* Allow numeric DTMF to switch between chanspy modes */
+ OPTION_DTMF_EXIT = (1 << 14), /* Set DTMF to exit, added for DAHDIScan integration */
+ OPTION_DTMF_CYCLE = (1 << 15), /* Custom DTMF for cycling next avaliable channel, (default is '*') */
+ OPTION_DAHDI_SCAN = (1 << 16), /* Scan groups in DAHDIScan mode */
} chanspy_opt_flags;
enum {
@@ -316,6 +357,8 @@ enum {
OPT_ARG_RECORD,
OPT_ARG_ENFORCED,
OPT_ARG_NAME,
+ OPT_ARG_EXIT,
+ OPT_ARG_CYCLE,
OPT_ARG_ARRAY_SIZE,
} chanspy_opt_args;
@@ -334,10 +377,10 @@ AST_APP_OPTIONS(spy_opts, {
AST_APP_OPTION('s', OPTION_NOTECH),
AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
+ AST_APP_OPTION_ARG('x', OPTION_DTMF_EXIT, OPT_ARG_EXIT),
+ AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
});
-static int next_unique_id_to_use = 0;
-
struct chanspy_translation_helper {
/* spy data */
struct ast_audiohook spy_audiohook;
@@ -347,6 +390,12 @@ struct chanspy_translation_helper {
int volfactor;
};
+struct spy_dtmf_options {
+ char exit;
+ char cycle;
+ char volume;
+};
+
static void *spy_alloc(struct ast_channel *chan, void *data)
{
/* just store the data pointer in the channel structure */
@@ -399,27 +448,21 @@ static struct ast_generator spygen = {
.generate = spy_generate,
};
-static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
+static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook)
{
int res = 0;
struct ast_channel *peer = NULL;
- ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
+ ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, autochan->chan->name);
- res = ast_audiohook_attach(chan, audiohook);
+ res = ast_audiohook_attach(autochan->chan, audiohook);
- if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
+ if (!res && ast_test_flag(autochan->chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) {
ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
}
return res;
}
-struct chanspy_ds {
- struct ast_channel *chan;
- char unique_id[20];
- ast_mutex_t lock;
-};
-
static void change_spy_mode(const char digit, struct ast_flags *flags)
{
if (digit == '4') {
@@ -434,8 +477,9 @@ static void change_spy_mode(const char digit, struct ast_flags *flags)
}
}
-static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
- int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
+static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan,
+ int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags,
+ char *exitcontext)
{
struct chanspy_translation_helper csth;
int running = 0, res, x = 0;
@@ -443,32 +487,22 @@ static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chansp
char *name;
struct ast_frame *f;
struct ast_silence_generator *silgen = NULL;
- struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
+ struct ast_autochan *spyee_bridge_autochan = NULL;
const char *spyer_name;
ast_channel_lock(chan);
spyer_name = ast_strdupa(chan->name);
ast_channel_unlock(chan);
- ast_mutex_lock(&spyee_chanspy_ds->lock);
- if (spyee_chanspy_ds->chan) {
- spyee = spyee_chanspy_ds->chan;
- ast_channel_lock(spyee);
- }
- ast_mutex_unlock(&spyee_chanspy_ds->lock);
-
- if (!spyee) {
- return 0;
- }
-
/* We now hold the channel lock on spyee */
- if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
- ast_channel_unlock(spyee);
+ if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan)) {
return 0;
}
- name = ast_strdupa(spyee->name);
+ ast_channel_lock(spyee_autochan->chan);
+ name = ast_strdupa(spyee_autochan->chan->name);
+ ast_channel_unlock(spyee_autochan->chan);
ast_verb(2, "Spying on channel %s\n", name);
manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
@@ -480,26 +514,23 @@ static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chansp
ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
- if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
+ if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) {
ast_audiohook_destroy(&csth.spy_audiohook);
- ast_channel_unlock(spyee);
return 0;
}
- ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
+ ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
- if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
- ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
+ if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) {
+ ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name);
}
- if ((spyee_bridge = ast_bridged_channel(spyee))) {
- ast_channel_lock(spyee_bridge);
- if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
- ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
+ if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) {
+ ast_channel_lock(spyee_bridge_autochan->chan);
+ if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) {
+ ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name);
}
- ast_channel_unlock(spyee_bridge);
+ ast_channel_unlock(spyee_bridge_autochan->chan);
}
- ast_channel_unlock(spyee);
- spyee = NULL;
ast_channel_lock(chan);
ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
@@ -589,10 +620,13 @@ static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chansp
}
}
- if (res == '*') {
+ if (res == user_options->cycle) {
running = 0;
break;
- } else if (res == '#') {
+ } else if (res == user_options->exit) {
+ running = -2;
+ break;
+ } else if (res == user_options->volume) {
if (!ast_strlen_zero(inp)) {
running = atoi(inp);
break;
@@ -632,128 +666,45 @@ static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chansp
ast_audiohook_detach(&csth.spy_audiohook);
ast_audiohook_unlock(&csth.spy_audiohook);
ast_audiohook_destroy(&csth.spy_audiohook);
-
+
+ if (spyee_bridge_autochan) {
+ ast_autochan_destroy(spyee_bridge_autochan);
+ }
+
ast_verb(2, "Done Spying on channel %s\n", name);
manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
return running;
}
-/*!
- * \note This relies on the embedded lock to be recursive, as it may be called
- * due to a call to chanspy_ds_free with the lock held there.
- */
-static void chanspy_ds_destroy(void *data)
-{
- struct chanspy_ds *chanspy_ds = data;
-
- /* Setting chan to be NULL is an atomic operation, but we don't want this
- * value to change while this lock is held. The lock is held elsewhere
- * while it performs non-atomic operations with this channel pointer */
-
- ast_mutex_lock(&chanspy_ds->lock);
- chanspy_ds->chan = NULL;
- ast_mutex_unlock(&chanspy_ds->lock);
-}
-
-static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
-{
- struct chanspy_ds *chanspy_ds = data;
-
- ast_mutex_lock(&chanspy_ds->lock);
- chanspy_ds->chan = new_chan;
- ast_mutex_unlock(&chanspy_ds->lock);
-}
-
-static const struct ast_datastore_info chanspy_ds_info = {
- .type = "chanspy",
- .destroy = chanspy_ds_destroy,
- .chan_fixup = chanspy_ds_chan_fixup,
-};
-
-static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
-{
- if (!chanspy_ds)
- return NULL;
-
- ast_mutex_lock(&chanspy_ds->lock);
- if (chanspy_ds->chan) {
- struct ast_datastore *datastore;
- struct ast_channel *chan;
-
- chan = chanspy_ds->chan;
-
- ast_channel_lock(chan);
- if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
- ast_channel_datastore_remove(chan, datastore);
- /* chanspy_ds->chan is NULL after this call */
- chanspy_ds_destroy(datastore->data);
- datastore->data = NULL;
- ast_datastore_free(datastore);
- }
- ast_channel_unlock(chan);
- }
- ast_mutex_unlock(&chanspy_ds->lock);
-
- return NULL;
-}
-
-/*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
-static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
+static struct ast_autochan *next_channel(struct ast_channel_iterator *iter,
+ struct ast_autochan *autochan, struct ast_channel *chan)
{
- struct ast_datastore *datastore = NULL;
-
- ast_mutex_lock(&chanspy_ds->lock);
+ struct ast_channel *next;
+ const size_t pseudo_len = strlen("DAHDI/pseudo");
- if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
- ast_mutex_unlock(&chanspy_ds->lock);
- chanspy_ds = chanspy_ds_free(chanspy_ds);
- ast_channel_unlock(chan);
+ if (!iter) {
return NULL;
}
-
- chanspy_ds->chan = chan;
- datastore->data = chanspy_ds;
- ast_channel_datastore_add(chan, datastore);
-
- return chanspy_ds;
-}
-
-static struct chanspy_ds *next_channel(struct ast_channel *chan,
- const struct ast_channel *last, const char *spec,
- const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
-{
- struct ast_channel *next;
- const size_t pseudo_len = strlen("DAHDI/pseudo");
redo:
- if (!ast_strlen_zero(spec))
- next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
- else if (!ast_strlen_zero(exten))
- next = ast_walk_channel_by_exten_locked(last, exten, context);
- else
- next = ast_channel_walk_locked(last);
-
- if (!next)
+ if (!(next = ast_channel_iterator_next(iter))) {
return NULL;
+ }
if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
- last = next;
- ast_channel_unlock(next);
goto redo;
} else if (next == chan) {
- last = next;
- ast_channel_unlock(next);
goto redo;
}
- return setup_chanspy_ds(next, chanspy_ds);
+ return ast_autochan_setup(next);
}
static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
- int volfactor, const int fd, const char *mygroup, const char *myenforced,
- const char *spec, const char *exten, const char *context, const char *mailbox,
- const char *name_context)
+ int volfactor, const int fd, struct spy_dtmf_options *user_options,
+ const char *mygroup, const char *myenforced, const char *spec, const char *exten,
+ const char *context, const char *mailbox, const char *name_context)
{
char nameprefix[AST_NAME_STRLEN];
char peer_name[AST_NAME_STRLEN + 5];
@@ -764,7 +715,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
char *ptr;
int num;
int num_spyed_upon = 1;
- struct chanspy_ds chanspy_ds = { 0, };
+ struct ast_channel_iterator *iter = NULL;
if (ast_test_flag(flags, OPTION_EXIT)) {
const char *c;
@@ -779,10 +730,6 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
ast_channel_unlock(chan);
}
- ast_mutex_init(&chanspy_ds.lock);
-
- snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
-
if (chan->_state != AST_STATE_UP)
ast_answer(chan);
@@ -791,8 +738,8 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
waitms = 100;
for (;;) {
- struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
- struct ast_channel *prev = NULL, *peer = NULL;
+ struct ast_autochan *autochan = NULL, *next_autochan = NULL;
+ struct ast_channel *prev = NULL;
if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
res = ast_streamfile(chan, "beep", chan->language);
@@ -813,6 +760,19 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
}
}
+ /* Set up the iterator we'll be using during this call */
+ if (!ast_strlen_zero(spec)) {
+ iter = ast_channel_iterator_by_name_new(0, spec, strlen(spec));
+ } else if (!ast_strlen_zero(exten)) {
+ iter = ast_channel_iterator_by_exten_new(0, exten, context);
+ } else {
+ iter = ast_channel_iterator_all_new(0);
+ }
+
+ if (!iter) {
+ return -1;
+ }
+
res = ast_waitfordigit(chan, waitms);
if (res < 0) {
ast_clear_flag(chan, AST_FLAG_SPYING);
@@ -832,38 +792,30 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
waitms = 100;
num_spyed_upon = 0;
- for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
- peer_chanspy_ds;
- chanspy_ds_free(peer_chanspy_ds), prev = peer,
- peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
- next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
+ for (autochan = next_channel(iter, autochan, chan);
+ autochan;
+ prev = autochan->chan, ast_autochan_destroy(autochan),
+ autochan = next_autochan ? next_autochan :
+ next_channel(iter, autochan, chan), next_autochan = NULL) {
int igrp = !mygroup;
int ienf = !myenforced;
char *s;
- peer = peer_chanspy_ds->chan;
-
- ast_mutex_unlock(&peer_chanspy_ds->lock);
-
- if (peer == prev) {
- ast_channel_unlock(peer);
- chanspy_ds_free(peer_chanspy_ds);
+ if (autochan->chan == prev) {
+ ast_autochan_destroy(autochan);
break;
}
if (ast_check_hangup(chan)) {
- ast_channel_unlock(peer);
- chanspy_ds_free(peer_chanspy_ds);
+ ast_autochan_destroy(autochan);
break;
}
- if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
- ast_channel_unlock(peer);
+ if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
continue;
}
- if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
- ast_channel_unlock(peer);
+ if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) {
continue;
}
@@ -874,14 +826,22 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
char dup_mygroup[512];
char *groups[NUM_SPYGROUPS];
char *mygroups[NUM_SPYGROUPS];
- const char *group;
+ const char *group = NULL;
int x;
int y;
ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
ARRAY_LEN(mygroups));
- if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
+ /* Before dahdi scan was part of chanspy, it would use the "GROUP" variable
+ * rather than "SPYGROUP", this check is done to preserve expected behavior */
+ if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
+ group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
+ } else {
+ group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
+ }
+
+ if (!ast_strlen_zero(group)) {
ast_copy_string(dup_group, group, sizeof(dup_group));
num_groups = ast_app_separate_args(dup_group, ':', groups,
ARRAY_LEN(groups));
@@ -898,10 +858,8 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
}
if (!igrp) {
- ast_channel_unlock(peer);
continue;
}
-
if (myenforced) {
char ext[AST_CHANNEL_NAME + 3];
char buffer[512];
@@ -909,7 +867,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
- ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
+ ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1);
if ((end = strchr(ext, '-'))) {
*end++ = ':';
*end = '\0';
@@ -927,18 +885,13 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
}
strcpy(peer_name, "spy-");
- strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
+ strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1);
ptr = strchr(peer_name, '/');
*ptr++ = '\0';
ptr = strsep(&ptr, "-");
for (s = peer_name; s < ptr; s++)
*s = tolower(*s);
- /* We have to unlock the peer channel here to avoid a deadlock.
- * So, when we need to dereference it again, we have to lock the
- * datastore and get the pointer from there to see if the channel
- * is still valid. */
- ast_channel_unlock(peer);
if (!ast_test_flag(flags, OPTION_QUIET)) {
if (ast_test_flag(flags, OPTION_NAME)) {
@@ -954,7 +907,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
res = ast_waitstream(chan, "");
}
if (res) {
- chanspy_ds_free(peer_chanspy_ds);
+ ast_autochan_destroy(autochan);
break;
}
} else {
@@ -966,42 +919,38 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
}
}
- res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
- num_spyed_upon++;
+ res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
+ num_spyed_upon++;
if (res == -1) {
- chanspy_ds_free(peer_chanspy_ds);
+ ast_autochan_destroy(autochan);
goto exit;
} else if (res == -2) {
res = 0;
- chanspy_ds_free(peer_chanspy_ds);
+ ast_autochan_destroy(autochan);
goto exit;
} else if (res > 1 && spec) {
struct ast_channel *next;
snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
- if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
- peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
- next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
+ if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
+ next_autochan = ast_autochan_setup(next);
+ next = ast_channel_unref(next);
} else {
/* stay on this channel, if it is still valid */
-
- ast_mutex_lock(&peer_chanspy_ds->lock);
- if (peer_chanspy_ds->chan) {
- ast_channel_lock(peer_chanspy_ds->chan);
- next_chanspy_ds = peer_chanspy_ds;
- peer_chanspy_ds = NULL;
+ if (!ast_check_hangup(autochan->chan)) {
+ next_autochan = ast_autochan_setup(autochan->chan);
} else {
/* the channel is gone */
- ast_mutex_unlock(&peer_chanspy_ds->lock);
- next_chanspy_ds = NULL;
+ next_autochan = NULL;
}
}
-
- peer = NULL;
}
}
+
+ iter = ast_channel_iterator_destroy(iter);
+
if (res == -1 || ast_check_hangup(chan))
break;
}
@@ -1011,10 +960,6 @@ exit:
ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
- ast_mutex_lock(&chanspy_ds.lock);
- ast_mutex_unlock(&chanspy_ds.lock);
- ast_mutex_destroy(&chanspy_ds.lock);
-
return res;
}
@@ -1025,6 +970,11 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
char *recbase = NULL;
int fd = 0;
struct ast_flags flags;
+ struct spy_dtmf_options user_options = {
+ .cycle = '*',
+ .volume = '#',
+ .exit = '\0',
+ };
int oldwf = 0;
int volfactor = 0;
int res;
@@ -1043,6 +993,7 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
args.spec = NULL;
if (args.options) {
+ char tmp;
ast_app_parse_options(spy_opts, &flags, opts, args.options);
if (ast_test_flag(&flags, OPTION_GROUP))
mygroup = opts[OPT_ARG_GROUP];
@@ -1051,6 +1002,24 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
!(recbase = opts[OPT_ARG_RECORD]))
recbase = "chanspy";
+ if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
+ tmp = opts[OPT_ARG_EXIT][0];
+ if (strchr("0123456789*#", tmp) && tmp != '\0') {
+ user_options.exit = tmp;
+ } else {
+ ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
+ }
+ }
+
+ if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
+ tmp = opts[OPT_ARG_CYCLE][0];
+ if (strchr("0123456789*#", tmp) && tmp != '\0') {
+ user_options.cycle = tmp;
+ } else {
+ ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
+ }
+ }
+
if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
int vol;
@@ -1065,7 +1034,7 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
if (ast_test_flag(&flags, OPTION_ENFORCED))
myenforced = opts[OPT_ARG_ENFORCED];
-
+
if (ast_test_flag(&flags, OPTION_NAME)) {
if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
char *delimiter;
@@ -1078,10 +1047,9 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
}
}
}
-
-
- } else
+ } else {
ast_clear_flag(&flags, AST_FLAGS_ALL);
+ }
oldwf = chan->writeformat;
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
@@ -1099,7 +1067,7 @@ static int chanspy_exec(struct ast_channel *chan, void *data)
}
}
- res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
+ res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
if (fd)
close(fd);
@@ -1117,6 +1085,11 @@ static int extenspy_exec(struct ast_channel *chan, void *data)
char *recbase = NULL;
int fd = 0;
struct ast_flags flags;
+ struct spy_dtmf_options user_options = {
+ .cycle = '*',
+ .volume = '#',
+ .exit = '\0',
+ };
int oldwf = 0;
int volfactor = 0;
int res;
@@ -1141,6 +1114,7 @@ static int extenspy_exec(struct ast_channel *chan, void *data)
if (args.options) {
char *opts[OPT_ARG_ARRAY_SIZE];
+ char tmp;
ast_app_parse_options(spy_opts, &flags, opts, args.options);
if (ast_test_flag(&flags, OPTION_GROUP))
@@ -1150,6 +1124,24 @@ static int extenspy_exec(struct ast_channel *chan, void *data)
!(recbase = opts[OPT_ARG_RECORD]))
recbase = "chanspy";
+ if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
+ tmp = opts[OPT_ARG_EXIT][0];
+ if (strchr("0123456789*#", tmp) && tmp != '\0') {
+ user_options.exit = tmp;
+ } else {
+ ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.");
+ }
+ }
+
+ if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
+ tmp = opts[OPT_ARG_CYCLE][0];
+ if (strchr("0123456789*#", tmp) && tmp != '\0') {
+ user_options.cycle = tmp;
+ } else {
+ ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.");
+ }
+ }
+
if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
int vol;
@@ -1162,7 +1154,6 @@ static int extenspy_exec(struct ast_channel *chan, void *data)
if (ast_test_flag(&flags, OPTION_PRIVATE))
ast_set_flag(&flags, OPTION_WHISPER);
-
if (ast_test_flag(&flags, OPTION_NAME)) {
if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
char *delimiter;
@@ -1176,8 +1167,9 @@ static int extenspy_exec(struct ast_channel *chan, void *data)
}
}
- } else
+ } else {
ast_clear_flag(&flags, AST_FLAGS_ALL);
+ }
oldwf = chan->writeformat;
if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
@@ -1196,7 +1188,7 @@ static int extenspy_exec(struct ast_channel *chan, void *data)
}
- res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
+ res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
if (fd)
close(fd);
@@ -1207,12 +1199,49 @@ static int extenspy_exec(struct ast_channel *chan, void *data)
return res;
}
+static int dahdiscan_exec(struct ast_channel *chan, void *data)
+{
+ const char *spec = "DAHDI";
+ struct ast_flags flags;
+ struct spy_dtmf_options user_options = {
+ .cycle = '#',
+ .volume = '\0',
+ .exit = '*',
+ };
+ int oldwf = 0;
+ int res;
+ char *mygroup = NULL;
+
+ ast_clear_flag(&flags, AST_FLAGS_ALL);
+
+ if (!ast_strlen_zero(data)) {
+ mygroup = ast_strdupa(data);
+ }
+ ast_set_flag(&flags, OPTION_DTMF_EXIT);
+ ast_set_flag(&flags, OPTION_DTMF_CYCLE);
+ ast_set_flag(&flags, OPTION_DAHDI_SCAN);
+
+ oldwf = chan->writeformat;
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+ return -1;
+ }
+
+ res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
+
+ if (oldwf && ast_set_write_format(chan, oldwf) < 0)
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+
+ return res;
+}
+
static int unload_module(void)
{
int res = 0;
res |= ast_unregister_application(app_chan);
res |= ast_unregister_application(app_ext);
+ res |= ast_unregister_application(app_dahdiscan);
return res;
}
@@ -1223,6 +1252,7 @@ static int load_module(void)
res |= ast_register_application_xml(app_chan, chanspy_exec);
res |= ast_register_application_xml(app_ext, extenspy_exec);
+ res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
return res;
}
diff --git a/apps/app_dahdiscan.c b/apps/app_dahdiscan.c
deleted file mode 100644
index 8251ffcbf..000000000
--- a/apps/app_dahdiscan.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 1999 - 2005, Digium, Inc.
- *
- * Mark Spencer <markster@digium.com>
- *
- * Modified from app_zapbarge by David Troy <dave@toad.net>
- *
- * Special thanks to comphealth.com for sponsoring this
- * GPL application.
- *
- * 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 DAHDI Scanner
- *
- * \author Mark Spencer <markster@digium.com>
- *
- * \ingroup applications
- */
-
-/*** MODULEINFO
- <depend>dahdi</depend>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <dahdi/user.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/config.h"
-#include "asterisk/app.h"
-#include "asterisk/utils.h"
-#include "asterisk/cli.h"
-#include "asterisk/say.h"
-#include "asterisk/options.h"
-
-/*** DOCUMENTATION
- <application name="DAHDIScan" language="en_US">
- <synopsis>
- Scan DAHDI channels to monitor calls.
- </synopsis>
- <syntax>
- <parameter name="group">
- <para>Limit scanning to a channel <replaceable>group</replaceable> by setting this option.</para>
- </parameter>
- </syntax>
- <description>
- <para>Allows a call center manager to monitor DAHDI channels in a
- convenient way. Use <literal>#</literal> to select the next channel and use <literal>*</literal> to exit.</para>
- </description>
- </application>
- ***/
-static char *app = "DAHDIScan";
-
-#define CONF_SIZE 160
-
-static struct ast_channel *get_dahdi_channel_locked(int num) {
- char name[80];
-
- snprintf(name, sizeof(name), "DAHDI/%d-1", num);
- return ast_get_channel_by_name_locked(name);
-}
-
-static int careful_write(int fd, unsigned char *data, int len)
-{
- int res;
- while (len) {
- res = write(fd, data, len);
- if (res < 1) {
- if (errno != EAGAIN) {
- ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
- return -1;
- } else {
- return 0;
- }
- }
- len -= res;
- data += res;
- }
- return 0;
-}
-
-static int conf_run(struct ast_channel *chan, int confno, int confflags)
-{
- int fd;
- struct dahdi_confinfo dahdic;
- struct ast_frame *f;
- struct ast_channel *c;
- struct ast_frame fr;
- int outfd;
- int ms;
- int nfds;
- int res;
- int flags;
- int retrydahdi;
- int origfd;
- int ret = -1;
- char input[4];
- int ic = 0;
-
- struct dahdi_bufferinfo bi;
- char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
- char *buf = __buf + AST_FRIENDLY_OFFSET;
-
- /* Set it into U-law mode (write) */
- if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
- ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
- goto outrun;
- }
-
- /* Set it into U-law mode (read) */
- if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
- ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
- goto outrun;
- }
- ast_indicate(chan, -1);
- retrydahdi = strcasecmp(chan->tech->type, "DAHDI");
- dahdiretry:
- origfd = chan->fds[0];
- if (retrydahdi) {
- fd = open("/dev/dahdi/pseudo", O_RDWR);
- if (fd < 0) {
- ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
- goto outrun;
- }
- /* Make non-blocking */
- flags = fcntl(fd, F_GETFL);
- if (flags < 0) {
- ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
- close(fd);
- goto outrun;
- }
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
- ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
- close(fd);
- goto outrun;
- }
- /* Setup buffering information */
- memset(&bi, 0, sizeof(bi));
- bi.bufsize = CONF_SIZE;
- bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
- bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
- bi.numbufs = 4;
- if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
- ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
- close(fd);
- goto outrun;
- }
- nfds = 1;
- } else {
- /* XXX Make sure we're not running on a pseudo channel XXX */
- fd = chan->fds[0];
- nfds = 0;
- }
- memset(&dahdic, 0, sizeof(dahdic));
- /* Check to see if we're in a conference... */
- dahdic.chan = 0;
- if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
- ast_log(LOG_WARNING, "Error getting conference\n");
- close(fd);
- goto outrun;
- }
- if (dahdic.confmode) {
- /* Whoa, already in a conference... Retry... */
- if (!retrydahdi) {
- ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
- retrydahdi = 1;
- goto dahdiretry;
- }
- }
- memset(&dahdic, 0, sizeof(dahdic));
- /* Add us to the conference */
- dahdic.chan = 0;
- dahdic.confno = confno;
- dahdic.confmode = DAHDI_CONF_MONITORBOTH;
-
- if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- close(fd);
- goto outrun;
- }
- ast_debug(1, "Placed channel %s in DAHDI channel %d monitor\n", chan->name, confno);
-
- for (;;) {
- outfd = -1;
- ms = -1;
- c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
- if (c) {
- if (c->fds[0] != origfd) {
- if (retrydahdi) {
- /* Kill old pseudo */
- close(fd);
- }
- ast_debug(1, "Ooh, something swapped out under us, starting over\n");
- retrydahdi = 0;
- goto dahdiretry;
- }
- f = ast_read(c);
- if (!f) {
- break;
- }
- if (f->frametype == AST_FRAME_DTMF) {
- if (f->subclass == '#') {
- ret = 0;
- break;
- } else if (f->subclass == '*') {
- ret = -1;
- break;
- } else {
- input[ic++] = f->subclass;
- }
- if (ic == 3) {
- input[ic++] = '\0';
- ic = 0;
- ret = atoi(input);
- ast_verb(3, "DAHDIScan: change channel to %d\n", ret);
- break;
- }
- }
-
- if (fd != chan->fds[0]) {
- if (f->frametype == AST_FRAME_VOICE) {
- if (f->subclass == AST_FORMAT_ULAW) {
- /* Carefully write */
- careful_write(fd, f->data.ptr, f->datalen);
- } else {
- ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
- }
- }
- }
- ast_frfree(f);
- } else if (outfd > -1) {
- res = read(outfd, buf, CONF_SIZE);
- if (res > 0) {
- memset(&fr, 0, sizeof(fr));
- fr.frametype = AST_FRAME_VOICE;
- fr.subclass = AST_FORMAT_ULAW;
- fr.datalen = res;
- fr.samples = res;
- fr.data.ptr = buf;
- fr.offset = AST_FRIENDLY_OFFSET;
- if (ast_write(chan, &fr) < 0) {
- ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
- /* break; */
- }
- } else {
- ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
- }
- }
- }
- if (f) {
- ast_frfree(f);
- }
- if (fd != chan->fds[0]) {
- close(fd);
- } else {
- /* Take out of conference */
- /* Add us to the conference */
- dahdic.chan = 0;
- dahdic.confno = 0;
- dahdic.confmode = 0;
- if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
- ast_log(LOG_WARNING, "Error setting conference\n");
- }
- }
-
- outrun:
-
- return ret;
-}
-
-static int conf_exec(struct ast_channel *chan, void *data)
-{
- int res=-1;
- int confflags = 0;
- int confno = 0;
- char confnostr[80] = "", *tmp = NULL;
- struct ast_channel *tempchan = NULL, *lastchan = NULL, *ichan = NULL;
- struct ast_frame *f;
- char *desired_group;
- int input = 0, search_group = 0;
-
- if (chan->_state != AST_STATE_UP)
- ast_answer(chan);
-
- desired_group = ast_strdupa(data);
- if (!ast_strlen_zero(desired_group)) {
- ast_verb(3, "Scanning for group %s\n", desired_group);
- search_group = 1;
- }
-
- for (;;) {
- if (ast_waitfor(chan, 100) < 0)
- break;
-
- f = ast_read(chan);
- if (!f)
- break;
- if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
- ast_frfree(f);
- break;
- }
- ast_frfree(f);
- ichan = NULL;
- if(input) {
- ichan = get_dahdi_channel_locked(input);
- input = 0;
- }
-
- tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
-
- if (!tempchan && !lastchan) {
- break;
- }
-
- if (tempchan && search_group) {
- const char *mygroup;
- if ((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
- ast_verb(3, "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
- } else {
- ast_channel_unlock(tempchan);
- lastchan = tempchan;
- continue;
- }
- }
- if (tempchan && (!strcmp(tempchan->tech->type, "DAHDI")) && (tempchan != chan)) {
- ast_verb(3, "DAHDI channel %s is in-use, monitoring...\n", tempchan->name);
- ast_copy_string(confnostr, tempchan->name, sizeof(confnostr));
- ast_channel_unlock(tempchan);
- if ((tmp = strchr(confnostr, '-'))) {
- *tmp = '\0';
- }
- confno = atoi(strchr(confnostr, '/') + 1);
- ast_stopstream(chan);
- ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
- res = conf_run(chan, confno, confflags);
- if (res < 0) {
- break;
- }
- input = res;
- } else if (tempchan) {
- ast_channel_unlock(tempchan);
- }
- lastchan = tempchan;
- }
- return res;
-}
-
-static int unload_module(void)
-{
- return ast_unregister_application(app);
-}
-
-static int load_module(void)
-{
- return ((ast_register_application_xml(app, conf_exec)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
-}
-
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan DAHDI channels application");
-
diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c
index 5ce217d7a..0cf691c59 100644
--- a/apps/app_directed_pickup.c
+++ b/apps/app_directed_pickup.c
@@ -135,17 +135,35 @@ static int can_pickup(struct ast_channel *chan)
return 0;
}
+struct pickup_by_name_args {
+ const char *name;
+ size_t len;
+};
+
+static int pickup_by_name_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = obj;
+ struct pickup_by_name_args *args = data;
+
+ ast_channel_lock(chan);
+ if (!strncasecmp(chan->name, args->name, args->len) && can_pickup(chan)) {
+ /* Return with the channel still locked on purpose */
+ return CMP_MATCH | CMP_STOP;
+ }
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
/*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
static struct ast_channel *my_ast_get_channel_by_name_locked(const char *channame)
{
- struct ast_channel *chan;
char *chkchan;
- size_t channame_len, chkchan_len;
+ struct pickup_by_name_args pickup_args;
- channame_len = strlen(channame);
- chkchan_len = channame_len + 2;
+ pickup_args.len = strlen(channame) + 2;
- chkchan = alloca(chkchan_len);
+ chkchan = alloca(pickup_args.len);
/* need to append a '-' for the comparison so we check full channel name,
* i.e SIP/hgc- , use a temporary variable so original stays the same for
@@ -154,15 +172,9 @@ static struct ast_channel *my_ast_get_channel_by_name_locked(const char *channam
strcpy(chkchan, channame);
strcat(chkchan, "-");
- for (chan = ast_walk_channel_by_name_prefix_locked(NULL, channame, channame_len);
- chan;
- chan = ast_walk_channel_by_name_prefix_locked(chan, channame, channame_len)) {
- if (!strncasecmp(chan->name, chkchan, chkchan_len) && can_pickup(chan)) {
- return chan;
- }
- ast_channel_unlock(chan);
- }
- return NULL;
+ pickup_args.name = chkchan;
+
+ return ast_channel_callback(pickup_by_name_cb, NULL, &pickup_args, 0);
}
/*! \brief Attempt to pick up specified channel named , does not use context */
@@ -171,76 +183,82 @@ static int pickup_by_channel(struct ast_channel *chan, char *pickup)
int res = 0;
struct ast_channel *target;
- if (!(target = my_ast_get_channel_by_name_locked(pickup)))
+ if (!(target = my_ast_get_channel_by_name_locked(pickup))) {
return -1;
+ }
/* Just check that we are not picking up the SAME as target */
- if (chan->name != target->name && chan != target) {
+ if (chan != target) {
res = pickup_do(chan, target);
}
+
ast_channel_unlock(target);
+ target = ast_channel_unref(target);
return res;
}
-struct pickup_criteria {
- const char *exten;
- const char *context;
-};
-
-static int find_by_exten(struct ast_channel *c, void *data)
-{
- struct pickup_criteria *info = data;
-
- return (!strcasecmp(c->macroexten, info->exten) || !strcasecmp(c->exten, info->exten)) &&
- !strcasecmp(c->dialcontext, info->context) &&
- can_pickup(c);
-}
-
/* Attempt to pick up specified extension with context */
static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
{
struct ast_channel *target = NULL;
- struct pickup_criteria search = {
- .exten = exten,
- .context = context,
- };
+ struct ast_channel_iterator *iter;
+ int res = -1;
+
+ if (!(iter = ast_channel_iterator_by_exten_new(0, exten, context))) {
+ return -1;
+ }
- target = ast_channel_search_locked(find_by_exten, &search);
+ while ((target = ast_channel_iterator_next(iter))) {
+ ast_channel_lock(target);
+ if (can_pickup(target)) {
+ break;
+ }
+ ast_channel_unlock(target);
+ target = ast_channel_unref(target);
+ }
if (target) {
- int res = pickup_do(chan, target);
+ res = pickup_do(chan, target);
ast_channel_unlock(target);
- target = NULL;
- return res;
+ target = ast_channel_unref(target);
}
- return -1;
+ return res;
}
-static int find_by_mark(struct ast_channel *c, void *data)
+static int find_by_mark(void *obj, void *arg, void *data, int flags)
{
+ struct ast_channel *c = obj;
const char *mark = data;
const char *tmp;
+ int res;
+
+ ast_channel_lock(c);
- return (tmp = pbx_builtin_getvar_helper(c, PICKUPMARK)) &&
+ res = (tmp = pbx_builtin_getvar_helper(c, PICKUPMARK)) &&
!strcasecmp(tmp, mark) &&
can_pickup(c);
+
+ ast_channel_unlock(c);
+
+ return res ? CMP_MATCH | CMP_STOP : 0;
}
/* Attempt to pick up specified mark */
static int pickup_by_mark(struct ast_channel *chan, const char *mark)
{
- struct ast_channel *target = ast_channel_search_locked(find_by_mark, (char *) mark);
+ struct ast_channel *target;
+ int res = -1;
- if (target) {
- int res = pickup_do(chan, target);
+ if ((target = ast_channel_callback(find_by_mark, NULL, (char *) mark, 0))) {
+ ast_channel_lock(target);
+ res = pickup_do(chan, target);
ast_channel_unlock(target);
- target = NULL;
- return res;
+ target = ast_channel_unref(target);
}
- return -1;
+ return res;
}
/* application entry point for Pickup() */
diff --git a/apps/app_minivm.c b/apps/app_minivm.c
index 3e5058067..9148ee130 100644
--- a/apps/app_minivm.c
+++ b/apps/app_minivm.c
@@ -1254,8 +1254,9 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
ast_safe_system(tmp2);
ast_debug(1, "Sent message to %s with command '%s' - %s\n", vmu->email, global_mailcmd, template->attachment ? "(media attachment)" : "");
ast_debug(3, "Actual command used: %s\n", tmp2);
- if (ast)
- ast_channel_free(ast);
+ if (ast) {
+ ast = ast_channel_release(ast);
+ }
return 0;
}
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index 3056e7e92..f67422aae 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
+#include "asterisk/autochan.h"
/*** DOCUMENTATION
<application name="MixMonitor" language="en_US">
@@ -138,7 +139,7 @@ struct mixmonitor {
char *post_process;
char *name;
unsigned int flags;
- struct mixmonitor_ds *mixmonitor_ds;
+ struct ast_autochan *autochan;
};
enum {
@@ -164,50 +165,6 @@ AST_APP_OPTIONS(mixmonitor_opts, {
AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
});
-/* This structure is used as a means of making sure that our pointer to
- * the channel we are monitoring remains valid. This is very similar to
- * what is used in app_chanspy.c.
- */
-struct mixmonitor_ds {
- struct ast_channel *chan;
- /* These condition variables are used to be sure that the channel
- * hangup code completes before the mixmonitor thread attempts to
- * free this structure. The combination of a bookean flag and a
- * ast_cond_t ensure that no matter what order the threads run in,
- * we are guaranteed to never have the waiting thread block forever
- * in the case that the signaling thread runs first.
- */
- unsigned int destruction_ok;
- ast_cond_t destruction_condition;
- ast_mutex_t lock;
-};
-
-static void mixmonitor_ds_destroy(void *data)
-{
- struct mixmonitor_ds *mixmonitor_ds = data;
-
- ast_mutex_lock(&mixmonitor_ds->lock);
- mixmonitor_ds->chan = NULL;
- mixmonitor_ds->destruction_ok = 1;
- ast_cond_signal(&mixmonitor_ds->destruction_condition);
- ast_mutex_unlock(&mixmonitor_ds->lock);
-}
-
-static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
-{
- struct mixmonitor_ds *mixmonitor_ds = data;
-
- ast_mutex_lock(&mixmonitor_ds->lock);
- mixmonitor_ds->chan = new_chan;
- ast_mutex_unlock(&mixmonitor_ds->lock);
-}
-
-static struct ast_datastore_info mixmonitor_ds_info = {
- .type = "mixmonitor",
- .destroy = mixmonitor_ds_destroy,
- .chan_fixup = mixmonitor_ds_chan_fixup,
-};
-
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
{
struct ast_channel *peer = NULL;
@@ -249,9 +206,7 @@ static void *mixmonitor_thread(void *obj)
if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR)))
continue;
- ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
- if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
- ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
+ if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
/* Initialize the file if not already done so */
if (!fs && !errflag) {
oflags = O_CREAT | O_WRONLY;
@@ -271,10 +226,7 @@ static void *mixmonitor_thread(void *obj)
/* Write out frame */
if (fs)
ast_writestream(fs, fr);
- } else {
- ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
}
-
/* All done! free it. */
ast_frame_free(fr, 0);
@@ -294,48 +246,12 @@ static void *mixmonitor_thread(void *obj)
ast_safe_system(mixmonitor->post_process);
}
- ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
- if (!mixmonitor->mixmonitor_ds->destruction_ok) {
- ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
- }
- ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
- ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
- ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
- ast_free(mixmonitor->mixmonitor_ds);
+ ast_autochan_destroy(mixmonitor->autochan);
ast_free(mixmonitor);
return NULL;
}
-static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
-{
- struct ast_datastore *datastore = NULL;
- struct mixmonitor_ds *mixmonitor_ds;
-
- if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
- return -1;
- }
-
- ast_mutex_init(&mixmonitor_ds->lock);
- ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
-
- if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
- ast_free(mixmonitor_ds);
- return -1;
- }
-
- /* No need to lock mixmonitor_ds since this is still operating in the channel's thread */
- mixmonitor_ds->chan = chan;
- datastore->data = mixmonitor_ds;
-
- ast_channel_lock(chan);
- ast_channel_datastore_add(chan, datastore);
- ast_channel_unlock(chan);
-
- mixmonitor->mixmonitor_ds = mixmonitor_ds;
- return 0;
-}
-
static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
int readvol, int writevol, const char *post_process)
{
@@ -369,7 +285,7 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename
/* Copy over flags and channel name */
mixmonitor->flags = flags;
- if (setup_mixmonitor_ds(mixmonitor, chan)) {
+ if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
return;
}
mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
@@ -512,12 +428,14 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
if (a->argc < 3)
return CLI_SHOWUSAGE;
- if (!(chan = ast_get_channel_by_name_prefix_locked(a->argv[2], strlen(a->argv[2])))) {
+ if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
/* Technically this is a failure, but we don't want 2 errors printing out */
return CLI_SUCCESS;
}
+ ast_channel_lock(chan);
+
if (!strcasecmp(a->argv[1], "start")) {
mixmonitor_exec(chan, a->argv[3]);
ast_channel_unlock(chan);
@@ -526,6 +444,8 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
ast_audiohook_detach_source(chan, mixmonitor_spy_type);
}
+ chan = ast_channel_unref(chan);
+
return CLI_SUCCESS;
}
diff --git a/apps/app_senddtmf.c b/apps/app_senddtmf.c
index 9842c412e..a1c28a8f9 100644
--- a/apps/app_senddtmf.c
+++ b/apps/app_senddtmf.c
@@ -100,23 +100,25 @@ static int manager_play_dtmf(struct mansession *s, const struct message *m)
{
const char *channel = astman_get_header(m, "Channel");
const char *digit = astman_get_header(m, "Digit");
- struct ast_channel *chan = ast_get_channel_by_name_locked(channel);
-
- if (!chan) {
- astman_send_error(s, m, "Channel not specified");
+ struct ast_channel *chan;
+
+ if (!(chan = ast_channel_get_by_name(channel))) {
+ astman_send_error(s, m, "Channel not found");
return 0;
}
+
if (ast_strlen_zero(digit)) {
astman_send_error(s, m, "No digit specified");
- ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
return 0;
}
ast_senddigit(chan, *digit, 0);
- ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
+
astman_send_ack(s, m, "DTMF successfully queued");
-
+
return 0;
}
diff --git a/apps/app_softhangup.c b/apps/app_softhangup.c
index a7ba753fd..6a4889acf 100644
--- a/apps/app_softhangup.c
+++ b/apps/app_softhangup.c
@@ -80,6 +80,7 @@ static int softhangup_exec(struct ast_channel *chan, void *data)
AST_APP_ARG(channel);
AST_APP_ARG(options);
);
+ struct ast_channel_iterator *iter;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SoftHangup requires an argument (Technology/resource)\n");
@@ -93,9 +94,12 @@ static int softhangup_exec(struct ast_channel *chan, void *data)
ast_app_parse_options(app_opts, &flags, opts, args.options);
lenmatch = strlen(args.channel);
- for (c = ast_walk_channel_by_name_prefix_locked(NULL, args.channel, lenmatch);
- c;
- c = ast_walk_channel_by_name_prefix_locked(c, args.channel, lenmatch)) {
+ if (!(iter = ast_channel_iterator_by_name_new(0, args.channel, lenmatch))) {
+ return -1;
+ }
+
+ while ((c = ast_channel_iterator_next(iter))) {
+ ast_channel_lock(c);
ast_copy_string(name, c->name, sizeof(name));
if (ast_test_flag(&flags, OPTION_ALL)) {
/* CAPI is set up like CAPI[foo/bar]/clcnt */
@@ -113,12 +117,16 @@ static int softhangup_exec(struct ast_channel *chan, void *data)
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
if (!ast_test_flag(&flags, OPTION_ALL)) {
ast_channel_unlock(c);
+ c = ast_channel_unref(c);
break;
}
}
ast_channel_unlock(c);
+ c = ast_channel_unref(c);
}
+ ast_channel_iterator_destroy(iter);
+
return 0;
}
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index bcfc0793b..27aabf4c7 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -4106,7 +4106,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
} else {
fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
}
- ast_channel_free(ast);
+ ast = ast_channel_release(ast);
} else {
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
}
@@ -4156,7 +4156,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
} else {
fprintf(p, "Subject: %s" ENDL, passdata);
}
- ast_channel_free(ast);
+ ast = ast_channel_release(ast);
} else {
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
}
@@ -4229,7 +4229,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
fprintf(p, "%s" ENDL, passdata);
- ast_channel_free(ast);
+ ast = ast_channel_release(ast);
} else
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
} else if (msgnum > -1) {
@@ -4413,7 +4413,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
fprintf(p, "From: %s <%s>\n", passdata, who);
- ast_channel_free(ast);
+ ast = ast_channel_release(ast);
} else
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
} else
@@ -4429,7 +4429,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
fprintf(p, "Subject: %s\n\n", passdata);
- ast_channel_free(ast);
+ ast = ast_channel_release(ast);
} else
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
} else {
@@ -4451,7 +4451,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
fprintf(p, "%s\n", passdata);
- ast_channel_free(ast);
+ ast = ast_channel_release(ast);
} else
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
} else {
diff --git a/build_tools/cflags.xml b/build_tools/cflags.xml
index 4567360d4..d0498a48f 100644
--- a/build_tools/cflags.xml
+++ b/build_tools/cflags.xml
@@ -32,8 +32,6 @@
<member name="TEST_TANDEM_TRANSCODING" displayname="New ulaw/alaw codec, turn on transcoding tests on init">
<depend>G711_NEW_ALGORITHM</depend>
</member>
- <member name="DEBUG_CHANNEL_LOCKS" displayname="Debug Channel Locking">
- </member>
<member name="MALLOC_DEBUG" displayname="Keep Track of Memory Allocations">
</member>
<member name="BUSYDETECT_TONEONLY" displayname="Enable additional comparision of only the tone duration not the silence part">
diff --git a/channels/chan_agent.c b/channels/chan_agent.c
index 535056ecb..b705b6f04 100644
--- a/channels/chan_agent.c
+++ b/channels/chan_agent.c
@@ -463,8 +463,9 @@ static int agent_cleanup(struct agent_pvt *p)
/* Release ownership of the agent to other threads (presumably running the login app). */
p->app_lock_flag = 0;
ast_cond_signal(&p->app_complete_cond);
- if (chan)
- ast_channel_free(chan);
+ if (chan) {
+ chan = ast_channel_release(chan);
+ }
if (p->dead) {
ast_mutex_destroy(&p->lock);
ast_mutex_destroy(&p->app_lock);
@@ -1124,7 +1125,7 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state)
p->owner = NULL;
tmp->tech_pvt = NULL;
p->app_sleep_cond = 1;
- ast_channel_free( tmp );
+ tmp = ast_channel_release(tmp);
ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
p->app_lock_flag = 0;
ast_cond_signal(&p->app_complete_cond);
@@ -1138,7 +1139,7 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state)
p->owner = NULL;
tmp->tech_pvt = NULL;
p->app_sleep_cond = 1;
- ast_channel_free( tmp );
+ tmp = ast_channel_release(tmp);
ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
return NULL;
}
diff --git a/channels/chan_bridge.c b/channels/chan_bridge.c
index bd1d0fbee..be41cd86c 100644
--- a/channels/chan_bridge.c
+++ b/channels/chan_bridge.c
@@ -204,7 +204,7 @@ static struct ast_channel *bridge_request(const char *type, int format, void *da
return NULL;
}
if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-output", p))) {
- ast_channel_free(p->input);
+ p->input = ast_channel_release(p->input);
ast_free(p);
return NULL;
}
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index d2f7f727c..44b854a77 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -12075,9 +12075,13 @@ static int pri_fixup_principle(struct dahdi_pri *pri, int principle, q931_call *
new->owner = old->owner;
old->owner = NULL;
if (new->owner) {
- ast_string_field_build(new->owner, name,
- "DAHDI/%d:%d-%d", pri->trunkgroup,
- new->channel, 1);
+ char newname[AST_CHANNEL_NAME];
+
+ snprintf(newname, sizeof(newname),
+ "DAHDI/%d:%d-%d", pri->trunkgroup, new->channel, 1);
+
+ ast_change_name(new->owner, newname);
+
new->owner->tech_pvt = new;
ast_channel_set_fd(new->owner, 0, new->subs[SUB_REAL].dfd);
new->subs[SUB_REAL].owner = old->subs[SUB_REAL].owner;
diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c
index 417817eb8..7b430fa09 100644
--- a/channels/chan_gtalk.c
+++ b/channels/chan_gtalk.c
@@ -1223,7 +1223,7 @@ static int gtalk_newcall(struct gtalk *client, ikspak *pak)
gtalk_action(client, p, "reject");
p->alreadygone = 1;
gtalk_hangup(chan);
- ast_channel_free(chan);
+ ast_channel_release(chan);
return -1;
}
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index e07858fe1..a55f75aae 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -4483,7 +4483,7 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
if (tmp) {
/* unlock and relock iaxsl[callno] to preserve locking order */
ast_mutex_unlock(&iaxsl[callno]);
- ast_channel_free(tmp);
+ tmp = ast_channel_release(tmp);
ast_mutex_lock(&iaxsl[callno]);
}
return NULL;
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 5518a7155..7e7ef3c4e 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -793,13 +793,12 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
ama = 0;
if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;1", p->exten, p->context, randnum))
|| !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x;2", p->exten, p->context, randnum))) {
- if (tmp)
- ast_channel_free(tmp);
- if (tmp2)
- ast_channel_free(tmp2);
+ if (tmp) {
+ tmp = ast_channel_release(tmp);
+ }
ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
return NULL;
- }
+ }
tmp2->tech = tmp->tech = &local_tech;
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 80b939393..0ba8290a1 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -1479,7 +1479,6 @@ static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state)
if (!tmp->nativeformats)
tmp->nativeformats = capability;
fmt = ast_best_codec(tmp->nativeformats);
- ast_string_field_build(tmp, name, "MGCP/%s@%s-%d", i->name, i->parent->name, sub->id);
if (sub->rtp)
ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0));
if (i->dtmfmode & (MGCP_DTMF_INBAND | MGCP_DTMF_HYBRID)) {
diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c
index c246b9b93..132f0d1d2 100644
--- a/channels/chan_misdn.c
+++ b/channels/chan_misdn.c
@@ -7738,7 +7738,9 @@ static void update_name(struct ast_channel *tmp, int port, int c)
snprintf(newname, sizeof(newname), "%s/%d-", misdn_type, chan_offset + c);
if (strncmp(tmp->name, newname, strlen(newname))) {
snprintf(newname, sizeof(newname), "%s/%d-u%d", misdn_type, chan_offset + c, glob_channel++);
+ ast_channel_lock(tmp);
ast_change_name(tmp, newname);
+ ast_channel_unlock(tmp);
chan_misdn_log(3, port, " --> updating channel name to [%s]\n", tmp->name);
}
}
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 292d4cd4c..c20ca70f5 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -10347,12 +10347,21 @@ static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
return 0;
}
-static int find_calling_channel(struct ast_channel *c, void *data) {
+static int find_calling_channel(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *c = obj;
struct sip_pvt *p = data;
+ int res;
+
+ ast_channel_lock(c);
- return (c->pbx &&
+ res = (c->pbx &&
(!strcasecmp(c->macroexten, p->exten) || !strcasecmp(c->exten, p->exten)) &&
(sip_cfg.notifycid == IGNORE_CONTEXT || !strcasecmp(c->context, p->context)));
+
+ ast_channel_unlock(c);
+
+ return res ? CMP_MATCH | CMP_STOP : 0;
}
/*! \brief Builds XML portion of state NOTIFY messages */
@@ -10471,15 +10480,16 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
callee must be dialing the same extension that is being monitored. Simply dialing
the hint'd device is not sufficient. */
if (sip_cfg.notifycid) {
- struct ast_channel *caller = ast_channel_search_locked(find_calling_channel, p);
+ struct ast_channel *caller;
- if (caller) {
+ if ((caller = ast_channel_callback(find_calling_channel, NULL, p, 0))) {
int need = strlen(caller->cid.cid_num) + strlen(p->fromdomain) + sizeof("sip:@");
local_target = alloca(need);
+ ast_channel_lock(caller);
snprintf(local_target, need, "sip:%s@%s", caller->cid.cid_num, p->fromdomain);
local_display = ast_strdupa(caller->cid.cid_name);
ast_channel_unlock(caller);
- caller = NULL;
+ caller = ast_channel_unref(caller);
}
}
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index 127bfed45..4706daa00 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -4433,8 +4433,8 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state
return NULL;
}
l = sub->parent;
- tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten,
- l->context, l->amaflags, "%s-%08x", l->fullname, (int) (long) sub);
+ tmp = ast_channel_alloc(1, state, l->cid_num, NULL, l->accountcode, l->exten,
+ l->context, l->amaflags, "%s@%s-%d", l->name, l->parent->name, sub->subtype);
if (unistimdebug)
ast_verb(0, "unistim_new sub=%d (%p) chan=%p\n", sub->subtype, sub, tmp);
if (!tmp) {
@@ -4449,8 +4449,6 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state
if (unistimdebug)
ast_verb(0, "Best codec = %d from nativeformats %d (line cap=%d global=%d)\n", fmt,
tmp->nativeformats, l->capability, CAPABILITY);
- ast_string_field_build(tmp, name, "USTM/%s@%s-%d", l->name, l->parent->name,
- sub->subtype);
if ((sub->rtp) && (sub->subtype == 0)) {
if (unistimdebug)
ast_verb(0, "New unistim channel with a previous rtp handle ?\n");
diff --git a/funcs/func_channel.c b/funcs/func_channel.c
index 5cf262154..bd6eaae42 100644
--- a/funcs/func_channel.c
+++ b/funcs/func_channel.c
@@ -352,7 +352,8 @@ static int func_channels_read(struct ast_channel *chan, const char *function, ch
regex_t re;
int res;
size_t buflen = 0;
-
+ struct ast_channel_iterator *iter;
+
buf[0] = '\0';
if (!ast_strlen_zero(data)) {
@@ -363,7 +364,15 @@ static int func_channels_read(struct ast_channel *chan, const char *function, ch
}
}
- for (c = ast_channel_walk_locked(NULL); c; ast_channel_unlock(c), c = ast_channel_walk_locked(c)) {
+ if (!(iter = ast_channel_iterator_all_new(0))) {
+ if (!ast_strlen_zero(data)) {
+ regfree(&re);
+ }
+ return -1;
+ }
+
+ while ((c = ast_channel_iterator_next(iter))) {
+ ast_channel_lock(c);
if (ast_strlen_zero(data) || regexec(&re, c->name, 0, NULL, 0) == 0) {
size_t namelen = strlen(c->name);
if (buflen + namelen + (ast_strlen_zero(buf) ? 0 : 1) + 1 < maxlen) {
@@ -377,8 +386,12 @@ static int func_channels_read(struct ast_channel *chan, const char *function, ch
ast_log(LOG_WARNING, "Number of channels exceeds the available buffer space. Output will be truncated!\n");
}
}
+ ast_channel_unlock(c);
+ c = ast_channel_unref(c);
}
+ ast_channel_iterator_destroy(iter);
+
if (!ast_strlen_zero(data)) {
regfree(&re);
}
diff --git a/funcs/func_global.c b/funcs/func_global.c
index 74df1757c..a501aee58 100644
--- a/funcs/func_global.c
+++ b/funcs/func_global.c
@@ -134,6 +134,7 @@ static int shared_read(struct ast_channel *chan, const char *cmd, char *data, ch
AST_APP_ARG(var);
AST_APP_ARG(chan);
);
+ struct ast_channel *c_ref = NULL;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
@@ -145,15 +146,20 @@ static int shared_read(struct ast_channel *chan, const char *cmd, char *data, ch
if (!ast_strlen_zero(args.chan)) {
char *prefix = alloca(strlen(args.chan) + 2);
sprintf(prefix, "%s-", args.chan);
- if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) {
+ if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' will be blank.\n", args.chan, args.var);
return -1;
}
- } else
- ast_channel_lock(chan);
+ chan = c_ref;
+ }
+
+ ast_channel_lock(chan);
if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
ast_channel_unlock(chan);
+ if (c_ref) {
+ c_ref = ast_channel_unref(c_ref);
+ }
return -1;
}
@@ -170,6 +176,10 @@ static int shared_read(struct ast_channel *chan, const char *cmd, char *data, ch
ast_channel_unlock(chan);
+ if (c_ref) {
+ c_ref = ast_channel_unref(c_ref);
+ }
+
return 0;
}
@@ -182,6 +192,7 @@ static int shared_write(struct ast_channel *chan, const char *cmd, char *data, c
AST_APP_ARG(var);
AST_APP_ARG(chan);
);
+ struct ast_channel *c_ref = NULL;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
@@ -193,17 +204,22 @@ static int shared_write(struct ast_channel *chan, const char *cmd, char *data, c
if (!ast_strlen_zero(args.chan)) {
char *prefix = alloca(strlen(args.chan) + 2);
sprintf(prefix, "%s-", args.chan);
- if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) {
+ if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' not set to '%s'.\n", args.chan, args.var, value);
return -1;
}
- } else
- ast_channel_lock(chan);
+ chan = c_ref;
+ }
+
+ ast_channel_lock(chan);
if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
if (!(varstore = ast_datastore_alloc(&shared_variable_info, NULL))) {
ast_log(LOG_ERROR, "Unable to allocate new datastore. Shared variable not set.\n");
ast_channel_unlock(chan);
+ if (c_ref) {
+ c_ref = ast_channel_unref(c_ref);
+ }
return -1;
}
@@ -211,6 +227,9 @@ static int shared_write(struct ast_channel *chan, const char *cmd, char *data, c
ast_log(LOG_ERROR, "Unable to allocate variable structure. Shared variable not set.\n");
ast_datastore_free(varstore);
ast_channel_unlock(chan);
+ if (c_ref) {
+ c_ref = ast_channel_unref(c_ref);
+ }
return -1;
}
@@ -241,6 +260,10 @@ static int shared_write(struct ast_channel *chan, const char *cmd, char *data, c
ast_channel_unlock(chan);
+ if (c_ref) {
+ c_ref = ast_channel_unref(c_ref);
+ }
+
return 0;
}
diff --git a/funcs/func_logic.c b/funcs/func_logic.c
index cd4f18e9b..6ec3a0847 100644
--- a/funcs/func_logic.c
+++ b/funcs/func_logic.c
@@ -230,18 +230,22 @@ static int acf_import(struct ast_channel *chan, const char *cmd, char *data, cha
AST_APP_ARG(varname);
);
AST_STANDARD_APP_ARGS(args, data);
- buf[0] = 0;
+
+ buf[0] = '\0';
+
if (!ast_strlen_zero(args.varname)) {
- struct ast_channel *chan2 = ast_get_channel_by_name_locked(args.channel);
- if (chan2) {
+ struct ast_channel *chan2;
+
+ if ((chan2 = ast_channel_get_by_name(args.channel))) {
char *s = alloca(strlen(args.varname) + 4);
- if (s) {
- sprintf(s, "${%s}", args.varname);
- pbx_substitute_variables_helper(chan2, s, buf, len);
- }
+ sprintf(s, "${%s}", args.varname);
+ ast_channel_lock(chan2);
+ pbx_substitute_variables_helper(chan2, s, buf, len);
ast_channel_unlock(chan2);
+ chan2 = ast_channel_unref(chan2);
}
}
+
return 0;
}
diff --git a/funcs/func_odbc.c b/funcs/func_odbc.c
index 385144e86..b754d3b7f 100644
--- a/funcs/func_odbc.c
+++ b/funcs/func_odbc.c
@@ -257,7 +257,7 @@ static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, co
if (chan)
ast_autoservice_stop(chan);
if (bogus_chan) {
- ast_channel_free(chan);
+ ast_channel_release(chan);
} else {
pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
}
@@ -367,7 +367,7 @@ static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, co
if (chan)
ast_autoservice_stop(chan);
if (bogus_chan)
- ast_channel_free(chan);
+ ast_channel_release(chan);
return 0;
}
@@ -473,7 +473,7 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
ast_autoservice_stop(chan);
}
if (bogus_chan) {
- ast_channel_free(chan);
+ ast_channel_release(chan);
}
return -1;
}
@@ -490,7 +490,7 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
ast_autoservice_stop(chan);
}
if (bogus_chan) {
- ast_channel_free(chan);
+ ast_channel_release(chan);
}
return -1;
}
@@ -517,7 +517,7 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
if (chan)
ast_autoservice_stop(chan);
if (bogus_chan)
- ast_channel_free(chan);
+ ast_channel_release(chan);
return res1;
}
@@ -560,7 +560,7 @@ static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, cha
if (chan)
ast_autoservice_stop(chan);
if (bogus_chan)
- ast_channel_free(chan);
+ ast_channel_release(chan);
return -1;
}
resultset = tmp;
@@ -656,7 +656,7 @@ end_acf_read:
if (chan)
ast_autoservice_stop(chan);
if (bogus_chan)
- ast_channel_free(chan);
+ ast_channel_release(chan);
return -1;
}
odbc_store->data = resultset;
@@ -669,7 +669,7 @@ end_acf_read:
if (chan)
ast_autoservice_stop(chan);
if (bogus_chan)
- ast_channel_free(chan);
+ ast_channel_release(chan);
return 0;
}
@@ -1059,7 +1059,7 @@ static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args
}
ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
- ast_channel_free(chan);
+ chan = ast_channel_release(chan);
if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
/* Execute the query */
@@ -1272,7 +1272,7 @@ static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
- ast_channel_free(chan);
+ chan = ast_channel_release(chan);
if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
/* Execute the query */
diff --git a/include/asterisk/autochan.h b/include/asterisk/autochan.h
new file mode 100644
index 000000000..2ace4f2c2
--- /dev/null
+++ b/include/asterisk/autochan.h
@@ -0,0 +1,112 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * 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 "smart" channels that update automatically if a channel is masqueraded
+ *
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#include "asterisk.h"
+#include "asterisk/linkedlists.h"
+
+#ifndef _ASTERISK_AUTOCHAN_H
+#define _ASTERISK_AUTOCHAN_H
+
+struct ast_autochan {
+ struct ast_channel *chan;
+ AST_LIST_ENTRY(ast_autochan) list;
+};
+
+/*!
+ * \par Just what the $!@# is an autochan?
+ *
+ * An ast_autochan is a structure which contains an ast_channel. The pointer
+ * inside an autochan has the ability to update itself if the channel it points
+ * to is masqueraded into a different channel.
+ *
+ * This has a great benefit for any application or service which creates a thread
+ * outside of the channel's main operating thread which keeps a pointer to said
+ * channel. when a masquerade occurs on the channel, the autochan's chan pointer
+ * will automatically update to point to the new channel.
+ *
+ * Some rules for autochans
+ *
+ * 1. If you are going to use an autochan, then be sure to always refer to the
+ * channel using the chan pointer inside the autochan if possible, since this is
+ * the pointer that will be updated during a masquerade.
+ *
+ * 2. If you are going to save off a pointer to the autochan's chan, then be sure
+ * to save off the pointer using ast_channel_ref and to unref the channel when you
+ * are finished with the pointer. If you do not do this and a masquerade occurs on
+ * the channel, then it is possible that your saved pointer will become invalid.
+ */
+
+/*!
+ * \brief set up a new ast_autochan structure
+ *
+ * \details
+ * Allocates and initializes an ast_autochan, sets the
+ * autochan's chan pointer to point to the chan parameter, and
+ * adds the autochan to the global list of autochans. The newly-
+ * created autochan is returned to the caller. This function will
+ * cause the refcount of chan to increase by 1.
+ *
+ * \param chan The channel our new autochan will point to
+ *
+ * \note autochans must be freed using ast_autochan_destroy
+ *
+ * \retval NULL Failure
+ * \retval non-NULL success
+ */
+struct ast_autochan *ast_autochan_setup(struct ast_channel *chan);
+
+/*!
+ * \brief destroy an ast_autochan structure
+ *
+ * \details
+ * Removes the passed-in autochan from the list of autochans and
+ * unrefs the channel that is pointed to. Also frees the autochan
+ * struct itself. This function will unref the channel reference
+ * which was made in ast_autochan_setup
+ *
+ * \param autochan The autochan that you wish to destroy
+ *
+ * \retval void
+ */
+void ast_autochan_destroy(struct ast_autochan *autochan);
+
+/*!
+ * \brief Switch what channel autochans point to
+ *
+ * \details
+ * Traverses the list of autochans. All autochans which point to
+ * old_chan will be updated to point to new_chan instead. Currently
+ * this is only called from ast_do_masquerade in channel.c.
+ *
+ * \pre Both channels must be locked before calling this function.
+ *
+ * \param old_chan The channel that autochans may currently point to
+ * \param new_chan The channel that we want to point those autochans to now
+ *
+ * \retval void
+ */
+void ast_autochan_new_channel(struct ast_channel *old_chan, struct ast_channel *new_chan);
+
+#endif /* _ASTERISK_AUTOCHAN_H */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index cdf34793e..8dc18e569 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -124,6 +124,7 @@ References:
#define _ASTERISK_CHANNEL_H
#include "asterisk/abstract_jb.h"
+#include "asterisk/astobj2.h"
#include "asterisk/poll-compat.h"
@@ -510,8 +511,58 @@ enum ast_t38_state {
};
/*!
+ * \page AstChannel ast_channel locking and reference tracking
+ *
+ * \par Creating Channels
+ * A channel is allocated using the ast_channel_alloc() function. When created, it is
+ * automatically inserted into the main channels hash table that keeps track of all
+ * active channels in the system. The hash key is based on the channel name. Because
+ * of this, if you want to change the name, you _must_ use ast_change_name(), not change
+ * the name field directly. When ast_channel_alloc() returns a channel pointer, you now
+ * hold a reference to that channel. In most cases this reference is given to ast_pbx_run().
+ *
+ * \par Channel Locking
+ * There is a lock associated with every ast_channel. It is allocated internally via astobj2.
+ * To lock or unlock a channel, you must use the ast_channel_lock() wrappers.
+ *
+ * Previously, before ast_channel was converted to astobj2, the channel lock was used in some
+ * additional ways that are no longer necessary. Before, the only way to ensure that a channel
+ * did not disappear out from under you if you were working with a channel outside of the channel
+ * thread that owns it, was to hold the channel lock. Now, that is no longer necessary.
+ * You simply must hold a reference to the channel to ensure it does not go away.
+ *
+ * The channel must be locked if you need to ensure that data that you reading from the channel
+ * does not change while you access it. Further, you must hold the channel lock if you are
+ * making a non-atomic change to channel data.
+ *
+ * \par Channel References
+ * There are multiple ways to get a reference to a channel. The first is that you hold a reference
+ * to a channel after creating it. The other ways involve using the channel search or the channel
+ * traversal APIs. These functions are the ast_channel_get_*() functions or ast_channel_iterator_*()
+ * functions. Once a reference is retrieved by one of these methods, you know that the channel will
+ * not go away. So, the channel should only get locked as needed for data access or modification.
+ * But, make sure that the reference gets released when you are done with it!
+ *
+ * There are different things you can do when you are done with a reference to a channel. The first
+ * is to simply release the reference using ast_channel_unref(). The other option is to call
+ * ast_channel_release(). This function is generally used where ast_channel_free() was used in
+ * the past. The release function releases a reference as well as ensures that the channel is no
+ * longer in the global channels container. That way, the channel will get destroyed as soon as any
+ * other pending references get released.
+ *
+ * \par Exceptions to the rules
+ * Even though ast_channel is reference counted, there are some places where pointers to an ast_channel
+ * get stored, but the reference count does not reflect it. The reason is mostly historical.
+ * The only places where this happens should be places where because of how the code works, we
+ * _know_ that the pointer to the channel will get removed before the channel goes away. The main
+ * example of this is in channel drivers. Channel drivers generally store a pointer to their owner
+ * ast_channel in their technology specific pvt struct. In this case, the channel drivers _know_
+ * that this pointer to the channel will be removed in time, because the channel's hangup callback
+ * gets called before the channel goes away.
+ */
+
+/*!
* \brief Main Channel structure associated with a channel.
- * This is the side of it mostly used by the pbx and call management.
*
* \note XXX It is important to remember to increment .cleancount each time
* this structure is changed. XXX
@@ -568,7 +619,6 @@ struct ast_channel {
struct timeval whentohangup; /*!< Non-zero, set to actual time when channel is to be hung up */
pthread_t blocker; /*!< If anyone is blocking, this is them */
- ast_mutex_t lock_dont_use; /*!< Lock a channel for some operations. See ast_channel_lock() */
/*!
* \brief Channel Caller ID information.
@@ -601,6 +651,7 @@ struct ast_channel {
struct ast_jb jb; /*!< The jitterbuffer state */
struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */
AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores; /*!< Data stores on the channel */
+ AST_LIST_HEAD_NOLOCK(autochans, ast_autochan) autochans; /*!< Autochans on the channel */
unsigned long insmpl; /*!< Track the read/written samples for monitor use */
unsigned long outsmpl; /*!< Track the read/written samples for monitor use */
@@ -642,13 +693,8 @@ struct ast_channel {
unsigned short transfercapability; /*!< ISDN Transfer Capability - AST_FLAG_DIGITAL is not enough */
- union {
- char unused_old_dtmfq[AST_MAX_EXTENSION]; /*!< (deprecated, use readq instead) Any/all queued DTMF characters */
- struct {
- struct ast_bridge *bridge; /*!< Bridge this channel is participating in */
- struct ast_timer *timer; /*!< timer object that provided timingfd */
- };
- };
+ struct ast_bridge *bridge; /*!< Bridge this channel is participating in */
+ struct ast_timer *timer; /*!< timer object that provided timingfd */
char context[AST_MAX_CONTEXT]; /*!< Dialplan: Current extension context */
char exten[AST_MAX_EXTENSION]; /*!< Dialplan: Current extension number */
@@ -951,12 +997,24 @@ int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type
/*!
* \brief Change channel name
*
- * \note The channel must be locked before calling this function.
+ * \pre The channel must be locked before calling this function.
+ *
+ * \param chan the channel to change the name of
+ * \param newname the name to change to
+ *
+ * \return nothing
*/
-void ast_change_name(struct ast_channel *chan, char *newname);
+void ast_change_name(struct ast_channel *chan, const char *newname);
-/*! \brief Free a channel structure */
-void ast_channel_free(struct ast_channel *);
+/*!
+ * \brief Unlink and release reference to a channel
+ *
+ * This function will unlink the channel from the global channels container
+ * if it is still there and also release the current reference to the channel.
+ *
+ * \return NULL, convenient for clearing invalid pointers
+ */
+struct ast_channel *ast_channel_release(struct ast_channel *chan);
/*!
* \brief Requests a channel
@@ -1106,6 +1164,8 @@ int ast_softhangup_nolock(struct ast_channel *chan, int cause);
*/
int ast_check_hangup(struct ast_channel *chan);
+int ast_check_hangup_locked(struct ast_channel *chan);
+
/*!
* \brief Compare a offset with the settings of when to hang a channel up
* \param chan channel on which to check for hang up
@@ -1470,44 +1530,6 @@ int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duratio
char *ast_recvtext(struct ast_channel *chan, int timeout);
/*!
- * \brief Browse channels in use
- * Browse the channels currently in use
- * \param prev where you want to start in the channel list
- * \return Returns the next channel in the list, NULL on end.
- * If it returns a channel, that channel *has been locked*!
- */
-struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev);
-
-/*! \brief Get channel by name or uniqueid (locks channel) */
-struct ast_channel *ast_get_channel_by_name_locked(const char *chan);
-
-/*! \brief Get channel by name or uniqueid prefix (locks channel) */
-struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen);
-
-/*! \brief Get channel by name or uniqueid prefix (locks channel) */
-struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name, const int namelen);
-
-/*! \brief Get channel by exten (and optionally context) and lock it */
-struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context);
-
-/*! \brief Get next channel by exten (and optionally context) and lock it */
-struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
- const char *context);
-
-/*!
- * \brief Search for a channel based on the passed channel matching callback
- * Search for a channel based on the specified is_match callback, and return the
- * first channel that we match. When returned, the channel will be locked. Note
- * that the is_match callback is called with the passed channel locked, and should
- * return 0 if there is no match, and non-zero if there is.
- * \param is_match callback executed on each channel until non-zero is returned, or we
- * run out of channels to search.
- * \param data data passed to the is_match callback during each invocation.
- * \return Returns the matched channel, or NULL if no channel was matched.
- */
-struct ast_channel *ast_channel_search_locked(int (*is_match)(struct ast_channel *, void *), void *data);
-
-/*!
* \brief Waits for a digit
* \param c channel to wait for a digit on
* \param ms how many milliseconds to wait
@@ -1808,8 +1830,22 @@ int ast_do_masquerade(struct ast_channel *chan);
/*!
* \brief Find bridged channel
+ *
+ * \note This function does _not_ return a reference to the bridged channel.
+ * The reason for this is mostly historical. It _should_ return a reference,
+ * but it will take a lot of work to make the code base account for that.
+ * So, for now, the old rules still apply for how to handle this function.
+ * If this function is being used from the channel thread that owns the channel,
+ * then a reference is already held, and channel locking is not required to
+ * guarantee that the channel will stay around. If this function is used
+ * outside of the associated channel thread, the channel parameter 'chan'
+ * MUST be locked before calling this function. Also, 'chan' must remain locked
+ * for the entire time that the result of this function is being used.
+ *
* \param chan Current channel
- */
+ *
+ * \return A pointer to the bridged channel
+*/
struct ast_channel *ast_bridged_channel(struct ast_channel *chan);
/*!
@@ -2018,6 +2054,163 @@ struct ast_group_info {
AST_LIST_ENTRY(ast_group_info) group_list;
};
+#define ast_channel_lock(chan) ao2_lock(chan)
+#define ast_channel_unlock(chan) ao2_unlock(chan)
+#define ast_channel_trylock(chan) ao2_trylock(chan)
+
+/*!
+ * \brief Lock two channels.
+ */
+#define ast_channel_lock_both(chan1, chan2) do { \
+ ast_channel_lock(chan1); \
+ while (ast_channel_trylock(chan2)) { \
+ ast_channel_unlock(chan1); \
+ sched_yield(); \
+ ast_channel_lock(chan1); \
+ } \
+ } while (0)
+
+#define ast_channel_ref(c) ({ ao2_ref(c, +1); (c); })
+#define ast_channel_unref(c) ({ ao2_ref(c, -1); (NULL); })
+
+/*! Channel Iterating @{ */
+
+/*!
+ * \brief A channel iterator
+ *
+ * This is an opaque type.
+ */
+struct ast_channel_iterator;
+
+/*!
+ * \brief Destroy a channel iterator
+ *
+ * \arg i the itereator to destroy
+ *
+ * This function is used to destroy a channel iterator that was retrieved by
+ * using one of the channel_iterator_new() functions.
+ *
+ * \return NULL, for convenience to clear out the pointer to the iterator that
+ * was just destroyed.
+ */
+struct ast_channel_iterator *ast_channel_iterator_destroy(struct ast_channel_iterator *i);
+
+/*!
+ * \brief Create a new channel iterator based on extension
+ *
+ * \arg ao2_flags astobj2 iterator flags
+ * \arg exten The extension that channels must be in
+ * \arg context The context that channels must be in (optional)
+ *
+ * After creating an iterator using this function, the ast_channel_iterator_next()
+ * function can be used to iterate through all channels that are currently
+ * in the specified context and extension.
+ *
+ * \retval NULL on failure
+ * \retval a new channel iterator based on the specified parameters
+ */
+struct ast_channel_iterator *ast_channel_iterator_by_exten_new(int ao2_flags, const char *exten,
+ const char *context);
+
+/*!
+ * \brief Create a new channel iterator based on name
+ *
+ * \arg ao2_flags astobj2 iterator flags
+ * \arg name channel name or channel uniqueid to match
+ * \arg name_len number of characters in the channel name to match on. This
+ * would be used to match based on name prefix. If matching on the full
+ * channel name is desired, then this parameter should be 0.
+ *
+ * After creating an iterator using this function, the ast_channel_iterator_next()
+ * function can be used to iterate through all channels that exist that have
+ * the specified name or name prefix.
+ *
+ * \retval NULL on failure
+ * \retval a new channel iterator based on the specified parameters
+ */
+struct ast_channel_iterator *ast_channel_iterator_by_name_new(int ao2_flags, const char *name,
+ size_t name_len);
+
+/*!
+ * \brief Create a new channel iterator
+ *
+ * \arg ao2_flags astobj2 iterator flags
+ *
+ * After creating an iterator using this function, the ast_channel_iterator_next()
+ * function can be used to iterate through all channels that exist.
+ *
+ * \retval NULL on failure
+ * \retval a new channel iterator
+ */
+struct ast_channel_iterator *ast_channel_iterator_all_new(int ao2_flags);
+
+/*!
+ * \brief Get the next channel for a channel iterator
+ *
+ * \arg i the channel iterator that was created using one of the
+ * channel_iterator_new() functions.
+ *
+ * This function should be used to iterate through all channels that match a
+ * specified set of parameters that were provided when the iterator was created.
+ *
+ * \retval the next channel that matches the parameters used when the iterator
+ * was created.
+ * \retval NULL, if no more channels match the iterator parameters.
+ */
+struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i);
+
+/*! @} End channel iterator definitions. */
+
+/*!
+ * \brief Call a function with every active channel
+ *
+ * This function executes a callback one time for each active channel on the
+ * system. The channel is provided as an argument to the function.
+ */
+struct ast_channel *ast_channel_callback(ao2_callback_data_fn *cb_fn, void *arg,
+ void *data, int ao2_flags);
+
+/*! @{ Channel search functions */
+
+/*!
+ * \brief Find a channel by name
+ *
+ * \arg name the name or uniqueid of the channel to search for
+ *
+ * Find a channel that has the same name as the provided argument.
+ *
+ * \retval a channel with the name specified by the argument
+ * \retval NULL if no channel was found
+ */
+struct ast_channel *ast_channel_get_by_name(const char *name);
+
+/*!
+ * \brief Find a channel by a name prefix
+ *
+ * \arg name The channel name or uniqueid prefix to search for
+ * \arg name_len Only search for up to this many characters from the name
+ *
+ * Find a channel that has the same name prefix as specified by the arguments.
+ *
+ * \retval a channel with the name prefix specified by the arguments
+ * \retval NULL if no channel was found
+ */
+struct ast_channel *ast_channel_get_by_name_prefix(const char *name, size_t name_len);
+
+/*!
+ * \brief Find a channel by extension and context
+ *
+ * \arg exten the extension to search for
+ * \arg context the context to search for (optional)
+ *
+ * Return a channel that is currently at the specified extension and context.
+ *
+ * \retval a channel that is at the specified extension and context
+ * \retval NULL if no channel was found
+ */
+struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *context);
+
+/*! @} End channel search functions. */
/*!
* \since 1.6.3
diff --git a/include/asterisk/lock.h b/include/asterisk/lock.h
index 1bd300e40..a23f0344c 100644
--- a/include/asterisk/lock.h
+++ b/include/asterisk/lock.h
@@ -58,7 +58,9 @@
#ifndef HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK
#include "asterisk/time.h"
#endif
+
#include "asterisk/logger.h"
+#include "asterisk/astobj2.h"
/* internal macro to profile mutexes. Only computes the delay on
* non-blocking calls.
@@ -272,13 +274,13 @@ int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, in
do { \
char __filename[80], __func[80], __mutex_name[80]; \
int __lineno; \
- int __res = ast_find_lock_info(&chan->lock_dont_use, __filename, sizeof(__filename), &__lineno, __func, sizeof(__func), __mutex_name, sizeof(__mutex_name)); \
+ int __res = ast_find_lock_info(ao2_object_get_lockaddr(chan), __filename, sizeof(__filename), &__lineno, __func, sizeof(__func), __mutex_name, sizeof(__mutex_name)); \
ast_channel_unlock(chan); \
usleep(1); \
if (__res < 0) { /* Shouldn't ever happen, but just in case... */ \
ast_channel_lock(chan); \
} else { \
- __ast_pthread_mutex_lock(__filename, __lineno, __func, __mutex_name, &chan->lock_dont_use); \
+ __ao2_lock(chan, __filename, __func, __lineno, __mutex_name); \
} \
} while (0)
@@ -2024,33 +2026,4 @@ AST_INLINE_API(int ast_atomic_dec_and_test(volatile int *p),
})
#endif
-#ifndef DEBUG_CHANNEL_LOCKS
-/*! \brief Lock a channel. If DEBUG_CHANNEL_LOCKS is defined
- in the Makefile, print relevant output for debugging */
-#define ast_channel_lock(x) ast_mutex_lock(&x->lock_dont_use)
-/*! \brief Unlock a channel. If DEBUG_CHANNEL_LOCKS is defined
- in the Makefile, print relevant output for debugging */
-#define ast_channel_unlock(x) ast_mutex_unlock(&x->lock_dont_use)
-/*! \brief Try locking a channel. If DEBUG_CHANNEL_LOCKS is defined
- in the Makefile, print relevant output for debugging */
-#define ast_channel_trylock(x) ast_mutex_trylock(&x->lock_dont_use)
-#else
-
-#define ast_channel_lock(a) __ast_channel_lock(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/*! \brief Lock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int __ast_channel_lock(struct ast_channel *chan, const char *file, int lineno, const char *func);
-
-#define ast_channel_unlock(a) __ast_channel_unlock(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/*! \brief Unlock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function
-*/
-int __ast_channel_unlock(struct ast_channel *chan, const char *file, int lineno, const char *func);
-
-#define ast_channel_trylock(a) __ast_channel_trylock(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
-/*! \brief Lock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int __ast_channel_trylock(struct ast_channel *chan, const char *file, int lineno, const char *func);
-#endif
-
#endif /* _ASTERISK_LOCK_H */
diff --git a/main/Makefile b/main/Makefile
index 183199626..e5225d68f 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -29,7 +29,7 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
astobj2.o hashtab.o global_datastores.o version.o \
features.o taskprocessor.o timing.o datastore.o xml.o xmldoc.o \
- strings.o bridging.o poll.o rtp_engine.o stun.o
+ strings.o bridging.o poll.o rtp_engine.o stun.o autochan.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static
diff --git a/main/autochan.c b/main/autochan.c
new file mode 100644
index 000000000..4ad65a371
--- /dev/null
+++ b/main/autochan.c
@@ -0,0 +1,94 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * 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 "smart" channels
+ *
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/autochan.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+
+struct ast_autochan *ast_autochan_setup(struct ast_channel *chan)
+{
+ struct ast_autochan *autochan;
+
+ if (!chan) {
+ return NULL;
+ }
+
+ if (!(autochan = ast_calloc(1, sizeof(*autochan)))) {
+ return NULL;
+ }
+
+ autochan->chan = ast_channel_ref(chan);
+
+ ast_channel_lock(autochan->chan);
+ AST_LIST_INSERT_TAIL(&autochan->chan->autochans, autochan, list);
+ ast_channel_unlock(autochan->chan);
+
+ ast_debug(1, "Created autochan %p to hold channel %s (%p)\n", autochan, chan->name, chan);
+
+ return autochan;
+}
+
+void ast_autochan_destroy(struct ast_autochan *autochan)
+{
+ struct ast_autochan *autochan_iter;
+
+ ast_channel_lock(autochan->chan);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&autochan->chan->autochans, autochan_iter, list) {
+ if (autochan_iter == autochan) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_debug(1, "Removed autochan %p from the list, about to free it\n", autochan);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ ast_channel_unlock(autochan->chan);
+
+ autochan->chan = ast_channel_unref(autochan->chan);
+
+ ast_free(autochan);
+}
+
+void ast_autochan_new_channel(struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+ struct ast_autochan *autochan;
+
+ AST_LIST_APPEND_LIST(&new_chan->autochans, &old_chan->autochans, list);
+
+ AST_LIST_TRAVERSE(&new_chan->autochans, autochan, list) {
+ if (autochan->chan == old_chan) {
+ autochan->chan = ast_channel_unref(old_chan);
+ autochan->chan = ast_channel_ref(new_chan);
+
+ ast_debug(1, "Autochan %p used to hold channel %s (%p) but now holds channel %s (%p)\n",
+ autochan, old_chan->name, old_chan, new_chan->name, new_chan);
+ }
+ }
+}
diff --git a/main/channel.c b/main/channel.c
index 7325bea24..d85f4d7de 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/slinfactory.h"
#include "asterisk/audiohook.h"
#include "asterisk/timing.h"
+#include "asterisk/autochan.h"
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
@@ -121,11 +122,16 @@ struct ast_chan_trace {
#endif
/*! \brief the list of registered channel types */
-static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist);
+static AST_RWLIST_HEAD_STATIC(backends, chanlist);
-/*! \brief the list of channels we have. Note that the lock for this list is used for
- both the channels list and the backends list. */
-static AST_RWLIST_HEAD_STATIC(channels, ast_channel);
+#ifdef LOW_MEMORY
+#define NUM_CHANNEL_BUCKETS 61
+#else
+#define NUM_CHANNEL_BUCKETS 1567
+#endif
+
+/*! \brief All active channels on the system */
+static struct ao2_container *channels;
/*! \brief map AST_CAUSE's to readable string representations
*
@@ -185,8 +191,10 @@ static const struct {
struct ast_variable *ast_channeltype_list(void)
{
struct chanlist *cl;
- struct ast_variable *var=NULL, *prev = NULL;
- AST_LIST_TRAVERSE(&backends, cl, list) {
+ struct ast_variable *var = NULL, *prev = NULL;
+
+ AST_RWLIST_RDLOCK(&backends);
+ AST_RWLIST_TRAVERSE(&backends, cl, list) {
if (prev) {
if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description, "")))
prev = prev->next;
@@ -195,6 +203,8 @@ struct ast_variable *ast_channeltype_list(void)
prev = var;
}
}
+ AST_RWLIST_UNLOCK(&backends);
+
return var;
}
@@ -223,17 +233,15 @@ static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd,
ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
ast_cli(a->fd, FORMAT, "----------", "-----------", "-----------", "-----------", "--------");
- AST_RWLIST_RDLOCK(&channels);
-
- AST_LIST_TRAVERSE(&backends, cl, list) {
+ AST_RWLIST_RDLOCK(&backends);
+ AST_RWLIST_TRAVERSE(&backends, cl, list) {
ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
(cl->tech->devicestate) ? "yes" : "no",
(cl->tech->indicate) ? "yes" : "no",
(cl->tech->transfer) ? "yes" : "no");
count_chan++;
}
-
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
ast_cli(a->fd, "----------\n%d channel drivers registered.\n", count_chan);
@@ -254,12 +262,14 @@ static char *complete_channeltypes(struct ast_cli_args *a)
wordlen = strlen(a->word);
- AST_LIST_TRAVERSE(&backends, cl, list) {
+ AST_RWLIST_RDLOCK(&backends);
+ AST_RWLIST_TRAVERSE(&backends, cl, list) {
if (!strncasecmp(a->word, cl->tech->type, wordlen) && ++which > a->n) {
ret = ast_strdup(cl->tech->type);
break;
}
}
+ AST_RWLIST_UNLOCK(&backends);
return ret;
}
@@ -283,9 +293,9 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd,
if (a->argc != 4)
return CLI_SHOWUSAGE;
- AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_RDLOCK(&backends);
- AST_LIST_TRAVERSE(&backends, cl, list) {
+ AST_RWLIST_TRAVERSE(&backends, cl, list) {
if (!strncasecmp(cl->tech->type, a->argv[3], strlen(cl->tech->type)))
break;
}
@@ -293,7 +303,7 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd,
if (!cl) {
ast_cli(a->fd, "\n%s is not a registered channel driver.\n", a->argv[3]);
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
return CLI_FAILURE;
}
@@ -321,7 +331,8 @@ static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd,
);
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
+
return CLI_SUCCESS;
}
@@ -469,7 +480,7 @@ int ast_check_hangup(struct ast_channel *chan)
return 1;
}
-static int ast_check_hangup_locked(struct ast_channel *chan)
+int ast_check_hangup_locked(struct ast_channel *chan)
{
int res;
ast_channel_lock(chan);
@@ -478,30 +489,28 @@ static int ast_check_hangup_locked(struct ast_channel *chan)
return res;
}
-/*! \brief Initiate system shutdown */
+static int ast_channel_softhangup_cb(void *obj, void *arg, int flags)
+{
+ struct ast_channel *chan = obj;
+
+ ast_softhangup(chan, AST_SOFTHANGUP_SHUTDOWN);
+
+ return 0;
+}
+
void ast_begin_shutdown(int hangup)
{
- struct ast_channel *c;
shutting_down = 1;
+
if (hangup) {
- AST_RWLIST_RDLOCK(&channels);
- AST_RWLIST_TRAVERSE(&channels, c, chan_list) {
- ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
- }
- AST_RWLIST_UNLOCK(&channels);
+ ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
}
}
/*! \brief returns number of active/allocated channels */
int ast_active_channels(void)
{
- struct ast_channel *c;
- int cnt = 0;
- AST_RWLIST_RDLOCK(&channels);
- AST_RWLIST_TRAVERSE(&channels, c, chan_list)
- cnt++;
- AST_RWLIST_UNLOCK(&channels);
- return cnt;
+ return channels ? ao2_container_count(channels) : 0;
}
/*! \brief Cancel a shutdown in progress */
@@ -557,28 +566,29 @@ int ast_channel_register(const struct ast_channel_tech *tech)
{
struct chanlist *chan;
- AST_RWLIST_WRLOCK(&channels);
+ AST_RWLIST_WRLOCK(&backends);
- AST_LIST_TRAVERSE(&backends, chan, list) {
+ AST_RWLIST_TRAVERSE(&backends, chan, list) {
if (!strcasecmp(tech->type, chan->tech->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", tech->type);
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
return -1;
}
}
if (!(chan = ast_calloc(1, sizeof(*chan)))) {
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
return -1;
}
chan->tech = tech;
- AST_LIST_INSERT_HEAD(&backends, chan, list);
+ AST_RWLIST_INSERT_HEAD(&backends, chan, list);
ast_debug(1, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
ast_verb(2, "Registered channel type '%s' (%s)\n", chan->tech->type, chan->tech->description);
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
+
return 0;
}
@@ -589,9 +599,9 @@ void ast_channel_unregister(const struct ast_channel_tech *tech)
ast_debug(1, "Unregistering channel type '%s'\n", tech->type);
- AST_RWLIST_WRLOCK(&channels);
+ AST_RWLIST_WRLOCK(&backends);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
if (chan->tech == tech) {
AST_LIST_REMOVE_CURRENT(list);
ast_free(chan);
@@ -601,7 +611,7 @@ void ast_channel_unregister(const struct ast_channel_tech *tech)
}
AST_LIST_TRAVERSE_SAFE_END;
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
}
/*! \brief Get handle to channel driver based on name */
@@ -610,16 +620,16 @@ const struct ast_channel_tech *ast_get_channel_tech(const char *name)
struct chanlist *chanls;
const struct ast_channel_tech *ret = NULL;
- AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_RDLOCK(&backends);
- AST_LIST_TRAVERSE(&backends, chanls, list) {
+ AST_RWLIST_TRAVERSE(&backends, chanls, list) {
if (!strcasecmp(name, chanls->tech->type)) {
ret = chanls->tech;
break;
}
}
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
return ret;
}
@@ -766,6 +776,8 @@ static const struct ast_channel_tech null_tech = {
.description = "Null channel (should not see this)",
};
+static void ast_channel_destructor(void *obj);
+
/*! \brief Create a new channel structure */
struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...)
{
@@ -781,8 +793,9 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_
return NULL;
}
- if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ if (!(tmp = ao2_alloc(sizeof(*tmp), ast_channel_destructor))) {
return NULL;
+ }
if (!(tmp->sched = sched_context_create())) {
ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
@@ -932,17 +945,15 @@ alertpipe_failed:
headp = &tmp->varshead;
AST_LIST_HEAD_INIT_NOLOCK(headp);
- ast_mutex_init(&tmp->lock_dont_use);
-
AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);
+
+ AST_LIST_HEAD_INIT_NOLOCK(&tmp->autochans);
ast_string_field_set(tmp, language, defaultlanguage);
tmp->tech = &null_tech;
- AST_RWLIST_WRLOCK(&channels);
- AST_RWLIST_INSERT_HEAD(&channels, tmp, chan_list);
- AST_RWLIST_UNLOCK(&channels);
+ ao2_link(channels, tmp);
/*\!note
* and now, since the channel structure is built, and has its name, let's
@@ -1121,167 +1132,181 @@ void ast_channel_undefer_dtmf(struct ast_channel *chan)
ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
}
-/*!
- * \brief Helper function to find channels.
- *
- * It supports these modes:
- *
- * prev != NULL : get channel next in list after prev
- * name != NULL : get channel with matching name
- * name != NULL && namelen != 0 : get channel whose name starts with prefix
- * exten != NULL : get channel whose exten or macroexten matches
- * context != NULL && exten != NULL : get channel whose context or macrocontext
- *
- * It returns with the channel's lock held. If getting the individual lock fails,
- * unlock and retry quickly up to 10 times, then give up.
- *
- * \note XXX Note that this code has cost O(N) because of the need to verify
- * that the object is still on the global list.
- *
- * \note XXX also note that accessing fields (e.g. c->name in ast_log())
- * can only be done with the lock held or someone could delete the
- * object while we work on it. This causes some ugliness in the code.
- * Note that removing the first ast_log() may be harmful, as it would
- * shorten the retry period and possibly cause failures.
- * We should definitely go for a better scheme that is deadlock-free.
- */
-static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
- const char *name, const int namelen,
- const char *context, const char *exten)
+struct ast_channel *ast_channel_callback(ao2_callback_data_fn *cb_fn, void *arg,
+ void *data, int ao2_flags)
{
- const char *msg = prev ? "deadlock" : "initial deadlock";
- int retries;
- struct ast_channel *c;
- const struct ast_channel *_prev = prev;
-
- for (retries = 0; retries < 200; retries++) {
- int done;
- /* Reset prev on each retry. See note below for the reason. */
- prev = _prev;
- AST_RWLIST_RDLOCK(&channels);
- AST_RWLIST_TRAVERSE(&channels, c, chan_list) {
- if (prev) { /* look for last item, first, before any evaluation */
- if (c != prev) /* not this one */
- continue;
- /* found, prepare to return c->next */
- if ((c = AST_RWLIST_NEXT(c, chan_list)) == NULL) break;
- /*!\note
- * We're done searching through the list for the previous item.
- * Any item after this point, we want to evaluate for a match.
- * If we didn't set prev to NULL here, then we would only
- * return matches for the first matching item (since the above
- * "if (c != prev)" would not permit any other potential
- * matches to reach the additional matching logic, below).
- * Instead, it would just iterate until it once again found the
- * original match, then iterate down to the end of the list and
- * quit.
- */
- prev = NULL;
- }
- if (name) { /* want match by name */
- if ((!namelen && strcasecmp(c->name, name) && strcmp(c->uniqueid, name)) ||
- (namelen && strncasecmp(c->name, name, namelen)))
- continue; /* name match failed */
- } else if (exten) {
- if (context && strcasecmp(c->context, context) &&
- strcasecmp(c->macrocontext, context))
- continue; /* context match failed */
- if (strcasecmp(c->exten, exten) &&
- strcasecmp(c->macroexten, exten))
- continue; /* exten match failed */
- }
- /* if we get here, c points to the desired record */
- break;
- }
- /* exit if chan not found or mutex acquired successfully */
- /* this is slightly unsafe, as we _should_ hold the lock to access c->name */
- done = c == NULL || ast_channel_trylock(c) == 0;
- if (!done) {
- ast_debug(1, "Avoiding %s for channel '%p'\n", msg, c);
- if (retries == 199) {
- /* We are about to fail due to a deadlock, so report this
- * while we still have the list lock.
- */
- ast_debug(1, "Failure, could not lock '%p' after %d retries!\n", c, retries);
- /* As we have deadlocked, we will skip this channel and
- * see if there is another match.
- * NOTE: No point doing this for a full-name match,
- * as there can be no more matches.
- */
- if (!(name && !namelen)) {
- prev = c;
- retries = -1;
- }
- }
- }
- AST_RWLIST_UNLOCK(&channels);
- if (done)
- return c;
- /* If we reach this point we basically tried to lock a channel and failed. Instead of
- * starting from the beginning of the list we can restore our saved pointer to the previous
- * channel and start from there.
- */
- prev = _prev;
- usleep(1); /* give other threads a chance before retrying */
+ return ao2_callback_data(channels, ao2_flags, cb_fn, arg, data);
+}
+
+struct ast_channel_iterator {
+ struct ao2_iterator i;
+ const char *name;
+ size_t name_len;
+ const char *exten;
+ const char *context;
+};
+
+struct ast_channel_iterator *ast_channel_iterator_destroy(struct ast_channel_iterator *i)
+{
+ if (i->name) {
+ ast_free((void *) i->name);
+ i->name = NULL;
}
+ if (i->exten) {
+ ast_free((void *) i->exten);
+ i->exten = NULL;
+ }
+
+ if (i->context) {
+ ast_free((void *) i->context);
+ i->context = NULL;
+ }
+
+ ast_free(i);
+
return NULL;
}
-/*! \brief Browse channels in use */
-struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
+static struct ast_channel_iterator *ast_channel_iterator_new(int ao2_flags, const char *name,
+ size_t name_len, const char *exten, const char *context)
{
- return channel_find_locked(prev, NULL, 0, NULL, NULL);
+ struct ast_channel_iterator *i;
+
+ if (!(i = ast_calloc(1, sizeof(*i)))) {
+ return NULL;
+ }
+
+ if (!ast_strlen_zero(exten) && !(i->exten = ast_strdup(exten))) {
+ goto return_error;
+ }
+
+ if (!ast_strlen_zero(context) && !(i->context = ast_strdup(context))) {
+ goto return_error;
+ }
+
+ if (!ast_strlen_zero(name) && !(i->name = ast_strdup(name))) {
+ goto return_error;
+ }
+
+ i->name_len = name_len;
+
+ i->i = ao2_iterator_init(channels, ao2_flags);
+
+ return i;
+
+return_error:
+ if (i->exten) {
+ ast_free((void *) i->exten);
+ i->exten = NULL;
+ }
+
+ if (i->context) {
+ ast_free((void *) i->context);
+ i->context = NULL;
+ }
+
+ ast_free(i);
+
+ return NULL;
}
-/*! \brief Get channel by name and lock it */
-struct ast_channel *ast_get_channel_by_name_locked(const char *name)
+struct ast_channel_iterator *ast_channel_iterator_by_exten_new(int ao2_flags, const char *exten,
+ const char *context)
{
- return channel_find_locked(NULL, name, 0, NULL, NULL);
+ return ast_channel_iterator_new(ao2_flags, NULL, 0, exten, context);
}
-/*! \brief Get channel by name prefix and lock it */
-struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen)
+struct ast_channel_iterator *ast_channel_iterator_by_name_new(int ao2_flags, const char *name,
+ size_t name_len)
{
- return channel_find_locked(NULL, name, namelen, NULL, NULL);
+ return ast_channel_iterator_new(ao2_flags, name, name_len, NULL, NULL);
}
-/*! \brief Get next channel by name prefix and lock it */
-struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name,
- const int namelen)
+struct ast_channel_iterator *ast_channel_iterator_all_new(int ao2_flags)
{
- return channel_find_locked(chan, name, namelen, NULL, NULL);
+ return ast_channel_iterator_new(ao2_flags, NULL, 0, NULL, NULL);
}
-/*! \brief Get channel by exten (and optionally context) and lock it */
-struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context)
+/*!
+ * \note This function will be reduced to 1 line of code once ao2 supports
+ * returning multiple objects from an ao2_callback() using OBJ_MULTIPLE.
+ */
+struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i)
{
- return channel_find_locked(NULL, NULL, 0, context, exten);
+ struct ast_channel *chan = NULL;
+
+ for (; (chan = ao2_iterator_next(&i->i));
+ ast_channel_unlock(chan), ast_channel_unref(chan)) {
+
+ ast_channel_lock(chan);
+
+ if (i->name) { /* match by name */
+ if (!i->name_len) {
+ if (strcasecmp(chan->name, i->name) && strcasecmp(chan->uniqueid, i->name)) {
+ continue; /* name match failed */
+ }
+ } else {
+ if (strncasecmp(chan->name, i->name, i->name_len) &&
+ strncasecmp(chan->uniqueid, i->name, i->name_len)) {
+ continue; /* name match failed */
+ }
+ }
+ } else if (i->exten) {
+ if (i->context && strcasecmp(chan->context, i->context) &&
+ strcasecmp(chan->macrocontext, i->context)) {
+ continue; /* context match failed */
+ }
+
+ if (strcasecmp(chan->exten, i->exten) &&
+ strcasecmp(chan->macroexten, i->exten)) {
+ continue; /* exten match failed */
+ }
+ }
+
+ ast_channel_unlock(chan);
+
+ break; /* chan points to the next chan desired. */
+ }
+
+ return chan;
}
-/*! \brief Get next channel by exten (and optionally context) and lock it */
-struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
- const char *context)
+static struct ast_channel *ast_channel_get_full(const char *name, size_t name_len,
+ const char *exten, const char *context)
{
- return channel_find_locked(chan, NULL, 0, context, exten);
+ struct ast_channel tmp_chan = {
+ .name = name,
+ /* This is sort of a hack. Basically, we're using an arbitrary field
+ * in ast_channel to pass the name_len for a prefix match. If this
+ * gets changed, then the compare callback must be changed, too. */
+ .rings = name_len,
+ };
+
+ if (exten) {
+ ast_copy_string(tmp_chan.exten, exten, sizeof(tmp_chan.exten));
+ }
+
+ if (context) {
+ ast_copy_string(tmp_chan.context, context, sizeof(tmp_chan.context));
+ }
+
+ return ao2_find(channels, &tmp_chan, OBJ_POINTER);
}
-/*! \brief Search for a channel based on the passed channel matching callback (first match) and return it, locked */
-struct ast_channel *ast_channel_search_locked(int (*is_match)(struct ast_channel *, void *), void *data)
+struct ast_channel *ast_channel_get_by_name(const char *name)
{
- struct ast_channel *c = NULL;
+ return ast_channel_get_full(name, 0, NULL, NULL);
+}
- AST_RWLIST_RDLOCK(&channels);
- AST_RWLIST_TRAVERSE(&channels, c, chan_list) {
- ast_channel_lock(c);
- if (is_match(c, data)) {
- break;
- }
- ast_channel_unlock(c);
- }
- AST_RWLIST_UNLOCK(&channels);
+struct ast_channel *ast_channel_get_by_name_prefix(const char *name, size_t name_len)
+{
+ return ast_channel_get_full(name, name_len, NULL, NULL);
+}
- return c;
+struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *context)
+{
+ return ast_channel_get_full(NULL, 0, exten, context);
}
/*! \brief Wait, look for hangups and condition arg */
@@ -1326,6 +1351,13 @@ static void free_cid(struct ast_callerid *cid)
cid->cid_dnid = cid->cid_num = cid->cid_name = cid->cid_ani = cid->cid_rdnis = NULL;
}
+struct ast_channel *ast_channel_release(struct ast_channel *chan)
+{
+ /* Safe, even if already unlinked. */
+ ao2_unlink(channels, chan);
+ return ast_channel_unref(chan);
+}
+
/*!
* \internal
* \brief Initialize the given party id structure.
@@ -1600,8 +1632,9 @@ void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
}
/*! \brief Free a channel structure */
-void ast_channel_free(struct ast_channel *chan)
+static void ast_channel_destructor(void *obj)
{
+ struct ast_channel *chan = obj;
int fd;
#ifdef HAVE_EPOLL
int i;
@@ -1611,18 +1644,8 @@ void ast_channel_free(struct ast_channel *chan)
struct varshead *headp;
struct ast_datastore *datastore = NULL;
char name[AST_CHANNEL_NAME], *dashptr;
-
- headp=&chan->varshead;
-
- AST_RWLIST_WRLOCK(&channels);
- if (!AST_RWLIST_REMOVE(&channels, chan, chan_list)) {
- AST_RWLIST_UNLOCK(&channels);
- ast_log(LOG_ERROR, "Unable to find channel in list to free. Assuming it has already been done.\n");
- }
- /* Lock and unlock the channel just to be sure nobody has it locked still
- due to a reference retrieved from the channel list. */
- ast_channel_lock(chan);
- ast_channel_unlock(chan);
+
+ headp = &chan->varshead;
/* Get rid of each of the data stores on the channel */
while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
@@ -1705,11 +1728,7 @@ void ast_channel_free(struct ast_channel *chan)
chan->zone = ast_tone_zone_unref(chan->zone);
}
- ast_mutex_destroy(&chan->lock_dont_use);
-
ast_string_field_free_memory(chan);
- ast_free(chan);
- AST_RWLIST_UNLOCK(&channels);
/* Queue an unknown state, because, while we know that this particular
* instance is dead, we don't know the state of all other possible
@@ -1991,8 +2010,8 @@ int ast_hangup(struct ast_channel *chan)
ast_cdr_detach(chan->cdr);
chan->cdr = NULL;
}
-
- ast_channel_free(chan);
+
+ chan = ast_channel_release(chan);
return res;
}
@@ -4033,12 +4052,12 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c
cause = &foo;
*cause = AST_CAUSE_NOTDEFINED;
- if (AST_RWLIST_RDLOCK(&channels)) {
- ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ if (AST_RWLIST_RDLOCK(&backends)) {
+ ast_log(LOG_WARNING, "Unable to lock technology backend list\n");
return NULL;
}
- AST_LIST_TRAVERSE(&backends, chan, list) {
+ AST_RWLIST_TRAVERSE(&backends, chan, list) {
if (strcasecmp(type, chan->tech->type))
continue;
@@ -4052,11 +4071,11 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c
if (res < 0) {
ast_log(LOG_WARNING, "No translator path exists for channel type %s (native 0x%x) to 0x%x\n", type, chan->tech->capabilities, format);
*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
return NULL;
}
}
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
if (!chan->tech->requester)
return NULL;
@@ -4069,7 +4088,7 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c
ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
*cause = AST_CAUSE_NOSUCHDRIVER;
- AST_RWLIST_UNLOCK(&channels);
+ AST_RWLIST_UNLOCK(&backends);
return NULL;
}
@@ -4363,10 +4382,16 @@ retrymasq:
return res;
}
-void ast_change_name(struct ast_channel *chan, char *newname)
+void ast_change_name(struct ast_channel *chan, const char *newname)
{
+ /* We must re-link, as the hash value will change here. */
+ ao2_unlink(channels, chan);
+
manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", chan->name, newname, chan->uniqueid);
+
ast_string_field_set(chan, name, newname);
+
+ ao2_link(channels, chan);
}
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
@@ -4462,12 +4487,6 @@ int ast_do_masquerade(struct ast_channel *original)
char masqn[AST_CHANNEL_NAME];
char zombn[AST_CHANNEL_NAME];
- ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
- clonechan->name, clonechan->_state, original->name, original->_state);
-
- manager_event(EVENT_FLAG_CALL, "Masquerade", "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n",
- clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
-
/* XXX This is a seriously wacked out operation. We're essentially putting the guts of
the clone channel into the original channel. Start by killing off the original
channel's backend. I'm not sure we're going to keep this function, because
@@ -4476,34 +4495,33 @@ int ast_do_masquerade(struct ast_channel *original)
/* We need the clone's lock, too */
ast_channel_lock(clonechan);
- ast_debug(2, "Got clone lock for masquerade on '%s' at %p\n", clonechan->name, &clonechan->lock_dont_use);
+ ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
+ clonechan->name, clonechan->_state, original->name, original->_state);
+
+ manager_event(EVENT_FLAG_CALL, "Masquerade", "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n",
+ clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
/* Having remembered the original read/write formats, we turn off any translation on either
one */
free_translation(clonechan);
free_translation(original);
-
/* Unlink the masquerade */
original->masq = NULL;
clonechan->masqr = NULL;
-
+
/* Save the original name */
ast_copy_string(orig, original->name, sizeof(orig));
/* Save the new name */
ast_copy_string(newn, clonechan->name, sizeof(newn));
/* Create the masq name */
snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
-
+
/* Copy the name from the clone channel */
- ast_string_field_set(original, name, newn);
+ ast_change_name(original, newn);
/* Mangle the name of the clone channel */
- ast_string_field_set(clonechan, name, masqn);
-
- /* Notify any managers of the change, first the masq then the other */
- manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clonechan->uniqueid);
- manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid);
+ ast_change_name(clonechan, masqn);
/* Swap the technologies */
t = original->tech;
@@ -4589,10 +4607,9 @@ int ast_do_masquerade(struct ast_channel *original)
return -1;
}
- snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
/* Mangle the name of the clone channel */
- ast_string_field_set(clonechan, name, zombn);
- manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", masqn, zombn, clonechan->uniqueid);
+ snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
+ ast_change_name(clonechan, zombn);
/* Update the type. */
t_pvt = original->monitor;
@@ -4619,6 +4636,8 @@ int ast_do_masquerade(struct ast_channel *original)
AST_LIST_APPEND_LIST(&original->datastores, &clonechan->datastores, entry);
}
+ ast_autochan_new_channel(clonechan, original);
+
clone_variables(original, clonechan);
/* Presense of ADSI capable CPE follows clone */
original->adsicpe = clonechan->adsicpe;
@@ -4692,7 +4711,7 @@ int ast_do_masquerade(struct ast_channel *original)
if (original->visible_indication) {
ast_indicate(original, original->visible_indication);
}
-
+
/* Now, at this point, the "clone" channel is totally F'd up. We mark it as
a zombie so nothing tries to touch it. If it's already been marked as a
zombie, then free it now (since it already is considered invalid). */
@@ -4709,7 +4728,7 @@ int ast_do_masquerade(struct ast_channel *original)
clonechan->hangupcause,
ast_cause2str(clonechan->hangupcause)
);
- ast_channel_free(clonechan);
+ clonechan = ast_channel_release(clonechan);
} else {
ast_debug(1, "Released clone lock on '%s'\n", clonechan->name);
ast_set_flag(clonechan, AST_FLAG_ZOMBIE);
@@ -5622,8 +5641,58 @@ void ast_moh_cleanup(struct ast_channel *chan)
ast_moh_cleanup_ptr(chan);
}
+static int ast_channel_hash_cb(const void *obj, const int flags)
+{
+ const struct ast_channel *chan = obj;
+
+ /* If the name isn't set, return 0 so that the ao2_find() search will
+ * start in the first bucket. */
+ if (ast_strlen_zero(chan->name)) {
+ return 0;
+ }
+
+ return ast_str_case_hash(chan->name);
+}
+
+static int ast_channel_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct ast_channel *chan = obj, *cmp_args = arg;
+ size_t name_len;
+ int ret = CMP_MATCH;
+
+ /* This is sort of a hack. Basically, we're using an arbitrary field
+ * in ast_channel to pass the name_len for a prefix match. If this
+ * gets changed, then the uses of ao2_find() must be changed, too. */
+ name_len = cmp_args->rings;
+
+ ast_channel_lock(chan);
+
+ if (cmp_args->name) { /* match by name */
+ if ((!name_len && strcasecmp(chan->name, cmp_args->name)) ||
+ (name_len && strncasecmp(chan->name, cmp_args->name, name_len))) {
+ ret = 0; /* name match failed */
+ }
+ } else if (cmp_args->exten) {
+ if (cmp_args->context && strcasecmp(chan->context, cmp_args->context) &&
+ strcasecmp(chan->macrocontext, cmp_args->context)) {
+ ret = 0; /* context match failed */
+ }
+ if (ret && strcasecmp(chan->exten, cmp_args->exten) &&
+ strcasecmp(chan->macroexten, cmp_args->exten)) {
+ ret = 0; /* exten match failed */
+ }
+ }
+
+ ast_channel_unlock(chan);
+
+ return ret;
+}
+
void ast_channels_init(void)
{
+ channels = ao2_container_alloc(NUM_CHANNEL_BUCKETS,
+ ast_channel_hash_cb, ast_channel_cmp_cb);
+
ast_cli_register_multiple(cli_channel, ARRAY_LEN(cli_channel));
}
@@ -5758,116 +5827,6 @@ const char *channelreloadreason2txt(enum channelreloadreason reason)
}
};
-#ifdef DEBUG_CHANNEL_LOCKS
-
-/*! \brief Unlock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function
-*/
-int __ast_channel_unlock(struct ast_channel *chan, const char *filename, int lineno, const char *func)
-{
- int res = 0;
- ast_debug(3, "::::==== Unlocking AST channel %s\n", chan->name);
-
- if (!chan) {
- ast_debug(1, "::::==== Unlocking non-existing channel \n");
- return 0;
- }
-#ifdef DEBUG_THREADS
- res = __ast_pthread_mutex_unlock(filename, lineno, func, "(channel lock)", &chan->lock_dont_use);
-#else
- res = ast_mutex_unlock(&chan->lock_dont_use);
-#endif
-
- if (option_debug > 2) {
-#ifdef DEBUG_THREADS
- int count = 0;
- if ((count = chan->lock_dont_use.track.reentrancy))
- ast_debug(3, ":::=== Still have %d locks (recursive)\n", count);
-#endif
- if (!res)
- ast_debug(3, "::::==== Channel %s was unlocked\n", chan->name);
- if (res == EINVAL) {
- ast_debug(3, "::::==== Channel %s had no lock by this thread. Failed unlocking\n", chan->name);
- }
- }
- if (res == EPERM) {
- /* We had no lock, so okay any way*/
- ast_debug(4, "::::==== Channel %s was not locked at all \n", chan->name);
- res = 0;
- }
- return res;
-}
-
-/*! \brief Lock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int __ast_channel_lock(struct ast_channel *chan, const char *filename, int lineno, const char *func)
-{
- int res;
-
- ast_debug(4, "====:::: Locking AST channel %s\n", chan->name);
-
-#ifdef DEBUG_THREADS
- res = __ast_pthread_mutex_lock(filename, lineno, func, "(channel lock)", &chan->lock_dont_use);
-#else
- res = ast_mutex_lock(&chan->lock_dont_use);
-#endif
-
- if (option_debug > 3) {
-#ifdef DEBUG_THREADS
- int count = 0;
- if ((count = chan->lock_dont_use.track.reentrancy))
- ast_debug(4, ":::=== Now have %d locks (recursive)\n", count);
-#endif
- if (!res)
- ast_debug(4, "::::==== Channel %s was locked\n", chan->name);
- if (res == EDEADLK) {
- /* We had no lock, so okey any way */
- ast_debug(4, "::::==== Channel %s was not locked by us. Lock would cause deadlock.\n", chan->name);
- }
- if (res == EINVAL) {
- ast_debug(4, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
- }
- }
- return res;
-}
-
-/*! \brief Lock AST channel (and print debugging output)
-\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
-int __ast_channel_trylock(struct ast_channel *chan, const char *filename, int lineno, const char *func)
-{
- int res;
-
- ast_debug(3, "====:::: Trying to lock AST channel %s\n", chan->name);
-#ifdef DEBUG_THREADS
- res = __ast_pthread_mutex_trylock(filename, lineno, func, "(channel lock)", &chan->lock_dont_use);
-#else
- res = ast_mutex_trylock(&chan->lock_dont_use);
-#endif
-
- if (option_debug > 2) {
-#ifdef DEBUG_THREADS
- int count = 0;
- if ((count = chan->lock_dont_use.track.reentrancy))
- ast_debug(3, ":::=== Now have %d locks (recursive)\n", count);
-#endif
- if (!res)
- ast_debug(3, "::::==== Channel %s was locked\n", chan->name);
- if (res == EBUSY) {
- /* We failed to lock */
- ast_debug(3, "::::==== Channel %s failed to lock. Not waiting around...\n", chan->name);
- }
- if (res == EDEADLK) {
- /* We had no lock, so okey any way*/
- ast_debug(3, "::::==== Channel %s was not locked. Lock would cause deadlock.\n", chan->name);
- }
- if (res == EINVAL)
- ast_debug(3, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
- }
- return res;
-}
-
-#endif
-
/*
* Wrappers for various ast_say_*() functions that call the full version
* of the same functions.
diff --git a/main/cli.c b/main/cli.c
index 1d3843183..8217e52d1 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -789,6 +789,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
int numchans = 0, concise = 0, verbose = 0, count = 0;
int fd, argc;
char **argv;
+ struct ast_channel_iterator *iter = NULL;
switch (cmd) {
case CLI_INIT:
@@ -831,10 +832,18 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
"CallerID", "Duration", "Accountcode", "BridgedTo");
}
- while ((c = ast_channel_walk_locked(c)) != NULL) {
- struct ast_channel *bc = ast_bridged_channel(c);
+ if (!count && !(iter = ast_channel_iterator_all_new(0))) {
+ return CLI_FAILURE;
+ }
+
+ for (; iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
+ struct ast_channel *bc;
char durbuf[10] = "-";
+ ast_channel_lock(c);
+
+ bc = ast_bridged_channel(c);
+
if (!count) {
if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
@@ -876,10 +885,15 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
}
}
- numchans++;
ast_channel_unlock(c);
}
+
+ if (iter) {
+ ast_channel_iterator_destroy(iter);
+ }
+
if (!concise) {
+ numchans = ast_active_channels();
ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
if (option_maxcalls)
ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
@@ -890,6 +904,7 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_cli(fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
}
+
return CLI_SUCCESS;
#undef FORMAT_STRING
@@ -914,15 +929,21 @@ static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_
case CLI_GENERATE:
return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
}
- if (a->argc != 4)
+
+ if (a->argc != 4) {
return CLI_SHOWUSAGE;
- c = ast_get_channel_by_name_locked(a->argv[3]);
- if (c) {
+ }
+
+ if ((c = ast_channel_get_by_name(a->argv[3]))) {
+ ast_channel_lock(c);
ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
ast_channel_unlock(c);
- } else
+ c = ast_channel_unref(c);
+ } else {
ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
+ }
+
return CLI_SUCCESS;
}
@@ -1174,10 +1195,41 @@ static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast
return CLI_SUCCESS;
}
+struct channel_set_debug_args {
+ int fd;
+ int is_off;
+};
+
+static int channel_set_debug(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = obj;
+ struct channel_set_debug_args *args = data;
+
+ ast_channel_lock(chan);
+
+ if (!(chan->fin & DEBUGCHAN_FLAG) || !(chan->fout & DEBUGCHAN_FLAG)) {
+ if (args->is_off) {
+ chan->fin &= ~DEBUGCHAN_FLAG;
+ chan->fout &= ~DEBUGCHAN_FLAG;
+ } else {
+ chan->fin |= DEBUGCHAN_FLAG;
+ chan->fout |= DEBUGCHAN_FLAG;
+ }
+ ast_cli(args->fd, "Debugging %s on channel %s\n", args->is_off ? "disabled" : "enabled",
+ chan->name);
+ }
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_channel *c = NULL;
- int is_all, is_off = 0;
+ struct channel_set_debug_args args = {
+ .fd = a->fd,
+ };
switch (cmd) {
case CLI_INIT:
@@ -1193,47 +1245,37 @@ static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, str
return NULL;
return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
}
+
/* 'core set debug channel {all|chan_id}' */
if (a->argc == e->args + 2) {
if (!strcasecmp(a->argv[e->args + 1], "off"))
- is_off = 1;
+ args.is_off = 1;
else
return CLI_SHOWUSAGE;
- } else if (a->argc != e->args + 1)
+ } else if (a->argc != e->args + 1) {
return CLI_SHOWUSAGE;
+ }
- is_all = !strcasecmp("all", a->argv[e->args]);
- if (is_all) {
- if (is_off) {
+ if (!strcasecmp("all", a->argv[e->args])) {
+ if (args.is_off) {
global_fin &= ~DEBUGCHAN_FLAG;
global_fout &= ~DEBUGCHAN_FLAG;
} else {
global_fin |= DEBUGCHAN_FLAG;
global_fout |= DEBUGCHAN_FLAG;
}
- c = ast_channel_walk_locked(NULL);
+ ast_channel_callback(channel_set_debug, NULL, &args, OBJ_NODATA | OBJ_MULTIPLE);
} else {
- c = ast_get_channel_by_name_locked(a->argv[e->args]);
- if (c == NULL)
+ if ((c = ast_channel_get_by_name(a->argv[e->args]))) {
+ channel_set_debug(c, NULL, &args, 0);
+ ast_channel_unref(c);
+ } else {
ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
- }
- while (c) {
- if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
- if (is_off) {
- c->fin &= ~DEBUGCHAN_FLAG;
- c->fout &= ~DEBUGCHAN_FLAG;
- } else {
- c->fin |= DEBUGCHAN_FLAG;
- c->fout |= DEBUGCHAN_FLAG;
- }
- ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
}
- ast_channel_unlock(c);
- if (!is_all)
- break;
- c = ast_channel_walk_locked(c);
}
- ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
+
+ ast_cli(a->fd, "Debugging on new channels is %s\n", args.is_off ? "disabled" : "enabled");
+
return CLI_SUCCESS;
}
@@ -1279,22 +1321,29 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
}
- if (a->argc != 4)
+ if (a->argc != 4) {
return CLI_SHOWUSAGE;
+ }
+
now = ast_tvnow();
- c = ast_get_channel_by_name_locked(a->argv[3]);
- if (!c) {
+
+ if (!(c = ast_channel_get_by_name(a->argv[3]))) {
ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
return CLI_SUCCESS;
}
+
+ ast_channel_lock(c);
+
if (c->cdr) {
elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
hour = elapsed_seconds / 3600;
min = (elapsed_seconds % 3600) / 60;
sec = elapsed_seconds % 60;
snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
- } else
+ } else {
strcpy(cdrtime, "N/A");
+ }
+
ast_cli(a->fd,
" -- General --\n"
" Name: %s\n"
@@ -1347,17 +1396,24 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
(ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
- if (pbx_builtin_serialize_variables(c, &out))
+ if (pbx_builtin_serialize_variables(c, &out)) {
ast_cli(a->fd," Variables:\n%s\n", ast_str_buffer(out));
- if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
+ }
+
+ if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1)) {
ast_cli(a->fd," CDR Variables:\n%s\n", ast_str_buffer(out));
+ }
+
#ifdef CHANNEL_TRACE
trace_enabled = ast_channel_trace_is_enabled(c);
ast_cli(a->fd, " Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled");
if (trace_enabled && ast_channel_trace_serialize(c, &out))
ast_cli(a->fd, " Trace:\n%s\n", ast_str_buffer(out));
#endif
+
ast_channel_unlock(c);
+ c = ast_channel_unref(c);
+
return CLI_SUCCESS;
}
@@ -1381,20 +1437,27 @@ char *ast_complete_channels(const char *line, const char *word, int pos, int sta
{
struct ast_channel *c = NULL;
int which = 0;
- int wordlen;
char notfound = '\0';
char *ret = &notfound; /* so NULL can break the loop */
+ struct ast_channel_iterator *iter;
- if (pos != rpos)
+ if (pos != rpos) {
return NULL;
+ }
- wordlen = strlen(word);
+ if (!(iter = ast_channel_iterator_by_name_new(0, word, strlen(word)))) {
+ return NULL;
+ }
- while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
- if (!strncasecmp(word, c->name, wordlen) && ++which > state)
+ while (ret == &notfound && (c = ast_channel_iterator_next(iter))) {
+ if (++which > state) {
+ ast_channel_lock(c);
ret = ast_strdup(c->name);
- ast_channel_unlock(c);
+ ast_channel_unlock(c);
+ }
+ ast_channel_unref(c);
}
+
return ret == &notfound ? NULL : ret;
}
diff --git a/main/devicestate.c b/main/devicestate.c
index 4572c6604..4e40d84a8 100644
--- a/main/devicestate.c
+++ b/main/devicestate.c
@@ -268,19 +268,15 @@ enum ast_device_state ast_parse_device_state(const char *device)
char match[AST_CHANNEL_NAME];
enum ast_device_state res;
- ast_copy_string(match, device, sizeof(match)-1);
- strcat(match, "-");
- chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
+ snprintf(match, sizeof(match), "%s-", device);
- if (!chan)
+ if (!(chan = ast_channel_get_by_name_prefix(match, strlen(match)))) {
return AST_DEVICE_UNKNOWN;
+ }
- if (chan->_state == AST_STATE_RINGING)
- res = AST_DEVICE_RINGING;
- else
- res = AST_DEVICE_INUSE;
+ res = (chan->_state == AST_STATE_RINGING) ? AST_DEVICE_RINGING : AST_DEVICE_INUSE;
- ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
return res;
}
diff --git a/main/features.c b/main/features.c
index 62263142a..5c311625e 100644
--- a/main/features.c
+++ b/main/features.c
@@ -720,11 +720,13 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, st
if ((c = strrchr(other_side, ';'))) {
*++c = '1';
}
- if ((tmpchan = ast_get_channel_by_name_locked(other_side))) {
+ if ((tmpchan = ast_channel_get_by_name(other_side))) {
+ ast_channel_lock(tmpchan);
if ((base_peer = ast_bridged_channel(tmpchan))) {
ast_copy_string(pu->peername, base_peer->name, sizeof(pu->peername));
}
ast_channel_unlock(tmpchan);
+ tmpchan = ast_channel_unref(tmpchan);
}
} else {
ast_copy_string(pu->peername, S_OR(args->orig_chan_name, peer->name), sizeof(pu->peername));
@@ -2883,33 +2885,35 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
to attend to; if the chan or peer changed names,
we have the before and after attached CDR's.
*/
-
+
if (new_chan_cdr) {
struct ast_channel *chan_ptr = NULL;
-
- if (strcasecmp(orig_channame, chan->name) != 0) {
- /* old channel */
- chan_ptr = ast_get_channel_by_name_locked(orig_channame);
- if (chan_ptr) {
- if (!ast_bridged_channel(chan_ptr)) {
- struct ast_cdr *cur;
- for (cur = chan_ptr->cdr; cur; cur = cur->next) {
- if (cur == chan_cdr) {
- break;
- }
- }
- if (cur)
- ast_cdr_specialized_reset(chan_cdr,0);
- }
- ast_channel_unlock(chan_ptr);
- }
- /* new channel */
- ast_cdr_specialized_reset(new_chan_cdr,0);
- } else {
- ast_cdr_specialized_reset(chan_cdr,0); /* nothing changed, reset the chan_cdr */
- }
+
+ if (strcasecmp(orig_channame, chan->name) != 0) {
+ /* old channel */
+ if ((chan_ptr == ast_channel_get_by_name(orig_channame))) {
+ ast_channel_lock(chan_ptr);
+ if (!ast_bridged_channel(chan_ptr)) {
+ struct ast_cdr *cur;
+ for (cur = chan_ptr->cdr; cur; cur = cur->next) {
+ if (cur == chan_cdr) {
+ break;
+ }
+ }
+ if (cur) {
+ ast_cdr_specialized_reset(chan_cdr, 0);
+ }
+ }
+ ast_channel_unlock(chan_ptr);
+ chan_ptr = ast_channel_unref(chan_ptr);
+ }
+ /* new channel */
+ ast_cdr_specialized_reset(new_chan_cdr, 0);
+ } else {
+ ast_cdr_specialized_reset(chan_cdr, 0); /* nothing changed, reset the chan_cdr */
+ }
}
-
+
{
struct ast_channel *chan_ptr = NULL;
new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
@@ -2917,8 +2921,8 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
ast_set_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */
if (strcasecmp(orig_peername, peer->name) != 0) {
/* old channel */
- chan_ptr = ast_get_channel_by_name_locked(orig_peername);
- if (chan_ptr) {
+ if ((chan_ptr = ast_channel_get_by_name(orig_peername))) {
+ ast_channel_lock(chan_ptr);
if (!ast_bridged_channel(chan_ptr)) {
struct ast_cdr *cur;
for (cur = chan_ptr->cdr; cur; cur = cur->next) {
@@ -2926,15 +2930,17 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
break;
}
}
- if (cur)
- ast_cdr_specialized_reset(peer_cdr,0);
+ if (cur) {
+ ast_cdr_specialized_reset(peer_cdr, 0);
+ }
}
ast_channel_unlock(chan_ptr);
+ chan_ptr = ast_channel_unref(chan_ptr);
}
/* new channel */
- ast_cdr_specialized_reset(new_peer_cdr,0);
+ ast_cdr_specialized_reset(new_peer_cdr, 0);
} else {
- ast_cdr_specialized_reset(peer_cdr,0); /* nothing changed, reset the peer_cdr */
+ ast_cdr_specialized_reset(peer_cdr, 0); /* nothing changed, reset the peer_cdr */
}
}
@@ -4157,12 +4163,8 @@ static int action_bridge(struct mansession *s, const struct message *m)
return 0;
}
- /* The same code must be executed for chana and chanb. To avoid a
- * theoretical deadlock, this code is separated so both chana and chanb will
- * not hold locks at the same time. */
-
/* Start with chana */
- chana = ast_get_channel_by_name_prefix_locked(channela, strlen(channela));
+ chana = ast_channel_get_by_name_prefix(channela, strlen(channela));
/* send errors if any of the channels could not be found/locked */
if (!chana) {
@@ -4180,16 +4182,16 @@ static int action_bridge(struct mansession *s, const struct message *m)
if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
NULL, NULL, 0, "Bridge/%s", chana->name))) {
astman_send_error(s, m, "Unable to create temporary channel!");
- ast_channel_unlock(chana);
+ chana = ast_channel_unref(chana);
return 1;
}
do_bridge_masquerade(chana, tmpchana);
- ast_channel_unlock(chana);
- chana = NULL;
+
+ chana = ast_channel_unref(chana);
/* now do chanb */
- chanb = ast_get_channel_by_name_prefix_locked(channelb, strlen(channelb));
+ chanb = ast_channel_get_by_name_prefix(channelb, strlen(channelb));
/* send errors if any of the channels could not be found/locked */
if (!chanb) {
char buf[256];
@@ -4208,12 +4210,13 @@ static int action_bridge(struct mansession *s, const struct message *m)
NULL, NULL, 0, "Bridge/%s", chanb->name))) {
astman_send_error(s, m, "Unable to create temporary channels!");
ast_hangup(tmpchana);
- ast_channel_unlock(chanb);
+ chanb = ast_channel_unref(chanb);
return 1;
}
+
do_bridge_masquerade(chanb, tmpchanb);
- ast_channel_unlock(chanb);
- chanb = NULL;
+
+ chanb = ast_channel_unref(chanb);
/* make the channels compatible, send error if we fail doing so */
if (ast_channel_make_compatible(tmpchana, tmpchanb)) {
@@ -4408,21 +4411,24 @@ static int manager_park(struct mansession *s, const struct message *m)
return 0;
}
- ch1 = ast_get_channel_by_name_locked(channel);
- if (!ch1) {
+ if (!(ch1 = ast_channel_get_by_name(channel))) {
snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
astman_send_error(s, m, buf);
return 0;
}
- ch2 = ast_get_channel_by_name_locked(channel2);
- if (!ch2) {
+ if (!(ch2 = ast_channel_get_by_name(channel2))) {
snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
astman_send_error(s, m, buf);
- ast_channel_unlock(ch1);
+ ast_channel_unref(ch1);
return 0;
}
+ ast_channel_lock(ch1);
+ while (ast_channel_trylock(ch2)) {
+ CHANNEL_DEADLOCK_AVOIDANCE(ch1);
+ }
+
if (!ast_strlen_zero(timeout)) {
sscanf(timeout, "%d", &to);
}
@@ -4438,19 +4444,26 @@ static int manager_park(struct mansession *s, const struct message *m)
ast_channel_unlock(ch1);
ast_channel_unlock(ch2);
+ ch1 = ast_channel_unref(ch1);
+ ch2 = ast_channel_unref(ch2);
+
return 0;
}
-static int find_channel_by_group(struct ast_channel *c, void *data) {
- struct ast_channel *chan = data;
+static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *c = data;
+ struct ast_channel *chan = obj;
- return !c->pbx &&
+ int i = !c->pbx &&
/* Accessing 'chan' here is safe without locking, because there is no way for
the channel do disappear from under us at this point. pickupgroup *could*
change while we're here, but that isn't a problem. */
(c != chan) &&
(chan->pickupgroup & c->callgroup) &&
((c->_state == AST_STATE_RINGING) || (c->_state == AST_STATE_RING));
+
+ return i ? CMP_MATCH | CMP_STOP : 0;
}
/*!
@@ -4463,43 +4476,57 @@ static int find_channel_by_group(struct ast_channel *c, void *data) {
*/
int ast_pickup_call(struct ast_channel *chan)
{
- struct ast_channel *cur = ast_channel_search_locked(find_channel_by_group, chan);
-
- if (cur) {
- struct ast_party_connected_line connected_caller;
-
- int res = -1;
- ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
-
- connected_caller = cur->connected;
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- ast_channel_update_connected_line(chan, &connected_caller);
-
- ast_party_connected_line_collect_caller(&connected_caller, &chan->cid);
- connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
- ast_channel_queue_connected_line_update(chan, &connected_caller);
+ struct ast_channel *cur;
+ struct ast_party_connected_line connected_caller;
+ int res;
+ const char *chan_name;
+ const char *cur_name;
- res = ast_answer(chan);
- if (res)
- ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
- res = ast_queue_control(chan, AST_CONTROL_ANSWER);
- if (res)
- ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
- res = ast_channel_masquerade(cur, chan);
- if (res)
- ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */
- if (!ast_strlen_zero(pickupsound)) {
- ast_stream_and_wait(cur, pickupsound, "");
- }
- ast_channel_unlock(cur);
- return res;
- } else {
+ if (!(cur = ast_channel_callback(find_channel_by_group, NULL, chan, 0))) {
ast_debug(1, "No call pickup possible...\n");
if (!ast_strlen_zero(pickupfailsound)) {
ast_stream_and_wait(chan, pickupfailsound, "");
}
+ return -1;
}
- return -1;
+
+ ast_channel_lock_both(cur, chan);
+
+ cur_name = ast_strdupa(cur->name);
+ chan_name = ast_strdupa(chan->name);
+
+ ast_debug(1, "Call pickup on chan '%s' by '%s'\n", cur_name, chan_name);
+
+ connected_caller = cur->connected;
+ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+ ast_channel_update_connected_line(chan, &connected_caller);
+
+ ast_party_connected_line_collect_caller(&connected_caller, &chan->cid);
+ connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+ ast_channel_queue_connected_line_update(chan, &connected_caller);
+
+ ast_channel_unlock(cur);
+ ast_channel_unlock(chan);
+
+ if (ast_answer(chan)) {
+ ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
+ }
+
+ if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
+ ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
+ }
+
+ if ((res = ast_channel_masquerade(cur, chan))) {
+ ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name, cur_name);
+ }
+
+ if (!ast_strlen_zero(pickupsound)) {
+ ast_stream_and_wait(cur, pickupsound, "");
+ }
+
+ cur = ast_channel_unref(cur);
+
+ return res;
}
static char *app_bridge = "Bridge";
@@ -4559,8 +4586,8 @@ static int bridge_exec(struct ast_channel *chan, void *data)
}
/* make sure we have a valid end point */
- if (!(current_dest_chan = ast_get_channel_by_name_prefix_locked(args.dest_chan,
- strlen(args.dest_chan)))) {
+ if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan,
+ strlen(args.dest_chan)))) {
ast_log(LOG_WARNING, "Bridge failed because channel %s does not exists or we "
"cannot get its lock\n", args.dest_chan);
manager_event(EVENT_FLAG_CALL, "BridgeExec",
@@ -4573,8 +4600,9 @@ static int bridge_exec(struct ast_channel *chan, void *data)
}
/* answer the channel if needed */
- if (current_dest_chan->_state != AST_STATE_UP)
+ if (current_dest_chan->_state != AST_STATE_UP) {
ast_answer(current_dest_chan);
+ }
/* try to allocate a place holder where current_dest_chan will be placed */
if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
@@ -4601,6 +4629,7 @@ static int bridge_exec(struct ast_channel *chan, void *data)
"Channel2: %s\r\n", chan->name, final_dest_chan->name);
ast_hangup(final_dest_chan); /* may be we should return this channel to the PBX? */
pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
+ current_dest_chan = ast_channel_unref(current_dest_chan);
return 0;
}
@@ -4618,6 +4647,8 @@ static int bridge_exec(struct ast_channel *chan, void *data)
}
}
+ current_dest_chan = ast_channel_unref(current_dest_chan);
+
/* do the bridge */
ast_bridge_call(chan, final_dest_chan, &bconfig);
diff --git a/main/logger.c b/main/logger.c
index 163548908..82a3be04b 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -584,7 +584,7 @@ static int rotate_file(const char *filename)
if (ast_safe_system(buf) != -1) {
ast_log(LOG_WARNING, "error executing '%s'\n", buf);
}
- ast_channel_free(c);
+ c = ast_channel_release(c);
}
return res;
}
diff --git a/main/manager.c b/main/manager.c
index 5989e4786..8a054b89b 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -1885,10 +1885,12 @@ static int action_hangup(struct mansession *s, const struct message *m)
int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
const char *name = astman_get_header(m, "Channel");
const char *cause = astman_get_header(m, "Cause");
+
if (ast_strlen_zero(name)) {
astman_send_error(s, m, "No channel specified");
return 0;
}
+
if (!ast_strlen_zero(cause)) {
char *endptr;
causecode = strtol(cause, &endptr, 10);
@@ -1898,11 +1900,13 @@ static int action_hangup(struct mansession *s, const struct message *m)
causecode = 0; /* do not set channel's hangupcause */
}
}
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
+
+ ast_channel_lock(c);
if (causecode > 0) {
ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
c->name, causecode, c->hangupcause);
@@ -1910,7 +1914,11 @@ static int action_hangup(struct mansession *s, const struct message *m)
}
ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT);
ast_channel_unlock(c);
+
+ c = ast_channel_unref(c);
+
astman_send_ack(s, m, "Channel Hungup");
+
return 0;
}
@@ -1934,8 +1942,7 @@ static int action_setvar(struct mansession *s, const struct message *m)
}
if (!ast_strlen_zero(name)) {
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
@@ -1944,7 +1951,7 @@ static int action_setvar(struct mansession *s, const struct message *m)
pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
if (c) {
- ast_channel_unlock(c);
+ c = ast_channel_unref(c);
}
astman_send_ack(s, m, "Variable Set");
@@ -1973,8 +1980,7 @@ static int action_getvar(struct mansession *s, const struct message *m)
}
if (!ast_strlen_zero(name)) {
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
@@ -1985,19 +1991,21 @@ static int action_getvar(struct mansession *s, const struct message *m)
c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/manager");
if (c) {
ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
- ast_channel_free(c);
+ c = ast_channel_release(c);
} else
ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
- } else
+ } else {
ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
+ }
varval = workspace;
} else {
pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
}
if (c) {
- ast_channel_unlock(c);
+ c = ast_channel_unref(c);
}
+
astman_start_ack(s, m);
astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
@@ -2033,6 +2041,7 @@ static int action_status(struct mansession *s, const struct message *m)
AST_APP_ARG(name)[100];
);
struct ast_str *str = ast_str_create(1000);
+ struct ast_channel_iterator *iter = NULL;
if (!ast_strlen_zero(id)) {
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
@@ -2041,15 +2050,20 @@ static int action_status(struct mansession *s, const struct message *m)
}
if (all) {
- c = ast_channel_walk_locked(NULL);
+ if (!(iter = ast_channel_iterator_all_new(0))) {
+ ast_free(str);
+ astman_send_error(s, m, "Memory Allocation Failure");
+ return 1;
+ }
+ c = ast_channel_iterator_next(iter);
} else {
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
ast_free(str);
return 0;
}
}
+
astman_send_ack(s, m, "Channel status will follow");
if (!ast_strlen_zero(cvariables)) {
@@ -2057,7 +2071,9 @@ static int action_status(struct mansession *s, const struct message *m)
}
/* if we look by name, we break after the first iteration */
- while (c) {
+ for (; c; c = ast_channel_iterator_next(iter)) {
+ ast_channel_lock(c);
+
if (!ast_strlen_zero(cvariables)) {
int i;
ast_str_reset(str);
@@ -2114,36 +2130,42 @@ static int action_status(struct mansession *s, const struct message *m)
c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, ast_str_buffer(str), idText);
} else {
astman_append(s,
- "Event: Status\r\n"
- "Privilege: Call\r\n"
- "Channel: %s\r\n"
- "CallerIDNum: %s\r\n"
- "CallerIDName: %s\r\n"
- "Account: %s\r\n"
- "State: %s\r\n"
- "%s"
- "Uniqueid: %s\r\n"
- "%s"
- "%s"
- "\r\n",
- c->name,
- S_OR(c->cid.cid_num, "<unknown>"),
- S_OR(c->cid.cid_name, "<unknown>"),
- c->accountcode,
- ast_state2str(c->_state), bridge, c->uniqueid, ast_str_buffer(str), idText);
+ "Event: Status\r\n"
+ "Privilege: Call\r\n"
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Account: %s\r\n"
+ "State: %s\r\n"
+ "%s"
+ "Uniqueid: %s\r\n"
+ "%s"
+ "%s"
+ "\r\n",
+ c->name,
+ S_OR(c->cid.cid_num, "<unknown>"),
+ S_OR(c->cid.cid_name, "<unknown>"),
+ c->accountcode,
+ ast_state2str(c->_state), bridge, c->uniqueid,
+ ast_str_buffer(str), idText);
}
+
ast_channel_unlock(c);
+ c = ast_channel_unref(c);
+
if (!all) {
break;
}
- c = ast_channel_walk_locked(c);
}
+
astman_append(s,
- "Event: StatusComplete\r\n"
- "%s"
- "Items: %d\r\n"
- "\r\n", idText, channels);
+ "Event: StatusComplete\r\n"
+ "%s"
+ "Items: %d\r\n"
+ "\r\n", idText, channels);
+
ast_free(str);
+
return 0;
}
@@ -2171,14 +2193,15 @@ static int action_sendtext(struct mansession *s, const struct message *m)
return 0;
}
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
+ ast_channel_lock(c);
res = ast_sendtext(c, textmsg);
ast_channel_unlock(c);
+ c = ast_channel_unref(c);
if (res > 0) {
astman_send_ack(s, m, "Success");
@@ -2215,38 +2238,44 @@ static int action_redirect(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Channel not specified");
return 0;
}
+
if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
astman_send_error(s, m, "Invalid priority");
return 0;
}
}
- /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
- chan = ast_get_channel_by_name_locked(name);
- if (!chan) {
+
+ if (!(chan = ast_channel_get_by_name(name))) {
char buf[256];
snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
astman_send_error(s, m, buf);
return 0;
}
- if (ast_check_hangup(chan)) {
+
+ if (ast_check_hangup_locked(chan)) {
astman_send_error(s, m, "Redirect failed, channel not up.");
- ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
return 0;
}
- if (!ast_strlen_zero(name2))
- chan2 = ast_get_channel_by_name_locked(name2);
- if (chan2 && ast_check_hangup(chan2)) {
+
+ if (!ast_strlen_zero(name2)) {
+ chan2 = ast_channel_get_by_name(name2);
+ }
+
+ if (chan2 && ast_check_hangup_locked(chan2)) {
astman_send_error(s, m, "Redirect failed, extra channel not up.");
- ast_channel_unlock(chan);
- ast_channel_unlock(chan2);
+ chan = ast_channel_unref(chan);
+ chan2 = ast_channel_unref(chan2);
return 0;
}
+
if (chan->pbx) {
ast_channel_lock(chan);
ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
ast_channel_unlock(chan);
}
+
res = ast_async_goto(chan, context, exten, pi);
if (!res) {
if (!ast_strlen_zero(name2)) {
@@ -2271,12 +2300,15 @@ static int action_redirect(struct mansession *s, const struct message *m)
} else {
astman_send_error(s, m, "Redirect failed");
}
+
if (chan) {
- ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
}
+
if (chan2) {
- ast_channel_unlock(chan2);
+ chan2 = ast_channel_unref(chan2);
}
+
return 0;
}
@@ -2312,7 +2344,7 @@ static int action_atxfer(struct mansession *s, const struct message *m)
return 0;
}
- if (!(chan = ast_get_channel_by_name_locked(name))) {
+ if (!(chan = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "Channel specified does not exist");
return 0;
}
@@ -2322,17 +2354,18 @@ static int action_atxfer(struct mansession *s, const struct message *m)
}
for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
- struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
+ struct ast_frame f = { AST_FRAME_DTMF, *feature_code };
ast_queue_frame(chan, &f);
}
for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
- struct ast_frame f = {AST_FRAME_DTMF, *feature_code};
+ struct ast_frame f = { AST_FRAME_DTMF, *feature_code };
ast_queue_frame(chan, &f);
}
+ chan = ast_channel_unref(chan);
+
astman_send_ack(s, m, "Atxfer successfully queued");
- ast_channel_unlock(chan);
return 0;
}
@@ -2761,20 +2794,26 @@ static int action_timeout(struct mansession *s, const struct message *m)
astman_send_error(s, m, "No channel specified");
return 0;
}
+
if (!timeout || timeout < 0) {
astman_send_error(s, m, "No timeout specified");
return 0;
}
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
when.tv_usec = (timeout - when.tv_sec) * 1000000.0;
+
+ ast_channel_lock(c);
ast_channel_setwhentohangup_tv(c, when);
ast_channel_unlock(c);
+ c = ast_channel_unref(c);
+
astman_send_ack(s, m, "Timeout Set");
+
return 0;
}
@@ -2952,6 +2991,7 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
struct ast_channel *c = NULL;
int numchans = 0;
int duration, durh, durm, durs;
+ struct ast_channel_iterator *iter;
if (!ast_strlen_zero(actionid)) {
snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
@@ -2959,12 +2999,20 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
actionidtext[0] = '\0';
}
+ if (!(iter = ast_channel_iterator_all_new(0))) {
+ astman_send_error(s, m, "Memory Allocation Failure");
+ return 1;
+ }
+
astman_send_listack(s, m, "Channels will follow", "start");
- while ((c = ast_channel_walk_locked(c)) != NULL) {
- struct ast_channel *bc = ast_bridged_channel(c);
+ for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
+ struct ast_channel *bc;
char durbuf[10] = "";
+ ast_channel_lock(c);
+
+ bc = ast_bridged_channel(c);
if (c->cdr && !ast_tvzero(c->cdr->start)) {
duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
durh = duration / 3600;
@@ -2992,7 +3040,9 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
"\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
+
ast_channel_unlock(c);
+
numchans++;
}
@@ -3003,6 +3053,8 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m
"%s"
"\r\n", numchans, actionidtext);
+ ast_channel_iterator_destroy(iter);
+
return 0;
}
diff --git a/main/pbx.c b/main/pbx.c
index 0c7105a09..1aad50dfb 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -3488,7 +3488,7 @@ void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead
cp4 = ast_func_read(bogus, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
/* Don't deallocate the varshead that was passed in */
memcpy(&bogus->varshead, &old, sizeof(bogus->varshead));
- ast_channel_free(bogus);
+ bogus = ast_channel_release(bogus);
} else
ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
}
@@ -6134,16 +6134,19 @@ static char *handle_show_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cl
if (a->argc != e->args + 1)
return CLI_SHOWUSAGE;
- if (!(chan = ast_get_channel_by_name_locked(a->argv[e->args]))) {
+ if (!(chan = ast_channel_get_by_name(a->argv[e->args]))) {
ast_cli(a->fd, "Channel '%s' not found\n", a->argv[e->args]);
return CLI_FAILURE;
}
pbx_builtin_serialize_variables(chan, &vars);
+
if (ast_str_strlen(vars)) {
ast_cli(a->fd, "\nVariables for channel %s:\n%s\n", a->argv[e->args], ast_str_buffer(vars));
}
- ast_channel_unlock(chan);
+
+ chan = ast_channel_unref(chan);
+
return CLI_SUCCESS;
}
@@ -6192,13 +6195,15 @@ static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli
var_name = a->argv[e->args + 1];
var_value = a->argv[e->args + 2];
- if (!(chan = ast_get_channel_by_name_locked(chan_name))) {
+ if (!(chan = ast_channel_get_by_name(chan_name))) {
ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
return CLI_FAILURE;
}
pbx_builtin_setvar_helper(chan, var_name, var_value);
- ast_channel_unlock(chan);
+
+ chan = ast_channel_unref(chan);
+
ast_cli(a->fd, "\n -- Channel variable '%s' set to '%s' for '%s'\n", var_name, var_value, chan_name);
return CLI_SUCCESS;
@@ -7303,11 +7308,11 @@ int ast_async_goto_by_name(const char *channame, const char *context, const char
struct ast_channel *chan;
int res = -1;
- chan = ast_get_channel_by_name_locked(channame);
- if (chan) {
+ if ((chan = ast_channel_get_by_name(channame))) {
res = ast_async_goto(chan, context, exten, priority);
- ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
}
+
return res;
}
@@ -7823,7 +7828,7 @@ static int ast_pbx_outgoing_cdr_failed(void)
if (!chan->cdr) {
/* allocation of the cdr failed */
- ast_channel_free(chan); /* free the channel */
+ chan = ast_channel_release(chan); /* free the channel */
return -1; /* return failure */
}
@@ -7834,7 +7839,7 @@ static int ast_pbx_outgoing_cdr_failed(void)
ast_cdr_failed(chan->cdr); /* set the status to failed */
ast_cdr_detach(chan->cdr); /* post and free the record */
chan->cdr = NULL;
- ast_channel_free(chan); /* free the channel */
+ chan = ast_channel_release(chan); /* free the channel */
return 0; /* success */
}
@@ -9030,14 +9035,14 @@ int pbx_builtin_importvar(struct ast_channel *chan, void *data)
name = strsep(&value,"=");
channel = strsep(&value,",");
if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
- struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
+ struct ast_channel *chan2 = ast_channel_get_by_name(channel);
if (chan2) {
char *s = alloca(strlen(value) + 4);
if (s) {
sprintf(s, "${%s}", value);
pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
}
- ast_channel_unlock(chan2);
+ chan2 = ast_channel_unref(chan2);
}
pbx_builtin_setvar_helper(chan, name, tmp);
}
diff --git a/res/res_agi.c b/res/res_agi.c
index efd8549b3..24dd5d0bb 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -543,20 +543,27 @@ static char *handle_cli_agi_add_cmd(struct ast_cli_entry *e, int cmd, struct ast
return NULL;
}
- if (a->argc < 4)
+ if (a->argc < 4) {
return CLI_SHOWUSAGE;
- chan = ast_get_channel_by_name_locked(a->argv[2]);
- if (!chan) {
+ }
+
+ if (!(chan = ast_channel_get_by_name(a->argv[2]))) {
ast_log(LOG_WARNING, "Channel %s does not exists or cannot lock it\n", a->argv[2]);
return CLI_FAILURE;
}
+
if (add_agi_cmd(chan, a->argv[3], (a->argc > 4 ? a->argv[4] : ""))) {
ast_log(LOG_WARNING, "failed to add AGI command to queue of channel %s\n", chan->name);
ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
return CLI_FAILURE;
}
+
ast_log(LOG_DEBUG, "Added AGI command to channel %s queue\n", chan->name);
+
ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
+
return CLI_SUCCESS;
}
@@ -578,24 +585,33 @@ static int action_add_agi_cmd(struct mansession *s, const struct message *m)
const char *cmdid = astman_get_header(m, "CommandID");
struct ast_channel *chan;
char buf[256];
+
if (ast_strlen_zero(channel) || ast_strlen_zero(cmdbuff)) {
astman_send_error(s, m, "Both, Channel and Command are *required*");
return 0;
}
- chan = ast_get_channel_by_name_locked(channel);
- if (!chan) {
+
+ if (!(chan = ast_channel_get_by_name(channel))) {
snprintf(buf, sizeof(buf), "Channel %s does not exists or cannot get its lock", channel);
astman_send_error(s, m, buf);
return 0;
}
+
+ ast_channel_lock(chan);
+
if (add_agi_cmd(chan, cmdbuff, cmdid)) {
snprintf(buf, sizeof(buf), "Failed to add AGI command to channel %s queue", chan->name);
astman_send_error(s, m, buf);
ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
return 0;
}
- astman_send_ack(s, m, "Added AGI command to queue");
+
ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
+
+ astman_send_ack(s, m, "Added AGI command to queue");
+
return 0;
}
@@ -1679,12 +1695,11 @@ static int handle_hangup(struct ast_channel *chan, AGI *agi, int argc, char **ar
return RESULT_SUCCESS;
} else if (argc == 2) {
/* one argument: look for info on the specified channel */
- c = ast_get_channel_by_name_locked(argv[1]);
- if (c) {
+ if ((c = ast_channel_get_by_name(argv[1]))) {
/* we have a matching channel */
- ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ c = ast_channel_unref(c);
ast_agi_send(agi->fd, chan, "200 result=1\n");
- ast_channel_unlock(c);
return RESULT_SUCCESS;
}
/* if we get this far no channel name matched the argument given */
@@ -1766,10 +1781,9 @@ static int handle_channelstatus(struct ast_channel *chan, AGI *agi, int argc, ch
return RESULT_SUCCESS;
} else if (argc == 3) {
/* one argument: look for info on the specified channel */
- c = ast_get_channel_by_name_locked(argv[2]);
- if (c) {
+ if ((c = ast_channel_get_by_name(argv[2]))) {
ast_agi_send(agi->fd, chan, "200 result=%d\n", c->_state);
- ast_channel_unlock(c);
+ c = ast_channel_unref(c);
return RESULT_SUCCESS;
}
/* if we get this far no channel name matched the argument given */
@@ -1817,21 +1831,27 @@ static int handle_getvariablefull(struct ast_channel *chan, AGI *agi, int argc,
char tmp[4096];
struct ast_channel *chan2=NULL;
- if ((argc != 4) && (argc != 5))
+ if (argc != 4 && argc != 5) {
return RESULT_SHOWUSAGE;
+ }
+
if (argc == 5) {
- chan2 = ast_get_channel_by_name_locked(argv[4]);
+ chan2 = ast_channel_get_by_name(argv[4]);
} else {
- chan2 = chan;
+ chan2 = ast_channel_ref(chan);
}
+
if (chan2) {
pbx_substitute_variables_helper(chan2, argv[3], tmp, sizeof(tmp) - 1);
ast_agi_send(agi->fd, chan, "200 result=1 (%s)\n", tmp);
} else {
ast_agi_send(agi->fd, chan, "200 result=0\n");
}
- if (chan2 && (chan2 != chan))
- ast_channel_unlock(chan2);
+
+ if (chan2) {
+ chan2 = ast_channel_unref(chan2);
+ }
+
return RESULT_SUCCESS;
}
diff --git a/res/res_clioriginate.c b/res/res_clioriginate.c
index 4ed08a44e..beb2522f4 100644
--- a/res/res_clioriginate.c
+++ b/res/res_clioriginate.c
@@ -201,15 +201,14 @@ static char *handle_redirect(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
name = a->argv[2];
dest = a->argv[3];
- chan = ast_get_channel_by_name_locked(name);
- if (!chan) {
+ if (!(chan = ast_channel_get_by_name(name))) {
ast_cli(a->fd, "Channel '%s' not found\n", name);
return CLI_FAILURE;
}
res = ast_async_parseable_goto(chan, dest);
- ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
if (!res) {
ast_cli(a->fd, "Channel '%s' successfully redirected to %s\n", name, dest);
diff --git a/res/res_monitor.c b/res/res_monitor.c
index 6bd96fbca..dde35a9be 100644
--- a/res/res_monitor.c
+++ b/res/res_monitor.c
@@ -579,34 +579,41 @@ static int start_monitor_action(struct mansession *s, const struct message *m)
astman_send_error(s, m, "No channel specified");
return 0;
}
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
if (ast_strlen_zero(fname)) {
- /* No filename base specified, default to channel name as per CLI */
+ /* No filename base specified, default to channel name as per CLI */
+ ast_channel_lock(c);
fname = ast_strdupa(c->name);
+ ast_channel_unlock(c);
/* Channels have the format technology/channel_name - have to replace that / */
- if ((d = strchr(fname, '/')))
+ if ((d = strchr(fname, '/'))) {
*d = '-';
+ }
}
if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
if (ast_monitor_change_fname(c, fname, 1)) {
astman_send_error(s, m, "Could not start monitoring channel");
- ast_channel_unlock(c);
+ c = ast_channel_unref(c);
return 0;
}
}
if (ast_true(mix)) {
+ ast_channel_lock(c);
ast_monitor_setjoinfiles(c, 1);
+ ast_channel_unlock(c);
}
- ast_channel_unlock(c);
+ c = ast_channel_unref(c);
+
astman_send_ack(s, m, "Started monitoring channel");
+
return 0;
}
@@ -621,22 +628,28 @@ static int stop_monitor_action(struct mansession *s, const struct message *m)
struct ast_channel *c = NULL;
const char *name = astman_get_header(m, "Channel");
int res;
+
if (ast_strlen_zero(name)) {
astman_send_error(s, m, "No channel specified");
return 0;
}
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
+
res = ast_monitor_stop(c, 1);
- ast_channel_unlock(c);
+
+ c = ast_channel_unref(c);
+
if (res) {
astman_send_error(s, m, "Could not stop monitoring channel");
return 0;
}
+
astman_send_ack(s, m, "Stopped monitoring channel");
+
return 0;
}
@@ -654,26 +667,32 @@ static int change_monitor_action(struct mansession *s, const struct message *m)
struct ast_channel *c = NULL;
const char *name = astman_get_header(m, "Channel");
const char *fname = astman_get_header(m, "File");
+
if (ast_strlen_zero(name)) {
astman_send_error(s, m, "No channel specified");
return 0;
}
+
if (ast_strlen_zero(fname)) {
astman_send_error(s, m, "No filename specified");
return 0;
}
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return 0;
}
+
if (ast_monitor_change_fname(c, fname, 1)) {
+ c = ast_channel_unref(c);
astman_send_error(s, m, "Could not change monitored filename of channel");
- ast_channel_unlock(c);
return 0;
}
- ast_channel_unlock(c);
+
+ c = ast_channel_unref(c);
+
astman_send_ack(s, m, "Changed monitor filename");
+
return 0;
}
@@ -688,31 +707,33 @@ enum MONITOR_PAUSING_ACTION
MONITOR_ACTION_PAUSE,
MONITOR_ACTION_UNPAUSE
};
-
+
static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
{
struct ast_channel *c = NULL;
const char *name = astman_get_header(m, "Channel");
-
+
if (ast_strlen_zero(name)) {
astman_send_error(s, m, "No channel specified");
return -1;
}
-
- c = ast_get_channel_by_name_locked(name);
- if (!c) {
+
+ if (!(c = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "No such channel");
return -1;
}
- if (action == MONITOR_ACTION_PAUSE)
+ if (action == MONITOR_ACTION_PAUSE) {
ast_monitor_pause(c);
- else
+ } else {
ast_monitor_unpause(c);
-
- ast_channel_unlock(c);
+ }
+
+ c = ast_channel_unref(c);
+
astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
- return 0;
+
+ return 0;
}
static char pause_monitor_action_help[] =
diff --git a/res/snmp/agent.c b/res/snmp/agent.c
index f7e08220d..5a13142d2 100644
--- a/res/snmp/agent.c
+++ b/res/snmp/agent.c
@@ -236,19 +236,32 @@ static u_char *ast_var_channels_table(struct variable *vp, oid *name, size_t *le
u_char *ret = NULL;
int i, bit;
struct ast_str *out = ast_str_alloca(2048);
+ struct ast_channel_iterator *iter;
if (header_simple_table(vp, name, length, exact, var_len, write_method, ast_active_channels()))
return NULL;
i = name[*length - 1] - 1;
- for (chan = ast_channel_walk_locked(NULL);
- chan && i;
- chan = ast_channel_walk_locked(chan), i--)
- ast_channel_unlock(chan);
- if (chan == NULL)
+
+ if (!(iter = ast_channel_iterator_all_new(0))) {
return NULL;
+ }
+
+ while ((chan = ast_channel_iterator_next(iter)) && i) {
+ ast_channel_unref(chan);
+ i--;
+ }
+
+ iter = ast_channel_iterator_destroy(iter);
+
+ if (chan == NULL) {
+ return NULL;
+ }
+
*var_len = sizeof(long_ret);
+ ast_channel_lock(chan);
+
switch (vp->magic) {
case ASTCHANINDEX:
long_ret = name[*length - 1];
@@ -503,7 +516,10 @@ static u_char *ast_var_channels_table(struct variable *vp, oid *name, size_t *le
default:
break;
}
+
ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
+
return ret;
}
@@ -567,13 +583,26 @@ static u_char *ast_var_channel_types_table(struct variable *vp, oid *name, size_
long_ret = tech->transfer ? 1 : 2;
return (u_char *)&long_ret;
case ASTCHANTYPECHANNELS:
+ {
+ struct ast_channel_iterator *iter;
+
long_ret = 0;
- for (chan = ast_channel_walk_locked(NULL); chan; chan = ast_channel_walk_locked(chan)) {
- if (chan->tech == tech)
+
+ if (!(iter = ast_channel_iterator_all_new(0))) {
+ return NULL;
+ }
+
+ while ((chan = ast_channel_iterator_next(iter))) {
+ if (chan->tech == tech) {
long_ret++;
- ast_channel_unlock(chan);
+ }
+ chan = ast_channel_unref(chan);
}
+
+ ast_channel_iterator_destroy(iter);
+
return (u_char *)&long_ret;
+ }
default:
break;
}
@@ -585,15 +614,25 @@ static u_char *ast_var_channel_bridge(struct variable *vp, oid *name, size_t *le
{
static unsigned long long_ret;
struct ast_channel *chan = NULL;
+ struct ast_channel_iterator *iter;
long_ret = 0;
- if (header_generic(vp, name, length, exact, var_len, write_method))
+
+ if (header_generic(vp, name, length, exact, var_len, write_method)) {
return NULL;
+ }
- while ((chan = ast_channel_walk_locked(chan))) {
- if (ast_bridged_channel(chan))
+ if (!(iter = ast_channel_iterator_all_new(0))) {
+ return NULL;
+ }
+
+ while ((chan = ast_channel_iterator_next(iter))) {
+ ast_channel_lock(chan);
+ if (ast_bridged_channel(chan)) {
long_ret++;
+ }
ast_channel_unlock(chan);
+ chan = ast_channel_unref(chan);
}
*var_len = sizeof(long_ret);