diff --git a/Makefile b/Makefile index 7ef73342cc..b57f77477f 100644 --- a/Makefile +++ b/Makefile @@ -1092,12 +1092,13 @@ menuselect/nmenuselect: menuselect/makeopts .lastclean 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 "" > $@ @echo >> $@ @echo "" >> $@ +@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 \ diff --git a/configs/samples/asterisk.conf.sample b/configs/samples/asterisk.conf.sample index f7b5bae980..afc4fbd7cc 100644 --- a/configs/samples/asterisk.conf.sample +++ b/configs/samples/asterisk.conf.sample @@ -132,6 +132,14 @@ documentation_language = en_US ; Set the language you want documentation ; cause Asterisk to search for sounds files in ; AST_DATA_DIR/sounds/custom before searching the ; normal directories like AST_DATA_DIR/sounds/. +;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. ; Changing the following lines may compromise your security. ;[files] diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 37352b7988..92370c9427 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -1455,15 +1455,17 @@ int ast_queue_answer(struct ast_channel *chan, const struct ast_stream_topology /*! * \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); @@ -3119,6 +3121,13 @@ struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i); /*! @} 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 * @@ -3126,41 +3135,50 @@ struct ast_channel *ast_channel_iterator_next(struct ast_channel_iterator *i); * 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); @@ -3181,6 +3199,16 @@ struct ast_channel *ast_channel_get_by_name_prefix(const char *name, size_t name */ 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. */ /*! @@ -5084,5 +5112,27 @@ void *ast_channel_get_stream_topology_change_source(struct ast_channel *chan); #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 */ diff --git a/include/asterisk/channel_internal.h b/include/asterisk/channel_internal.h index 1b994fa9b4..4da7def4ef 100644 --- a/include/asterisk/channel_internal.h +++ b/include/asterisk/channel_internal.h @@ -39,4 +39,11 @@ void ast_channel_internal_set_stream_topology_change_source( 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 */ diff --git a/include/asterisk/options.h b/include/asterisk/options.h index 315788386d..0970374f0e 100644 --- a/include/asterisk/options.h +++ b/include/asterisk/options.h @@ -32,6 +32,7 @@ extern "C" { #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 */ diff --git a/main/Makefile b/main/Makefile index e6aa7acece..946e891aa7 100644 --- a/main/Makefile +++ b/main/Makefile @@ -31,7 +31,10 @@ ifeq ($(PJPROJECT_BUNDLED),yes) 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 diff --git a/main/asterisk.c b/main/asterisk.c index b0f8a14311..1fe8843a19 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -245,6 +245,7 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/utf8.h" #include "../defaults.h" +#include "channelstorage.h" /*** DOCUMENTATION @@ -578,6 +579,8 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c 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, "\n* Subsystems\n"); ast_cli(a->fd, " -------------\n"); diff --git a/main/channel.c b/main/channel.c index e3fe7de18c..da70f35413 100644 --- a/main/channel.c +++ b/main/channel.c @@ -75,6 +75,8 @@ #include "asterisk/stream.h" #include "asterisk/message.h" +#include "channelstorage.h" + /*** DOCUMENTATION ***/ @@ -120,9 +122,6 @@ struct chanlist { /*! \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 @@ -481,7 +480,7 @@ void ast_channel_softhangup_withcause_locked(struct ast_channel *chan, int cause 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; @@ -492,13 +491,13 @@ static int ast_channel_softhangup_cb(void *obj, void *arg, int flags) 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) @@ -706,23 +705,35 @@ static const struct ast_channel_tech null_tech = { 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; @@ -919,12 +930,12 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char */ 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); } @@ -932,9 +943,9 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char /* 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); @@ -1302,193 +1313,122 @@ void ast_channel_undefer_dtmf(struct ast_channel *chan) } } -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); + if (!current_channel_storage_instance) { + return NULL; + } + if (!cb_fn) { + ast_log(LOG_ERROR, "callback function must be provided\n"); + return NULL; + } + return CHANNELSTORAGE_API(current_channel_storage_instance, callback, cb_fn, arg, data, ao2_flags); } -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) && strcasecmp(ast_channel_macrocontext(chan), context)) { - ret = 0; /* Context match failed, continue */ - } else if (strcasecmp(ast_channel_exten(chan), exten) && strcasecmp(ast_channel_macroexten(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; - } - - 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; -}; - 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; + } + if (ast_strlen_zero(name)) { + ast_log(LOG_ERROR, "name must be provided\n"); 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; - } - - /* 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) @@ -1611,7 +1551,7 @@ int ast_safe_sleep_without_silence(struct ast_channel *chan, int ms) 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); } @@ -2605,7 +2545,7 @@ void ast_hangup(struct ast_channel *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); @@ -6866,13 +6806,13 @@ static void __ast_change_name_nolink(struct ast_channel *chan, const char *newna 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) @@ -6989,6 +6929,9 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann 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 @@ -7011,15 +6954,21 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann * 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) { @@ -7430,9 +7379,10 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann 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); @@ -7896,38 +7846,6 @@ void ast_moh_cleanup(struct ast_channel *chan) ast_moh_cleanup_ptr(chan); } -static int ast_channel_hash_cb(const void *obj, const int flags) -{ - const 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. */ @@ -8101,28 +8019,87 @@ struct varshead *ast_channel_get_ari_vars(struct ast_channel *chan) 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); @@ -10743,7 +10720,7 @@ int ast_channel_get_cc_agent_type(struct ast_channel *chan, char *agent_type, si 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) @@ -11252,3 +11229,4 @@ void ast_channel_clear_flag(struct ast_channel *chan, unsigned int flag) ast_clear_flag(ast_channel_flags(chan), flag); ast_channel_unlock(chan); } + diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index 8f3227779a..8bf09e1c0d 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -48,184 +48,13 @@ #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 */ - struct ast_channel_monitor *monitor; /*!< Channel monitoring */ - 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 macropriority; /*!< Macro: Current non-macro priority. See app_macro.c */ - 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 macrocontext[AST_MAX_CONTEXT]; /*!< Macro: Current non-macro context. See app_macro.c */ - char macroexten[AST_MAX_EXTENSION]; /*!< Macro: Current non-macro extension. See app_macro.c */ - 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; @@ -276,7 +105,6 @@ void ast_channel_##field##_build(struct ast_channel *chan, const char *fmt, ...) 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); @@ -305,6 +133,31 @@ DEFINE_STRINGFIELD_GETTER_FOR(parkinglot); 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'); @@ -1486,6 +1339,13 @@ void ast_channel_internal_swap_uniqueid_and_linkedid(struct ast_channel *a, stru * 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; diff --git a/main/channel_private.h b/main/channel_private.h new file mode 100644 index 0000000000..4db189c28a --- /dev/null +++ b/main/channel_private.h @@ -0,0 +1,209 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, Sangoma Technologies Corporation + * + * George Joseph + * + * 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 */ + struct ast_channel_monitor *monitor; /*!< Channel monitoring */ + 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 */ + int macropriority; /*!< Macro: Current non-macro priority. See app_macro.c */ + 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 macrocontext[AST_MAX_CONTEXT]; /*!< Macro: Current non-macro context. See app_macro.c */ + char macroexten[AST_MAX_EXTENSION]; /*!< Macro: Current non-macro extension. See app_macro.c */ + 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_ */ diff --git a/main/channelstorage.c b/main/channelstorage.c new file mode 100644 index 0000000000..52b0fef971 --- /dev/null +++ b/main/channelstorage.c @@ -0,0 +1,524 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, Sangoma Technologies Corporation + * + * George Joseph + * + * 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; +} + diff --git a/main/channelstorage.h b/main/channelstorage.h new file mode 100644 index 0000000000..4dd01b1d56 --- /dev/null +++ b/main/channelstorage.h @@ -0,0 +1,98 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, Sangoma Technologies Corporation + * + * George Joseph + * + * 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_ */ diff --git a/main/channelstorage_ao2_legacy.c b/main/channelstorage_ao2_legacy.c new file mode 100644 index 0000000000..cfffb1574b --- /dev/null +++ b/main/channelstorage_ao2_legacy.c @@ -0,0 +1,397 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, Sangoma Technologies Corporation + * + * George Joseph + * + * 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); +} + + diff --git a/main/channelstorage_cpp_map_name_id.cc b/main/channelstorage_cpp_map_name_id.cc new file mode 100644 index 0000000000..b61fa8b9aa --- /dev/null +++ b/main/channelstorage_cpp_map_name_id.cc @@ -0,0 +1,454 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, Sangoma Technologies Corporation + * + * George Joseph + * + * 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 +#include +#include +#include +#include +#include + +#include "asterisk/logger.h" +#include "asterisk/lock.h" +#include "channelstorage.h" +#include "channel_private.h" + +typedef std::map 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); +} diff --git a/main/channelstorage_makeopts.xml b/main/channelstorage_makeopts.xml new file mode 100644 index 0000000000..a9f10cb717 --- /dev/null +++ b/main/channelstorage_makeopts.xml @@ -0,0 +1,10 @@ + + + > + core + CXX11 + no + + diff --git a/main/options.c b/main/options.c index 6787d1b3cd..f3177b5ec0 100644 --- a/main/options.c +++ b/main/options.c @@ -40,6 +40,7 @@ #include "asterisk/utils.h" #include "../defaults.h" +#include "channelstorage.h" #include #include @@ -474,6 +475,8 @@ void load_asterisk_conf(void) 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); } } if (!ast_opt_remote) {