summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES3
-rw-r--r--build_tools/cflags.xml9
-rw-r--r--channels/chan_sip.c1
-rw-r--r--channels/sip/include/sip.h3
-rwxr-xr-xconfigure196
-rw-r--r--configure.ac36
-rw-r--r--include/asterisk/ari.h11
-rw-r--r--include/asterisk/autoconfig.h.in21
-rw-r--r--include/asterisk/compiler.h6
-rw-r--r--include/asterisk/http_websocket.h18
-rw-r--r--include/asterisk/optional_api.h293
-rw-r--r--main/asterisk.c5
-rw-r--r--main/loader.c102
-rw-r--r--main/optional_api.c365
-rw-r--r--res/ari/ari_websockets.c16
-rw-r--r--res/ari/internal.h22
-rw-r--r--res/res_ari.c11
-rw-r--r--res/res_ari_events.c1
-rw-r--r--res/res_http_websocket.c55
-rw-r--r--rest-api-templates/res_ari_resource.c.mustache7
-rw-r--r--rest-api-templates/swagger_model.py2
-rw-r--r--tests/test_optional_api.c187
22 files changed, 1011 insertions, 359 deletions
diff --git a/CHANGES b/CHANGES
index 9a223fdd1..f7078ea79 100644
--- a/CHANGES
+++ b/CHANGES
@@ -63,6 +63,9 @@ Build System
Please see https://wiki.asterisk.org/wiki/x/J4GLAQ for more information on
configuring and installing PJSIP for usage with Asterisk.
+ * Optional API was re-implemented to be more portable, and no longer requires
+ weak reference support from the compiler. The build option OPTIONAL_API may
+ be disabled to disable Optional API support.
Applications
------------------
diff --git a/build_tools/cflags.xml b/build_tools/cflags.xml
index c4d15a67b..e7bec1672 100644
--- a/build_tools/cflags.xml
+++ b/build_tools/cflags.xml
@@ -1,4 +1,4 @@
- <category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes" remove_on_change=".lastclean">
+<category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes" remove_on_change=".lastclean">
<member name="DONT_OPTIMIZE" displayname="Disable Optimizations by the Compiler">
<support_level>core</support_level>
</member>
@@ -30,6 +30,13 @@
-->
<support_level>extended</support_level>
</member>
+ <member name="OPTIONAL_API" displayname="Enable the optional API">
+ <!-- Added to manually disable the optional API, since
+ it's now supported on all systems.
+ -->
+ <defaultenabled>yes</defaultenabled>
+ <support_level>extended</support_level>
+ </member>
<member name="BETTER_BACKTRACES" displayname="Use libbfd (GPL) to generate better inline backtraces">
<depend>BFD</depend>
<depend>DLADDR</depend>
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index e6d9cffc7..55b1ed550 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -298,6 +298,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stasis_system.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/features_config.h"
+#include "asterisk/http_websocket.h"
/*** DOCUMENTATION
<application name="SIPDtmfMode" language="en_US">
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 724581b71..c0704ec79 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -35,7 +35,6 @@
#include "asterisk/indications.h"
#include "asterisk/security_events.h"
#include "asterisk/features.h"
-#include "asterisk/http_websocket.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/netsock2.h"
#include "asterisk/features_config.h"
@@ -769,6 +768,8 @@ struct sip_settings {
int default_max_forwards; /*!< Default max forwards (SIP Anti-loop) */
};
+struct ast_websocket;
+
/*! \brief The SIP socket definition */
struct sip_socket {
enum ast_transport type; /*!< UDP, TCP or TLS */
diff --git a/configure b/configure
index 2d04b6d11..3dff95f60 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
#! /bin/sh
-# From configure.ac Revision: 395985 .
+# From configure.ac Revision: 397868 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for asterisk trunk.
#
@@ -684,6 +684,7 @@ PKG_CONFIG_PATH
PKG_CONFIG
PBX_DLADDR
PBX_IP_MTU_DISCOVER
+PBX_RTLD_NOLOAD
PBX_GLOB_BRACE
PBX_GLOB_NOMAGIC
AST_RPATH
@@ -696,7 +697,6 @@ AST_TRAMPOLINES
AST_DECLARATION_AFTER_STATEMENT
GC_LDFLAGS
GC_CFLAGS
-PBX_WEAKREF
PBX_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
PBX_PTHREAD_RWLOCK_INITIALIZER
AST_ASTERISKSSL
@@ -16564,28 +16564,17 @@ CFLAGS="$saved_CFLAGS"
-# Support weak symbols on a platform specific basis. The Mac OS X
-# (Darwin) support must be isolated from the other platforms because
-# it has caused other platforms to fail.
-#
-case "${OSARCH}" in
- darwin*)
- # Allow weak symbol support on Darwin platforms only because there
- # is active community support for it.
- # However, Darwin seems to break weak symbols for each new version.
- #
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weak_import' support" >&5
-$as_echo_n "checking for compiler 'attribute weak_import' support... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute may_alias' support" >&5
+$as_echo_n "checking for compiler 'attribute may_alias' support... " >&6; }
saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
+
if test "x" = "x"
then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
- void __attribute__((weak_import)) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__((may_alias)) *test(void *muffin, ...) {return (void *) 0;}
int
main ()
{
@@ -16597,10 +16586,10 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- PBX_WEAKREF=1
+
cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak_import 1
+#define HAVE_ATTRIBUTE_may_alias 1
_ACEOF
else
@@ -16624,10 +16613,10 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- PBX_WEAKREF=1
+
cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak_import 1
+#define HAVE_ATTRIBUTE_may_alias 1
_ACEOF
else
@@ -16643,21 +16632,17 @@ CFLAGS="$saved_CFLAGS"
- # Several other platforms including Linux have GCC versions that
- # define the weak attribute. However, this attribute is only
- # setup for use in the code by Darwin.
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weak' support" >&5
-$as_echo_n "checking for compiler 'attribute weak' support... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute constructor' support" >&5
+$as_echo_n "checking for compiler 'attribute constructor' support... " >&6; }
saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
+
if test "x" = "x"
then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
- void __attribute__((weak)) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__((constructor)) *test(void *muffin, ...) {return (void *) 0;}
int
main ()
{
@@ -16669,10 +16654,10 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- PBX_WEAKREF=1
+
cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak 1
+#define HAVE_ATTRIBUTE_constructor 1
_ACEOF
else
@@ -16696,82 +16681,10 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- PBX_WEAKREF=1
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak 1
-_ACEOF
-
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-
-CFLAGS="$saved_CFLAGS"
-
-
- ;;
- linux-gnu)
- # Primarily support weak symbols on Linux platforms.
- #
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weakref' support" >&5
-$as_echo_n "checking for compiler 'attribute weakref' support... " >&6; }
-saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
-
-if test "xweakref("foo")" = "x"
-then
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-static void __attribute__((weakref)) *test(void *muffin, ...) {return (void *) 0;}
-int
-main ()
-{
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
- PBX_WEAKREF=1
cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
-_ACEOF
-
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
-int
-main ()
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
- PBX_WEAKREF=1
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
+#define HAVE_ATTRIBUTE_constructor 1
_ACEOF
else
@@ -16786,24 +16699,18 @@ fi
CFLAGS="$saved_CFLAGS"
- ;;
- *)
- # Allow weak symbols on other platforms. However, any problems
- # with this feature on other platforms must be fixed by the
- # community.
- #
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weakref' support" >&5
-$as_echo_n "checking for compiler 'attribute weakref' support... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute destructor' support" >&5
+$as_echo_n "checking for compiler 'attribute destructor' support... " >&6; }
saved_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
-if test "xweakref("foo")" = "x"
+
+if test "x" = "x"
then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-static void __attribute__((weakref)) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__((destructor)) *test(void *muffin, ...) {return (void *) 0;}
int
main ()
{
@@ -16815,10 +16722,10 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- PBX_WEAKREF=1
+
cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
+#define HAVE_ATTRIBUTE_destructor 1
_ACEOF
else
@@ -16830,7 +16737,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
int
main ()
{
@@ -16842,10 +16749,10 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
- PBX_WEAKREF=1
+
cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
+#define HAVE_ATTRIBUTE_destructor 1
_ACEOF
else
@@ -16860,8 +16767,6 @@ fi
CFLAGS="$saved_CFLAGS"
- ;;
-esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -ffunction-sections support" >&5
$as_echo_n "checking for -ffunction-sections support... " >&6; }
@@ -17454,6 +17359,53 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test "x${PBX_RTLD_NOLOAD}" != "x1"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for RTLD_NOLOAD in dlfcn.h" >&5
+$as_echo_n "checking for RTLD_NOLOAD in dlfcn.h... " >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${RTLD_NOLOAD_DIR}" != "x"; then
+ RTLD_NOLOAD_INCLUDE="-I${RTLD_NOLOAD_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${RTLD_NOLOAD_INCLUDE}"
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+ #include <dlfcn.h>
+int
+main ()
+{
+#if defined(RTLD_NOLOAD)
+ int foo = 0;
+ #else
+ int foo = bar;
+ #endif
+ 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ PBX_RTLD_NOLOAD=1
+
+$as_echo "#define HAVE_RTLD_NOLOAD 1" >>confdefs.h
+
+
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
+
+
+
+
if test "x${PBX_IP_MTU_DISCOVER}" != "x1"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for IP_MTU_DISCOVER in netinet/in.h" >&5
$as_echo_n "checking for IP_MTU_DISCOVER in netinet/in.h... " >&6; }
diff --git a/configure.ac b/configure.ac
index 69228b517..e409327fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -957,37 +957,9 @@ AST_GCC_ATTRIBUTE(always_inline)
AST_GCC_ATTRIBUTE(deprecated)
AST_GCC_ATTRIBUTE(sentinel)
AST_GCC_ATTRIBUTE(warn_unused_result)
-
-# Support weak symbols on a platform specific basis. The Mac OS X
-# (Darwin) support must be isolated from the other platforms because
-# it has caused other platforms to fail.
-#
-case "${OSARCH}" in
- darwin*)
- # Allow weak symbol support on Darwin platforms only because there
- # is active community support for it.
- # However, Darwin seems to break weak symbols for each new version.
- #
- AST_GCC_ATTRIBUTE(weak_import, [], [], PBX_WEAKREF)
-
- # Several other platforms including Linux have GCC versions that
- # define the weak attribute. However, this attribute is only
- # setup for use in the code by Darwin.
- AST_GCC_ATTRIBUTE(weak, [], [], PBX_WEAKREF)
- ;;
- linux-gnu)
- # Primarily support weak symbols on Linux platforms.
- #
- AST_GCC_ATTRIBUTE(weakref, [weakref("foo")], static, PBX_WEAKREF)
- ;;
- *)
- # Allow weak symbols on other platforms. However, any problems
- # with this feature on other platforms must be fixed by the
- # community.
- #
- AST_GCC_ATTRIBUTE(weakref, [weakref("foo")], static, PBX_WEAKREF)
- ;;
-esac
+AST_GCC_ATTRIBUTE(may_alias)
+AST_GCC_ATTRIBUTE(constructor)
+AST_GCC_ATTRIBUTE(destructor)
AC_MSG_CHECKING(for -ffunction-sections support)
saved_CFLAGS="${CFLAGS}"
@@ -1181,6 +1153,8 @@ AST_C_DEFINE_CHECK([GLOB_NOMAGIC], [GLOB_NOMAGIC], [glob.h])
AST_C_DEFINE_CHECK([GLOB_BRACE], [GLOB_BRACE], [glob.h])
+AST_C_DEFINE_CHECK([RTLD_NOLOAD], [RTLD_NOLOAD], [dlfcn.h])
+
AST_C_DEFINE_CHECK([IP_MTU_DISCOVER], [IP_MTU_DISCOVER], [netinet/in.h])
AC_CHECK_HEADER([libkern/OSAtomic.h],
diff --git a/include/asterisk/ari.h b/include/asterisk/ari.h
index 4f2954000..dfeef513c 100644
--- a/include/asterisk/ari.h
+++ b/include/asterisk/ari.h
@@ -21,7 +21,7 @@
/*! \file
*
- * \brief Stasis RESTful API hooks.
+ * \brief Asterisk RESTful API hooks.
*
* This header file is used mostly as glue code between generated declarations
* and res_ari.c.
@@ -31,7 +31,14 @@
#include "asterisk/http.h"
#include "asterisk/json.h"
-#include "asterisk/http_websocket.h"
+
+/* Forward-declare websocket structs. This avoids including http_websocket.h,
+ * which causes optional_api stuff to happen, which makes optional_api more
+ * difficult to debug. */
+
+struct ast_websocket_server;
+
+struct ast_websocket;
/*!
* \brief Configured encoding format for JSON output.
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 1796a3f68..87a769ed0 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -82,12 +82,21 @@
/* Define to 1 if your GCC C compiler supports the 'const' attribute. */
#undef HAVE_ATTRIBUTE_const
+/* Define to 1 if your GCC C compiler supports the 'constructor' attribute. */
+#undef HAVE_ATTRIBUTE_constructor
+
/* Define to 1 if your GCC C compiler supports the 'deprecated' attribute. */
#undef HAVE_ATTRIBUTE_deprecated
+/* Define to 1 if your GCC C compiler supports the 'destructor' attribute. */
+#undef HAVE_ATTRIBUTE_destructor
+
/* Define to 1 if your GCC C compiler supports the 'malloc' attribute. */
#undef HAVE_ATTRIBUTE_malloc
+/* Define to 1 if your GCC C compiler supports the 'may_alias' attribute. */
+#undef HAVE_ATTRIBUTE_may_alias
+
/* Define to 1 if your GCC C compiler supports the 'pure' attribute. */
#undef HAVE_ATTRIBUTE_pure
@@ -101,15 +110,6 @@
attribute. */
#undef HAVE_ATTRIBUTE_warn_unused_result
-/* Define to 1 if your GCC C compiler supports the 'weak' attribute. */
-#undef HAVE_ATTRIBUTE_weak
-
-/* Define to 1 if your GCC C compiler supports the 'weak_import' attribute. */
-#undef HAVE_ATTRIBUTE_weak_import
-
-/* Define to 1 if your GCC C compiler supports the 'weakref' attribute. */
-#undef HAVE_ATTRIBUTE_weakref
-
/* Define to 1 if you have the Debug symbol decoding library. */
#undef HAVE_BFD
@@ -720,6 +720,9 @@
/* Define to 1 if you have the `roundl' function. */
#undef HAVE_ROUNDL
+/* Define if your system has the RTLD_NOLOAD headers. */
+#undef HAVE_RTLD_NOLOAD
+
/* Define to 1 if your system has /sbin/launchd. */
#undef HAVE_SBIN_LAUNCHD
diff --git a/include/asterisk/compiler.h b/include/asterisk/compiler.h
index 91112dbfe..77b5de40e 100644
--- a/include/asterisk/compiler.h
+++ b/include/asterisk/compiler.h
@@ -71,6 +71,12 @@
#define attribute_warn_unused_result
#endif
+#ifdef HAVE_ATTRIBUTE_may_alias
+#define attribute_may_alias __attribute__((may_alias))
+#else
+#define attribute_may_alias
+#endif
+
/* Some older version of GNU gcc (3.3.5 on OpenBSD 4.3 for example) dont like 'NULL' as sentinel */
#define SENTINEL ((char *)NULL)
diff --git a/include/asterisk/http_websocket.h b/include/asterisk/http_websocket.h
index 82c7ad8b6..10cb9a023 100644
--- a/include/asterisk/http_websocket.h
+++ b/include/asterisk/http_websocket.h
@@ -22,6 +22,8 @@
#include "asterisk/http.h"
#include "asterisk/optional_api.h"
+#include <errno.h>
+
/*!
* \file http_websocket.h
* \brief Support for WebSocket connections within the Asterisk HTTP server.
@@ -71,7 +73,7 @@ typedef void (*ast_websocket_callback)(struct ast_websocket *session, struct ast
* \retval \c NULL on error
* \since 12
*/
-struct ast_websocket_server *ast_websocket_server_create(void);
+AST_OPTIONAL_API(struct ast_websocket_server *, ast_websocket_server_create, (void), { return NULL; });
/*!
* \brief Callback suitable for use with a \ref ast_http_uri.
@@ -79,7 +81,7 @@ struct ast_websocket_server *ast_websocket_server_create(void);
* Set the data field of the ast_http_uri to \ref ast_websocket_server.
* \since 12
*/
-int ast_websocket_uri_cb(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers);
+AST_OPTIONAL_API(int, ast_websocket_uri_cb, (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers), { return -1; });
/*!
* \brief Add a sub-protocol handler to the default /ws server
@@ -141,7 +143,7 @@ AST_OPTIONAL_API(int, ast_websocket_server_remove_protocol, (struct ast_websocke
*
* \note Once an AST_WEBSOCKET_OPCODE_CLOSE opcode is received the socket will be closed
*/
-AST_OPTIONAL_API(int, ast_websocket_read, (struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_read, (struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented), { errno = ENOSYS; return -1;});
/*!
* \brief Construct and transmit a WebSocket frame
@@ -154,7 +156,7 @@ AST_OPTIONAL_API(int, ast_websocket_read, (struct ast_websocket *session, char *
* \retval 0 if successfully written
* \retval -1 if error occurred
*/
-AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length), { errno = ENOSYS; return -1;});
/*!
* \brief Close a WebSocket session by sending a message with the CLOSE opcode and an optional code
@@ -165,7 +167,7 @@ AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum
* \retval 0 if successfully written
* \retval -1 if error occurred
*/
-AST_OPTIONAL_API(int, ast_websocket_close, (struct ast_websocket *session, uint16_t reason), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_close, (struct ast_websocket *session, uint16_t reason), { errno = ENOSYS; return -1;});
/*!
* \brief Enable multi-frame reconstruction up to a certain number of bytes
@@ -207,7 +209,7 @@ AST_OPTIONAL_API(void, ast_websocket_unref, (struct ast_websocket *session), {re
*
* \note You must *not* directly read from or write to this file descriptor. It should only be used for polling.
*/
-AST_OPTIONAL_API(int, ast_websocket_fd, (struct ast_websocket *session), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_fd, (struct ast_websocket *session), { errno = ENOSYS; return -1;});
/*!
* \brief Get the remote address for a WebSocket connected session.
@@ -222,7 +224,7 @@ AST_OPTIONAL_API(struct ast_sockaddr *, ast_websocket_remote_address, (struct as
* \retval 0 if unsecure
* \retval 1 if secure
*/
-AST_OPTIONAL_API(int, ast_websocket_is_secure, (struct ast_websocket *session), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_is_secure, (struct ast_websocket *session), { errno = ENOSYS; return -1;});
/*!
* \brief Set the socket of a WebSocket session to be non-blocking.
@@ -230,6 +232,6 @@ AST_OPTIONAL_API(int, ast_websocket_is_secure, (struct ast_websocket *session),
* \retval 0 on success
* \retval -1 on failure
*/
-AST_OPTIONAL_API(int, ast_websocket_set_nonblock, (struct ast_websocket *session), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_set_nonblock, (struct ast_websocket *session), { errno = ENOSYS; return -1;});
#endif
diff --git a/include/asterisk/optional_api.h b/include/asterisk/optional_api.h
index cc31ce0a6..7d66d2e47 100644
--- a/include/asterisk/optional_api.h
+++ b/include/asterisk/optional_api.h
@@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 2008, Digium, Inc.
+ * Copyright (C) 2008-2013, Digium, Inc.
*
* Kevin P. Fleming <kpfleming@digium.com>
*
@@ -30,19 +30,6 @@
* have only a part of their functionality dependent on the APIs, and can
* provide the remainder even if the APIs are not available.
*
- * To accomodate this situation, the AST_OPTIONAL_API macro allows an API
- * function to be declared in a special way, if Asterisk being built on a
- * platform that supports special compiler and dynamic linker attributes.
- * If so the API function will actually be a weak symbol, which means if the
- * provider of the API is not loaded, the symbol can still be referenced (unlike a
- * strong symbol, which would cause an immediate fault if not defined when
- * referenced), but it will return NULL signifying the linker/loader was
- * not able to resolve the symbol. In addition, the macro defines a hidden
- * 'stub' version of the API call, using a provided function body, and uses
- * various methods to make the API function symbol actually resolve to
- * that hidden stub, but only when the *real* provider of the symbol has
- * not been found.
- *
* An example can be found in agi.h:
*
* \code
@@ -74,19 +61,6 @@
* apply special aliases to the function prototype; this can be done
* by defining AST_API_MODULE just before including the header file
* containing the AST_OPTIONAL_API macro calls.
- *
- * \note If the platform does not provide adequate resources,
- * then the AST_OPTIONAL_API macro will result in a non-optional function
- * definition; this means that any consumers of the API functions so
- * defined will require that the provider of the API functions be
- * loaded before they can reference the symbols.
- *
- * WARNING WARNING WARNING WARNING WARNING
- *
- * You MUST add the AST_MODFLAG_GLOBAL_SYMBOLS to the module for which you
- * are enabling optional_api functionality, or it will fail to work.
- *
- * WARNING WARNING WARNING WARNING WARNING
*/
/*!
@@ -99,150 +73,185 @@
*/
#define AST_OPTIONAL_API_UNAVAILABLE INT_MIN
-
-#if defined(HAVE_ATTRIBUTE_weak_import) || defined(HAVE_ATTRIBUTE_weak)
-
-/*
- * This is the Darwin (Mac OS/X) implementation, that only provides the 'weak'
- * or 'weak_import' compiler attribute for weak symbols. On this platform,
- *
- * - The module providing the API will only provide a '__' prefixed version
- * of the API function to other modules (this will be hidden from the other
- * modules by the macros), so any modules compiled against older versions
- * of the module that provided a non-prefixed version of the API function
- * will fail to link at runtime.
- * - In the API module itself, access to the API function without using a
- * prefixed name is provided by a static pointer variable that holds the
- * function address.
- * - 'Consumer' modules of the API will use a combination of a weak_import or
- * weak symbol, a local stub function, a pointer variable and a constructor
- * function (which initializes that pointer variable as the module is being
- * loaded) to provide safe, optional access to the API function without any
- * special code being required.
+/*!
+ * \def AST_OPTIONAL_API_NAME(name)
+ * \brief Expands to the name of the implementation function.
*/
-#if defined(HAVE_ATTRIBUTE_weak_import)
-#define __default_attribute weak_import /* pre-Lion */
-#else
-#define __default_attribute weak /* Lion-onwards */
-#endif
-
-#define AST_OPTIONAL_API_NAME(name) __##name
-
-#if defined(AST_API_MODULE)
+/*!
+ * \def AST_OPTIONAL_API(result, name, proto, stub)
+ * \brief Declare an optional API function
+ *
+ * \param result The type of result the function returns
+ * \param name The name of the function
+ * \param proto The prototype (arguments) of the function
+ * \param stub The code block that will be used by the hidden stub when needed
+ *
+ * Example usage:
+ * \code
+ * AST_OPTIONAL_API(int, ast_agi_register, (struct ast_module *mod, agi_command *cmd),
+ * { return AST_OPTIONAL_API_UNAVAILABLE; });
+ * \endcode
+ */
-#define AST_OPTIONAL_API(result, name, proto, stub) \
- result AST_OPTIONAL_API_NAME(name) proto; \
- static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const name = AST_OPTIONAL_API_NAME(name);
+/*!
+ * \def AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub)
+ * \brief Declare an optional API function with compiler attributes
+ *
+ * \param result The type of result the function returns
+ * \param attr Any compiler attributes to be applied to the function (without the __attribute__ wrapper)
+ * \param name The name of the function
+ * \param proto The prototype (arguments) of the function
+ * \param stub The code block that will be used by the hidden stub when needed
+ */
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
- result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto; \
- static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const name = AST_OPTIONAL_API_NAME(name);
+#if defined(OPTIONAL_API)
-#else
+#if !defined(HAVE_ATTRIBUTE_constructor) || !defined(HAVE_ATTRIBUTE_constructor)
+#error OPTIONAL_API requires compiler constructor/destructor support
+#endif
-#define AST_OPTIONAL_API(result, name, proto, stub) \
- static result __stub__##name proto stub; \
- __attribute__((__default_attribute)) typeof(__stub__##name) AST_OPTIONAL_API_NAME(name); \
- static attribute_unused typeof(__stub__##name) * name; \
- static void __attribute__((constructor)) __init__##name(void) { name = AST_OPTIONAL_API_NAME(name) ? : __stub__##name; }
+/*!
+ * \internal
+ * \brief Function pointer to an optional API function.
+ *
+ * Functions that are declared as optional may have any signature they want;
+ * they are cast to this type as needed. We don't use a \c void pointer, because
+ * technically data and function pointers are incompatible.
+ *
+ * \note
+ * The may_alias attribute is to avoid type punning/strict aliasing warnings
+ * with older GCC's.
+ */
+typedef void (*ast_optional_fn)(void) attribute_may_alias;
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
- static __attribute__((attr)) result __stub__##name proto stub; \
- __attribute__((attr, __default_attribute)) typeof(__stub__##name) AST_OPTIONAL_API_NAME(name); \
- static attribute_unused __attribute__((attr)) typeof(__stub__##name) * name; \
- static void __attribute__((constructor)) __init__##name(void) { name = AST_OPTIONAL_API_NAME(name) ? : __stub__##name; }
+/*!
+ * \internal
+ * \brief Provide an implementation of an optional API.
+ *
+ * Any declared usages of this function are linked.
+ *
+ * \param symname Name of the provided function.
+ * \param impl Function pointer to the implementation function.
+ */
+void ast_optional_api_provide(const char *symname, ast_optional_fn impl);
-#endif
+/*!
+ * \internal
+ * \brief Remove an implementation of an optional API.
+ *
+ * Any declared usages of this function are unlinked.
+ *
+ * \param symname Name of the provided function.
+ * \param impl Function pointer to the implementation function.
+ */
+void ast_optional_api_unprovide(const char *symname, ast_optional_fn impl);
-/* End of Darwin (Mac OS/X) implementation */
+/*!
+ * \internal
+ * \brief Define a usage of an optional API.
+ *
+ * If the API has been provided, it will be linked into \a optional_ref.
+ * Otherwise, it will be linked to \a stub until an implementation is provided.
+ *
+ * \param symname Name of the function to use.
+ * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
+ * \param stub Stub function to link to when impl is not available.
+ * \param module Name of the module requesting the API.
+ */
+void ast_optional_api_use(const char *symname, ast_optional_fn *optional_ref,
+ ast_optional_fn stub, const char *module);
-#elif defined(HAVE_ATTRIBUTE_weakref)
+/*!
+ * \internal
+ * \brief Remove a usage of an optional API.
+ *
+ * The \a optional_ref will be linked to the \a stub provided at use time,
+ * will no longer be updated if the API is provided/removed.
+ *
+ * \param symname Name of the function to use.
+ * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
+ * \param module Name of the module requesting the API.
+ */
+void ast_optional_api_unuse(const char *symname, ast_optional_fn *optional_ref,
+ const char *module);
-/*
- * This is the generic GCC implementation, used when the 'weakref'
- * compiler attribute is available. On these platforms:
- *
- * - The module providing the API will provide a '__' prefixed version
- * of the API function to other modules (this will be hidden from the other
- * modules by the macros), and also a non-prefixed alias so that modules
- * compiled against older versions of the module that provided a non-prefixed
- * version of the API function will continue to link properly.
- * - In the API module itself, access to the API function without using a
- * prefixed name is provided by the non-prefixed alias described above.
- * - 'Consumer' modules of the API will use a combination of a weakref
- * symbol, a local stub function, a pointer variable and a constructor function
- * (which initializes that pointer variable as the module is being loaded)
- * to provide safe, optional access to the API function without any special
- * code being required.
+/*!
+ * \brief Call at exit to clean up optional_api internals.
+ *
+ * Since the optional_api code might run before main() starts, it can't safely
+ * register its own cleanup handlers. That has to be done within main().
*/
+void optional_api_cleanup(void);
#define AST_OPTIONAL_API_NAME(name) __##name
#if defined(AST_API_MODULE)
-
-#define AST_OPTIONAL_API(result, name, proto, stub) \
- result AST_OPTIONAL_API_NAME(name) proto; \
- static __attribute__((alias(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(AST_OPTIONAL_API_NAME(name)) name;
-
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
- result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto; \
- static __attribute__((alias(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(AST_OPTIONAL_API_NAME(name)) name;
+/* Module defining the API */
+
+#define AST_OPTIONAL_API_IMPL_INIT(name) \
+ static void __attribute__((constructor)) __init__##name##_impl(void) { \
+ ast_optional_api_provide(#name, \
+ (ast_optional_fn)AST_OPTIONAL_API_NAME(name)); \
+ } \
+ static void __attribute__((destructor)) __dtor__##name##_impl(void) { \
+ ast_optional_api_unprovide(#name, \
+ (ast_optional_fn)AST_OPTIONAL_API_NAME(name)); \
+ }
+
+#define AST_OPTIONAL_API(result, name, proto, stub) \
+ result AST_OPTIONAL_API_NAME(name) proto; \
+ static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const \
+ name = AST_OPTIONAL_API_NAME(name); \
+ AST_OPTIONAL_API_IMPL_INIT(name)
+
+#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
+ result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto; \
+ static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const \
+ name = AST_OPTIONAL_API_NAME(name); \
+ AST_OPTIONAL_API_IMPL_INIT(name)
#else
+/* Module using the API */
-#define AST_OPTIONAL_API(result, name, proto, stub) \
- static result __stub__##name proto stub; \
- static __attribute__((weakref(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(__stub__##name) __ref__##name; \
- static attribute_unused typeof(__stub__##name) * name; \
- static void __attribute__((constructor)) __init__##name(void) { name = __ref__##name ? : __stub__##name; }
+#define AST_OPTIONAL_API_INIT(name) \
+ static void __attribute__((constructor)) __init__##name(void) { \
+ ast_optional_api_use(#name, (ast_optional_fn *)&name, \
+ (ast_optional_fn)__stub__##name, \
+ AST_MODULE); \
+ } \
+ static void __attribute__((destructor)) __dtor__##name(void) { \
+ ast_optional_api_unuse(#name, (ast_optional_fn *)&name, \
+ AST_MODULE); \
+ }
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
- static __attribute__((attr)) result __stub__##name proto stub; \
- static __attribute__((attr, weakref(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(__stub__##name) __ref__##name; \
- static attribute_unused __attribute__((attr)) typeof(__stub__##name) * name; \
- static void __attribute__((constructor)) __init__##name(void) { name = __ref__##name ? : __stub__##name; }
+#define AST_OPTIONAL_API(result, name, proto, stub) \
+ static result __stub__##name proto stub; \
+ static attribute_unused \
+ typeof(__stub__##name) * name; \
+ AST_OPTIONAL_API_INIT(name)
-#endif
+#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
+ static __attribute__((attr)) result __stub__##name proto stub; \
+ static attribute_unused __attribute__((attr)) \
+ typeof(__stub__##name) * name; \
+ AST_OPTIONAL_API_INIT(name)
-/* End of GCC implementation */
+#endif /* defined(AST_API_MODULE) */
-#else
+#else /* defined(OPTIONAL_API) */
-/* This is the non-optional implementation. */
+/* Non-optional API */
#define AST_OPTIONAL_API_NAME(name) name
-/*!
- * \brief Define an optional API function
- *
- * \param result The type of result the function returns
- * \param name The name of the function
- * \param proto The prototype (arguments) of the function
- * \param stub The code block that will be used by the hidden stub when needed
- *
- * Example usage:
- * \code
- * AST_OPTIONAL_API(int, ast_agi_register, (struct ast_module *mod, agi_command *cmd),
- * { return AST_OPTIONAL_API_UNAVAILABLE; });
- * \endcode
- */
-#define AST_OPTIONAL_API(result, name, proto, stub) result AST_OPTIONAL_API_NAME(name) proto
+#define AST_OPTIONAL_API(result, name, proto, stub) \
+ result AST_OPTIONAL_API_NAME(name) proto
-/*!
- * \brief Define an optional API function with compiler attributes
- *
- * \param result The type of result the function returns
- * \param attr Any compiler attributes to be applied to the function (without the __attribute__ wrapper)
- * \param name The name of the function
- * \param proto The prototype (arguments) of the function
- * \param stub The code block that will be used by the hidden stub when needed
- */
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto
-
-/* End of non-optional implementation */
+#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
+ result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto
-#endif
+#endif /* defined(OPTIONAL_API) */
#undef AST_API_MODULE
diff --git a/main/asterisk.c b/main/asterisk.c
index 7e5ddac58..df5561860 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -247,6 +247,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/stasis_endpoints.h"
#include "asterisk/stasis_system.h"
#include "asterisk/security_events.h"
+#include "asterisk/optional_api.h"
#include "../defaults.h"
@@ -4165,6 +4166,10 @@ int main(int argc, char *argv[])
ast_el_read_history(filename);
}
+#if defined(OPTIONAL_API)
+ ast_register_cleanup(optional_api_cleanup);
+#endif
+
ast_json_init();
ast_ulaw_init();
ast_alaw_init();
diff --git a/main/loader.c b/main/loader.c
index ddfbffe9f..4065043aa 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -185,6 +185,8 @@ void ast_module_register(const struct ast_module_info *info)
mod = resource_being_loaded;
}
+ ast_verb(5, "Registering module %s\n", info->name);
+
mod->info = info;
AST_LIST_HEAD_INIT(&mod->users);
@@ -230,6 +232,7 @@ void ast_module_unregister(const struct ast_module_info *info)
AST_LIST_UNLOCK(&module_list);
if (mod) {
+ ast_verb(5, "Unregistering module %s\n", info->name);
AST_LIST_HEAD_DESTROY(&mod->users);
ast_free(mod);
}
@@ -403,16 +406,83 @@ static struct ast_module *find_resource(const char *resource, int do_lock)
}
#ifdef LOADABLE_MODULES
+
+/*!
+ * \brief dlclose(), with failure logging.
+ */
+static void logged_dlclose(const char *name, void *lib)
+{
+ if (dlclose(lib) != 0) {
+ ast_log(LOG_WARNING, "Failed to unload %s: %s\n",
+ name, dlerror());
+ }
+}
+
+#if defined(HAVE_RTLD_NOLOAD)
+/*!
+ * \brief Check to see if the given resource is loaded.
+ *
+ * \param resource_name Name of the resource, including .so suffix.
+ * \return False (0) if module is not loaded.
+ * \return True (non-zero) if module is loaded.
+ */
+static int is_module_loaded(const char *resource_name)
+{
+ char fn[PATH_MAX] = "";
+ void *lib;
+
+ ast_verb(10, "Checking if %s is loaded\n", resource_name);
+
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR,
+ resource_name);
+
+ lib = dlopen(fn, RTLD_LAZY | RTLD_NOLOAD);
+
+ if (lib) {
+ ast_verb(10, " %s loaded\n", resource_name);
+ logged_dlclose(resource_name, lib);
+ return 1;
+ }
+
+ ast_verb(10, " %s not loaded\n", resource_name);
+ return 0;
+}
+#endif
+
static void unload_dynamic_module(struct ast_module *mod)
{
+ char *name = ast_strdupa(ast_module_name(mod));
void *lib = mod->lib;
/* WARNING: the structure pointed to by mod is going to
disappear when this operation succeeds, so we can't
dereference it */
- if (lib)
- while (!dlclose(lib));
+ if (!lib) {
+ return;
+ }
+
+ logged_dlclose(name, lib);
+
+ /* There are several situations where the module might still be resident
+ * in memory.
+ *
+ * If somehow there was another dlopen() on the same module (unlikely,
+ * since that all is supposed to happen in loader.c).
+ *
+ * Or the lazy resolution of a global symbol (very likely, since that is
+ * how we load all of our modules that export global symbols).
+ *
+ * Avoid the temptation of repeating the dlclose(). The other code that
+ * dlopened the module still has its module reference, and should close
+ * it itself. In other situations, dlclose() will happily return success
+ * for as many times as you wish to call it.
+ */
+#if defined(HAVE_RTLD_NOLOAD)
+ if (is_module_loaded(name)) {
+ ast_log(LOG_WARNING, "Module '%s' could not be completely unloaded\n", name);
+ }
+#endif
}
static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, struct ast_heap *resource_heap, int required);
@@ -461,7 +531,7 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
/* no, it did not, so close it and return */
- while (!dlclose(lib));
+ logged_dlclose(resource_in, lib);
/* note that the module's destructor will call ast_module_unregister(),
which will free the structure we allocated in resource_being_loaded */
return NULL;
@@ -472,32 +542,11 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
/* if we are being asked only to load modules that provide global symbols,
and this one does not, then close it and return */
if (global_symbols_only && !wants_global) {
- while (!dlclose(lib));
+ logged_dlclose(resource_in, lib);
return NULL;
}
- /* This section is a workaround for a gcc 4.1 bug that has already been
- * fixed in later versions. Unfortunately, some distributions, such as
- * RHEL/CentOS 5, distribute gcc 4.1, so we're stuck with having to deal
- * with this issue. This basically ensures that optional_api modules are
- * loaded before any module which requires their functionality. */
-#if !defined(HAVE_ATTRIBUTE_weak_import) && !defined(HAVE_ATTRIBUTE_weakref)
- if (!ast_strlen_zero(mod->info->nonoptreq)) {
- /* Force any required dependencies to load */
- char *each, *required_resource = ast_strdupa(mod->info->nonoptreq);
- while ((each = strsep(&required_resource, ","))) {
- struct ast_module *dependency;
- each = ast_strip(each);
- dependency = find_resource(each, 0);
- /* Is it already loaded? */
- if (!dependency) {
- load_resource(each, global_symbols_only, resource_heap, 1);
- }
- }
- }
-#endif
-
- while (!dlclose(lib));
+ logged_dlclose(resource_in, lib);
resource_being_loaded = NULL;
/* start the load process again */
@@ -523,6 +572,7 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
return AST_LIST_LAST(&module_list);
}
+
#endif
void ast_module_shutdown(void)
diff --git a/main/optional_api.c b/main/optional_api.c
new file mode 100644
index 000000000..f48fe11a1
--- /dev/null
+++ b/main/optional_api.c
@@ -0,0 +1,365 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@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.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/optional_api.h"
+#include "asterisk/utils.h"
+
+#if defined(OPTIONAL_API)
+
+/*
+ * \file Optional API innards.
+ *
+ * The calls to ast_optional_api_*() happen implicitly from \c __constructor__
+ * calls which are defined in header files. This means that some number of them
+ * happen before main() is called. This makes calling most Asterisk APIs
+ * dangerous, since we could be called before they are initialized. This
+ * includes things like AO2, malloc debug, and static mutexes.
+ *
+ * Another limitation is that most functions are called from the midst of
+ * dlopen() or dlclose(), and there is no opportunity to return a failure code.
+ * The best we can do is log an error, and call ast_do_crash().
+ *
+ * Fortunately, there are some constraints that help us out. The \c
+ * ast_optional_api_*() are called during module loads, which happens either
+ * before main(), or during dlopen() calls. These are already serialized, so we
+ * don't have to lock ourselves.
+ */
+
+/*! \brief A user of an optional API */
+struct optional_api_user {
+ /*! Pointer to function pointer to link */
+ ast_optional_fn *optional_ref;
+ /*! Stub to use when impl is unavailable */
+ ast_optional_fn stub;
+ /*! Name of the module using the API */
+ char module[];
+};
+
+/*! \brief An optional API */
+struct optional_api {
+ /*! Pointer to the implementation function; could be null */
+ ast_optional_fn impl;
+ /*! Variable length array of users of this API */
+ struct optional_api_user **users;
+ /*! Allocated size of the \a users array */
+ size_t users_maxlen;
+ /*! Number of entries in the \a users array */
+ size_t users_len;
+ /*! Name of the optional API function */
+ char symname[];
+};
+
+/*!
+ * \brief Free an \ref optional_api_user.
+ *
+ * \param user User struct to free.
+ */
+static void optional_api_user_destroy(struct optional_api_user *user)
+{
+ *user->optional_ref = user->stub;
+ free(user);
+}
+
+/*!
+ * \brief Create an \ref optional_api_user.
+ *
+ * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
+ * \param stub Stub function to link to when impl is not available.
+ * \param module Name of the module requesting the API.
+ *
+ * \return New \ref optional_api_user.
+ * \return \c NULL on error.
+ */
+static struct optional_api_user *optional_api_user_create(
+ ast_optional_fn *optional_ref, ast_optional_fn stub, const char *module)
+{
+ struct optional_api_user *user;
+ size_t size = sizeof(*user) + strlen(module) + 1;
+
+ user = calloc(1, size);
+ if (!user) {
+ return NULL;
+ }
+
+ user->optional_ref = optional_ref;
+ user->stub = stub;
+ strcpy(user->module, module); /* SAFE */
+
+ return user;
+}
+
+/*!
+ * \brief Free an \ref optional_api.
+ *
+ * \param api API struct to free.
+ */
+static void optional_api_destroy(struct optional_api *api)
+{
+ while (api->users_len--) {
+ optional_api_user_destroy(api->users[api->users_len]);
+ }
+ free(api->users);
+ api->users = NULL;
+ api->users_maxlen = 0;
+ free(api);
+}
+
+/*!
+ * \brief Create an \ref optional_api.
+ *
+ * \param symname Name of the optional function.
+ * \return New \ref optional_api.
+ * \return \c NULL on error.
+ */
+static struct optional_api *optional_api_create(const char *symname)
+{
+ struct optional_api *api;
+ size_t size;
+
+ ast_verb(6, "%s: building api object\n", symname);
+ size = sizeof(*api) + strlen(symname) + 1;
+ api = calloc(1, size);
+ if (!api) {
+ ast_log(LOG_ERROR, "Failed to allocate api\n");
+ return NULL;
+ }
+
+ strcpy(api->symname, symname); /* SAFE */
+
+ return api;
+}
+
+/*! Array of \ref optional_api functions */
+struct {
+ /*! Variable length array of API's */
+ struct optional_api **list;
+ /*! Allocated size of the \a list array */
+ size_t maxlen;
+ /*! Number of entries in the \a list array */
+ size_t len;
+} apis;
+
+void optional_api_cleanup(void)
+{
+ while (apis.len--) {
+ optional_api_destroy(apis.list[apis.len]);
+ }
+ free(apis.list);
+ apis.list = NULL;
+ apis.maxlen = 0;
+}
+
+/*!
+ * \brief Gets (or creates) the \ref optional_api for the give function.
+ *
+ * \param sysname Name of the function to look up.
+ * \return Corresponding \ref optional_api.
+ * \return \c NULL on error.
+ */
+static struct optional_api *get_api(const char *symname)
+{
+ struct optional_api *api;
+ size_t i;
+
+ /* Find one, if we already have it */
+ for (i = 0; i < apis.len; ++i) {
+ if (strcmp(symname, apis.list[i]->symname) == 0) {
+ return apis.list[i];
+ }
+ }
+
+ /* API not found. Build one */
+ api = optional_api_create(symname);
+
+ /* Grow the list, if needed */
+ if (apis.len + 1 > apis.maxlen) {
+ size_t new_maxlen = apis.maxlen ? 2 * apis.maxlen : 1;
+ struct optional_api **new_list =
+ realloc(apis.list, new_maxlen * sizeof(*new_list));
+
+ if (!new_list) {
+ optional_api_destroy(api);
+ ast_log(LOG_ERROR, "Failed to allocate api list\n");
+ return NULL;
+ }
+
+ apis.maxlen = new_maxlen;
+ apis.list = new_list;
+ }
+
+ apis.list[apis.len++] = api;
+
+ return api;
+}
+
+/*!
+ * \brief Re-links a given \a user against its associated \a api.
+ *
+ * If the \a api has an implementation, the \a user is linked to that
+ * implementation. Otherwise, the \a user is linked to its \a stub.
+ *
+ * \param user \ref optional_api_user to link.
+ * \param api \ref optional_api to link.
+ */
+static void optional_api_user_relink(struct optional_api_user *user,
+ struct optional_api *api)
+{
+ if (api->impl && *user->optional_ref != api->impl) {
+ ast_verb(4, "%s: linking for %s\n", api->symname, user->module);
+ *user->optional_ref = api->impl;
+ } else if (!api->impl && *user->optional_ref != user->stub) {
+ ast_verb(4, "%s: stubbing for %s\n", api->symname,
+ user->module);
+ *user->optional_ref = user->stub;
+ }
+}
+
+/*!
+ * \brief Sets the implementation function pointer for an \a api.
+ *
+ * \param api API to implement/stub out.
+ * \param impl Pointer to implementation function. Can be 0 to remove
+ * implementation.
+ */
+static void optional_api_set_impl(struct optional_api *api,
+ ast_optional_fn impl)
+{
+ size_t i;
+
+ api->impl = impl;
+
+ /* re-link all users */
+ for (i = 0; i < api->users_len; ++i) {
+ optional_api_user_relink(api->users[i], api);
+ }
+}
+
+void ast_optional_api_provide(const char *symname, ast_optional_fn impl)
+{
+ struct optional_api *api;
+
+ ast_verb(4, "%s: providing\n", symname);
+
+ api = get_api(symname);
+ if (!api) {
+ ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
+ ast_do_crash();
+ return;
+ }
+
+ optional_api_set_impl(api, impl);
+}
+
+void ast_optional_api_unprovide(const char *symname, ast_optional_fn impl)
+{
+ struct optional_api *api;
+
+ ast_verb(4, "%s: un-providing\n", symname);
+
+ api = get_api(symname);
+ if (!api) {
+ ast_log(LOG_ERROR, "%s: Could not find api\n", symname);
+ ast_do_crash();
+ return;
+ }
+
+ optional_api_set_impl(api, 0);
+}
+
+void ast_optional_api_use(const char *symname, ast_optional_fn *optional_ref,
+ ast_optional_fn stub, const char *module)
+{
+ struct optional_api_user *user;
+ struct optional_api *api;
+
+
+ api = get_api(symname);
+ if (!api) {
+ ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
+ ast_do_crash();
+ return;
+ }
+
+ user = optional_api_user_create(optional_ref, stub, module);
+ if (!user) {
+ ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
+ ast_do_crash();
+ return;
+ }
+
+ /* Add user to the API */
+ if (api->users_len + 1 > api->users_maxlen) {
+ size_t new_maxlen = api->users_maxlen ?
+ 2 * api->users_maxlen : 1;
+ struct optional_api_user **new_list =
+ realloc(api->users, new_maxlen * sizeof(*new_list));
+
+ if (!new_list) {
+ optional_api_user_destroy(user);
+ ast_log(LOG_ERROR, "Failed to allocate api list\n");
+ ast_do_crash();
+ return;
+ }
+
+ api->users_maxlen = new_maxlen;
+ api->users = new_list;
+ }
+
+ api->users[api->users_len++] = user;
+
+ optional_api_user_relink(user, api);
+}
+
+void ast_optional_api_unuse(const char *symname, ast_optional_fn *optional_ref,
+ const char *module)
+{
+ struct optional_api *api;
+ size_t i;
+
+ api = get_api(symname);
+ if (!api) {
+ ast_log(LOG_ERROR, "%s: Could not find api\n", symname);
+ ast_do_crash();
+ return;
+ }
+
+ for (i = 0; i < api->users_len; ++i) {
+ struct optional_api_user *user = api->users[i];
+ if (user->optional_ref == optional_ref) {
+ if (*user->optional_ref != user->stub) {
+ ast_verb(4, "%s: stubbing for %s\n", symname,
+ module);
+ *user->optional_ref = user->stub;
+ }
+
+ /* Remove from the list */
+ api->users[i] = api->users[--api->users_len];
+
+ optional_api_user_destroy(user);
+ return;
+ }
+ }
+
+ ast_log(LOG_ERROR, "%s: Could not find user %s\n", symname, module);
+}
+
+#endif /* defined(OPTIONAL_API) */
diff --git a/res/ari/ari_websockets.c b/res/ari/ari_websockets.c
index a34e0f691..20639e12e 100644
--- a/res/ari/ari_websockets.c
+++ b/res/ari/ari_websockets.c
@@ -20,8 +20,10 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-#include "asterisk/astobj2.h"
#include "asterisk/ari.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/http_websocket.h"
+#include "internal.h"
/*! \file
*
@@ -163,3 +165,15 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
return ast_websocket_write(session->ws_session,
AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str));
}
+
+void ari_handle_websocket(struct ast_websocket_server *ws_server,
+ struct ast_tcptls_session_instance *ser, const char *uri,
+ enum ast_http_method method, struct ast_variable *get_params,
+ struct ast_variable *headers)
+{
+ struct ast_http_uri fake_urih = {
+ .data = ws_server,
+ };
+ ast_websocket_uri_cb(ser, &fake_urih, uri, method, get_params,
+ headers);
+}
diff --git a/res/ari/internal.h b/res/ari/internal.h
index ffacd4939..8453747f1 100644
--- a/res/ari/internal.h
+++ b/res/ari/internal.h
@@ -25,7 +25,9 @@
* \author David M. Lee, II <dlee@digium.com>
*/
+#include "asterisk/http.h"
#include "asterisk/json.h"
+#include "asterisk/stringfields.h"
/*! @{ */
@@ -139,5 +141,25 @@ struct ast_ari_conf_user *ast_ari_config_validate_user(const char *username,
/*! @} */
+/* Forward-declare websocket structs. This avoids including http_websocket.h,
+ * which causes optional_api stuff to happen, which makes optional_api more
+ * difficult to debug. */
+
+struct ast_websocket_server;
+
+/*!
+ * \brief Wrapper for invoking the websocket code for an incoming connection.
+ *
+ * \param ws_server WebSocket server to invoke.
+ * \param ser HTTP session.
+ * \param uri Requested URI.
+ * \param method Requested HTTP method.
+ * \param get_params Parsed query parameters.
+ * \param headers Parsed HTTP headers.
+ */
+void ari_handle_websocket(struct ast_websocket_server *ws_server,
+ struct ast_tcptls_session_instance *ser, const char *uri,
+ enum ast_http_method method, struct ast_variable *get_params,
+ struct ast_variable *headers);
#endif /* ARI_INTERNAL_H_ */
diff --git a/res/res_ari.c b/res/res_ari.c
index 4777bb7c2..5475efce9 100644
--- a/res/res_ari.c
+++ b/res/res_ari.c
@@ -124,11 +124,11 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "ari/internal.h"
+#include "asterisk/ari.h"
#include "asterisk/astobj2.h"
#include "asterisk/module.h"
#include "asterisk/paths.h"
-#include "asterisk/ari.h"
-#include "ari/internal.h"
#include <string.h>
#include <sys/stat.h>
@@ -522,11 +522,8 @@ void ast_ari_invoke(struct ast_tcptls_session_instance *ser,
if (handler->ws_server && method == AST_HTTP_GET) {
/* WebSocket! */
- struct ast_http_uri fake_urih = {
- .data = handler->ws_server,
- };
- ast_websocket_uri_cb(ser, &fake_urih, uri, method, get_params,
- headers);
+ ari_handle_websocket(handler->ws_server, ser, uri, method,
+ get_params, headers);
/* Since the WebSocket code handles the connection, we shouldn't
* do anything else; setting no_response */
response->no_response = 1;
diff --git a/res/res_ari_events.c b/res/res_ari_events.c
index 567167f14..17337bbc8 100644
--- a/res/res_ari_events.c
+++ b/res/res_ari_events.c
@@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#if defined(AST_DEVMODE)
#include "ari/ari_model_validators.h"
#endif
+#include "asterisk/http_websocket.h"
#define MAX_VALS 128
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index 129e92c19..b03745e6f 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -107,18 +107,24 @@ struct ast_websocket_server {
struct ao2_container *protocols; /*!< Container for registered protocols */
};
-static void websocket_server_dtor(void *obj)
+static void websocket_server_internal_dtor(void *obj)
{
struct ast_websocket_server *server = obj;
ao2_cleanup(server->protocols);
server->protocols = NULL;
}
-struct ast_websocket_server *ast_websocket_server_create(void)
+static void websocket_server_dtor(void *obj)
+{
+ websocket_server_internal_dtor(obj);
+ ast_module_unref(ast_module_info->self);
+}
+
+static struct ast_websocket_server *websocket_server_create_impl(void (*dtor)(void *))
{
RAII_VAR(struct ast_websocket_server *, server, NULL, ao2_cleanup);
- server = ao2_alloc(sizeof(*server), websocket_server_dtor);
+ server = ao2_alloc(sizeof(*server), dtor);
if (!server) {
return NULL;
}
@@ -132,6 +138,17 @@ struct ast_websocket_server *ast_websocket_server_create(void)
return server;
}
+static struct ast_websocket_server *websocket_server_internal_create(void)
+{
+ return websocket_server_create_impl(websocket_server_internal_dtor);
+}
+
+struct ast_websocket_server *AST_OPTIONAL_API_NAME(ast_websocket_server_create)(void)
+{
+ ast_module_ref(ast_module_info->self);
+ return websocket_server_create_impl(websocket_server_dtor);
+}
+
/*! \brief Destructor function for sessions */
static void session_destroy_fn(void *obj)
{
@@ -512,7 +529,7 @@ static struct websocket_protocol *one_protocol(
return ao2_callback(server->protocols, OBJ_NOLOCK, NULL, NULL);
}
-int ast_websocket_uri_cb(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
+int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
{
struct ast_variable *v;
char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
@@ -521,6 +538,8 @@ int ast_websocket_uri_cb(struct ast_tcptls_session_instance *ser, const struct a
struct ast_websocket *session;
struct ast_websocket_server *server;
+ SCOPED_MODULE_USE(ast_module_info->self);
+
/* Upgrade requests are only permitted on GET methods */
if (method != AST_HTTP_GET) {
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
@@ -674,7 +693,7 @@ int ast_websocket_uri_cb(struct ast_tcptls_session_instance *ser, const struct a
}
static struct ast_http_uri websocketuri = {
- .callback = ast_websocket_uri_cb,
+ .callback = AST_OPTIONAL_API_NAME(ast_websocket_uri_cb),
.description = "Asterisk HTTP WebSocket",
.uri = "ws",
.has_subtree = 0,
@@ -719,7 +738,7 @@ end:
ast_websocket_unref(session);
}
-int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_websocket_callback callback)
+static int websocket_add_protocol_internal(const char *name, ast_websocket_callback callback)
{
struct ast_websocket_server *ws_server = websocketuri.data;
if (!ws_server) {
@@ -728,7 +747,16 @@ int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_webs
return ast_websocket_server_add_protocol(ws_server, name, callback);
}
-int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback)
+int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_websocket_callback callback)
+{
+ int res = websocket_add_protocol_internal(name, callback);
+ if (res == 0) {
+ ast_module_ref(ast_module_info->self);
+ }
+ return res;
+}
+
+static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)
{
struct ast_websocket_server *ws_server = websocketuri.data;
if (!ws_server) {
@@ -737,14 +765,23 @@ int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_w
return ast_websocket_server_remove_protocol(ws_server, name, callback);
}
+int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback)
+{
+ int res = websocket_remove_protocol_internal(name, callback);
+ if (res == 0) {
+ ast_module_unref(ast_module_info->self);
+ }
+ return res;
+}
+
static int load_module(void)
{
- websocketuri.data = ast_websocket_server_create();
+ websocketuri.data = websocket_server_internal_create();
if (!websocketuri.data) {
return AST_MODULE_LOAD_FAILURE;
}
ast_http_uri_link(&websocketuri);
- ast_websocket_add_protocol("echo", websocket_echo_callback);
+ websocket_add_protocol_internal("echo", websocket_echo_callback);
return 0;
}
diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache
index e6b2a88f4..8e043e682 100644
--- a/rest-api-templates/res_ari_resource.c.mustache
+++ b/rest-api-templates/res_ari_resource.c.mustache
@@ -53,6 +53,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#if defined(AST_DEVMODE)
#include "ari/ari_model_validators.h"
#endif
+{{^has_websocket}}
+{{! Only include http_websocket if necessary. Otherwise we'll do a lot of
+ * unnecessary optional_api intialization, which makes optional_api harder
+ * to debug
+ }}
+#include "asterisk/http_websocket.h"
+{{/has_websocket}}
#define MAX_VALS 128
diff --git a/rest-api-templates/swagger_model.py b/rest-api-templates/swagger_model.py
index aa065b342..af52266b4 100644
--- a/rest-api-templates/swagger_model.py
+++ b/rest-api-templates/swagger_model.py
@@ -632,6 +632,8 @@ class ApiDeclaration(Stringify):
api_json = api_decl_json.get('apis') or []
self.apis = [
Api().load(j, processor, context) for j in api_json]
+ self.has_websocket = filter(lambda api: api.has_websocket,
+ self.apis) == []
models = api_decl_json.get('models').items() or []
self.models = [Model().load(id, json, processor, context)
for (id, json) in models]
diff --git a/tests/test_optional_api.c b/tests/test_optional_api.c
new file mode 100644
index 000000000..841dd5f86
--- /dev/null
+++ b/tests/test_optional_api.c
@@ -0,0 +1,187 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@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 Test optional API.
+ *
+ * This tests exercise the underlying implementation functions. Acutal usage
+ * won't look anything like this; it would use the wrapper macros.
+ *
+ * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
+ *
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <depend>OPTIONAL_API</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/optional_api.h"
+#include "asterisk/test.h"
+
+#define CATEGORY "/main/optional_api/"
+
+enum was_called {
+ NONE,
+ STUB,
+ IMPL
+};
+
+enum was_called was_called_result;
+
+ast_optional_fn test_optional_ref;
+
+static void test_optional_stub(void)
+{
+ was_called_result = STUB;
+}
+
+static void test_optional_impl(void)
+{
+ was_called_result = IMPL;
+}
+
+static void test_optional(void)
+{
+ was_called_result = NONE;
+ if (test_optional_ref) {
+ test_optional_ref();
+ }
+}
+
+#define SYMNAME "test_option"
+
+AST_TEST_DEFINE(test_provide_first)
+{
+ enum ast_test_result_state res;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = CATEGORY;
+ info->summary = "Test optional API publishing.";
+ info->description = "Test optional API publishing.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ res = AST_TEST_FAIL;
+ test_optional_ref = 0;
+
+ ast_optional_api_provide(SYMNAME, test_optional_impl);
+
+ ast_optional_api_use(SYMNAME, &test_optional_ref, test_optional_stub,
+ AST_MODULE);
+
+ test_optional();
+
+ if (was_called_result != IMPL) {
+ ast_test_status_update(test, "Expected %d, was %d",
+ IMPL, was_called_result);
+ goto done;
+ }
+
+ res = AST_TEST_PASS;
+
+ done:
+ ast_optional_api_unuse(SYMNAME, &test_optional_ref, AST_MODULE);
+ ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+ return res;
+}
+
+AST_TEST_DEFINE(test_provide_last)
+{
+ enum ast_test_result_state res;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = CATEGORY;
+ info->summary = "Test optional API publishing.";
+ info->description = "Test optional API publishing.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ res = AST_TEST_FAIL;
+ test_optional_ref = 0;
+
+ ast_optional_api_use(SYMNAME, &test_optional_ref, test_optional_stub,
+ AST_MODULE);
+
+ test_optional();
+ if (was_called_result != STUB) {
+ ast_test_status_update(test, "Expected %d, was %d",
+ STUB, was_called_result);
+ goto done;
+ }
+
+ ast_optional_api_provide(SYMNAME, test_optional_impl);
+
+ test_optional();
+ if (was_called_result != IMPL) {
+ ast_test_status_update(test, "Expected %d, was %d",
+ IMPL, was_called_result);
+ ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+ goto done;
+ }
+
+ ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+
+ test_optional();
+ if (was_called_result != STUB) {
+ ast_test_status_update(test, "Expected %d, was %d",
+ STUB, was_called_result);
+ ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+ goto done;
+ }
+
+ res = AST_TEST_PASS;
+
+ done:
+ ast_optional_api_unuse(SYMNAME, &test_optional_ref, AST_MODULE);
+ return res;
+}
+
+static int unload_module(void)
+{
+ AST_TEST_UNREGISTER(test_provide_first);
+ AST_TEST_UNREGISTER(test_provide_last);
+ return 0;
+}
+
+static int load_module(void)
+{
+ AST_TEST_REGISTER(test_provide_first);
+ AST_TEST_REGISTER(test_provide_last);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ARI testing",
+ .load = load_module,
+ .unload = unload_module,
+ );