aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--channels/chan_local.c330
1 files changed, 180 insertions, 150 deletions
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 710d7ebea..778654a1a 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -49,10 +49,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/manager.h"
#include "asterisk/stringfields.h"
#include "asterisk/devicestate.h"
+#include "asterisk/astobj2.h"
static const char tdesc[] = "Local Proxy Channel Driver";
#define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
+/* right now we are treating the locals astobj2 container as a
+ * list. If there is ever a reason to make this more efficient
+ * increasing the bucket size would help. */
+static const int BUCKET_SIZE = 1;
+
+static struct ao2_container *locals;
static struct ast_jb_conf g_jb_conf = {
.flags = 0,
@@ -105,7 +112,6 @@ static const struct ast_channel_tech local_tech = {
};
struct local_pvt {
- ast_mutex_t lock; /* Channel private lock */
unsigned int flags; /* Private flags */
char context[AST_MAX_CONTEXT]; /* Context to call */
char exten[AST_MAX_EXTENSION]; /* Extension to call */
@@ -118,15 +124,11 @@ struct local_pvt {
AST_LIST_ENTRY(local_pvt) list; /* Next entity */
};
-#define LOCAL_GLARE_DETECT (1 << 0) /*!< Detect glare on hangup */
-#define LOCAL_CANCEL_QUEUE (1 << 1) /*!< Cancel queue */
-#define LOCAL_ALREADY_MASQED (1 << 2) /*!< Already masqueraded */
-#define LOCAL_LAUNCHED_PBX (1 << 3) /*!< PBX was launched */
-#define LOCAL_NO_OPTIMIZATION (1 << 4) /*!< Do not optimize using masquerading */
-#define LOCAL_BRIDGE (1 << 5) /*!< Report back the "true" channel as being bridged to */
-#define LOCAL_MOH_PASSTHRU (1 << 6) /*!< Pass through music on hold start/stop frames */
-
-static AST_LIST_HEAD_STATIC(locals, local_pvt);
+#define LOCAL_ALREADY_MASQED (1 << 0) /*!< Already masqueraded */
+#define LOCAL_LAUNCHED_PBX (1 << 1) /*!< PBX was launched */
+#define LOCAL_NO_OPTIMIZATION (1 << 2) /*!< Do not optimize using masquerading */
+#define LOCAL_BRIDGE (1 << 3) /*!< Report back the "true" channel as being bridged to */
+#define LOCAL_MOH_PASSTHRU (1 << 4) /*!< Pass through music on hold start/stop frames */
static int local_setoption(struct ast_channel *chan, int option, void * data, int datalen)
{
@@ -157,7 +159,7 @@ startover:
return -1;
}
- while (ast_mutex_trylock(&p->lock)) {
+ while (ao2_trylock(p)) {
ast_channel_unlock(chan);
sched_yield();
ast_channel_lock(chan);
@@ -172,14 +174,14 @@ startover:
otherchan = (write_info->chan == p->owner) ? p->chan : p->owner;
if (!otherchan || otherchan == write_info->chan) {
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
ast_channel_unlock(chan);
ast_log(LOG_WARNING, "Could not update other side of %s, other side went away.\n", chan->name);
return 0;
}
if (ast_channel_trylock(otherchan)) {
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
ast_channel_unlock(chan);
goto startover;
}
@@ -187,7 +189,7 @@ startover:
res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value);
ast_channel_unlock(otherchan);
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
ast_channel_unlock(chan);
return res;
@@ -200,6 +202,7 @@ static int local_devicestate(void *data)
char *context = NULL, *opts = NULL;
int res;
struct local_pvt *lp;
+ struct ao2_iterator it;
if (!(context = strchr(exten, '@'))) {
ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
@@ -219,28 +222,21 @@ static int local_devicestate(void *data)
return AST_DEVICE_INVALID;
res = AST_DEVICE_NOT_INUSE;
- AST_LIST_LOCK(&locals);
- AST_LIST_TRAVERSE(&locals, lp, list) {
+
+ it = ao2_iterator_init(locals, 0);
+ while ((lp = ao2_iterator_next(&it))) {
if (!strcmp(exten, lp->exten) && !strcmp(context, lp->context) && lp->owner) {
res = AST_DEVICE_INUSE;
+ ao2_ref(lp, -1);
break;
}
+ ao2_ref(lp, -1);
}
- AST_LIST_UNLOCK(&locals);
+ ao2_iterator_destroy(&it);
return res;
}
-/*!
- * \note Assumes the pvt is no longer in the pvts list
- */
-static struct local_pvt *local_pvt_destroy(struct local_pvt *pvt)
-{
- ast_mutex_destroy(&pvt->lock);
- ast_free(pvt);
- return NULL;
-}
-
/*! \brief Return the bridged channel of a Local channel */
static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
{
@@ -253,7 +249,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct
return NULL;
}
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
if (ast_test_flag(p, LOCAL_BRIDGE)) {
/* Find the opposite channel */
@@ -267,7 +263,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct
}
}
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
return bridged;
}
@@ -287,17 +283,19 @@ static int local_queryoption(struct ast_channel *ast, int option, void *data, in
return -1;
}
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
try_again:
if (!chan) {
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
return -1;
}
if (ast_channel_trylock(chan)) {
- DEADLOCK_AVOIDANCE(&p->lock);
+ ao2_unlock(p);
+ sched_yield();
+ ao2_lock(p);
chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
goto try_again;
}
@@ -305,25 +303,35 @@ try_again:
bridged = ast_bridged_channel(chan);
if (!bridged) {
/* can't query channel unless we are bridged */
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
ast_channel_unlock(chan);
return -1;
}
if (ast_channel_trylock(bridged)) {
ast_channel_unlock(chan);
- DEADLOCK_AVOIDANCE(&p->lock);
+ ao2_unlock(p);
+ sched_yield();
+ ao2_lock(p);
chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
goto try_again;
}
res = ast_channel_queryoption(bridged, option, data, datalen, 0);
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
ast_channel_unlock(chan);
ast_channel_unlock(bridged);
return res;
}
+/*! \brief queue a frame on a to either the p->owner or p->chan
+ *
+ * \note the local_pvt MUST have it's ref count bumped before entering this function and
+ * decremented after this function is called. This is a side effect of the deadlock
+ * avoidance that is necessary to lock 2 channels and a tech_pvt. Without a ref counted
+ * local_pvt, it is impossible to guarantee it will not be destroyed by another thread
+ * during deadlock avoidance.
+ */
static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f,
struct ast_channel *us, int us_locked)
{
@@ -341,41 +349,24 @@ static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_fra
return 0;
}
- /* Set glare detection */
- ast_set_flag(p, LOCAL_GLARE_DETECT);
-
/* Ensure that we have both channels locked */
while (other && ast_channel_trylock(other)) {
int res;
- if ((res = ast_mutex_unlock(&p->lock))) {
+ if ((res = ao2_unlock(p))) {
ast_log(LOG_ERROR, "chan_local bug! '&p->lock' was not locked when entering local_queue_frame! (%s)\n", strerror(res));
return -1;
}
if (us && us_locked) {
do {
CHANNEL_DEADLOCK_AVOIDANCE(us);
- } while (ast_mutex_trylock(&p->lock));
+ } while (ao2_trylock(p));
} else {
usleep(1);
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
}
other = isoutbound ? p->owner : p->chan;
}
- /* Since glare detection only occurs within this function, and because
- * a pvt flag cannot be set without having the pvt lock, this is the only
- * location where we could detect a cancelling of the queue. */
- if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
- /* We had a glare on the hangup. Forget all this business,
- return and destroy p. */
- ast_mutex_unlock(&p->lock);
- p = local_pvt_destroy(p);
- if (other) {
- ast_channel_unlock(other);
- }
- return -1;
- }
-
if (other) {
if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_RINGING) {
ast_setstate(other, AST_STATE_RINGING);
@@ -384,8 +375,6 @@ static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_fra
ast_channel_unlock(other);
}
- ast_clear_flag(p, LOCAL_GLARE_DETECT);
-
return 0;
}
@@ -398,16 +387,18 @@ static int local_answer(struct ast_channel *ast)
if (!p)
return -1;
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
+ ao2_ref(p, 1);
isoutbound = IS_OUTBOUND(ast, p);
if (isoutbound) {
/* Pass along answer since somebody answered us */
struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
res = local_queue_frame(p, isoutbound, &answer, ast, 1);
- } else
+ } else {
ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
- if (!res)
- ast_mutex_unlock(&p->lock);
+ }
+ ao2_unlock(p);
+ ao2_ref(p, -1);
return res;
}
@@ -498,7 +489,8 @@ static int local_write(struct ast_channel *ast, struct ast_frame *f)
return -1;
/* Just queue for delivery to the other side */
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
+ ao2_ref(p, 1); /* ref for local_queue_frame */
isoutbound = IS_OUTBOUND(ast, p);
if (isoutbound && f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
check_bridge(p);
@@ -508,8 +500,9 @@ static int local_write(struct ast_channel *ast, struct ast_frame *f)
ast_debug(1, "Not posting to queue since already masked on '%s'\n", ast->name);
res = 0;
}
- if (!res)
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
+ ao2_ref(p, -1);
+
return res;
}
@@ -520,11 +513,11 @@ static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
if (!p)
return -1;
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
if ((p->owner != oldchan) && (p->chan != oldchan)) {
ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
return -1;
}
if (p->owner == oldchan)
@@ -535,12 +528,12 @@ static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
/* Do not let a masquerade cause a Local channel to be bridged to itself! */
if (!ast_check_hangup(newchan) && (p->owner->_bridge == p->chan || p->chan->_bridge == p->owner)) {
ast_log(LOG_WARNING, "You can not bridge a Local channel to itself!\n");
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
ast_queue_hangup(newchan);
return -1;
}
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
return 0;
}
@@ -554,6 +547,8 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da
if (!p)
return -1;
+ ao2_ref(p, 1); /* ref for local_queue_frame */
+
/* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_HOLD) {
ast_moh_start(ast, data, NULL);
@@ -561,15 +556,16 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da
ast_moh_stop(ast);
} else {
/* Queue up a frame representing the indication as a control frame */
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
isoutbound = IS_OUTBOUND(ast, p);
f.subclass = condition;
f.data.ptr = (void*)data;
f.datalen = datalen;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1)))
- ast_mutex_unlock(&p->lock);
+ res = local_queue_frame(p, isoutbound, &f, ast, 1);
+ ao2_unlock(p);
}
+ ao2_ref(p, -1);
return res;
}
@@ -583,11 +579,13 @@ static int local_digit_begin(struct ast_channel *ast, char digit)
if (!p)
return -1;
- ast_mutex_lock(&p->lock);
+ ao2_ref(p, 1); /* ref for local_queue_frame */
+ ao2_lock(p);
isoutbound = IS_OUTBOUND(ast, p);
f.subclass = digit;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
- ast_mutex_unlock(&p->lock);
+ res = local_queue_frame(p, isoutbound, &f, ast, 0);
+ ao2_unlock(p);
+ ao2_ref(p, -1);
return res;
}
@@ -602,12 +600,14 @@ static int local_digit_end(struct ast_channel *ast, char digit, unsigned int dur
if (!p)
return -1;
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
+ ao2_ref(p, 1); /* ref for local_queue_frame */
isoutbound = IS_OUTBOUND(ast, p);
f.subclass = digit;
f.len = duration;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
- ast_mutex_unlock(&p->lock);
+ res = local_queue_frame(p, isoutbound, &f, ast, 0);
+ ao2_unlock(p);
+ ao2_ref(p, -1);
return res;
}
@@ -622,12 +622,14 @@ static int local_sendtext(struct ast_channel *ast, const char *text)
if (!p)
return -1;
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
+ ao2_ref(p, 1); /* ref for local_queue_frame */
isoutbound = IS_OUTBOUND(ast, p);
f.data.ptr = (char *) text;
f.datalen = strlen(text) + 1;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
- ast_mutex_unlock(&p->lock);
+ res = local_queue_frame(p, isoutbound, &f, ast, 0);
+ ao2_unlock(p);
+ ao2_ref(p, -1);
return res;
}
@@ -641,13 +643,15 @@ static int local_sendhtml(struct ast_channel *ast, int subclass, const char *dat
if (!p)
return -1;
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
+ ao2_ref(p, 1); /* ref for local_queue_frame */
isoutbound = IS_OUTBOUND(ast, p);
f.subclass = subclass;
f.data.ptr = (char *)data;
f.datalen = datalen;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
- ast_mutex_unlock(&p->lock);
+ res = local_queue_frame(p, isoutbound, &f, ast, 0);
+ ao2_unlock(p);
+ ao2_ref(p, -1);
return res;
}
@@ -656,14 +660,14 @@ static int local_sendhtml(struct ast_channel *ast, int subclass, const char *dat
static int local_call(struct ast_channel *ast, char *dest, int timeout)
{
struct local_pvt *p = ast->tech_pvt;
- int res;
+ int res = 0;
struct ast_var_t *varptr = NULL, *new;
size_t len, namelen;
if (!p)
return -1;
-
- ast_mutex_lock(&p->lock);
+
+ ao2_lock(p);
/*
* Note that cid_num and cid_name aren't passed in the ast_channel_alloc
@@ -686,7 +690,7 @@ static int local_call(struct ast_channel *ast, char *dest, int timeout)
if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
return -1;
}
@@ -712,7 +716,7 @@ static int local_call(struct ast_channel *ast, char *dest, int timeout)
if (!(res = ast_pbx_start(p->chan)))
ast_set_flag(p, LOCAL_LAUNCHED_PBX);
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
return res;
}
@@ -723,12 +727,15 @@ static int local_hangup(struct ast_channel *ast)
int isoutbound;
struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP, .data.uint32 = ast->hangupcause };
struct ast_channel *ochan = NULL;
- int glaredetect = 0, res = 0;
if (!p)
return -1;
- ast_mutex_lock(&p->lock);
+ /* we MUST give the tech_pvt a ref here since we are unlocking the
+ * channel during deadlock avoidance. */
+ ao2_ref(p, 1);
+
+ ao2_lock(p);
isoutbound = IS_OUTBOUND(ast, p);
@@ -742,74 +749,88 @@ static int local_hangup(struct ast_channel *ast)
if ((status) && (p->owner)) {
/* Deadlock avoidance */
while (p->owner && ast_channel_trylock(p->owner)) {
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
if (p->chan) {
ast_channel_unlock(p->chan);
}
- usleep(1);
+ sched_yield();
if (p->chan) {
ast_channel_lock(p->chan);
}
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
}
if (p->owner) {
pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
ast_channel_unlock(p->owner);
}
}
+ if (!p->chan) {
+ /* chan was == to ast and was !NULL before deadlock avoidance started, if chan
+ * is NULL now, then we should bail because that channel
+ * hungup already. This is possible because we let go of the
+ * lock given to the ast channel passed to this function during
+ * deadlock avoidance. */
+ ao2_unlock(p);
+ ao2_ref(p, -1);
+ return 0;
+ }
p->chan = NULL;
ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
ast_module_user_remove(p->u_chan);
} else {
ast_module_user_remove(p->u_owner);
while (p->chan && ast_channel_trylock(p->chan)) {
- ast_mutex_unlock(&p->lock);
+ ao2_unlock(p);
if (p->owner) {
ast_channel_unlock(p->owner);
}
- usleep(1);
+ sched_yield();
if (p->owner) {
ast_channel_lock(p->owner);
}
- ast_mutex_lock(&p->lock);
+ ao2_lock(p);
}
-
- p->owner = NULL;
if (p->chan) {
ast_queue_hangup(p->chan);
ast_channel_unlock(p->chan);
}
+
+ if (!p->owner) {
+ /* owner was == to ast and was !NULL before deadlock avoidance started, if
+ * owner is NULL now, then we should bail because that channel
+ * hungup already. This is possible because we let go of the
+ * lock given to the ast channel passed to this function during
+ * deadlock avoidance. */
+ ao2_unlock(p);
+ ao2_ref(p, -1);
+ return 0;
+ }
+ p->owner = NULL;
}
-
+
ast->tech_pvt = NULL;
-
+
if (!p->owner && !p->chan) {
- /* Okay, done with the private part now, too. */
- glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
- /* If we have a queue holding, don't actually destroy p yet, but
- let local_queue do it. */
- if (glaredetect)
- ast_set_flag(p, LOCAL_CANCEL_QUEUE);
+ ao2_unlock(p);
+
/* Remove from list */
- AST_LIST_LOCK(&locals);
- AST_LIST_REMOVE(&locals, p, list);
- AST_LIST_UNLOCK(&locals);
- ast_mutex_unlock(&p->lock);
- /* And destroy */
- if (!glaredetect) {
- p = local_pvt_destroy(p);
- }
+ ao2_unlink(locals, p);
+ ao2_ref(p, -1);
return 0;
}
- if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
+ if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX)) {
/* Need to actually hangup since there is no PBX */
ochan = p->chan;
- else
- res = local_queue_frame(p, isoutbound, &f, NULL, 1);
- if (!res)
- ast_mutex_unlock(&p->lock);
- if (ochan)
+ } else {
+ local_queue_frame(p, isoutbound, &f, NULL, 1);
+ }
+
+ ao2_unlock(p);
+ if (ochan) {
ast_hangup(ochan);
+ }
+
+ ao2_ref(p, -1);
return 0;
}
@@ -819,11 +840,11 @@ static struct local_pvt *local_alloc(const char *data, int format)
struct local_pvt *tmp = NULL;
char *c = NULL, *opts = NULL;
- if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ if (!(tmp = ao2_alloc(sizeof(*tmp), NULL))) {
return NULL;
+ }
/* Initialize private structure information */
- ast_mutex_init(&tmp->lock);
ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
memcpy(&tmp->jb_conf, &g_jb_conf, sizeof(tmp->jb_conf));
@@ -867,14 +888,11 @@ static struct local_pvt *local_alloc(const char *data, int format)
} else {
#endif
/* Add to list */
- AST_LIST_LOCK(&locals);
- AST_LIST_INSERT_HEAD(&locals, tmp, list);
- AST_LIST_UNLOCK(&locals);
+ ao2_link(locals, tmp);
#if 0
}
#endif
-
- return tmp;
+ return tmp; /* this is returned with a ref */
}
/*! \brief Start new local channel */
@@ -950,11 +968,9 @@ static struct ast_channel *local_request(const char *type, int format, void *dat
/* Allocate a new private structure and then Asterisk channel */
if ((p = local_alloc(data, format))) {
if (!(chan = local_new(p, AST_STATE_DOWN))) {
- AST_LIST_LOCK(&locals);
- AST_LIST_REMOVE(&locals, p, list);
- AST_LIST_UNLOCK(&locals);
- p = local_pvt_destroy(p);
+ ao2_unlink(locals, p);
}
+ ao2_ref(p, -1); /* kill the ref from the alloc */
}
return chan;
@@ -964,6 +980,7 @@ static struct ast_channel *local_request(const char *type, int format, void *dat
static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct local_pvt *p = NULL;
+ struct ao2_iterator it;
switch (cmd) {
case CLI_INIT:
@@ -979,16 +996,19 @@ static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
if (a->argc != 3)
return CLI_SHOWUSAGE;
- AST_LIST_LOCK(&locals);
- if (!AST_LIST_EMPTY(&locals)) {
- AST_LIST_TRAVERSE(&locals, p, list) {
- ast_mutex_lock(&p->lock);
- ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
- ast_mutex_unlock(&p->lock);
- }
- } else
+ if (ao2_container_count(locals) == 0) {
ast_cli(a->fd, "No local channels in use\n");
- AST_LIST_UNLOCK(&locals);
+ return RESULT_SUCCESS;
+ }
+
+ it = ao2_iterator_init(locals, 0);
+ while ((p = ao2_iterator_next(&it))) {
+ ao2_lock(p);
+ ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
+ ao2_unlock(p);
+ ao2_ref(p, -1);
+ }
+ ao2_iterator_destroy(&it);
return CLI_SUCCESS;
}
@@ -997,12 +1017,22 @@ static struct ast_cli_entry cli_local[] = {
AST_CLI_DEFINE(locals_show, "List status of local channels"),
};
+static int locals_cmp_cb(void *obj, void *arg, int flags)
+{
+ return (obj == arg) ? CMP_MATCH : 0;
+}
+
/*! \brief Load module into PBX, register channel */
static int load_module(void)
{
+ if (!(locals = ao2_container_alloc(BUCKET_SIZE, NULL, locals_cmp_cb))) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
/* Make sure we can register our channel type */
if (ast_channel_register(&local_tech)) {
ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
+ ao2_ref(locals, -1);
return AST_MODULE_LOAD_FAILURE;
}
ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
@@ -1013,21 +1043,21 @@ static int load_module(void)
static int unload_module(void)
{
struct local_pvt *p = NULL;
+ struct ao2_iterator it;
/* First, take us out of the channel loop */
ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
ast_channel_unregister(&local_tech);
- if (!AST_LIST_LOCK(&locals)) {
- /* Hangup all interfaces if they have an owner */
- AST_LIST_TRAVERSE(&locals, p, list) {
- if (p->owner)
- ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+
+ it = ao2_iterator_init(locals, 0);
+ while ((p = ao2_iterator_next(&it))) {
+ if (p->owner) {
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
}
- AST_LIST_UNLOCK(&locals);
- } else {
- ast_log(LOG_WARNING, "Unable to lock the monitor\n");
- return -1;
- }
+ ao2_ref(p, -1);
+ }
+ ao2_iterator_destroy(&it);
+ ao2_ref(locals, -1);
return 0;
}