summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure96
-rw-r--r--configure.ac1
-rw-r--r--include/asterisk/data_buffer.h144
-rw-r--r--main/asterisk.c2
-rw-r--r--main/data_buffer.c314
-rw-r--r--main/indications.c33
-rw-r--r--res/res_pjsip/pjsip_transport_events.c55
-rw-r--r--tests/test_data_buffer.c313
8 files changed, 828 insertions, 130 deletions
diff --git a/configure b/configure
index 9245063da..86ffdea88 100755
--- a/configure
+++ b/configure
@@ -25886,102 +25886,6 @@ fi
-if test "x${PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "x1" -a "${USE_PJSIP_EVSUB_SET_UAS_TIMEOUT}" != "no"; then
- pbxlibdir=""
- # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it.
- if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then
- if test -d ${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib; then
- pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/lib"
- else
- pbxlibdir="-L${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}"
- fi
- fi
-
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS"
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjsip_evsub_set_uas_timeout in -lpjsip" >&5
-$as_echo_n "checking for pjsip_evsub_set_uas_timeout in -lpjsip... " >&6; }
-if ${ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-/* Override any GCC internal prototype to avoid an error.
- Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pjsip_evsub_set_uas_timeout ();
-int
-main ()
-{
-return pjsip_evsub_set_uas_timeout ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout=yes
-else
- ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" >&5
-$as_echo "$ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" >&6; }
-if test "x$ac_cv_lib_pjsip_pjsip_evsub_set_uas_timeout" = xyes; then :
- AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND=yes
-else
- AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND=no
-fi
-
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_PJSIP_EVSUB_SET_UAS_TIMEOUT_FOUND}" = "yes"; then
- PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
- # if --with-PJSIP_EVSUB_SET_UAS_TIMEOUT=DIR has been specified, use it.
- if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}" != "x"; then
- PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="-I${PJSIP_EVSUB_SET_UAS_TIMEOUT_DIR}/include"
- fi
- PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE="${PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE} $PJPROJECT_CFLAGS"
-
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default"
-if test "x$ac_cv_header_pjsip_h" = xyes; then :
- PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=1
-else
- PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${PJSIP_EVSUB_SET_UAS_TIMEOUT_HEADER_FOUND}" = "x0" ; then
- PJSIP_EVSUB_SET_UAS_TIMEOUT_LIB=""
- PJSIP_EVSUB_SET_UAS_TIMEOUT_INCLUDE=""
- else
-
- PBX_PJSIP_EVSUB_SET_UAS_TIMEOUT=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_PJSIP_EVSUB_SET_UAS_TIMEOUT 1
-_ACEOF
-
- fi
- fi
-fi
-
-
-
if test "x${PBX_PJSIP_TSX_LAYER_FIND_TSX2}" != "x1" -a "${USE_PJSIP_TSX_LAYER_FIND_TSX2}" != "no"; then
pbxlibdir=""
# if --with-PJSIP_TSX_LAYER_FIND_TSX2=DIR has been specified, use it.
diff --git a/configure.ac b/configure.ac
index 219f85d0c..e2af23493 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2303,7 +2303,6 @@ if test "$USE_PJPROJECT" != "no" ; then
AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
AST_EXT_LIB_CHECK([PJSIP_INV_SESSION_REF], [pjsip], [pjsip_inv_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
AST_EXT_LIB_CHECK([PJSIP_AUTH_CLT_DEINIT], [pjsip], [pjsip_auth_clt_deinit], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
- AST_EXT_LIB_CHECK([PJSIP_EVSUB_SET_UAS_TIMEOUT], [pjsip], [pjsip_evsub_set_uas_timeout], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
AST_EXT_LIB_CHECK([PJSIP_TSX_LAYER_FIND_TSX2], [pjsip], [pjsip_tsx_layer_find_tsx2], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
fi
fi
diff --git a/include/asterisk/data_buffer.h b/include/asterisk/data_buffer.h
new file mode 100644
index 000000000..dacbaa5e4
--- /dev/null
+++ b/include/asterisk/data_buffer.h
@@ -0,0 +1,144 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2018, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ * Ben Ford <bford@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Data Buffer API
+ *
+ * A data buffer acts as a ring buffer of data. It is given a fixed
+ * number of data packets to store (which may be dynamically changed).
+ * Given a number it will store a data packet at that position relative
+ * to the others. Given a number it will retrieve the given data packet
+ * if it is present. This is purposely a storage of arbitrary things so
+ * that it can be used for multiple things.
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Ben Ford <bford@digium.com>
+ */
+
+#ifndef _AST_DATA_BUFFER_H_
+#define _AST_DATA_BUFFER_H_
+
+/*!
+ * \brief A buffer of data payloads.
+ */
+struct ast_data_buffer;
+
+/*!
+ * \brief A callback function to free a data payload in a data buffer
+ *
+ * \param The data payload
+ */
+typedef void (*ast_data_buffer_free_callback)(void *data);
+
+/*!
+ * \brief Allocate a data buffer
+ *
+ * \param free_fn Callback function to free a data payload
+ * \param size The maximum number of data payloads to contain in the data buffer
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note free_fn can be NULL. It is up to the consumer of this API to ensure that memory is
+ * managed appropriately.
+ *
+ * \since 15.4.0
+ */
+struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size);
+
+/*!
+ * \brief Resize a data buffer
+ *
+ * \param buffer The data buffer
+ * \param size The new maximum size of the data buffer
+ *
+ * \note If the data buffer is shrunk any old data payloads will be freed using the configured callback.
+ * The data buffer is flexible and can be used for multiple purposes. Therefore it is up to the
+ * caller of the function to know whether or not a buffer should have its size changed. Increasing
+ * the size of the buffer may make sense in some scenarios, but shrinking should always be handled
+ * with caution since data can be lost.
+ *
+ * \since 15.4.0
+ */
+void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size);
+
+/*!
+ * \brief Place a data payload at a position in the data buffer
+ *
+ * \param buffer The data buffer
+ * \param pos The position of the data payload
+ * \param payload The data payload
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note It is up to the consumer of this API to ensure proper memory management of data payloads
+ *
+ * \since 15.4.0
+ */
+int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload);
+
+/*!
+ * \brief Retrieve a data payload from the data buffer
+ *
+ * \param buffer The data buffer
+ * \param pos The position of the data payload
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This does not remove the data payload from the data buffer. It will be removed when it is displaced.
+ *
+ * \since 15.4.0
+ */
+void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos);
+
+/*!
+ * \brief Free a data buffer (and all held data payloads)
+ *
+ * \param buffer The data buffer
+ *
+ * \since 15.4.0
+ */
+void ast_data_buffer_free(struct ast_data_buffer *buffer);
+
+/*!
+ * \brief Return the number of payloads in a data buffer
+ *
+ * \param buffer The data buffer
+ *
+ * \retval the number of data payloads
+ *
+ * \since 15.4.0
+ */
+size_t ast_data_buffer_count(const struct ast_data_buffer *buffer);
+
+/*!
+ * \brief Return the maximum number of payloads a data buffer can hold
+ *
+ * \param buffer The data buffer
+ *
+ * \retval the maximum number of data payloads
+ *
+ * \since 15.4.0
+ */
+size_t ast_data_buffer_max(const struct ast_data_buffer *buffer);
+
+#endif /* _AST_DATA_BUFFER_H */
diff --git a/main/asterisk.c b/main/asterisk.c
index 36b1b54a1..2e80ffaf6 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -3963,7 +3963,7 @@ int main(int argc, char *argv[])
*
* \todo Document these options
*/
- optind = 0;
+ optind = 1;
while ((c = getopt(argc, argv, getopt_settings)) != -1) {
/*!\note Please keep the ordering here to alphabetical, capital letters
* first. This will make it easier in the future to select unused
diff --git a/main/data_buffer.c b/main/data_buffer.c
new file mode 100644
index 000000000..ccbffd22d
--- /dev/null
+++ b/main/data_buffer.c
@@ -0,0 +1,314 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2018, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ * Ben Ford <bford@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Data Buffer API
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Ben Ford <bford@digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/logger.h"
+#include "asterisk/strings.h"
+#include "asterisk/data_buffer.h"
+#include "asterisk/linkedlists.h"
+
+/*!
+ * \brief The number of payloads to increment the cache by
+ */
+#define CACHED_PAYLOAD_MAX 5
+
+/*!
+ * \brief Payload entry placed inside of the data buffer list
+ */
+struct data_buffer_payload_entry {
+ /*! \brief The payload for this position */
+ void *payload;
+ /*! \brief The provided position for this */
+ size_t pos;
+ /*! \brief Linked list information */
+ AST_LIST_ENTRY(data_buffer_payload_entry) list;
+};
+
+/*!
+ * \brief Data buffer containing fixed number of data payloads
+ */
+struct ast_data_buffer {
+ /*! \brief Callback function to free a data payload */
+ ast_data_buffer_free_callback free_fn;
+ /*! \brief A linked list of data payloads */
+ AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) payloads;
+ /*! \brief A linked list of unused cached data payloads */
+ AST_LIST_HEAD_NOLOCK(, data_buffer_payload_entry) cached_payloads;
+ /*! \brief The current number of data payloads in the buffer */
+ size_t count;
+ /*! \brief Maximum number of data payloads in the buffer */
+ size_t max;
+ /*! \brief The current number of data payloads in the cache */
+ size_t cache_count;
+};
+
+static void free_fn_do_nothing(void *data)
+{
+ return;
+}
+
+/*!
+ * \brief Helper function to allocate a data payload
+ */
+static struct data_buffer_payload_entry *data_buffer_payload_alloc(void *payload, size_t pos)
+{
+ struct data_buffer_payload_entry *data_payload;
+
+ data_payload = ast_calloc(1, sizeof(*data_payload));
+ if (!data_payload) {
+ return NULL;
+ }
+
+ data_payload->payload = payload;
+ data_payload->pos = pos;
+
+ return data_payload;
+}
+
+/*!
+ * \brief Helper function that sets the cache to its maximum number of payloads
+ */
+static void ast_data_buffer_cache_adjust(struct ast_data_buffer *buffer)
+{
+ int buffer_space;
+
+ ast_assert(buffer != NULL);
+
+ buffer_space = buffer->max - buffer->count;
+
+ if (buffer->cache_count == buffer_space) {
+ return;
+ }
+
+ if (buffer->cache_count < buffer_space) {
+ /* Add payloads to the cache, if able */
+ while (buffer->cache_count < CACHED_PAYLOAD_MAX && buffer->cache_count < buffer_space) {
+ struct data_buffer_payload_entry *buffer_payload;
+
+ buffer_payload = data_buffer_payload_alloc(NULL, -1);
+ if (buffer_payload) {
+ AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list);
+ buffer->cache_count++;
+ continue;
+ }
+
+ ast_log(LOG_ERROR, "Failed to allocate memory to the cache.");
+ break;
+ }
+ } else if (buffer->cache_count > buffer_space) {
+ /* Remove payloads from the cache */
+ while (buffer->cache_count > buffer_space) {
+ struct data_buffer_payload_entry *buffer_payload;
+
+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);
+ if (buffer_payload) {
+ ast_free(buffer_payload);
+ buffer->cache_count--;
+ continue;
+ }
+
+ ast_log(LOG_ERROR, "Failed to remove memory from the cache.");
+ break;
+ }
+ }
+}
+
+struct ast_data_buffer *ast_data_buffer_alloc(ast_data_buffer_free_callback free_fn, size_t size)
+{
+ struct ast_data_buffer *buffer;
+
+ ast_assert(size != 0);
+
+ buffer = ast_calloc(1, sizeof(*buffer));
+ if (!buffer) {
+ return NULL;
+ }
+
+ AST_LIST_HEAD_INIT_NOLOCK(&buffer->payloads);
+ AST_LIST_HEAD_INIT_NOLOCK(&buffer->cached_payloads);
+
+ /* If free_fn is NULL, just use free_fn_do_nothing as a default */
+ buffer->free_fn = free_fn ? free_fn : free_fn_do_nothing;
+ buffer->max = size;
+
+ ast_data_buffer_cache_adjust(buffer);
+
+ return buffer;
+}
+
+void ast_data_buffer_resize(struct ast_data_buffer *buffer, size_t size)
+{
+ struct data_buffer_payload_entry *existing_payload;
+
+ ast_assert(buffer != NULL);
+
+ /* The buffer must have at least a size of 1 */
+ ast_assert(size > 0);
+
+ if (buffer->max == size) {
+ return;
+ }
+
+ /* If the size is decreasing, some payloads will need to be freed */
+ if (buffer->max > size) {
+ int remove = buffer->max - size;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {
+ if (remove) {
+ AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
+ buffer->free_fn(existing_payload->payload);
+ ast_free(existing_payload);
+ buffer->count--;
+ remove--;
+ continue;
+ }
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+
+ buffer->max = size;
+ ast_data_buffer_cache_adjust(buffer);
+}
+
+int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payload)
+{
+ struct data_buffer_payload_entry *buffer_payload = NULL;
+ struct data_buffer_payload_entry *existing_payload;
+ int inserted = 0;
+
+ ast_assert(buffer != NULL);
+ ast_assert(payload != NULL);
+
+ /* If the data buffer has reached its maximum size then the head goes away and
+ * we will reuse its buffer payload
+ */
+ if (buffer->count == buffer->max) {
+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
+ buffer->free_fn(buffer_payload->payload);
+ buffer->count--;
+
+ /* Update this buffer payload with its new information */
+ buffer_payload->payload = payload;
+ buffer_payload->pos = pos;
+ }
+ if (!buffer_payload) {
+ if (!buffer->cache_count) {
+ ast_data_buffer_cache_adjust(buffer);
+ }
+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list);
+ buffer->cache_count--;
+
+ /* Update the payload from the cache with its new information */
+ buffer_payload->payload = payload;
+ buffer_payload->pos = pos;
+ }
+ if (!buffer_payload) {
+ return -1;
+ }
+
+ /* Given the position find its ideal spot within the buffer */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, existing_payload, list) {
+ /* If it's already in the buffer, drop it */
+ if (existing_payload->pos == pos) {
+ ast_debug(3, "Packet with position %zu is already in buffer. Not inserting.\n", pos);
+ inserted = -1;
+ break;
+ }
+
+ if (existing_payload->pos > pos) {
+ AST_LIST_INSERT_BEFORE_CURRENT(buffer_payload, list);
+ inserted = 1;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (inserted == -1) {
+ return 0;
+ }
+
+ if (!inserted) {
+ AST_LIST_INSERT_TAIL(&buffer->payloads, buffer_payload, list);
+ }
+
+ buffer->count++;
+
+ return 0;
+}
+
+void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos)
+{
+ struct data_buffer_payload_entry *buffer_payload;
+
+ ast_assert(buffer != NULL);
+
+ AST_LIST_TRAVERSE(&buffer->payloads, buffer_payload, list) {
+ if (buffer_payload->pos == pos) {
+ return buffer_payload->payload;
+ }
+ }
+
+ return NULL;
+}
+
+void ast_data_buffer_free(struct ast_data_buffer *buffer)
+{
+ struct data_buffer_payload_entry *buffer_payload;
+
+ ast_assert(buffer != NULL);
+
+ while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list))) {
+ buffer->free_fn(buffer_payload->payload);
+ ast_free(buffer_payload);
+ }
+
+ while ((buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->cached_payloads, list))) {
+ ast_free(buffer_payload);
+ }
+
+ ast_free(buffer);
+}
+
+size_t ast_data_buffer_count(const struct ast_data_buffer *buffer)
+{
+ ast_assert(buffer != NULL);
+
+ return buffer->count;
+}
+
+size_t ast_data_buffer_max(const struct ast_data_buffer *buffer)
+{
+ ast_assert(buffer != NULL);
+
+ return buffer->max;
+}
diff --git a/main/indications.c b/main/indications.c
index 9b0976809..c9f02416f 100644
--- a/main/indications.c
+++ b/main/indications.c
@@ -632,9 +632,7 @@ static struct ast_tone_zone *ast_tone_zone_alloc(void)
static char *complete_country(struct ast_cli_args *a)
{
- char *res = NULL;
struct ao2_iterator i;
- int which = 0;
size_t wordlen;
struct ast_tone_zone *tz;
@@ -642,17 +640,17 @@ static char *complete_country(struct ast_cli_args *a)
i = ao2_iterator_init(ast_tone_zones, 0);
while ((tz = ao2_iterator_next(&i))) {
- if (!strncasecmp(a->word, tz->country, wordlen) && ++which > a->n) {
- res = ast_strdup(tz->country);
- }
- tz = ast_tone_zone_unref(tz);
- if (res) {
- break;
+ if (!strncasecmp(a->word, tz->country, wordlen)) {
+ if (ast_cli_completion_add(ast_strdup(tz->country))) {
+ ast_tone_zone_unref(tz);
+ break;
+ }
}
+ ast_tone_zone_unref(tz);
}
ao2_iterator_destroy(&i);
- return res;
+ return NULL;
}
static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -718,17 +716,17 @@ static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct
static char *complete_indications(struct ast_cli_args *a)
{
- char *res = NULL;
- int which = 0;
size_t wordlen;
struct ast_tone_zone_sound *ts;
- struct ast_tone_zone *tz, tmp_tz = {
+ struct ast_tone_zone *tz;
+ struct ast_tone_zone tmp_tz = {
.nrringcadence = 0,
};
ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
- if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
+ tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER);
+ if (!tz) {
return NULL;
}
@@ -736,16 +734,17 @@ static char *complete_indications(struct ast_cli_args *a)
ast_tone_zone_lock(tz);
AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
- if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
- res = ast_strdup(ts->name);
- break;
+ if (!strncasecmp(a->word, ts->name, wordlen)) {
+ if (ast_cli_completion_add(ast_strdup(ts->name))) {
+ break;
+ }
}
}
ast_tone_zone_unlock(tz);
tz = ast_tone_zone_unref(tz);
- return res;
+ return NULL;
}
static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
diff --git a/res/res_pjsip/pjsip_transport_events.c b/res/res_pjsip/pjsip_transport_events.c
index c701b8411..cc7b7c077 100644
--- a/res/res_pjsip/pjsip_transport_events.c
+++ b/res/res_pjsip/pjsip_transport_events.c
@@ -114,6 +114,36 @@ static void transport_monitor_dtor(void *vdoomed)
AST_VECTOR_FREE(&monitored->monitors);
}
+/*!
+ * \internal
+ * \brief Do registered callbacks for the transport.
+ * \since 13.21.0
+ *
+ * \param transports Active transports container
+ * \param transport Which transport to do callbacks for.
+ *
+ * \return Nothing
+ */
+static void transport_state_do_reg_callbacks(struct ao2_container *transports, pjsip_transport *transport)
+{
+ struct transport_monitor *monitored;
+
+ monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_UNLINK);
+ if (monitored) {
+ int idx;
+
+ for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
+ struct transport_monitor_notifier *notifier;
+
+ notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
+ ast_debug(3, "running callback %p(%p) for transport %s\n",
+ notifier->cb, notifier->data, transport->obj_name);
+ notifier->cb(notifier->data);
+ }
+ ao2_ref(monitored, -1);
+ }
+}
+
/*! \brief Callback invoked when transport state changes occur */
static void transport_state_callback(pjsip_transport *transport,
pjsip_transport_state state, const pjsip_transport_state_info *info)
@@ -147,6 +177,7 @@ static void transport_state_callback(pjsip_transport *transport,
if (!transport->is_shutdown) {
pjsip_transport_shutdown(transport);
}
+ transport_state_do_reg_callbacks(transports, transport);
break;
case PJSIP_TP_STATE_SHUTDOWN:
/*
@@ -157,23 +188,17 @@ static void transport_state_callback(pjsip_transport *transport,
*/
transport->is_shutdown = PJ_TRUE;
- monitored = ao2_find(transports, transport->obj_name,
- OBJ_SEARCH_KEY | OBJ_UNLINK);
- if (monitored) {
- int idx;
-
- for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
- struct transport_monitor_notifier *notifier;
-
- notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
- ast_debug(3, "running callback %p(%p) for transport %s\n",
- notifier->cb, notifier->data, transport->obj_name);
- notifier->cb(notifier->data);
- }
- ao2_ref(monitored, -1);
- }
+ transport_state_do_reg_callbacks(transports, transport);
+ break;
+ case PJSIP_TP_STATE_DESTROY:
+ transport_state_do_reg_callbacks(transports, transport);
break;
default:
+ /*
+ * We have to have a default case because the enum is
+ * defined by a third-party library.
+ */
+ ast_assert(0);
break;
}
diff --git a/tests/test_data_buffer.c b/tests/test_data_buffer.c
new file mode 100644
index 000000000..11fdc7b6f
--- /dev/null
+++ b/tests/test_data_buffer.c
@@ -0,0 +1,313 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2018, Digium, Inc.
+ *
+ * Ben Ford <bford@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Media Stream API Unit Tests
+ *
+ * \author Ben Ford <bford@digium.com>
+ *
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/data_buffer.h"
+
+#define BUFFER_MAX_NOMINAL 10
+
+struct mock_payload
+{
+ int id;
+};
+
+/* Ensures that RAII_VAR will not trip ast_assert(buffer != NULL) in the callback */
+static void ast_data_buffer_free_wrapper(struct ast_data_buffer *buffer)
+{
+ if (!buffer) {
+ return;
+ }
+
+ ast_data_buffer_free(buffer);
+}
+
+AST_TEST_DEFINE(buffer_create)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_create";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer create unit test";
+ info->description =
+ "Test that creating a data buffer results in a buffer with the expected values";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 0,
+ "Newly created buffer does not have the expected payload count");
+ ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL,
+ "Newly created buffer does not have the expected max size");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(buffer_put)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+ struct mock_payload *payload;
+ struct mock_payload *fetched_payload;
+ int ret;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_put";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer put unit test";
+ info->description =
+ "Test that putting payloads in the buffer yields the expected results";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, 2);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 0,
+ "Newly created buffer is not empty");
+
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for first payload");
+
+ payload->id = 2;
+ ret = ast_data_buffer_put(buffer, 2, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Adding a payload to an empty buffer did not return the expected value");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 1,
+ "Adding a payload to an empty buffer did not update count to the expected value");
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 2);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get only payload from buffer given valid arguments");
+
+ ast_data_buffer_put(buffer, 2, payload);
+
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 1,
+ "Adding a payload that is already in the buffer should not do anything");
+
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for second payload");
+
+ payload->id = 1;
+ ast_data_buffer_put(buffer, 1, payload);
+ fetched_payload = ast_data_buffer_get(buffer, 1);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get a payload from buffer given valid arguments");
+ ast_test_validate(test, ast_data_buffer_count(buffer) == 2,
+ "Buffer does not have the expected count after removing a payload");
+ ast_test_validate(test, fetched_payload->id == 1,
+ "Did not get the expected payload from the buffer");
+
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for third payload");
+
+ payload->id = 3;
+ ret = ast_data_buffer_put(buffer, 3, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Failed to replace a payload in the buffer");
+ ast_test_validate(test, ast_data_buffer_count(buffer) <= 2,
+ "Buffer count exceeded the max");
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 3);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get a payload from buffer at position 3 given valid arguments");
+ ast_test_validate(test, fetched_payload->id == 3,
+ "Did not get the expected payload at position 3 from the buffer");
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, 2);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get a payload from buffer at position 2 given valid arguments");
+ ast_test_validate(test, fetched_payload->id == 2,
+ "Did not get the expected payload at position 2 from the buffer");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(buffer_resize)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_resize";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer resize unit test";
+ info->description =
+ "Tests resizing a data buffer to make sure it has the expected outcome";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+
+ ast_data_buffer_resize(buffer, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL,
+ "Trying to resize buffer to same size should not change its max size");
+
+ ast_data_buffer_resize(buffer, BUFFER_MAX_NOMINAL + 2);
+
+ ast_test_validate(test, ast_data_buffer_max(buffer) == BUFFER_MAX_NOMINAL + 2,
+ "Increasing buffer size did not return the expected max");
+
+ ast_data_buffer_resize(buffer, 1);
+
+ ast_test_validate(test, ast_data_buffer_max(buffer) == 1,
+ "Decreasing buffer size did not return the expected max");
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(buffer_nominal)
+{
+ RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
+ struct mock_payload *payload;
+ struct mock_payload *fetched_payload;
+ int ret;
+ int i;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "buffer_nominal";
+ info->category = "/main/data_buffer/";
+ info->summary = "buffer nominal unit test";
+ info->description =
+ "Tests the normal usage of a data buffer to ensure the expected payloads "
+ "are present after multiple insertions";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ buffer = ast_data_buffer_alloc(ast_free_ptr, BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, buffer != NULL,
+ "Failed to create buffer with valid arguments");
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for payload %d", i);
+
+ ret = ast_data_buffer_put(buffer, i, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Failed to add payload %d to buffer", i);
+ }
+
+ ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL,
+ "Buffer does not have the expected count after adding payloads");
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get payload at position %d during first loop", i);
+ }
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ payload = ast_calloc(1, sizeof(*payload));
+
+ ast_test_validate(test, payload != NULL,
+ "Failed to allocate memory for payload %d", i + BUFFER_MAX_NOMINAL);
+
+ ret = ast_data_buffer_put(buffer, i + BUFFER_MAX_NOMINAL, payload);
+
+ ast_test_validate(test, ret == 0,
+ "Failed to add payload %d to buffer", i + BUFFER_MAX_NOMINAL);
+ }
+
+ ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL,
+ "Buffer does not have the expected count after replacing payloads");
+
+ for (i = 1; i <= BUFFER_MAX_NOMINAL; i++) {
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i);
+
+ ast_test_validate(test, fetched_payload == NULL,
+ "Got an unexpected payload at position %d", i);
+
+ fetched_payload = (struct mock_payload *)ast_data_buffer_get(buffer, i + BUFFER_MAX_NOMINAL);
+
+ ast_test_validate(test, fetched_payload != NULL,
+ "Failed to get payload at position %d during second loop", i + BUFFER_MAX_NOMINAL);
+ }
+
+ return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+ AST_TEST_UNREGISTER(buffer_create);
+ AST_TEST_UNREGISTER(buffer_put);
+ AST_TEST_UNREGISTER(buffer_resize);
+ AST_TEST_UNREGISTER(buffer_nominal);
+ return 0;
+}
+
+static int load_module(void)
+{
+ AST_TEST_REGISTER(buffer_create);
+ AST_TEST_REGISTER(buffer_put);
+ AST_TEST_REGISTER(buffer_resize);
+ AST_TEST_REGISTER(buffer_nominal);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Data buffer API test module");