menuselect/makeopts: makeopts .lastclean
+$(MAKE_MENUSELECT) makeopts
-menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc) $(wildcard $(dir)/*.xml)) build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml utils/utils.xml agi/agi.xml configure makeopts
+menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc) $(wildcard $(dir)/*.xml)) main/channelstorage_makeopts.xml build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml utils/utils.xml agi/agi.xml configure makeopts
@echo "Generating input for menuselect ..."
@echo "<?xml version=\"1.0\"?>" > $@
@echo >> $@
@echo "<menu name=\"Asterisk Module and Build Option Selection\">" >> $@
+@for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SILENTMAKE) -C $${dir} SUBDIR=$${dir} moduleinfo >> $@; done
+ @cat main/channelstorage_makeopts.xml >> $@
@cat build_tools/cflags.xml >> $@
+@for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SILENTMAKE) -C $${dir} SUBDIR=$${dir} makeopts >> $@; done
@if [ "${AST_DEVMODE}" = "yes" ]; then \
; cause Asterisk to search for sounds files in
; AST_DATA_DIR/sounds/custom before searching the
; normal directories like AST_DATA_DIR/sounds/<lang>.
+;channel_storage_backend = ao2_legacy ; Select the channel storage backend
+ ; to use for live operation.
+ ; ao2_legacy: Original implementation (default)
+ ; Depending on compile options, the following may also be
+ ; available:
+ ; cpp_map_name_id: Use C++ Maps to index both
+ ; channel name and channel uniqueid.
+ ; See http://s.asterisk.net/dc679ec3 for more information.
;disable_remote_console_shell = no; Prevent remote console CLI sessions
; from executing shell commands with the '!' prefix.
; Default: no
/*!
* \brief Change channel name
*
- * \pre Absolutely all channels _MUST_ be unlocked before calling this function.
+ * \pre Absolutely all channels and the channel storage backend _MUST_ be
+ * unlocked before calling this function.
*
* \param chan the channel to change the name of
* \param newname the name to change to
*
- * \note this function must _NEVER_ be used when any channels are locked
- * regardless if it is the channel who's name is being changed or not because
- * it invalidates our channel container locking order... lock container first,
- * then the individual channels, never the other way around.
+ * \note this function must _NEVER_ be used when any channels or the channel
+ * storage backend are locked regardless if it is the channel who's name is
+ * being changed or not because it invalidates our channel container locking
+ * order... lock container first, then the individual channels, never the
+ * other way around.
*/
void ast_change_name(struct ast_channel *chan, const char *newname);
/*! @} End channel iterator definitions. */
+/*! @{ Channel search functions */
+
+/*!
+* \warning Absolutely _NO_ channel locks should be held while calling any of
+* these functions.
+*/
+
/*!
* \brief Call a function with every active channel
*
* This function executes a callback one time for each active channel on the
* system. The channel is provided as an argument to the function.
*
- * \note Absolutely _NO_ channel locks should be held before calling this function.
* \since 1.8
*/
struct ast_channel *ast_channel_callback(ao2_callback_data_fn *cb_fn, void *arg,
void *data, int ao2_flags);
-/*! @{ Channel search functions */
-
/*!
- * \brief Find a channel by name
+ * \brief Find a channel by name or uniqueid
*
- * \param name the name or uniqueid of the channel to search for
+ * \param search the name or uniqueid of the channel to search for
*
* \details
- * Find a channel that has the same name as the provided argument.
+ * First searches for a channel with a matching name. If not found
+ * a search for a channel with a matching uniqueid is done.
*
- * \retval a channel with the name specified by the argument
+ * \retval a channel with a matching name or uniqueid
* \retval NULL if no channel was found
*
+ *\note The fallback search by uniqueid is a historical thing. If you
+ * know the search term is a uniqueid, use \ref ast_channel_get_by_uniqueid
+ * instead.
+ *
* \since 1.8
*/
-struct ast_channel *ast_channel_get_by_name(const char *name);
+struct ast_channel *ast_channel_get_by_name(const char *search);
/*!
* \brief Find a channel by a name prefix
*
- * \param name The channel name or uniqueid prefix to search for
- * \param name_len Only search for up to this many characters from the name
+ * \param search The channel name or uniqueid prefix to search for
+ * \param len Only search for up to this many characters from the search term
*
* \details
- * Find a channel that has the same name prefix as specified by the arguments.
+ * Search for a channel that has the same name prefix as specified by the
+ * search term. If not found, search for an exact match on the uniqueid.
+ * Searching by partial uniqueid doesn't make any sense as it's usually
+ * a system-name plus a timestamp and is not supported.
*
- * \retval a channel with the name prefix specified by the arguments
+ * \retval a channel with a matching name or uniqueid
* \retval NULL if no channel was found
*
+ *\note The fallback search by uniqueid is a historical thing. If you
+ * know the search term is a uniqueid, use \ref ast_channel_get_by_uniqueid
+ * instead.
+ *
* \since 1.8
*/
struct ast_channel *ast_channel_get_by_name_prefix(const char *name, size_t name_len);
*/
struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *context);
+/*!
+ * \brief Find a channel by a uniqueid
+ *
+ * \param uniqueid The uniqueid to search for
+ *
+ * \retval a channel with the uniqueid specified by the arguments
+ * \retval NULL if no channel was found
+ */
+struct ast_channel *ast_channel_get_by_uniqueid(const char *uniqueid);
+
/*! @} End channel search functions. */
/*!
#define ast_channel_has_tech_function(chan, function) \
(ast_channel_tech(chan) ? ast_channel_tech(chan)->function != NULL : 0)
+/*!
+ * \brief Get the name of the current channel storage driver
+ *
+ * \return The name of the current channel storage driver
+ */
+const char *ast_channel_get_current_storage_driver_name(void);
+
+/*!
+ * \internal
+ * \brief Set the current channel storage driver
+ *
+ * \param driver_name The name of the driver to set as the current driver
+ *
+ * \return 0 on success, -1 on failure
+ *
+ * \warning Changing the channel storage driver while Asterisk is running is
+ * not supported. This function will return an error if called while
+ * the ast_fully_booted flag is set. The function is exposed only
+ * because options.c needs it to set the driver when reading
+ * asterisk.conf.
+ */
+int internal_channel_set_current_storage_driver(const char *driver_name);
#endif /* _ASTERISK_CHANNEL_H */
void ast_channel_internal_swap_stream_topology(struct ast_channel *chan1,
struct ast_channel *chan2);
+/*! \brief The current channel storage driver */
+extern const struct ast_channelstorage_driver *current_channel_storage_driver;
+extern struct ast_channelstorage_instance *current_channel_storage_instance;
+
+void ast_channel_close_storage(void);
+int ast_channel_open_storage(void);
+
#endif /* ASTERISK_CHANNEL_INTERNAL_H */
#define AST_CACHE_DIR_LEN 512
#define AST_FILENAME_MAX 80
#define AST_CHANNEL_NAME 80 /*!< Max length of an ast_channel name */
+#define AST_CHANNEL_STORAGE_BACKEND_NAME_LEN 80 /*!< Max length of storage backend name */
/*! \ingroup main_options */
SRC:=$(filter-out libasteriskpj.c,$(SRC))
endif
OBJSFILTER:=$(MOD_OBJS) fskmodem_int.o fskmodem_float.o cygload.o buildinfo.o
-SRC_CC:=$(wildcard *.cc)
+
+SRC_CC:=$(filter-out $(addsuffix .cc,$(MENUSELECT_CHANNELSTORAGE)),$(wildcard *.cc))
+SRC:=$(filter-out $(addsuffix .c,$(MENUSELECT_CHANNELSTORAGE)),$(SRC))
+
OBJS=$(filter-out $(OBJSFILTER),$(SRC:.c=.o) $(SRC_CC:.cc=.oo))
# we need to link in the objects statically, not as a library, because
#include "asterisk/utf8.h"
#include "../defaults.h"
+#include "channelstorage.h"
/*** DOCUMENTATION
<managerEvent language="en_US" name="FullyBooted">
ast_cli(a->fd, " RTP dynamic payload types: %u-%u\n",
AST_RTP_PT_FIRST_DYNAMIC, AST_RTP_MAX_PT - 1);
}
+ ast_cli(a->fd, " Channel storage backend: %s\n",
+ ast_channel_get_current_storage_driver_name());
ast_cli(a->fd, " Shell on remote consoles: %s\n",
ast_option_disable_remote_console_shell ? "Disabled" : "Enabled");
#include "asterisk/stream.h"
#include "asterisk/message.h"
+#include "channelstorage.h"
+
/*** DOCUMENTATION
***/
/*! \brief the list of registered channel types */
static AST_RWLIST_HEAD_STATIC(backends, chanlist);
-/*! \brief All active channels on the system */
-static struct ao2_container *channels;
-
/*! \brief map AST_CAUSE's to readable string representations
*
* \ref causes.h
ast_channel_unlock(chan);
}
-static int ast_channel_softhangup_cb(void *obj, void *arg, int flags)
+static int ast_channel_softhangup_cb(void *obj, void *arg, void *data, int flags)
{
struct ast_channel *chan = obj;
void ast_softhangup_all(void)
{
- ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
+ ast_channel_callback(ast_channel_softhangup_cb, NULL, NULL, 0);
}
/*! \brief returns number of active/allocated channels */
int ast_active_channels(void)
{
- return channels ? ao2_container_count(channels) : 0;
+ return current_channel_storage_instance ? CHANNELSTORAGE_API(current_channel_storage_instance, active_channels) : 0;
}
int ast_undestroyed_channels(void)
static void ast_channel_destructor(void *obj);
static void ast_dummy_channel_destructor(void *obj);
-static int ast_channel_by_uniqueid_cb(void *obj, void *arg, void *data, int flags);
-static int does_id_conflict(const char *uniqueid)
+static int do_ids_conflict(const struct ast_assigned_ids *assignedids)
{
struct ast_channel *conflict;
- size_t length = 0;
- if (ast_strlen_zero(uniqueid)) {
+ if (!assignedids) {
return 0;
}
- conflict = ast_channel_callback(ast_channel_by_uniqueid_cb, (char *) uniqueid, &length, OBJ_NOLOCK);
- if (conflict) {
- ast_log(LOG_ERROR, "Channel Unique ID '%s' already in use by channel %s(%p)\n",
- uniqueid, ast_channel_name(conflict), conflict);
- ast_channel_unref(conflict);
- return 1;
+ if (!ast_strlen_zero(assignedids->uniqueid)) {
+ conflict = CHANNELSTORAGE_API(current_channel_storage_instance,
+ get_by_uniqueid, assignedids->uniqueid);
+ if (conflict) {
+ ast_log(LOG_ERROR, "Channel Unique ID '%s' already in use by channel %s(%p)\n",
+ assignedids->uniqueid, ast_channel_name(conflict), conflict);
+ ast_channel_unref(conflict);
+ return 1;
+ }
+ }
+
+ if (!ast_strlen_zero(assignedids->uniqueid2)) {
+ conflict = CHANNELSTORAGE_API(current_channel_storage_instance,
+ get_by_uniqueid, assignedids->uniqueid2);
+ if (conflict) {
+ ast_log(LOG_ERROR, "Channel Unique ID2 '%s' already in use by channel %s(%p)\n",
+ assignedids->uniqueid2, ast_channel_name(conflict), conflict);
+ ast_channel_unref(conflict);
+ return 1;
+ }
}
return 0;
*/
ast_channel_lock(tmp);
- ao2_lock(channels);
+ CHANNELSTORAGE_API(current_channel_storage_instance, wrlock);
- if (assignedids && (does_id_conflict(assignedids->uniqueid) || does_id_conflict(assignedids->uniqueid2))) {
+ if (do_ids_conflict(assignedids)) {
ast_channel_internal_errno_set(AST_CHANNEL_ERROR_ID_EXISTS);
- ao2_unlock(channels);
ast_channel_unlock(tmp);
+ CHANNELSTORAGE_API(current_channel_storage_instance, unlock);
/* See earlier channel creation abort comment above. */
return ast_channel_unref(tmp);
}
/* Finalize and link into the channels container. */
ast_channel_internal_finalize(tmp);
ast_atomic_fetchadd_int(&chancount, +1);
- ao2_link_flags(channels, tmp, OBJ_NOLOCK);
+ CHANNELSTORAGE_API(current_channel_storage_instance, insert, tmp, OBJ_NOLOCK, 0);
+ CHANNELSTORAGE_API(current_channel_storage_instance, unlock);
- ao2_unlock(channels);
if (endpoint) {
ast_endpoint_add_channel(endpoint, tmp);
}
}
-struct ast_channel *ast_channel_callback(ao2_callback_data_fn *cb_fn, void *arg,
- void *data, int ao2_flags)
+struct ast_channel *ast_channel_callback(
+ ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
{
- return ao2_callback_data(channels, ao2_flags, cb_fn, arg, data);
-}
-
-static int ast_channel_by_name_cb(void *obj, void *arg, void *data, int flags)
-{
- struct ast_channel *chan = obj;
- const char *name = arg;
- size_t name_len = *(size_t *) data;
- int ret = CMP_MATCH;
-
- if (ast_strlen_zero(name)) {
- ast_log(LOG_ERROR, "BUG! Must supply a channel name or partial name to match!\n");
- return CMP_STOP;
- }
-
- ast_channel_lock(chan);
- if ((!name_len && strcasecmp(ast_channel_name(chan), name))
- || (name_len && strncasecmp(ast_channel_name(chan), name, name_len))) {
- ret = 0; /* name match failed, keep looking */
- }
- ast_channel_unlock(chan);
-
- return ret;
-}
-
-static int ast_channel_by_exten_cb(void *obj, void *arg, void *data, int flags)
-{
- struct ast_channel *chan = obj;
- char *context = arg;
- char *exten = data;
- int ret = CMP_MATCH;
-
- if (ast_strlen_zero(exten) || ast_strlen_zero(context)) {
- ast_log(LOG_ERROR, "BUG! Must have a context and extension to match!\n");
- return CMP_STOP;
- }
-
- ast_channel_lock(chan);
- if (strcasecmp(ast_channel_context(chan), context)) {
- ret = 0; /* Context match failed, continue */
- } else if (strcasecmp(ast_channel_exten(chan), exten)) {
- ret = 0; /* Extension match failed, continue */
- }
- ast_channel_unlock(chan);
-
- return ret;
-}
-
-static int ast_channel_by_uniqueid_cb(void *obj, void *arg, void *data, int flags)
-{
- struct ast_channel *chan = obj;
- char *uniqueid = arg;
- size_t id_len = *(size_t *) data;
- int ret = CMP_MATCH;
-
- if (ast_strlen_zero(uniqueid)) {
- ast_log(LOG_ERROR, "BUG! Must supply a uniqueid or partial uniqueid to match!\n");
- return CMP_STOP;
+ if (!current_channel_storage_instance) {
+ return NULL;
}
-
- ast_channel_lock(chan);
- if ((!id_len && strcasecmp(ast_channel_uniqueid(chan), uniqueid))
- || (id_len && strncasecmp(ast_channel_uniqueid(chan), uniqueid, id_len))) {
- ret = 0; /* uniqueid match failed, keep looking */
+ if (!cb_fn) {
+ ast_log(LOG_ERROR, "callback function must be provided\n");
+ return NULL;
}
- ast_channel_unlock(chan);
-
- return ret;
+ return CHANNELSTORAGE_API(current_channel_storage_instance, callback, cb_fn, arg, data, ao2_flags);
}
-struct ast_channel_iterator {
- /* 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)
{
- ao2_iterator_destroy(i->active_iterator);
- ast_free(i);
-
- return NULL;
+ if (!current_channel_storage_instance || !i) {
+ return NULL;
+ }
+ return CHANNELSTORAGE_API(current_channel_storage_instance, iterator_destroy, i);
}
struct ast_channel_iterator *ast_channel_iterator_by_exten_new(const char *exten, const char *context)
{
- struct ast_channel_iterator *i;
- char *l_exten = (char *) exten;
- char *l_context = (char *) context;
-
- if (!(i = ast_calloc(1, sizeof(*i)))) {
+ if (!current_channel_storage_instance) {
return NULL;
}
-
- i->active_iterator = (void *) ast_channel_callback(ast_channel_by_exten_cb,
- l_context, l_exten, OBJ_MULTIPLE);
- if (!i->active_iterator) {
- ast_free(i);
+ if (ast_strlen_zero(exten) || ast_strlen_zero(context)) {
+ ast_log(LOG_ERROR, "exten and context must be provided\n");
return NULL;
}
-
- return i;
+ return CHANNELSTORAGE_API(current_channel_storage_instance, iterator_by_exten_new, exten, context);
}
-
struct ast_channel_iterator *ast_channel_iterator_by_name_new(const char *name, size_t name_len)
{
- struct ast_channel_iterator *i;
- char *l_name = (char *) name;
-
- if (!(i = ast_calloc(1, sizeof(*i)))) {
+ if (!current_channel_storage_instance) {
return NULL;
}
-
- i->active_iterator = (void *) ast_channel_callback(ast_channel_by_name_cb,
- l_name, &name_len,
- OBJ_MULTIPLE | (name_len == 0 /* match the whole word, so optimize */ ? OBJ_KEY : 0));
- if (!i->active_iterator) {
- ast_free(i);
+ if (ast_strlen_zero(name)) {
+ ast_log(LOG_ERROR, "name must be provided\n");
return NULL;
}
-
- return i;
+ return CHANNELSTORAGE_API(current_channel_storage_instance, iterator_by_name_new, name, name_len);
}
struct ast_channel_iterator *ast_channel_iterator_all_new(void)
{
- struct ast_channel_iterator *i;
-
- if (!(i = ast_calloc(1, sizeof(*i)))) {
+ if (!current_channel_storage_instance) {
return NULL;
}
-
- i->simple_iterator = ao2_iterator_init(channels, 0);
- i->active_iterator = &i->simple_iterator;
-
- return i;
+ return CHANNELSTORAGE_API(current_channel_storage_instance, iterator_all_new);
}
struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i)
{
- return ao2_iterator_next(i->active_iterator);
-}
-
-/* Legacy function, not currently used for lookups, but we need a cmp_fn */
-static int ast_channel_cmp_cb(void *obj, void *arg, int flags)
-{
- ast_log(LOG_ERROR, "BUG! Should never be called!\n");
- return CMP_STOP;
+ if (!current_channel_storage_instance || !i) {
+ return NULL;
+ }
+ return CHANNELSTORAGE_API(current_channel_storage_instance, iterator_next, i);
}
+/*
+ * REMINDER: Historically, this function can be provided a channel name
+ * or uniqueid. This is a bit confusing, but it is what it is.
+ */
struct ast_channel *ast_channel_get_by_name_prefix(const char *name, size_t name_len)
{
- struct ast_channel *chan;
- char *l_name = (char *) name;
-
- if (ast_strlen_zero(l_name)) {
- /* We didn't have a name to search for so quit. */
+ if (!current_channel_storage_instance) {
return NULL;
}
-
- chan = ast_channel_callback(ast_channel_by_name_cb, l_name, &name_len,
- (name_len == 0) /* optimize if it is a complete name match */ ? OBJ_KEY : 0);
- if (chan) {
- return chan;
+ if (ast_strlen_zero(name)) {
+ ast_log(LOG_ERROR, "name must be provided\n");
+ return NULL;
}
- /* Now try a search for uniqueid. */
- return ast_channel_callback(ast_channel_by_uniqueid_cb, l_name, &name_len, 0);
+ return CHANNELSTORAGE_API(current_channel_storage_instance, get_by_name_prefix_or_uniqueid, name, name_len);
}
+/*
+ * REMINDER: Historically, this function can be provided a channel name
+ * or uniqueid. This is a bit confusing, but it is what it is.
+ */
struct ast_channel *ast_channel_get_by_name(const char *name)
{
- return ast_channel_get_by_name_prefix(name, 0);
+ if (!current_channel_storage_instance) {
+ return NULL;
+ }
+ if (ast_strlen_zero(name)) {
+ ast_log(LOG_ERROR, "name must be provided\n");
+ return NULL;
+ }
+
+ return CHANNELSTORAGE_API(current_channel_storage_instance, get_by_name_prefix_or_uniqueid, name, 0);
}
struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *context)
{
- char *l_exten = (char *) exten;
- char *l_context = (char *) context;
+ if (!current_channel_storage_instance) {
+ return NULL;
+ }
+ if (ast_strlen_zero(exten) || ast_strlen_zero(context)) {
+ ast_log(LOG_ERROR, "exten and context must be provided\n");
+ return NULL;
+ }
+ return CHANNELSTORAGE_API(current_channel_storage_instance, get_by_exten, exten, context);
+}
- return ast_channel_callback(ast_channel_by_exten_cb, l_context, l_exten, 0);
+struct ast_channel *ast_channel_get_by_uniqueid(const char *uniqueid)
+{
+ if (!current_channel_storage_instance) {
+ return NULL;
+ }
+ if (ast_strlen_zero(uniqueid)) {
+ ast_log(LOG_ERROR, "uniqueid must be provided\n");
+ return NULL;
+ }
+ return CHANNELSTORAGE_API(current_channel_storage_instance, get_by_uniqueid, uniqueid);
}
int ast_is_deferrable_frame(const struct ast_frame *frame)
struct ast_channel *ast_channel_release(struct ast_channel *chan)
{
/* Safe, even if already unlinked. */
- ao2_unlink(channels, chan);
+ ast_channel_unlink(chan);
return ast_channel_unref(chan);
}
* longer be needed.
*/
ast_pbx_hangup_handler_run(chan);
- ao2_unlink(channels, chan);
+ ast_channel_unlink(chan);
ast_channel_lock(chan);
destroy_hooks(chan);
void ast_change_name(struct ast_channel *chan, const char *newname)
{
/* We must re-link, as the hash value will change here. */
- ao2_lock(channels);
+ CHANNELSTORAGE_API(current_channel_storage_instance, wrlock);
ast_channel_lock(chan);
- ao2_unlink(channels, chan);
+ CHANNELSTORAGE_API(current_channel_storage_instance, remove, chan, 0);
__ast_change_name_nolink(chan, newname);
- ao2_link(channels, chan);
+ CHANNELSTORAGE_API(current_channel_storage_instance, insert, chan, 0, 0);
ast_channel_unlock(chan);
- ao2_unlock(channels);
+ CHANNELSTORAGE_API(current_channel_storage_instance, unlock);
}
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
char clone_sending_dtmf_digit;
struct timeval clone_sending_dtmf_tv;
+ ast_debug(3, "Masquerading %s(%u) into the structure of %s(%u)\n",
+ ast_channel_name(clonechan), ast_channel_state(clonechan),
+ ast_channel_name(original), ast_channel_state(original));
/* XXX This operation is a bit odd. We're essentially putting the guts of
* the clone channel into the original channel. Start by killing off the
* original channel's backend. While the features are nice, which is the
* has restabilized the channels to hold off ast_hangup() and until
* AST_FLAG_ZOMBIE can be set on the clonechan.
*/
- ao2_lock(channels);
+ CHANNELSTORAGE_API(current_channel_storage_instance, wrlock);
/* Bump the refs to ensure that they won't disappear on us. */
ast_channel_ref(original);
ast_channel_ref(clonechan);
- /* unlink from channels container as name (which is the hash value) will change */
- ao2_unlink(channels, original);
- ao2_unlink(channels, clonechan);
+ /*
+ * Since channel name and unique id will change, and both could be keys
+ * in the channel storage backend, we need to remove them from the backend.
+ * We'll add them back in after the changes are compete.
+ */
+ CHANNELSTORAGE_API(current_channel_storage_instance, remove, original, 0);
+ CHANNELSTORAGE_API(current_channel_storage_instance, remove, clonechan, 0);
+
+ CHANNELSTORAGE_API(current_channel_storage_instance, unlock);
moh_is_playing = ast_test_flag(ast_channel_flags(original), AST_FLAG_MOH);
if (moh_is_playing) {
ast_channel_unlock(original);
ast_channel_unlock(clonechan);
- ao2_link(channels, clonechan);
- ao2_link(channels, original);
- ao2_unlock(channels);
+ CHANNELSTORAGE_API(current_channel_storage_instance, wrlock);
+ CHANNELSTORAGE_API(current_channel_storage_instance, insert, clonechan, 0, 0);
+ CHANNELSTORAGE_API(current_channel_storage_instance, insert, original, 0, 0);
+ CHANNELSTORAGE_API(current_channel_storage_instance, unlock);
/* Release our held safety references. */
ast_channel_unref(original);
ast_moh_cleanup_ptr(chan);
}
-static int ast_channel_hash_cb(const void *obj, const int flags)
-{
- const char *name = (flags & OBJ_KEY) ? obj : ast_channel_name((struct ast_channel *) 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(name)) {
- return 0;
- }
-
- return ast_str_case_hash(name);
-}
-
-/*!
- * \internal
- * \brief Print channel object key (name).
- * \since 12.0.0
- *
- * \param v_obj A pointer to the object we want the key printed.
- * \param where User data needed by prnt to determine where to put output.
- * \param prnt Print output callback function to use.
- */
-static void prnt_channel_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
-{
- struct ast_channel *chan = v_obj;
-
- if (!chan) {
- return;
- }
- prnt(where, "%s", ast_channel_name(chan));
-}
-
/*!
* \brief List of channel variables to append to all channel-related events.
*/
return channel_get_external_vars(&ari_vars, chan);
}
+void ast_channel_close_storage(void)
+{
+ ast_channelstorage_close(current_channel_storage_instance);
+}
+
static void channels_shutdown(void)
{
free_external_channelvars(&ami_vars);
free_external_channelvars(&ari_vars);
ast_cli_unregister_multiple(cli_channel, ARRAY_LEN(cli_channel));
- if (channels) {
- ao2_container_unregister("channels");
- ao2_ref(channels, -1);
- channels = NULL;
- }
+
+ ast_channelstorage_close(current_channel_storage_instance);
+ current_channel_storage_instance = NULL;
+
ast_channel_unregister(&surrogate_tech);
}
+int ast_channel_open_storage()
+{
+ if (!current_channel_storage_driver) {
+ int rc = internal_channel_set_current_storage_driver(AST_CHANNELSTORAGE_DEFAULT_TYPE);
+ if (rc) {
+ ast_log(LOG_ERROR, "No channel storage backends available\n");
+ return -1;
+ }
+ }
+
+ current_channel_storage_instance =
+ ast_channelstorage_open(current_channel_storage_driver, "channels");
+ if (!current_channel_storage_instance) {
+ ast_log(LOG_ERROR, "Failed to open channel storage driver '%s'\n",
+ current_channel_storage_driver->driver_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+const char *ast_channel_get_current_storage_driver_name(void)
+{
+ return current_channel_storage_driver ?
+ current_channel_storage_driver->driver_name : "NOT SET";
+}
+
+int internal_channel_set_current_storage_driver(const char *driver_name)
+{
+ if (ast_fully_booted) {
+ ast_log(LOG_ERROR, "Cannot change channel storage driver after Asterisk has started\n");
+ return -1;
+ }
+ current_channel_storage_driver = ast_channelstorage_get_driver(driver_name);
+ if (current_channel_storage_driver) {
+ return 0;
+ }
+ ast_log(LOG_WARNING,
+ "Invalid channel storage backend '%s' specified. Attempting to use default '%s'.\n",
+ driver_name, AST_CHANNELSTORAGE_DEFAULT_TYPE);
+ current_channel_storage_driver = ast_channelstorage_get_driver(AST_CHANNELSTORAGE_DEFAULT_TYPE);
+ if (current_channel_storage_driver) {
+ return 0;
+ }
+ ast_log(LOG_ERROR, "Unable to find default channel storage backend '%s'.\n",
+ AST_CHANNELSTORAGE_DEFAULT_TYPE);
+ return -1;
+}
+
int ast_channels_init(void)
{
- channels = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, AST_NUM_CHANNEL_BUCKETS,
- ast_channel_hash_cb, NULL, ast_channel_cmp_cb);
- if (!channels) {
+
+ if (ast_channelstorage_init() != 0) {
+ return -1;
+ }
+
+ /*
+ * channel_storage_type is a global variable set by options.c
+ * from the "channel_storage_backend" option in asterisk.conf.
+ */
+ if (ast_channel_open_storage() != 0) {
return -1;
}
- ao2_container_register("channels", channels, prnt_channel_key);
ast_channel_register(&surrogate_tech);
void ast_channel_unlink(struct ast_channel *chan)
{
- ao2_unlink(channels, chan);
+ CHANNELSTORAGE_API(current_channel_storage_instance, remove, chan, 1);
}
struct ast_bridge *ast_channel_get_bridge(const struct ast_channel *chan)
ast_clear_flag(ast_channel_flags(chan), flag);
ast_channel_unlock(chan);
}
+
#include "asterisk/stream.h"
#include "asterisk/test.h"
#include "asterisk/vector.h"
+#include "channel_private.h"
+#include "channelstorage.h"
-/*!
- * \brief Channel UniqueId structure
- * \note channel creation time used for determining LinkedId Propagation
- */
-struct ast_channel_id {
- time_t creation_time; /*!< Creation time */
- int creation_unique; /*!< sub-second unique value */
- char unique_id[AST_MAX_UNIQUEID]; /*!< Unique Identifier */
- char tenant_id[AST_MAX_TENANT_ID]; /*!< Multi-tenant identifier */
-};
-
-/*!
- * \brief Main Channel structure associated with a channel.
- *
- * \note When adding fields to this structure, it is important to add the field
- * 'in position' with like-aligned fields, so as to keep the compiler from
- * having to add padding to align fields. The structure's fields are sorted
- * in this order: pointers, structures, long, int/enum, short, char. This
- * is especially important on 64-bit architectures, where mixing 4-byte
- * and 8-byte fields causes 4 bytes of padding to be added before many
- * 8-byte fields.
- */
-struct ast_channel {
- const struct ast_channel_tech *tech; /*!< Technology (point to channel driver) */
- void *tech_pvt; /*!< Private data used by the technology driver */
- void *music_state; /*!< Music State*/
- void *generatordata; /*!< Current generator data if there is any */
- struct ast_generator *generator; /*!< Current active data generator */
- struct ast_channel *masq; /*!< Channel that will masquerade as us */
- struct ast_channel *masqr; /*!< Who we are masquerading as */
- const char *blockproc; /*!< Procedure causing blocking */
- const char *appl; /*!< Current application */
- const char *data; /*!< Data passed to current application */
- struct ast_sched_context *sched; /*!< Schedule context */
- struct ast_filestream *stream; /*!< Stream itself. */
- struct ast_filestream *vstream; /*!< Video Stream itself. */
- ast_timing_func_t timingfunc;
- void *timingdata;
- struct ast_pbx *pbx; /*!< PBX private structure for this channel */
- struct ast_trans_pvt *writetrans; /*!< Write translation path */
- struct ast_trans_pvt *readtrans; /*!< Read translation path */
- struct ast_audiohook_list *audiohooks;
- struct ast_framehook_list *framehooks;
- struct ast_cdr *cdr; /*!< Call Detail Record */
- struct ast_tone_zone *zone; /*!< Tone zone as set in indications.conf or
- * in the CHANNEL dialplan function */
- ast_callid callid; /*!< Bound call identifier pointer */
- struct ao2_container *dialed_causes; /*!< Contains tech-specific and Asterisk cause data from dialed channels */
-
- AST_DECLARE_STRING_FIELDS(
- AST_STRING_FIELD(name); /*!< ASCII unique channel name */
- AST_STRING_FIELD(language); /*!< Language requested for voice prompts */
- AST_STRING_FIELD(musicclass); /*!< Default music class */
- AST_STRING_FIELD(latest_musicclass); /*!< Latest active music class */
- AST_STRING_FIELD(accountcode); /*!< Account code for billing */
- AST_STRING_FIELD(peeraccount); /*!< Peer account code for billing */
- AST_STRING_FIELD(userfield); /*!< Userfield for CEL billing */
- AST_STRING_FIELD(call_forward); /*!< Where to forward to if asked to dial on this interface */
- AST_STRING_FIELD(parkinglot); /*! Default parking lot, if empty, default parking lot */
- AST_STRING_FIELD(hangupsource); /*! Who is responsible for hanging up this channel */
- AST_STRING_FIELD(dialcontext); /*!< Dial: Extension context that we were called from */
- );
-
- struct ast_channel_id uniqueid; /*!< Unique Channel Identifier - can be specified on creation */
- struct ast_channel_id linkedid; /*!< Linked Channel Identifier - oldest propagated when bridged */
-
- struct timeval whentohangup; /*!< Non-zero, set to actual time when channel is to be hung up */
- pthread_t blocker; /*!< If anyone is blocking, this is them */
-
- /*!
- * \brief Dialed/Called information.
- * \note Set on incoming channels to indicate the originally dialed party.
- * \note Dialed Number Identifier (DNID)
- */
- struct ast_party_dialed dialed;
-
- /*!
- * \brief Channel Caller ID information.
- * \note The caller id information is the caller id of this
- * channel when it is used to initiate a call.
- */
- struct ast_party_caller caller;
-
- /*!
- * \brief Channel Connected Line ID information.
- * \note The connected line information identifies the channel
- * connected/bridged to this channel.
- */
- struct ast_party_connected_line connected;
-
- /*!
- * \brief Channel Connected Line ID information that was last indicated.
- */
- struct ast_party_connected_line connected_indicated;
-
- /*! \brief Redirecting/Diversion information */
- struct ast_party_redirecting redirecting;
-
- struct ast_frame dtmff; /*!< DTMF frame */
- struct varshead varshead; /*!< A linked list for channel variables. See \ref AstChanVar */
- ast_group_t callgroup; /*!< Call group for call pickups */
- ast_group_t pickupgroup; /*!< Pickup group - which calls groups can be picked up? */
- struct ast_namedgroups *named_callgroups; /*!< Named call group for call pickups */
- struct ast_namedgroups *named_pickupgroups; /*!< Named pickup group - which call groups can be picked up? */
- struct timeval creationtime; /*!< The time of channel creation */
- struct timeval answertime; /*!< The time the channel was answered */
- struct ast_readq_list readq;
- struct ast_jb jb; /*!< The jitterbuffer state */
- struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */
- struct ast_hangup_handler_list hangup_handlers;/*!< Hangup handlers on the channel. */
- struct ast_datastore_list datastores; /*!< Data stores on the channel */
- struct ast_autochan_list autochans; /*!< Autochans on the channel */
- unsigned long insmpl; /*!< Track the read/written samples for monitor use */
- unsigned long outsmpl; /*!< Track the read/written samples for monitor use */
-
- int blocker_tid; /*!< If anyone is blocking, this is their thread id */
- AST_VECTOR(, int) fds; /*!< File descriptors for channel -- Drivers will poll on
- * these file descriptors, so at least one must be non -1.
- * See \arg \ref AstFileDesc */
- int softhangup; /*!< Whether or not we have been hung up... Do not set this value
- * directly, use ast_softhangup() */
- int fdno; /*!< Which fd had an event detected on */
- int streamid; /*!< For streaming playback, the schedule ID */
- int vstreamid; /*!< For streaming video playback, the schedule ID */
- struct ast_format *oldwriteformat; /*!< Original writer format */
- int timingfd; /*!< Timing fd */
- enum ast_channel_state state; /*!< State of line -- Don't write directly, use ast_setstate() */
- int rings; /*!< Number of rings so far */
- int priority; /*!< Dialplan: Current extension priority */
- int amaflags; /*!< Set BEFORE PBX is started to determine AMA flags */
- enum ast_channel_adsicpe adsicpe; /*!< Whether or not ADSI is detected on CPE */
- unsigned int fin; /*!< Frames in counters. The high bit is a debug mask, so
- * the counter is only in the remaining bits */
- unsigned int fout; /*!< Frames out counters. The high bit is a debug mask, so
- * the counter is only in the remaining bits */
- int hangupcause; /*!< Why is the channel hanged up. See causes.h */
- unsigned int finalized:1; /*!< Whether or not the channel has been successfully allocated */
- struct ast_flags flags; /*!< channel flags of AST_FLAG_ type */
- int alertpipe[2];
- struct ast_format_cap *nativeformats; /*!< Kinds of data this channel can natively handle */
- struct ast_format *readformat; /*!< Requested read format (after translation) */
- struct ast_format *writeformat; /*!< Requested write format (before translation) */
- struct ast_format *rawreadformat; /*!< Raw read format (before translation) */
- struct ast_format *rawwriteformat; /*!< Raw write format (after translation) */
- unsigned int emulate_dtmf_duration; /*!< Number of ms left to emulate DTMF for */
- int visible_indication; /*!< Indication currently playing on the channel */
- int hold_state; /*!< Current Hold/Unhold state */
-
- unsigned short transfercapability; /*!< ISDN Transfer Capability - AST_FLAG_DIGITAL is not enough */
-
- struct ast_bridge *bridge; /*!< Bridge this channel is participating in */
- struct ast_bridge_channel *bridge_channel;/*!< The bridge_channel this channel is linked with. */
- struct ast_timer *timer; /*!< timer object that provided timingfd */
-
- char context[AST_MAX_CONTEXT]; /*!< Dialplan: Current extension context */
- char exten[AST_MAX_EXTENSION]; /*!< Dialplan: Current extension number */
- char lastcontext[AST_MAX_CONTEXT]; /*!< Dialplan: Previous extension context */
- char lastexten[AST_MAX_EXTENSION]; /*!< Dialplan: Previous extension number */
- char unbridged; /*!< non-zero if the bridge core needs to re-evaluate the current
- bridging technology which is in use by this channel's bridge. */
- char is_t38_active; /*!< non-zero if T.38 is active on this channel. */
- char dtmf_digit_to_emulate; /*!< Digit being emulated */
- char sending_dtmf_digit; /*!< Digit this channel is currently sending out. (zero if not sending) */
- struct timeval sending_dtmf_tv; /*!< The time this channel started sending the current digit. (Invalid if sending_dtmf_digit is zero.) */
- struct stasis_topic *topic; /*!< Topic for this channel */
- struct stasis_forward *channel_forward; /*!< Subscription for event forwarding to all channel topic */
- struct stasis_forward *endpoint_forward; /*!< Subscription for event forwarding to endpoint's topic */
- struct ast_stream_topology *stream_topology; /*!< Stream topology */
- void *stream_topology_change_source; /*!< Source that initiated a stream topology change */
- struct ast_stream *default_streams[AST_MEDIA_TYPE_END]; /*!< Default streams indexed by media type */
- struct ast_channel_snapshot *snapshot; /*!< The current up to date snapshot of the channel */
- struct ast_flags snapshot_segment_flags; /*!< Flags regarding the segments of the snapshot */
-};
+/*! \brief The current channel storage driver */
+const struct ast_channelstorage_driver *current_channel_storage_driver;
+/*! \brief The current channel storage instance */
+struct ast_channelstorage_instance *current_channel_storage_instance;
/*! \brief The monotonically increasing integer counter for channel uniqueids */
static int uniqueint;
va_end(ap); \
}
-DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(name, 0, 1, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
DEFINE_STRINGFIELD_SETTERS_AND_INVALIDATE_FOR(language, 1, 0, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
DEFINE_STRINGFIELD_SETTERS_FOR(musicclass, 0);
DEFINE_STRINGFIELD_SETTERS_FOR(latest_musicclass, 0);
DEFINE_STRINGFIELD_GETTER_FOR(hangupsource);
DEFINE_STRINGFIELD_GETTER_FOR(dialcontext);
+void ast_channel_name_set(struct ast_channel *chan, const char *value)
+{
+ ast_assert(!ast_strlen_zero(value));
+ ast_assert(!chan->linked_in_container);
+ if (!strcmp(value, chan->name)) return;
+ ast_string_field_set(chan, name, value);
+ ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
+}
+
+void ast_channel_name_build_va(struct ast_channel *chan, const char *fmt, va_list ap)
+{
+ ast_assert(!chan->linked_in_container);
+ ast_string_field_build_va(chan, name, fmt, ap);
+ ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE); \
+}
+
+void ast_channel_name_build(struct ast_channel *chan, const char *fmt, ...)
+{
+ va_list ap;
+ ast_assert(!chan->linked_in_container);
+ va_start(ap, fmt);
+ ast_channel_name_build_va(chan, fmt, ap);
+ va_end(ap);
+}
+
const char *ast_channel_uniqueid(const struct ast_channel *chan)
{
ast_assert(chan->uniqueid.unique_id[0] != '\0');
* segment. This is due to the masquerade process invalidating all segments.
*/
+ /*
+ * Since unique ids can be a key in the channel storage backend,
+ * ensure that neither channel is linked in or the keys will be
+ * invalid.
+ */
+ ast_assert(!a->linked_in_container && !b->linked_in_container);
+
temp = a->uniqueid;
a->uniqueid = b->uniqueid;
b->uniqueid = temp;
--- /dev/null
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2024, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph@sangoma.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.
+ */
+
+#ifndef CHANNEL_PRIVATE_H_
+#define CHANNEL_PRIVATE_H_
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*!
+ * \brief Channel UniqueId structure
+ * \note channel creation time used for determining LinkedId Propagation
+ */
+struct ast_channel_id {
+ time_t creation_time; /*!< Creation time */
+ int creation_unique; /*!< sub-second unique value */
+ char unique_id[AST_MAX_UNIQUEID]; /*!< Unique Identifier */
+ char tenant_id[AST_MAX_TENANT_ID]; /*!< Multi-tenant identifier */
+};
+
+/*!
+ * \brief Main Channel structure associated with a channel.
+ *
+ * \note When adding fields to this structure, it is important to add the field
+ * 'in position' with like-aligned fields, so as to keep the compiler from
+ * having to add padding to align fields. The structure's fields are sorted
+ * in this order: pointers, structures, long, int/enum, short, char. This
+ * is especially important on 64-bit architectures, where mixing 4-byte
+ * and 8-byte fields causes 4 bytes of padding to be added before many
+ * 8-byte fields.
+ */
+struct ast_channel {
+ const struct ast_channel_tech *tech; /*!< Technology (point to channel driver) */
+ void *tech_pvt; /*!< Private data used by the technology driver */
+ void *music_state; /*!< Music State*/
+ void *generatordata; /*!< Current generator data if there is any */
+ struct ast_generator *generator; /*!< Current active data generator */
+ struct ast_channel *masq; /*!< Channel that will masquerade as us */
+ struct ast_channel *masqr; /*!< Who we are masquerading as */
+ const char *blockproc; /*!< Procedure causing blocking */
+ const char *appl; /*!< Current application */
+ const char *data; /*!< Data passed to current application */
+ struct ast_sched_context *sched; /*!< Schedule context */
+ struct ast_filestream *stream; /*!< Stream itself. */
+ struct ast_filestream *vstream; /*!< Video Stream itself. */
+ ast_timing_func_t timingfunc;
+ void *timingdata;
+ struct ast_pbx *pbx; /*!< PBX private structure for this channel */
+ struct ast_trans_pvt *writetrans; /*!< Write translation path */
+ struct ast_trans_pvt *readtrans; /*!< Read translation path */
+ struct ast_audiohook_list *audiohooks;
+ struct ast_framehook_list *framehooks;
+ struct ast_cdr *cdr; /*!< Call Detail Record */
+ struct ast_tone_zone *zone; /*!< Tone zone as set in indications.conf or
+ * in the CHANNEL dialplan function */
+ ast_callid callid; /*!< Bound call identifier pointer */
+ struct ao2_container *dialed_causes; /*!< Contains tech-specific and Asterisk cause data from dialed channels */
+
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name); /*!< ASCII unique channel name */
+ AST_STRING_FIELD(language); /*!< Language requested for voice prompts */
+ AST_STRING_FIELD(musicclass); /*!< Default music class */
+ AST_STRING_FIELD(latest_musicclass); /*!< Latest active music class */
+ AST_STRING_FIELD(accountcode); /*!< Account code for billing */
+ AST_STRING_FIELD(peeraccount); /*!< Peer account code for billing */
+ AST_STRING_FIELD(userfield); /*!< Userfield for CEL billing */
+ AST_STRING_FIELD(call_forward); /*!< Where to forward to if asked to dial on this interface */
+ AST_STRING_FIELD(parkinglot); /*! Default parking lot, if empty, default parking lot */
+ AST_STRING_FIELD(hangupsource); /*! Who is responsible for hanging up this channel */
+ AST_STRING_FIELD(dialcontext); /*!< Dial: Extension context that we were called from */
+ );
+
+ struct ast_channel_id uniqueid; /*!< Unique Channel Identifier - can be specified on creation */
+ struct ast_channel_id linkedid; /*!< Linked Channel Identifier - oldest propagated when bridged */
+
+ struct timeval whentohangup; /*!< Non-zero, set to actual time when channel is to be hung up */
+ pthread_t blocker; /*!< If anyone is blocking, this is them */
+
+ /*!
+ * \brief Dialed/Called information.
+ * \note Set on incoming channels to indicate the originally dialed party.
+ * \note Dialed Number Identifier (DNID)
+ */
+ struct ast_party_dialed dialed;
+
+ /*!
+ * \brief Channel Caller ID information.
+ * \note The caller id information is the caller id of this
+ * channel when it is used to initiate a call.
+ */
+ struct ast_party_caller caller;
+
+ /*!
+ * \brief Channel Connected Line ID information.
+ * \note The connected line information identifies the channel
+ * connected/bridged to this channel.
+ */
+ struct ast_party_connected_line connected;
+
+ /*!
+ * \brief Channel Connected Line ID information that was last indicated.
+ */
+ struct ast_party_connected_line connected_indicated;
+
+ /*! \brief Redirecting/Diversion information */
+ struct ast_party_redirecting redirecting;
+
+ struct ast_frame dtmff; /*!< DTMF frame */
+ struct varshead varshead; /*!< A linked list for channel variables. See \ref AstChanVar */
+ ast_group_t callgroup; /*!< Call group for call pickups */
+ ast_group_t pickupgroup; /*!< Pickup group - which calls groups can be picked up? */
+ struct ast_namedgroups *named_callgroups; /*!< Named call group for call pickups */
+ struct ast_namedgroups *named_pickupgroups; /*!< Named pickup group - which call groups can be picked up? */
+ struct timeval creationtime; /*!< The time of channel creation */
+ struct timeval answertime; /*!< The time the channel was answered */
+ struct ast_readq_list readq;
+ struct ast_jb jb; /*!< The jitterbuffer state */
+ struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */
+ struct ast_hangup_handler_list hangup_handlers;/*!< Hangup handlers on the channel. */
+ struct ast_datastore_list datastores; /*!< Data stores on the channel */
+ struct ast_autochan_list autochans; /*!< Autochans on the channel */
+ unsigned long insmpl; /*!< Track the read/written samples for monitor use */
+ unsigned long outsmpl; /*!< Track the read/written samples for monitor use */
+
+ int blocker_tid; /*!< If anyone is blocking, this is their thread id */
+ AST_VECTOR(, int) fds; /*!< File descriptors for channel -- Drivers will poll on
+ * these file descriptors, so at least one must be non -1.
+ * See \arg \ref AstFileDesc */
+ int softhangup; /*!< Whether or not we have been hung up... Do not set this value
+ * directly, use ast_softhangup() */
+ int fdno; /*!< Which fd had an event detected on */
+ int streamid; /*!< For streaming playback, the schedule ID */
+ int vstreamid; /*!< For streaming video playback, the schedule ID */
+ struct ast_format *oldwriteformat; /*!< Original writer format */
+ int timingfd; /*!< Timing fd */
+ enum ast_channel_state state; /*!< State of line -- Don't write directly, use ast_setstate() */
+ int rings; /*!< Number of rings so far */
+ int priority; /*!< Dialplan: Current extension priority */
+ int amaflags; /*!< Set BEFORE PBX is started to determine AMA flags */
+ enum ast_channel_adsicpe adsicpe; /*!< Whether or not ADSI is detected on CPE */
+ unsigned int fin; /*!< Frames in counters. The high bit is a debug mask, so
+ * the counter is only in the remaining bits */
+ unsigned int fout; /*!< Frames out counters. The high bit is a debug mask, so
+ * the counter is only in the remaining bits */
+ int hangupcause; /*!< Why is the channel hanged up. See causes.h */
+ unsigned int finalized:1; /*!< Whether or not the channel has been successfully allocated */
+ struct ast_flags flags; /*!< channel flags of AST_FLAG_ type */
+ int alertpipe[2];
+ struct ast_format_cap *nativeformats; /*!< Kinds of data this channel can natively handle */
+ struct ast_format *readformat; /*!< Requested read format (after translation) */
+ struct ast_format *writeformat; /*!< Requested write format (before translation) */
+ struct ast_format *rawreadformat; /*!< Raw read format (before translation) */
+ struct ast_format *rawwriteformat; /*!< Raw write format (after translation) */
+ unsigned int emulate_dtmf_duration; /*!< Number of ms left to emulate DTMF for */
+ int visible_indication; /*!< Indication currently playing on the channel */
+ int hold_state; /*!< Current Hold/Unhold state */
+
+ unsigned short transfercapability; /*!< ISDN Transfer Capability - AST_FLAG_DIGITAL is not enough */
+
+ struct ast_bridge *bridge; /*!< Bridge this channel is participating in */
+ struct ast_bridge_channel *bridge_channel;/*!< The bridge_channel this channel is linked with. */
+ struct ast_timer *timer; /*!< timer object that provided timingfd */
+
+ char context[AST_MAX_CONTEXT]; /*!< Dialplan: Current extension context */
+ char exten[AST_MAX_EXTENSION]; /*!< Dialplan: Current extension number */
+ char lastcontext[AST_MAX_CONTEXT]; /*!< Dialplan: Previous extension context */
+ char lastexten[AST_MAX_EXTENSION]; /*!< Dialplan: Previous extension number */
+ char unbridged; /*!< non-zero if the bridge core needs to re-evaluate the current
+ bridging technology which is in use by this channel's bridge. */
+ char is_t38_active; /*!< non-zero if T.38 is active on this channel. */
+ char dtmf_digit_to_emulate; /*!< Digit being emulated */
+ char sending_dtmf_digit; /*!< Digit this channel is currently sending out. (zero if not sending) */
+ struct timeval sending_dtmf_tv; /*!< The time this channel started sending the current digit. (Invalid if sending_dtmf_digit is zero.) */
+ struct stasis_topic *topic; /*!< Topic for this channel */
+ struct stasis_forward *channel_forward; /*!< Subscription for event forwarding to all channel topic */
+ struct stasis_forward *endpoint_forward; /*!< Subscription for event forwarding to endpoint's topic */
+ struct ast_stream_topology *stream_topology; /*!< Stream topology */
+ void *stream_topology_change_source; /*!< Source that initiated a stream topology change */
+ struct ast_stream *default_streams[AST_MEDIA_TYPE_END]; /*!< Default streams indexed by media type */
+ struct ast_channel_snapshot *snapshot; /*!< The current up to date snapshot of the channel */
+ struct ast_flags snapshot_segment_flags; /*!< Flags regarding the segments of the snapshot */
+ int linked_in_container; /*!< Whether this channel is linked in a storage container */
+};
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* CHANNEL_PRIVATE_H_ */
--- /dev/null
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2024, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph@sangoma.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.
+ */
+
+#include "asterisk.h"
+#include "asterisk/options.h"
+#include "channelstorage.h"
+
+static AST_VECTOR(, const struct ast_channelstorage_driver *) storage_drivers;
+
+int ast_channelstorage_register_driver(
+ const struct ast_channelstorage_driver *driver_type)
+{
+ if (storage_drivers.elems == NULL) {
+ AST_VECTOR_INIT(&storage_drivers, 10);
+ }
+ return AST_VECTOR_APPEND(&storage_drivers, driver_type);
+}
+
+const struct ast_channelstorage_driver *ast_channelstorage_get_driver(
+ const char *driver_name)
+{
+ int i;
+
+ for (i = 0; i < AST_VECTOR_SIZE(&storage_drivers); i++) {
+ const struct ast_channelstorage_driver *dt =
+ AST_VECTOR_GET(&storage_drivers, i);
+ if (strcasecmp(driver_name, dt->driver_name) == 0) {
+ return dt;
+ }
+ }
+ return NULL;
+}
+
+struct ast_channelstorage_instance *ast_channelstorage_open(
+ const struct ast_channelstorage_driver *storage_driver,
+ const char *instance_name)
+{
+ struct ast_channelstorage_instance *storage_instance = NULL;
+
+ storage_instance = storage_driver->open(instance_name);
+ if (!storage_instance) {
+ ast_log(LOG_ERROR, "Failed to open channel storage driver '%s'\n",
+ storage_driver->driver_name);
+ return NULL;
+ }
+
+ return storage_instance;
+};
+
+void ast_channelstorage_close(struct ast_channelstorage_instance *storage_instance)
+{
+ CHANNELSTORAGE_API(storage_instance, close);
+};
+
+int channelstorage_exten_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = (struct ast_channel *)obj;
+ const char *context = (const char *)arg;
+ const char *exten = (const char *)data;
+ int ret = 0;
+
+ ao2_lock(chan);
+ if (strcasecmp(ast_channel_context(chan), context) == 0 &&
+ strcasecmp(ast_channel_exten(chan), exten) == 0) {
+ ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
+ }
+ ao2_unlock(chan);
+
+ return ret;
+}
+
+struct ast_channel *channelstorage_by_exten(struct ast_channelstorage_instance *driver,
+ const char *exten, const char *context)
+{
+ char *l_exten = (char *) exten;
+ char *l_context = (char *) context;
+
+ return CHANNELSTORAGE_API(driver, callback, channelstorage_exten_cb, l_context, l_exten, 0);
+}
+
+int channelstorage_name_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = obj;
+ const char *name = arg;
+ size_t name_len = *(size_t *) data;
+ int ret = 0;
+
+ if (name_len == 0) {
+ if(strcasecmp(ast_channel_name(chan), name) == 0) {
+ ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
+ }
+ } else {
+ if (strncasecmp(ast_channel_name(chan), name, name_len) == 0) {
+ ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
+ }
+ }
+
+ return ret;
+}
+
+struct ast_channel *channelstorage_by_name_or_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *name)
+{
+ return CHANNELSTORAGE_API(driver, get_by_name_prefix_or_uniqueid, name, 0);
+}
+
+struct ast_channel *channelstorage_by_name_prefix_or_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *name, size_t name_len)
+{
+ struct ast_channel *chan = NULL;
+
+ chan = CHANNELSTORAGE_API(driver, get_by_name_prefix, name, name_len);
+ if (chan) {
+ return chan;
+ }
+
+ if (name_len == 0) {
+ chan = CHANNELSTORAGE_API(driver, get_by_uniqueid, name);
+ }
+
+ return chan;
+}
+
+int channelstorage_uniqueid_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = obj;
+ char *uniqueid = arg;
+ int ret = 0;
+
+ if(strcasecmp(ast_channel_uniqueid(chan), uniqueid) == 0) {
+ ret = CMP_MATCH | CMP_STOP;
+ }
+
+ return ret;
+}
+
+struct ast_channel *channelstorage_by_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *uniqueid)
+{
+ return CHANNELSTORAGE_API(driver, callback, channelstorage_uniqueid_cb, (char *)uniqueid, NULL, 0);
+}
+
+#ifdef TEST_FRAMEWORK
+#include "asterisk/test.h"
+#include "channel_private.h"
+
+static void mock_channel_destructor(void *obj)
+{
+ struct ast_channel *chan = obj;
+ ast_string_field_free_memory(chan);
+}
+
+struct test_info {
+ struct ast_test *test;
+ struct ast_channelstorage_instance *storage_instance;
+ enum ast_test_result_state res;
+};
+
+static void *test_storage_thread(void *data)
+{
+ struct test_info *test_info = data;
+ struct ast_test *test = test_info->test;
+ struct ast_channelstorage_instance *storage_instance = test_info->storage_instance;
+ struct ast_channel *mock_channel;
+ enum ast_test_result_state res = AST_TEST_PASS;
+ int i;
+ struct timeval start;
+ struct timeval end;
+ int64_t elapsed;
+ char search1[128];
+ char search2[128];
+ int rc = 0;
+ long int rand = ast_random();
+ struct ast_channel_iterator *iter;
+ int collen = 25;
+ int CHANNEL_COUNT = 500;
+ struct ast_cli_args *cli_args = ast_test_get_cli_args(test);
+ struct ast_channel **test_channels;
+
+ for (i = 0; i < cli_args->argc; i++) {
+ if (ast_begins_with(cli_args->argv[i], "channel-count=")) {
+ sscanf(cli_args->argv[i], "channel-count=%d", &CHANNEL_COUNT);
+ }
+ }
+ test_channels = ast_calloc(CHANNEL_COUNT, sizeof(*test_channels));
+ ast_test_status_update(test, "%*s: %8d\n", collen, "Channel Count", CHANNEL_COUNT);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ test_channels[i] = ao2_alloc(sizeof(*mock_channel), mock_channel_destructor);
+ ast_test_validate_cleanup(test, test_channels[i], res, done);
+ ast_string_field_init(test_channels[i], 128);
+ ast_string_field_build(test_channels[i], name, "TestChannel-%ld-%04d-something", rand, i);
+ snprintf(test_channels[i]->context, AST_MAX_CONTEXT, "TestContext-%ld-%04d", rand, i % 100);
+ snprintf(test_channels[i]->exten, AST_MAX_EXTENSION, "TestExten-%ld-%04d", rand, i % 10);
+ snprintf(test_channels[i]->uniqueid.unique_id, AST_MAX_UNIQUEID, "TestUniqueid-%ld-%04d-something", rand, i);
+ rc = CHANNELSTORAGE_API(storage_instance, insert, test_channels[i], 0, 1);
+ ast_test_validate_cleanup_custom(test, rc == 0, res, done, "Unable to insert channel %s\n", test_channels[i]->name);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ i = CHANNELSTORAGE_API(storage_instance, active_channels);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "create channels", elapsed);
+ ast_test_validate_cleanup(test, i == CHANNEL_COUNT, res, done);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "testchannel-%ld-%04d-something", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_prefix_or_uniqueid, search1, 0);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+ ast_test_validate_cleanup(test, mock_channel == test_channels[i], res, done);
+ ast_test_validate_cleanup(test,
+ strcasecmp(ast_channel_name(mock_channel), search1) == 0, res, done);
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "by name exact", elapsed);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestUniqueid-%ld-%04d-something", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_uniqueid, search1);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "by uniqueid exact", elapsed);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestUniqueid-%ld-%04d-something", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_prefix_or_uniqueid, search1, 0);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "by uniqueid via nm", elapsed);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestChannel-%ld-%04d", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_prefix_or_uniqueid, search1, strlen(search1));
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "by name prefix", elapsed);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestContext-%ld-%04d", rand, i % 100);
+ sprintf(search2, "TestExten-%ld-%04d", rand, i % 10);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_exten, search2, search1);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "by context/exten", elapsed);
+
+#if 0
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestChannel-%ld-%04d-something", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_or_uniqueid, search1);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+
+ CHANNELSTORAGE_API(storage_instance, wrlock);
+
+ sprintf(mock_channel->context, "TestXXContext-%ld-%04d", rand, i);
+ sprintf(search1, "TestContext-%ld-%04d", rand, i);
+
+ rc = CHANNELSTORAGE_API(storage_instance, update, mock_channel,
+ AST_CHANNELSTORAGE_UPDATE_CONTEXT, search1, mock_channel->context, 0);
+ ast_test_validate_cleanup(test, rc == 0, res, done);
+
+ sprintf(mock_channel->exten, "TestXXExten-%ld-%04d", rand, i);
+ sprintf(search2, "TestExten-%ld-%04d", rand, i);
+
+ rc = CHANNELSTORAGE_API(storage_instance, update, mock_channel,
+ AST_CHANNELSTORAGE_UPDATE_EXTEN, search2, mock_channel->exten, 0);
+ CHANNELSTORAGE_API(storage_instance, unlock);
+
+ ast_test_validate_cleanup(test, rc == 0, res, done);
+
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "update", elapsed);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestXXContext-%ld-%04d", rand, i);
+ sprintf(search2, "TestXXExten-%ld-%04d", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_exten, search2, search1);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "by context/exten2", elapsed);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestChannel-%ld-%04d-something", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_or_uniqueid, search1);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+ sprintf(search2, "TestXXChannel-%ld-%04d", rand, i);
+ rc = CHANNELSTORAGE_API(storage_instance, update, mock_channel,
+ AST_CHANNELSTORAGE_UPDATE_NAME, search1, search2, 1);
+ ast_channel_unref(mock_channel);
+ ast_test_validate_cleanup(test, rc == 0, res, done);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "change name", elapsed);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ sprintf(search1, "TestXXChannel-%ld-%04d", rand, i);
+ mock_channel = CHANNELSTORAGE_API(storage_instance, get_by_name_or_uniqueid, search1);
+ ast_test_validate_cleanup_custom(test, mock_channel, res, done,"Channel %s not found\n", search1);
+ ast_channel_unref(mock_channel);
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "by name exact2", elapsed);
+#endif
+
+ i = 0;
+ start = ast_tvnow();
+ iter = CHANNELSTORAGE_API(storage_instance, iterator_all_new);
+ for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
+ ast_channel_unref(mock_channel)) {
+ i++;
+ }
+ CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "iter all chan", elapsed);
+ ast_test_validate_cleanup_custom(test, i == CHANNEL_COUNT, res, done,
+ "Expected %d channels, got %d, in container: %d\n", CHANNEL_COUNT, i,
+ CHANNELSTORAGE_API(storage_instance, active_channels));
+
+ i = 0;
+ start = ast_tvnow();
+ sprintf(search1, "TestChannel-%ld-%03d", rand, (CHANNEL_COUNT - 11) / 10);
+ iter = CHANNELSTORAGE_API(storage_instance, iterator_by_name_new, search1, strlen(search1));
+ ast_test_validate_cleanup(test, iter != NULL, res, done);
+ for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
+ ast_channel_unref(mock_channel)) {
+ ast_test_validate_cleanup_custom(test, strncmp(search1,
+ ast_channel_name(mock_channel), strlen(search1)) == 0, res, done, "Expected %s got %s\n",
+ search1, ast_channel_name(mock_channel));
+ i++;
+ }
+ CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "iter 10 partial name", elapsed);
+ ast_test_validate_cleanup_custom(test, i == 10, res, done,
+ "Expected %d channels, got %d, in container: %d\n", 10, i,
+ CHANNELSTORAGE_API(storage_instance, active_channels));
+
+ i = 0;
+ start = ast_tvnow();
+ sprintf(search1, "TestContext-%ld-%04d", rand, 50);
+ sprintf(search2, "TestExten-%ld-%04d", rand, 0);
+ iter = CHANNELSTORAGE_API(storage_instance, iterator_by_exten_new, search2, search1);
+ ast_test_validate_cleanup(test, iter != NULL, res, done);
+ for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
+ ast_channel_unref(mock_channel)) {
+ ast_test_validate_cleanup_custom(test,
+ (strcmp(search1, mock_channel->context) == 0 &&
+ strcmp(search2, mock_channel->exten) == 0), res, done, "Expected %s-%s got %s-%s\n",
+ search1, search2, mock_channel->context, mock_channel->exten);
+ i++;
+ }
+ CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "iter context/exten", elapsed);
+ ast_test_validate_cleanup_custom(test, i == (CHANNEL_COUNT / 100), res, done,
+ "Expected %d channels, got %d, in container: %d\n", (CHANNEL_COUNT / 100), i,
+ CHANNEL_COUNT);
+
+done:
+ CHANNELSTORAGE_API(storage_instance, unlock);
+
+ start = ast_tvnow();
+ for (i = 0; i < CHANNEL_COUNT; i++) {
+ if (test_channels[i]) {
+ rc = CHANNELSTORAGE_API(storage_instance, remove, test_channels[i], 0);
+ ast_channel_unref(test_channels[i]);
+ test_channels[i] = NULL;
+ }
+ }
+ end = ast_tvnow();
+ elapsed = ast_tvdiff_us(end, start);
+ ast_test_status_update(test, "%*s: %8ld\n", collen, "del all channels", elapsed);
+ ast_test_validate_cleanup(test, i == CHANNEL_COUNT, res, done);
+ rc = CHANNELSTORAGE_API(storage_instance, active_channels);
+ ast_test_validate_cleanup_custom(test, rc == 0, res, final,
+ "There are still %d channels in the container\n", rc);
+
+ test_info->res = res;
+ return NULL;
+
+final:
+ iter = CHANNELSTORAGE_API(storage_instance, iterator_all_new);
+ for (; (mock_channel = CHANNELSTORAGE_API(storage_instance, iterator_next, iter));
+ ast_channel_unref(mock_channel)) {
+ ast_test_status_update(test, "%p %s\n", mock_channel, ast_channel_name(mock_channel));
+ i++;
+ }
+ CHANNELSTORAGE_API(storage_instance, iterator_destroy, iter);
+
+ test_info->res = res;
+ return NULL;
+}
+
+static enum ast_test_result_state test_storage(struct ast_test_info *info,
+ enum ast_test_command cmd, struct ast_test *test,
+ const char *storage_name, const char *summary)
+{
+ const struct ast_channelstorage_driver *storage_driver;
+ struct test_info ti = {
+ .test = test,
+ .storage_instance = NULL,
+ .res = AST_TEST_PASS,
+ };
+ pthread_t thread;
+ int rc = 0;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = storage_name;
+ info->category = "/main/channelstorage/";
+ info->summary = summary;
+ info->description = info->summary;
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ storage_driver = ast_channelstorage_get_driver(info->name);
+ if (!storage_driver) {
+ ast_test_status_update(test, "Storage driver %s not registered\n", info->name);
+ return AST_TEST_NOT_RUN;
+ }
+ ti.storage_instance = ast_channelstorage_open(storage_driver, "channels_test");
+ ast_test_validate(test, ti.storage_instance, res);
+
+ rc = ast_pthread_create(&thread, NULL, test_storage_thread, &ti);
+ if (rc) {
+ ast_channelstorage_close(ti.storage_instance);
+ ast_test_status_update(test, "Failed to create thread: %s\n", strerror(rc));
+ return AST_TEST_FAIL;
+ }
+ pthread_join(thread, NULL);
+ ast_channelstorage_close(ti.storage_instance);
+
+ return ti.res;
+}
+
+#define DEFINE_STORAGE_TEST(_name) \
+AST_TEST_DEFINE(_name) \
+{ \
+ return test_storage(info, cmd, test, #_name, "Channel Storage test for " #_name); \
+}
+
+DEFINE_STORAGE_TEST(ao2_legacy)
+
+DEFINE_STORAGE_TEST(cpp_map_name_id)
+
+#define REGISTER_STORAGE_TEST(_name) \
+({ \
+ if (ast_channelstorage_get_driver(#_name)) { \
+ AST_TEST_REGISTER(_name); \
+ } \
+})
+#endif
+
+static void channelstorage_shutdown(void)
+{
+#ifdef TEST_FRAMEWORK
+ /* Unregistering a test that wasn't previously registered is safe */
+ AST_TEST_UNREGISTER(cpp_map_name_id);
+ AST_TEST_UNREGISTER(ao2_legacy);
+#endif
+}
+
+int ast_channelstorage_init(void)
+{
+#ifdef TEST_FRAMEWORK
+ /* Tests run in the reverse order registered */
+ REGISTER_STORAGE_TEST(cpp_map_name_id);
+ AST_TEST_REGISTER(ao2_legacy);
+#endif
+ ast_register_cleanup(channelstorage_shutdown);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2024, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph@sangoma.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.
+ */
+
+#ifndef CHANNELSTORAGE_H_
+#define CHANNELSTORAGE_H_
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk.h"
+#include "asterisk/channel.h"
+#include "asterisk/channel_internal.h"
+
+#define AST_CHANNELSTORAGE_DEFAULT_TYPE "ao2_legacy"
+
+struct ast_channelstorage_driver {
+ const char *driver_name;
+ struct ast_channelstorage_instance* (*open)(const char *instance_name);
+};
+
+int ast_channelstorage_register_driver(
+ const struct ast_channelstorage_driver *driver_name);
+
+const struct ast_channelstorage_driver *ast_channelstorage_get_driver(
+ const char *driver_name);
+
+struct ast_channelstorage_driver_pvt;
+
+struct ast_channelstorage_instance {
+ struct ast_channelstorage_driver_pvt *handle;
+ void *lock_handle;
+ void (*close)(struct ast_channelstorage_instance *driver);
+ int (*insert)(struct ast_channelstorage_instance *driver, struct ast_channel *chan, int flags, int lock);
+ int (*remove)(struct ast_channelstorage_instance *driver, struct ast_channel *chan, int lock);
+ void (*rdlock)(struct ast_channelstorage_instance *driver);
+ void (*wrlock)(struct ast_channelstorage_instance *driver);
+ void (*unlock)(struct ast_channelstorage_instance *driver);
+ int (*active_channels)(struct ast_channelstorage_instance *driver);
+ struct ast_channel *(*callback)(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn,
+ void *arg, void *data, int ao2_flags);
+ struct ast_channel *(*get_by_name_prefix)(struct ast_channelstorage_instance *driver, const char *name, size_t len);
+ struct ast_channel *(*get_by_name_prefix_or_uniqueid)(struct ast_channelstorage_instance *driver, const char *name, size_t len);
+ struct ast_channel *(*get_by_exten)(struct ast_channelstorage_instance *driver, const char *exten, const char *context);
+ struct ast_channel *(*get_by_uniqueid)(struct ast_channelstorage_instance *driver, const char *uniqueid);
+ struct ast_channel_iterator *(*iterator_all_new)(struct ast_channelstorage_instance *driver);
+ struct ast_channel_iterator *(*iterator_by_exten_new)
+ (struct ast_channelstorage_instance *driver, const char *exten, const char *context);
+ struct ast_channel_iterator *(*iterator_by_name_new)
+ (struct ast_channelstorage_instance *driver, const char *driver_name, size_t name_len);
+ struct ast_channel *(*iterator_next)(struct ast_channelstorage_instance *driver, struct ast_channel_iterator *i);
+ struct ast_channel_iterator *(*iterator_destroy)(
+ struct ast_channelstorage_instance *driver, struct ast_channel_iterator *i);
+ char name[0];
+};
+
+#define CHANNELSTORAGE_API(_instance, _func, ...) \
+ (_instance)->_func((_instance), ##__VA_ARGS__)
+
+int ast_channelstorage_init(void);
+
+struct ast_channelstorage_instance *ast_channelstorage_open(
+ const struct ast_channelstorage_driver *storage_driver, const char *instance_name);
+
+void ast_channelstorage_close(struct ast_channelstorage_instance *storage_instance);
+
+int channelstorage_exten_cb(void *obj, void *arg, void *data, int flags);
+struct ast_channel *channelstorage_by_exten(struct ast_channelstorage_instance *driver,
+ const char *exten, const char *context);
+int channelstorage_name_cb(void *obj, void *arg, void *data, int flags);
+struct ast_channel *channelstorage_by_name_or_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *name);
+struct ast_channel *channelstorage_by_name_prefix_or_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *name, size_t name_len);
+int channelstorage_uniqueid_cb(void *obj, void *arg, void *data, int flags);
+struct ast_channel *channelstorage_by_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *uniqueid);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* CHANNELSTORAGE_H_ */
--- /dev/null
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2024, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph@sangoma.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.
+ */
+
+#include "asterisk.h"
+
+#include "asterisk/channel.h"
+#include "asterisk/astobj2.h"
+#include "channelstorage.h"
+#include "channel_private.h"
+
+struct ast_channelstorage_driver_pvt {
+ struct ao2_container *handle;
+};
+
+#define getdb(driver) (driver->handle->handle)
+
+static void lock_driver(struct ast_channelstorage_instance *driver)
+{
+ ao2_lock(getdb(driver));
+}
+
+static void unlock_driver(struct ast_channelstorage_instance *driver)
+{
+ ao2_unlock(getdb(driver));
+}
+
+static int insert_channel(struct ast_channelstorage_instance *driver,
+ struct ast_channel *chan, int flags, int lock)
+{
+ int ret = ao2_link_flags(getdb(driver), chan, flags);
+ if (ret == 1) {
+ chan->linked_in_container = 1;
+ }
+ return ret ? 0 : -1;
+}
+
+static int delete_channel(struct ast_channelstorage_instance *driver,
+ struct ast_channel *chan, int lock)
+{
+ ao2_unlink(getdb(driver), chan);
+ chan->linked_in_container = 0;
+ return 0;
+}
+
+/*! \brief returns number of active/allocated channels */
+static int active_channels(struct ast_channelstorage_instance *driver)
+{
+ return getdb(driver) ? ao2_container_count(getdb(driver)) : 0;
+}
+
+static struct ast_channel *callback(struct ast_channelstorage_instance *driver,
+ ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
+{
+ return ao2_callback_data(getdb(driver), ao2_flags, cb_fn, arg, data);
+}
+
+static int by_name_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = obj;
+ const char *name = arg;
+ size_t name_len = *(size_t *) data;
+ int ret = 0;
+
+ ast_channel_lock(chan);
+ if (name_len == 0) {
+ if(strcasecmp(ast_channel_name(chan), name) == 0) {
+ ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
+ }
+ } else {
+ if (strncasecmp(ast_channel_name(chan), name, name_len) == 0) {
+ ret = CMP_MATCH | ((flags & OBJ_MULTIPLE) ? 0 : CMP_STOP);
+ }
+ }
+ ast_channel_unlock(chan);
+
+ return ret;
+}
+
+static int by_exten_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = obj;
+ char *context = arg;
+ char *exten = data;
+ int ret = CMP_MATCH;
+
+ ast_channel_lock(chan);
+ if (strcasecmp(ast_channel_context(chan), context)) {
+ ret = 0; /* Context match failed, continue */
+ } else if (strcasecmp(ast_channel_exten(chan), exten)) {
+ ret = 0; /* Extension match failed, continue */
+ }
+ ast_channel_unlock(chan);
+
+ return ret;
+}
+
+static int by_uniqueid_cb(void *obj, void *arg, void *data, int flags)
+{
+ struct ast_channel *chan = obj;
+ char *uniqueid = arg;
+ size_t id_len = *(size_t *) data;
+ int ret = CMP_MATCH;
+
+ if (ast_strlen_zero(uniqueid)) {
+ ast_log(LOG_ERROR, "BUG! Must supply a uniqueid or partial uniqueid to match!\n");
+ return CMP_STOP;
+ }
+
+ ast_channel_lock(chan);
+ if ((!id_len && strcasecmp(ast_channel_uniqueid(chan), uniqueid))
+ || (id_len && strncasecmp(ast_channel_uniqueid(chan), uniqueid, id_len))) {
+ ret = 0; /* uniqueid match failed, keep looking */
+ }
+ ast_channel_unlock(chan);
+
+ return ret;
+}
+
+struct ast_channel_iterator {
+ /* 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;
+};
+
+static struct ast_channel_iterator *iterator_destroy(struct ast_channelstorage_instance *driver,
+ struct ast_channel_iterator *i)
+{
+ ao2_iterator_destroy(i->active_iterator);
+ ast_free(i);
+
+ return NULL;
+}
+
+static struct ast_channel_iterator *iterator_by_exten_new(struct ast_channelstorage_instance *driver,
+ const char *exten, const char *context)
+{
+ struct ast_channel_iterator *i;
+ char *l_exten = (char *) exten;
+ char *l_context = (char *) context;
+
+ if (!(i = ast_calloc(1, sizeof(*i)))) {
+ return NULL;
+ }
+
+ i->active_iterator = (void *) callback(driver, by_exten_cb,
+ l_context, l_exten, OBJ_MULTIPLE);
+ if (!i->active_iterator) {
+ ast_free(i);
+ return NULL;
+ }
+
+ return i;
+}
+
+static struct ast_channel_iterator *iterator_by_name_new(struct ast_channelstorage_instance *driver,
+ const char *name, size_t name_len)
+{
+ struct ast_channel_iterator *i;
+ char *l_name = (char *) name;
+
+ if (!(i = ast_calloc(1, sizeof(*i)))) {
+ return NULL;
+ }
+
+ i->active_iterator = (void *) callback(driver, by_name_cb,
+ l_name, &name_len,
+ OBJ_MULTIPLE | (name_len == 0 /* match the whole word, so optimize */ ? OBJ_KEY : 0));
+ if (!i->active_iterator) {
+ ast_free(i);
+ return NULL;
+ }
+
+ return i;
+}
+
+static struct ast_channel_iterator *iterator_all_new(struct ast_channelstorage_instance *driver)
+{
+ struct ast_channel_iterator *i;
+
+ if (!(i = ast_calloc(1, sizeof(*i)))) {
+ return NULL;
+ }
+
+ i->simple_iterator = ao2_iterator_init(getdb(driver), 0);
+ i->active_iterator = &i->simple_iterator;
+
+ return i;
+}
+
+static struct ast_channel *iterator_next(struct ast_channelstorage_instance *driver,
+ struct ast_channel_iterator *i)
+{
+ return ao2_iterator_next(i->active_iterator);
+}
+
+static struct ast_channel *get_by_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *uniqueid)
+{
+ char *l_name = (char *) uniqueid;
+ size_t name_len = strlen(uniqueid);
+
+ struct ast_channel *chan = callback(driver, by_uniqueid_cb, l_name, &name_len, 0);
+ return chan;
+}
+
+static struct ast_channel *get_by_name_prefix(struct ast_channelstorage_instance *driver,
+ const char *name, size_t name_len)
+{
+ struct ast_channel *chan;
+ char *l_name = (char *) name;
+
+ if (ast_strlen_zero(l_name)) {
+ /* We didn't have a name to search for so quit. */
+ return NULL;
+ }
+
+ chan = callback(driver, by_name_cb, l_name, &name_len,
+ (name_len == 0) /* optimize if it is a complete name match */ ? OBJ_KEY : 0);
+ if (chan) {
+ return chan;
+ }
+
+ /* Now try a search for uniqueid. */
+ chan = callback(driver, by_uniqueid_cb, l_name, &name_len, 0);
+ return chan;
+}
+
+static struct ast_channel *get_by_exten(struct ast_channelstorage_instance *driver,
+ const char *exten, const char *context)
+{
+ char *l_exten = (char *) exten;
+ char *l_context = (char *) context;
+
+ return callback(driver, by_exten_cb, l_context, l_exten, 0);
+}
+
+static int hash_cb(const void *obj, const int flags)
+{
+ const char *name = (flags & OBJ_KEY) ? obj : ast_channel_name((struct ast_channel *) 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(name)) {
+ return 0;
+ }
+
+ return ast_str_case_hash(name);
+}
+
+/*!
+ * \internal
+ * \brief Print channel object key (name).
+ * \since 12.0.0
+ *
+ * \param v_obj A pointer to the object we want the key printed.
+ * \param where User data needed by prnt to determine where to put output.
+ * \param prnt Print output callback function to use.
+ */
+static void prnt_channel_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+ struct ast_channel *chan = v_obj;
+
+ if (!chan) {
+ return;
+ }
+ prnt(where, "%s", ast_channel_name(chan));
+}
+
+static void close_instance(struct ast_channelstorage_instance *driver)
+{
+ ast_debug(1, "Closing ao2_container channel storage driver %s\n", driver ? driver->name : "NULL");
+ if (!driver) {
+ return;
+ }
+
+ if (driver->handle) {
+ if (getdb(driver)) {
+ ao2_container_unregister(driver->name);
+ ao2_ref(getdb(driver), -1);
+ getdb(driver) = NULL;
+ }
+ ast_free(driver->handle);
+ driver->handle = NULL;
+ }
+ ast_free(driver);
+}
+
+static struct ast_channelstorage_instance channelstorage_instance = {
+ .handle = NULL,
+ .close = close_instance,
+ .insert = insert_channel,
+ .remove = delete_channel,
+ .rdlock = lock_driver,
+ .wrlock = lock_driver,
+ .unlock = unlock_driver,
+ .active_channels = active_channels,
+ .callback = callback,
+ .get_by_name_prefix_or_uniqueid = get_by_name_prefix,
+ .get_by_exten = get_by_exten,
+ .get_by_uniqueid = get_by_uniqueid,
+ .iterator_all_new = iterator_all_new,
+ .iterator_by_name_new = iterator_by_name_new,
+ .iterator_by_exten_new = iterator_by_exten_new,
+ .iterator_next = iterator_next,
+ .iterator_destroy = iterator_destroy,
+};
+
+static int channel_cmp_cb(void *obj_left, void *obj_right, int flags)
+{
+ struct ast_channel *tps_left = obj_left;
+ struct ast_channel *tps_right = obj_right;
+ const char *right_key = obj_right;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ default:
+ case OBJ_SEARCH_OBJECT:
+ right_key = ast_channel_name(tps_right);
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcasecmp(ast_channel_name(tps_left), right_key);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ cmp = strncasecmp(ast_channel_name(tps_left), right_key, strlen(right_key));
+ break;
+ }
+ return cmp == 0 ? CMP_MATCH : 0;
+}
+
+
+static struct ast_channelstorage_instance* get_instance(const char *name)
+{
+ const char *_name = name ? name : "default";
+ struct ast_channelstorage_instance* driver = ast_calloc(1,
+ sizeof(*driver) + strlen(_name) + 1);
+
+ ast_debug(1, "Opening channel storage driver %s\n", _name);
+
+ if (!driver) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for channel storage driver %s\n",
+ _name);
+ return NULL;
+ }
+ memcpy(driver, &channelstorage_instance, sizeof(*driver));
+ strcpy(driver->name, _name); /* Safe */
+ driver->handle = ast_calloc(1, sizeof(*driver->handle));
+ if (!driver->handle) {
+ close_instance(driver);
+ ast_log(LOG_ERROR, "Failed to allocate memory for channel storage driver %s\n",
+ _name);
+ return NULL;
+ }
+
+ getdb(driver) = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
+ AST_NUM_CHANNEL_BUCKETS, hash_cb, NULL, channel_cmp_cb);
+ if (!driver->handle) {
+ ast_log(LOG_ERROR, "Failed to create channel storage driver %s\n",
+ _name);
+ close_instance(driver);
+ return NULL;
+ }
+ ao2_container_register(name, getdb(driver), prnt_channel_key);
+ ast_debug(1, "Opened channel storage driver %s. driver: %p container: %p\n",
+ _name, driver, driver->handle);
+
+ return driver;
+}
+
+static struct ast_channelstorage_driver driver_type = {
+ .driver_name = "ao2_legacy",
+ .open = get_instance,
+};
+
+static void __attribute__((constructor)) __startup(void)
+{
+ ast_channelstorage_register_driver(&driver_type);
+}
+
+
--- /dev/null
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2024, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph@sangoma.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.
+ */
+
+#include <memory>
+#include <string>
+#include <map>
+#include <unordered_map>
+#include <cassert>
+#include <utility>
+
+#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "channelstorage.h"
+#include "channel_private.h"
+
+typedef std::map<std::string, struct ast_channel *> ChannelMap;
+
+struct mni_channelstorage_driver_pvt {
+ ChannelMap by_name;
+ ChannelMap by_uniqueid;
+};
+
+#define getdb(driver) (((struct mni_channelstorage_driver_pvt *)driver->handle)->by_name)
+#define map_by_id(driver) (((struct mni_channelstorage_driver_pvt *)driver->handle)->by_uniqueid)
+
+static void rdlock(struct ast_channelstorage_instance *driver)
+{
+ ast_rwlock_rdlock((ast_rwlock_t*)driver->lock_handle);
+}
+
+static void wrlock(struct ast_channelstorage_instance *driver)
+{
+ ast_rwlock_wrlock((ast_rwlock_t*)driver->lock_handle);
+}
+
+static void unlock(struct ast_channelstorage_instance *driver)
+{
+ ast_rwlock_unlock((ast_rwlock_t*)driver->lock_handle);
+}
+
+static int insert_channel(struct ast_channelstorage_instance *driver,
+ struct ast_channel *chan, int flags, int lock)
+{
+ char *l_name = NULL;
+ char *l_uniqueid = NULL;
+ bool success = false;
+ if (!chan) {
+ return -1;
+ }
+
+ if (lock) {
+ wrlock(driver);
+ }
+ l_name = ast_str_to_lower(ast_strdupa(ast_channel_name(chan)));
+ l_uniqueid = ast_str_to_lower(ast_strdupa(ast_channel_uniqueid(chan)));
+
+ auto rtn = getdb(driver).emplace(l_name, ao2_bump(chan));
+ if (rtn.second) {
+ rtn = map_by_id(driver).emplace(l_uniqueid, ao2_bump(chan));
+ if (!rtn.second) {
+ ast_log(LOG_ERROR, "Unable to insert channel '%s' '%s'\n",
+ ast_channel_name(chan), ast_channel_uniqueid(chan));
+ ast_channel_unref(chan);
+ getdb(driver).erase(l_name);
+ ast_channel_unref(chan);
+ }
+ success = rtn.second;
+ } else {
+ ast_log(LOG_ERROR, "Unable to insert channel '%s'\n", ast_channel_name(chan));
+ ast_channel_unref(chan);
+ }
+
+ if (success) {
+ chan->linked_in_container = 1;
+ }
+ if (lock) {
+ unlock(driver);
+ }
+ return success ? 0 : -1;
+}
+
+static int delete_channel(struct ast_channelstorage_instance *driver,
+ struct ast_channel *chan, int lock)
+{
+ char *l_name = NULL;
+ char *l_uniqueid = NULL;
+ if (!chan) {
+ return -1;
+ }
+
+ if (!chan->linked_in_container) {
+ return 0;
+ }
+
+ if (lock) {
+ wrlock(driver);
+ }
+
+ l_name = ast_str_to_lower(ast_strdupa(ast_channel_name(chan)));
+ l_uniqueid = ast_str_to_lower(ast_strdupa(ast_channel_uniqueid(chan)));
+
+ auto deleted = getdb(driver).erase(l_name);
+ if (deleted) {
+ ast_channel_unref(chan);
+ }
+ deleted = map_by_id(driver).erase(l_uniqueid);
+ if (deleted) {
+ ast_channel_unref(chan);
+ }
+ chan->linked_in_container = 0;
+
+ if (lock) {
+ unlock(driver);
+ }
+ return 0;
+}
+
+/*! \brief returns number of active/allocated channels */
+static int active_channels(struct ast_channelstorage_instance *driver)
+{
+ return driver ? getdb(driver).size() : 0;
+}
+
+static struct ast_channel *callback(struct ast_channelstorage_instance *driver,
+ ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
+{
+ struct ast_channel *chan = NULL;
+ ChannelMap::const_iterator it;
+
+ if (!cb_fn) {
+ return NULL;
+ }
+
+ rdlock(driver);
+ for (it = getdb(driver).begin(); it != getdb(driver).end(); it++) {
+ chan = it->second;
+ if (cb_fn(chan, arg, data, ao2_flags) == (CMP_MATCH | CMP_STOP)) {
+ ao2_bump(chan);
+ break;
+ }
+ }
+ unlock(driver);
+
+ return chan;
+}
+
+enum cpp_map_iterator_type {
+ ITERATOR_ALL,
+ ITERATOR_BY_NAME,
+ ITERATOR_BY_EXTEN,
+};
+
+struct mni_channel_iterator {
+ ChannelMap::const_iterator it;
+ ChannelMap::const_iterator it_end;
+ enum cpp_map_iterator_type it_type;
+ char *channel_name;
+ size_t channel_name_len;
+ char *context;
+ char *exten;
+
+ mni_channel_iterator(ChannelMap::const_iterator it,
+ ChannelMap::const_iterator it_end, char *name, size_t name_len)
+ : it(it), it_end(it_end), it_type(ITERATOR_BY_NAME), channel_name(name), channel_name_len(name_len),
+ context(NULL), exten(NULL)
+ {
+ }
+
+ mni_channel_iterator(ChannelMap::const_iterator it,
+ ChannelMap::const_iterator it_end, char *context, char *exten)
+ : it(it), it_end(it_end), it_type(ITERATOR_BY_EXTEN), channel_name(NULL), channel_name_len(0),
+ context(context), exten(exten)
+ {
+ }
+
+ mni_channel_iterator(ChannelMap::const_iterator it, ChannelMap::const_iterator it_end)
+ : it(it), it_end(it_end), it_type(ITERATOR_ALL), channel_name(NULL), channel_name_len(0),
+ context(NULL), exten(NULL)
+ {
+ }
+
+ ~mni_channel_iterator()
+ {
+ ast_free(channel_name);
+ ast_free(context);
+ ast_free(exten);
+ }
+};
+
+static struct ast_channel_iterator *iterator_destroy(struct ast_channelstorage_instance *driver,
+ struct ast_channel_iterator *ai)
+{
+ struct mni_channel_iterator *i = (struct mni_channel_iterator *)ai;
+ delete i;
+ return NULL;
+}
+
+static struct ast_channel_iterator *iterator_all_new(struct ast_channelstorage_instance *driver)
+{
+ struct mni_channel_iterator *i = new mni_channel_iterator(
+ getdb(driver).begin(), getdb(driver).end());
+ if (!i) {
+ return NULL;
+ }
+
+ if (i->it == getdb(driver).end()) {
+ delete i;
+ return NULL;
+ }
+
+ return (struct ast_channel_iterator *)i;
+}
+
+static struct ast_channel *iterator_next(struct ast_channelstorage_instance *driver,
+ struct ast_channel_iterator *ai)
+{
+ struct mni_channel_iterator *i = (struct mni_channel_iterator *)ai;
+ struct ast_channel *chan = NULL;
+
+ if (i->it == i->it_end) {
+ return NULL;
+ }
+
+ if (i->it_type == ITERATOR_ALL) {
+ chan = ao2_bump(i->it->second);
+ ++i->it;
+ return chan;
+ }
+
+ if (i->it_type == ITERATOR_BY_NAME) {
+ chan = ao2_bump(i->it->second);
+ ++i->it;
+ return chan;
+ }
+
+ /* ITERATOR_BY_EXTEN */
+ while (i->it != i->it_end) {
+ int ret = channelstorage_exten_cb(i->it->second, i->context, i->exten, 0);
+ if (ret & CMP_MATCH) {
+ chan = ao2_bump(i->it->second);
+ ++i->it;
+ return chan;
+ }
+ ++i->it;
+ }
+
+ return NULL;
+}
+
+static struct ast_channel_iterator *iterator_by_name_new(struct ast_channelstorage_instance *driver,
+ const char *name, size_t name_len)
+{
+ char *l_name = NULL;
+ char *u_name = NULL;
+ struct mni_channel_iterator *i;
+ size_t new_name_len = 0;
+
+ if (ast_strlen_zero(name)) {
+ return NULL;
+ }
+
+ l_name = ast_str_to_lower(ast_strdupa(name));
+ if (name_len == 0) {
+ name_len = strlen(name);
+ }
+ l_name[name_len] = '\0';
+ new_name_len = strlen(l_name);
+ u_name = (char *)ast_alloca(new_name_len + 2);
+ sprintf(u_name, "%s%c", l_name, '\xFF');
+
+ i = new mni_channel_iterator(getdb(driver).lower_bound(l_name),
+ getdb(driver).upper_bound(u_name));
+ if (!i) {
+ return NULL;
+ }
+
+ if (i->it == getdb(driver).end()) {
+ delete i;
+ return NULL;
+ }
+
+ return (struct ast_channel_iterator *)i;
+}
+
+static struct ast_channel_iterator *iterator_by_exten_new(struct ast_channelstorage_instance *driver,
+ const char *exten, const char *context)
+{
+ struct mni_channel_iterator *i =
+ new mni_channel_iterator(getdb(driver).begin(),
+ getdb(driver).end(),
+ ast_str_to_lower(ast_strdup(context)), ast_str_to_lower(ast_strdup(exten)));
+ if (!i) {
+ return NULL;
+ }
+
+ if (i->it == getdb(driver).end()) {
+ delete i;
+ return NULL;
+ }
+
+ return (struct ast_channel_iterator *)i;
+}
+
+static struct ast_channel *get_by_uniqueid(struct ast_channelstorage_instance *driver,
+ const char *uniqueid)
+{
+ struct ast_channel *chan = NULL;
+ char *search = uniqueid ? ast_str_to_lower(ast_strdupa(uniqueid)) : NULL;
+ if (ast_strlen_zero(uniqueid)) {
+ return NULL;
+ }
+
+ auto rtn = map_by_id(driver).find(search);
+ if (rtn != map_by_id(driver).end()) {
+ chan = ao2_bump((struct ast_channel *)rtn->second);
+ }
+
+ return chan;
+}
+
+static struct ast_channel *get_by_name_exact(struct ast_channelstorage_instance *driver,
+ const char *name)
+{
+ char *search = name ? ast_str_to_lower(ast_strdupa(name)) : NULL;
+ if (ast_strlen_zero(name)) {
+ return NULL;
+ }
+ auto chan = getdb(driver).find(search);
+ if (chan != getdb(driver).end()) {
+ return ao2_bump((struct ast_channel *)chan->second);
+ }
+
+ return NULL;
+}
+
+static struct ast_channel *get_by_name_prefix(struct ast_channelstorage_instance *driver,
+ const char *name, size_t name_len)
+{
+ struct ast_channel *chan = NULL;
+ char *l_name = NULL;
+
+ if (name_len == 0) {
+ chan = get_by_name_exact(driver, name);
+ return chan;
+ }
+
+ l_name = ast_str_to_lower(ast_strdupa(name));
+ auto rtn = getdb(driver).lower_bound(l_name);
+ if (rtn != getdb(driver).end()) {
+ chan = ao2_bump((struct ast_channel *)rtn->second);
+ }
+ return chan;
+}
+
+
+static void close_instance(struct ast_channelstorage_instance *driver)
+{
+ ast_debug(1, "Closing channel storage driver %s\n", driver ? driver->name : "NULL");
+ if (!driver) {
+ return;
+ }
+
+ if (driver->handle) {
+ delete (struct mni_channelstorage_driver_pvt *)driver->handle;
+ driver->handle = NULL;
+ }
+ ast_free(driver->lock_handle);
+ driver->lock_handle = NULL;
+ ast_free(driver);
+}
+
+static struct ast_channelstorage_instance channelstorage_instance = {
+ .handle = NULL,
+ .lock_handle = NULL,
+ .close = close_instance,
+ .insert = insert_channel,
+ .remove = delete_channel,
+ .rdlock = rdlock,
+ .wrlock = wrlock,
+ .unlock = unlock,
+ .active_channels = active_channels,
+ .callback = callback,
+ .get_by_name_prefix= get_by_name_prefix,
+ .get_by_name_prefix_or_uniqueid = channelstorage_by_name_prefix_or_uniqueid,
+ .get_by_exten = channelstorage_by_exten,
+ .get_by_uniqueid = get_by_uniqueid,
+ .iterator_all_new = iterator_all_new,
+ .iterator_by_exten_new = iterator_by_exten_new,
+ .iterator_by_name_new = iterator_by_name_new,
+ .iterator_next = iterator_next,
+ .iterator_destroy = iterator_destroy,
+};
+
+static struct ast_channelstorage_instance* get_instance(const char *name)
+{
+ const char *_name = name ? name : "default";
+ struct ast_channelstorage_instance* driver =
+ (struct ast_channelstorage_instance*)ast_calloc(1,
+ sizeof(*driver) + strlen(_name) + 1);
+
+ ast_debug(1, "Opening channel storage driver %s\n", _name);
+
+ if (!driver) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for channel storage driver %s\n",
+ _name);
+ return NULL;
+ }
+ memcpy(driver, &channelstorage_instance, sizeof(*driver));
+ strcpy(driver->name, _name); /* Safe */
+
+ driver->handle = (struct ast_channelstorage_driver_pvt *)new mni_channelstorage_driver_pvt();
+
+ if (!driver->handle) {
+ ast_log(LOG_ERROR, "Failed to create channel storage driver %s\n",
+ _name);
+ ast_free(driver);
+ return NULL;
+ }
+ driver->lock_handle = ast_calloc(1, sizeof(ast_rwlock_t));
+ if (!driver->lock_handle) {
+ ast_log(LOG_ERROR, "Failed to create container lock for channel storage driver %s\n",
+ _name);
+ close_instance(driver);
+ return NULL;
+ }
+ ast_rwlock_init((ast_rwlock_t *)driver->lock_handle);
+
+ return driver;
+}
+
+static struct ast_channelstorage_driver driver_type = {
+ .driver_name = "cpp_map_name_id",
+ .open = get_instance,
+};
+
+static void __attribute__((constructor)) __startup(void)
+{
+ ast_channelstorage_register_driver(&driver_type);
+}
--- /dev/null
+<category name="MENUSELECT_CHANNELSTORAGE" displayname="Alternate Channel Storage Backends">
+ <member name="channelstorage_cpp_map_name_id"
+ displayname="Include the C++ Map Name/ID channelstorage backend. Requires a C++11 compiler."
+ remove_on_change="main/channelstorage_cpp_map_name_id.oo main/channelstorage_cpp_map_name_id.ii">
+ >
+ <support_level>core</support_level>
+ <depend>CXX11</depend>
+ <defaultenabled>no</defaultenabled>
+ </member>
+</category>
#include "asterisk/utils.h"
#include "../defaults.h"
+#include "channelstorage.h"
#include <sys/time.h>
#include <sys/resource.h>
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIDE_MESSAGING_AMI_EVENTS);
} else if (!strcasecmp(v->name, "sounds_search_custom_dir")) {
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_SOUNDS_SEARCH_CUSTOM);
+ } else if (!strcasecmp(v->name, "channel_storage_backend")) {
+ internal_channel_set_current_storage_driver(v->value);
} else if (!strcasecmp(v->name, "disable_remote_console_shell")) {
ast_option_disable_remote_console_shell = ast_true(v->value);
}