summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-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
9 files changed, 750 insertions, 550 deletions
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);
}