/* 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));
+ iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
} else if (!ast_strlen_zero(exten)) {
- iter = ast_channel_iterator_by_exten_new(0, exten, context);
+ iter = ast_channel_iterator_by_exten_new(exten, context);
} else {
- iter = ast_channel_iterator_all_new(0);
+ iter = ast_channel_iterator_all_new();
}
if (!iter) {
struct ast_channel_iterator *iter;
int res = -1;
- if (!(iter = ast_channel_iterator_by_exten_new(0, exten, context))) {
+ if (!(iter = ast_channel_iterator_by_exten_new(exten, context))) {
return -1;
}
target = ast_channel_unref(target);
}
+ ast_channel_iterator_destroy(iter);
+
if (target) {
res = pickup_do(chan, target);
ast_channel_unlock(target);
ast_app_parse_options(app_opts, &flags, opts, args.options);
lenmatch = strlen(args.channel);
- if (!(iter = ast_channel_iterator_by_name_new(0, args.channel, lenmatch))) {
+ if (!(iter = ast_channel_iterator_by_name_new(args.channel, lenmatch))) {
return -1;
}
}
}
- if (!(iter = ast_channel_iterator_all_new(0))) {
+ if (!(iter = ast_channel_iterator_all_new())) {
if (!ast_strlen_zero(data)) {
regfree(&re);
}
#define _ASTERISK_ASTOBJ2_H
#include "asterisk/compat.h"
+#include "asterisk/linkedlists.h"
/*! \file
* \ref AstObj2
can be:
OBJ_UNLINK - to remove the object, once found, from the container.
OBJ_NODATA - don't return the object if found (no ref count change)
- OBJ_MULTIPLE - don't stop at first match (not fully implemented)
+ OBJ_MULTIPLE - don't stop at first match
OBJ_POINTER - if set, 'arg' is an object pointer, and a hashtable
search will be done. If not, a traversal is done.
- flags can be
OBJ_UNLINK - to remove the object, once found, from the container.
OBJ_NODATA - don't return the object if found (no ref count change)
- OBJ_MULTIPLE - don't stop at first match (not fully implemented)
+ OBJ_MULTIPLE - don't stop at first match
OBJ_POINTER - if set, 'arg' is an object pointer, and a hashtable
search will be done. If not, a traversal is done through
all the hashtable 'buckets'..
*/
enum search_flags {
/*! Unlink the object for which the callback function
- * returned CMP_MATCH . This is the only way to extract
- * objects from a container. */
+ * returned CMP_MATCH.
+ */
OBJ_UNLINK = (1 << 0),
/*! On match, don't return the object hence do not increase
- * its refcount. */
+ * its refcount.
+ */
OBJ_NODATA = (1 << 1),
- /*! Don't stop at the first match in ao2_callback()
- * \note This is not fully implemented. Using OBJ_MULTIME with OBJ_NODATA
- * is perfectly fine. The part that is not implemented is the case where
- * multiple objects should be returned by ao2_callback().
+ /*! Don't stop at the first match in ao2_callback().
*/
OBJ_MULTIPLE = (1 << 2),
/*! obj is an object of the same type as the one being searched for,
* so use the object's hash function for optimized searching.
* The search function is unaffected (i.e. use the one passed as
- * argument, or match_by_addr if none specified). */
+ * argument, or match_by_addr if none specified).
+ */
OBJ_POINTER = (1 << 3),
/*!
* \brief Continue if a match is not found in the hashed out bucket
void *__ao2_unlink(struct ao2_container *c, void *obj);
-/*! \brief Used as return value if the flag OBJ_MULTIPLE is set */
-struct ao2_list {
- struct ao2_list *next;
- void *obj; /* pointer to the user portion of the object */
-};
-
/*@} */
/*! \brief
should immediately stop, or both (via bitwise ORing), if you find a
match and want to end the traversal, and 0 if the object is not a match,
but the traversal should continue. This is the function that is applied
- to each object traversed. It's arguments are:
+ to each object traversed. Its arguments are:
(void *obj, void *arg, int flags), where:
obj is an object
arg is the same as arg passed into ao2_callback
flags is the same as flags passed into ao2_callback (flags are
also used by ao2_callback).
* \param arg passed to the callback.
- * \return A pointer to the object found/marked,
- * a pointer to a list of objects matching comparison function,
- * NULL if not found.
+ * \return when OBJ_MULTIPLE is not included in the flags parameter,
+ * the return value will be either the object found or NULL if no
+ * no matching object was found. if OBJ_MULTIPLE is included,
+ * the return value will be a pointer to an ao2_iterator object,
+ * which must be destroyed with ao2_iterator_destroy() when the
+ * caller no longer needs it.
*
* If the function returns any objects, their refcount is incremented,
* and the caller is in charge of decrementing them once done.
- * Also, in case of multiple values returned, the list used
- * to store the objects must be freed by the caller.
*
* Typically, ao2_callback() is used for two purposes:
* - to perform some action (including removal from the container) on one
* we can say this looking at flags value.
* If p points to an object we will search for the object pointed
* by this value, otherwise we serch for a key value.
- * If the key is not uniq we only find the first matching valued.
- * If we use the OBJ_MARK flags, we mark all the objects matching
- * the condition.
+ * If the key is not unique we only find the first matching valued.
*
* The use of flags argument is the follow:
*
* Callbacks use OBJ_NODATA as a default
* functions such as find() do
* OBJ_MULTIPLE return multiple matches
- * Default for _find() is no.
- * to a key (not yet supported)
+ * Default is no.
* OBJ_POINTER the pointer is an object pointer
*
- * In case we return a list, the callee must take care to destroy
- * that list when no longer used.
- *
* \note When the returned object is no longer in use, ao2_ref() should
* be used to free the additional reference possibly created by this function.
*
* while retrieving the next object from it.
*/
AO2_ITERATOR_DONTLOCK = (1 << 0),
+ /*! Indicates that the iterator was dynamically allocated by
+ * astobj2 API and should be freed by ao2_iterator_destroy().
+ */
+ AO2_ITERATOR_MALLOCD = (1 << 1),
+ /*! Indicates that before the iterator returns an object from
+ * the container being iterated, the object should be unlinked
+ * from the container.
+ */
+ AO2_ITERATOR_UNLINK = (1 << 2),
};
/*!
/*!
* \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)
*
*
* \since 1.6.3
*/
-struct ast_channel_iterator *ast_channel_iterator_by_exten_new(int ao2_flags, const char *exten,
- const char *context);
+struct ast_channel_iterator *ast_channel_iterator_by_exten_new(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
*
* \since 1.6.3
*/
-struct ast_channel_iterator *ast_channel_iterator_by_name_new(int ao2_flags, const char *name,
- size_t name_len);
+struct ast_channel_iterator *ast_channel_iterator_by_name_new(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.
*
*
* \since 1.6.3
*/
-struct ast_channel_iterator *ast_channel_iterator_all_new(int ao2_flags);
+struct ast_channel_iterator *ast_channel_iterator_all_new(void);
/*!
* \brief Get the next channel for a channel iterator
#endif
#include "asterisk/logger.h"
-#include "asterisk/astobj2.h"
/* internal macro to profile mutexes. Only computes the delay on
* non-blocking calls.
* first word of the user-data, which we make sure is always
* allocated. */
memset(obj, '\0', sizeof(struct astobj2 *) + sizeof(void *) );
- free(obj);
+ ast_free(obj);
}
return ret;
void *ret = NULL;
ao2_callback_fn *cb_default = NULL;
ao2_callback_data_fn *cb_withdata = NULL;
+ struct ao2_container *multi_container = NULL;
+ struct ao2_iterator *multi_iterator = NULL;
if (INTERNAL_OBJ(c) == NULL) /* safety check on the argument */
return NULL;
if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
- ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
- return NULL;
+ /* we need to return an ao2_iterator with the results,
+ * as there could be more than one. the iterator will
+ * hold the only reference to a container that has all the
+ * matching objects linked into it, so when the iterator
+ * is destroyed, the container will be automatically
+ * destroyed as well.
+ */
+ if (!(multi_container = __ao2_container_alloc(1, NULL, NULL))) {
+ return NULL;
+ }
+ if (!(multi_iterator = ast_calloc(1, sizeof(*multi_iterator)))) {
+ ao2_ref(multi_container, -1);
+ return NULL;
+ }
}
/* override the match function if necessary */
}
} else {
/* We do this here to avoid the per object casting penalty (even though
- that is probably optimized away anyway. */
+ that is probably optimized away anyway). */
if (type == WITH_DATA) {
cb_withdata = cb_fn;
} else {
i = last;
break;
}
+
/* we have a match (CMP_MATCH) here */
if (!(flags & OBJ_NODATA)) { /* if must return the object, record the value */
/* it is important to handle this case before the unlink */
ret = EXTERNAL_OBJ(cur->astobj);
- if (tag)
- __ao2_ref_debug(ret, 1, tag, file, line, funcname);
- else
- __ao2_ref(ret, 1);
+ if (!(flags & (OBJ_UNLINK | OBJ_MULTIPLE))) {
+ if (tag)
+ __ao2_ref_debug(ret, 1, tag, file, line, funcname);
+ else
+ __ao2_ref(ret, 1);
+ }
}
- if (flags & OBJ_UNLINK) { /* must unlink */
- struct bucket_list *x = cur;
+ /* if we are in OBJ_MULTIPLE mode, link the object into the
+ * container that will hold the results
+ */
+ if (ret && (multi_container != NULL)) {
+ __ao2_link(multi_container, ret);
+ ret = NULL;
+ }
+ if (flags & OBJ_UNLINK) { /* must unlink */
/* we are going to modify the container, so update version */
ast_atomic_fetchadd_int(&c->version, 1);
AST_LIST_REMOVE_CURRENT(entry);
- /* update number of elements and version */
+ /* update number of elements */
ast_atomic_fetchadd_int(&c->elements, -1);
- if (tag)
- __ao2_ref_debug(EXTERNAL_OBJ(x->astobj), -1, tag, file, line, funcname);
- else
- __ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
- free(x); /* free the link record */
+ if (!(flags & OBJ_NODATA)) {
+ if (tag)
+ __ao2_ref_debug(EXTERNAL_OBJ(cur->astobj), -1, tag, file, line, funcname);
+ else
+ __ao2_ref(EXTERNAL_OBJ(cur->astobj), -1);
+ }
+ ast_free(cur); /* free the link record */
}
- if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
+ if ((match & CMP_STOP) || (multi_container == NULL)) {
/* We found the only match we need */
i = last; /* force exit from outer loop */
break;
}
- if (!(flags & OBJ_NODATA)) {
-#if 0 /* XXX to be completed */
- /*
- * This is the multiple-return case. We need to link
- * the object in a list. The refcount is already increased.
- */
-#endif
- }
}
AST_LIST_TRAVERSE_SAFE_END;
if (ret) {
- /* This assumes OBJ_MULTIPLE with !OBJ_NODATA is still not implemented */
break;
}
}
}
ao2_unlock(c);
- return ret;
+ if (multi_container != NULL) {
+ *multi_iterator = ao2_iterator_init(multi_container,
+ AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK | AO2_ITERATOR_MALLOCD);
+ ao2_ref(multi_container, -1);
+ return multi_iterator;
+ } else {
+ return ret;
+ }
}
void *__ao2_callback_debug(struct ao2_container *c,
void ao2_iterator_destroy(struct ao2_iterator *i)
{
ao2_ref(i->c, -1);
- i->c = NULL;
+ if (i->flags & AO2_ITERATOR_MALLOCD) {
+ ast_free(i);
+ } else {
+ i->c = NULL;
+ }
}
/*
/* optimization. If the container is unchanged and
* we have a pointer, try follow it
*/
- if (a->c->version == a->c_version && (p = a->obj) ) {
- if ( (p = AST_LIST_NEXT(p, entry)) )
+ if (a->c->version == a->c_version && (p = a->obj)) {
+ if ((p = AST_LIST_NEXT(p, entry)))
goto found;
/* nope, start from the next bucket */
a->bucket++;
found:
if (p) {
- a->version = p->version;
- a->obj = p;
- a->c_version = a->c->version;
ret = EXTERNAL_OBJ(p->astobj);
- /* inc refcount of returned object */
- *q = p;
+ if (a->flags & AO2_ITERATOR_UNLINK) {
+ /* we are going to modify the container, so update version */
+ ast_atomic_fetchadd_int(&a->c->version, 1);
+ AST_LIST_REMOVE(&a->c->buckets[a->bucket], p, entry);
+ /* update number of elements */
+ ast_atomic_fetchadd_int(&a->c->elements, -1);
+ a->version = 0;
+ a->obj = NULL;
+ a->c_version = a->c->version;
+ ast_free(p);
+ } else {
+ a->version = p->version;
+ a->obj = p;
+ a->c_version = a->c->version;
+ /* inc refcount of returned object */
+ *q = p;
+ }
}
return ret;
}
struct ast_channel_iterator {
- struct ao2_iterator i;
- const char *name;
- size_t name_len;
- const char *exten;
- const char *context;
+ /* storage for non-dynamically allocated iterator */
+ struct ao2_iterator simple_iterator;
+ /* pointer to the actual iterator (simple_iterator or a dynamically
+ * allocated iterator)
+ */
+ struct ao2_iterator *active_iterator;
};
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;
- }
-
+ ao2_iterator_destroy(i->active_iterator);
ast_free(i);
return NULL;
}
-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)
+static struct ast_channel_iterator *channel_iterator_search(const char *name,
+ size_t name_len, const char *exten,
+ const char *context)
{
struct ast_channel_iterator *i;
+ 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 (!(i = ast_calloc(1, sizeof(*i)))) {
return NULL;
}
- if (!ast_strlen_zero(exten) && !(i->exten = ast_strdup(exten))) {
- goto return_error;
+ if (exten) {
+ ast_copy_string(tmp_chan.exten, exten, sizeof(tmp_chan.exten));
}
- if (!ast_strlen_zero(context) && !(i->context = ast_strdup(context))) {
- goto return_error;
+ if (context) {
+ ast_copy_string(tmp_chan.context, context, sizeof(tmp_chan.context));
}
- if (!ast_strlen_zero(name) && !(i->name = ast_strdup(name))) {
- goto return_error;
+ if (!(i->active_iterator = ao2_find(channels, &tmp_chan,
+ OBJ_MULTIPLE | ((!ast_strlen_zero(name) && (name_len == 0)) ? OBJ_POINTER : 0)))) {
+ ast_free(i);
+ return NULL;
}
- 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;
-}
-
-struct ast_channel_iterator *ast_channel_iterator_by_exten_new(int ao2_flags, const char *exten,
- const char *context)
-{
- return ast_channel_iterator_new(ao2_flags, NULL, 0, exten, context);
}
-struct ast_channel_iterator *ast_channel_iterator_by_name_new(int ao2_flags, const char *name,
- size_t name_len)
+struct ast_channel_iterator *ast_channel_iterator_by_exten_new(const char *exten, const char *context)
{
- return ast_channel_iterator_new(ao2_flags, name, name_len, NULL, NULL);
+ return channel_iterator_search(NULL, 0, exten, context);
}
-struct ast_channel_iterator *ast_channel_iterator_all_new(int ao2_flags)
+struct ast_channel_iterator *ast_channel_iterator_by_name_new(const char *name, size_t name_len)
{
- return ast_channel_iterator_new(ao2_flags, NULL, 0, NULL, NULL);
+ return channel_iterator_search(name, name_len, NULL, NULL);
}
-/*!
- * \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)
+struct ast_channel_iterator *ast_channel_iterator_all_new(void)
{
- 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 */
- }
+ struct ast_channel_iterator *i;
- if (strcasecmp(chan->exten, i->exten) &&
- strcasecmp(chan->macroexten, i->exten)) {
- continue; /* exten match failed */
- }
- }
+ if (!(i = ast_calloc(1, sizeof(*i)))) {
+ return NULL;
+ }
- ast_channel_unlock(chan);
+ i->simple_iterator = ao2_iterator_init(channels, 0);
+ i->active_iterator = &i->simple_iterator;
- break; /* chan points to the next chan desired. */
- }
+ return i;
+}
- return chan;
+struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i)
+{
+ return ao2_iterator_next(i->active_iterator);
}
static int ast_channel_cmp_cb(void *obj, void *arg, int flags)
}
static struct ast_channel *ast_channel_get_full(const char *name, size_t name_len,
- const char *exten, const char *context)
+ const char *exten, const char *context)
{
struct ast_channel tmp_chan = {
.name = name,
"CallerID", "Duration", "Accountcode", "PeerAccount", "BridgedTo");
}
- if (!count && !(iter = ast_channel_iterator_all_new(0))) {
+ if (!count && !(iter = ast_channel_iterator_all_new())) {
return CLI_FAILURE;
}
return NULL;
}
- if (!(iter = ast_channel_iterator_by_name_new(0, word, strlen(word)))) {
+ if (!(iter = ast_channel_iterator_by_name_new(word, strlen(word)))) {
return NULL;
}
ast_channel_unref(c);
}
+ ast_channel_iterator_destroy(iter);
+
return ret == ¬found ? NULL : ret;
}
}
if (all) {
- if (!(iter = ast_channel_iterator_all_new(0))) {
+ if (!(iter = ast_channel_iterator_all_new())) {
ast_free(str);
astman_send_error(s, m, "Memory Allocation Failure");
return 1;
}
}
+ if (iter) {
+ ast_channel_iterator_destroy(iter);
+ }
+
astman_append(s,
"Event: StatusComplete\r\n"
"%s"
idText[0] = '\0';
}
- if (!(iter = ast_channel_iterator_all_new(0))) {
+ if (!(iter = ast_channel_iterator_all_new())) {
astman_send_error(s, m, "Memory Allocation Failure");
return 1;
}
i = name[*length - 1] - 1;
- if (!(iter = ast_channel_iterator_all_new(0))) {
+ if (!(iter = ast_channel_iterator_all_new())) {
return NULL;
}
long_ret = 0;
- if (!(iter = ast_channel_iterator_all_new(0))) {
+ if (!(iter = ast_channel_iterator_all_new())) {
return NULL;
}
return NULL;
}
- if (!(iter = ast_channel_iterator_all_new(0))) {
+ if (!(iter = ast_channel_iterator_all_new())) {
return NULL;
}
chan = ast_channel_unref(chan);
}
+ ast_channel_iterator_destroy(iter);
+
*var_len = sizeof(long_ret);
return (vp->magic == ASTCHANBRIDGECOUNT) ? (u_char *) &long_ret : NULL;