summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES8
-rw-r--r--Makefile5
-rwxr-xr-xbuild_tools/make_check_alembic29
-rwxr-xr-xconfigure52
-rw-r--r--configure.ac1
-rw-r--r--funcs/func_cdr.c41
-rw-r--r--include/asterisk/res_pjproject.h96
-rw-r--r--main/asterisk.c5
-rw-r--r--main/logger.c2
-rw-r--r--main/taskprocessor.c29
-rw-r--r--makeopts.in1
-rw-r--r--res/res_pjproject.c256
-rw-r--r--res/res_pjproject.exports.in6
-rw-r--r--res/res_pjsip.c64
-rw-r--r--res/res_pjsip/presence_xml.c8
-rw-r--r--res/res_pjsip_log_forwarder.c186
-rw-r--r--res/stasis/control.c16
17 files changed, 592 insertions, 213 deletions
diff --git a/CHANGES b/CHANGES
index 3571ed227..925911189 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,14 @@
--- Functionality changes from Asterisk 13.7.0 to Asterisk 13.8.0 ------------
------------------------------------------------------------------------------
+res_pjproject
+------------------
+ * This module is the successor of res_pjsip_log_forwarder. As well as
+ handling the log forwarding (which now displays as 'pjproject:0' instead
+ of 'pjsip:0'), it also adds a 'pjproject show buildopts' command to the CLI.
+ This displays the compiled-in options of the pjproject installation
+ Asterisk is currently running against.
+
res_pjsip
------------------
diff --git a/Makefile b/Makefile
index d4ae9fd61..5a729ed07 100644
--- a/Makefile
+++ b/Makefile
@@ -1010,6 +1010,10 @@ else
rest-api/resources.json .
endif
+check-alembic: makeopts
+ @find contrib/ast-db-manage/ -name '*.pyc' -delete
+ @ALEMBIC=$(ALEMBIC) build_tools/make_check_alembic config cdr voicemail >&2
+
.PHONY: menuselect
.PHONY: main
.PHONY: sounds
@@ -1031,6 +1035,7 @@ endif
.PHONY: _clean
.PHONY: ari-stubs
.PHONY: basic-pbx
+.PHONY: check-alembic
.PHONY: $(SUBDIRS_INSTALL)
.PHONY: $(SUBDIRS_DIST_CLEAN)
.PHONY: $(SUBDIRS_CLEAN)
diff --git a/build_tools/make_check_alembic b/build_tools/make_check_alembic
new file mode 100755
index 000000000..ecc5fc1c9
--- /dev/null
+++ b/build_tools/make_check_alembic
@@ -0,0 +1,29 @@
+#!/bin/sh
+if [ -z "$ALEMBIC" -o ! -d contrib/ast-db-manage ]; then
+ echo "Run 'make check-alembic' to use this script" >&2
+ exit 1
+fi
+
+if [ "$ALEMBIC" = ":" ]; then
+ echo "Install alembic and re-run configure before using this target."
+ exit 1
+fi
+
+cd contrib/ast-db-manage
+
+FOUNDERROR=
+for id in "$@"; do
+ if [ -n "$($ALEMBIC -c ${id}.ini.sample branches)" ]; then
+ echo "Alembic branches exist for $id - details follow:"
+ # This second run is needed to display the errors because
+ # formatting was lost in the first execution.
+ $ALEMBIC -c ${id}.ini.sample branches
+ # Display all errors before reporting failure to Make.
+ FOUNDERROR=yes
+ fi
+done
+
+if [ -n "$FOUNDERROR" ]; then
+ # One or more failures.
+ exit 1
+fi
diff --git a/configure b/configure
index a0468b9e5..44435a670 100755
--- a/configure
+++ b/configure
@@ -1162,6 +1162,7 @@ SHA1SUM
LDCONFIG
DOWNLOAD
FETCH
+ALEMBIC
GIT
XMLSTARLET
XMLLINT
@@ -7465,6 +7466,47 @@ $as_echo "no" >&6; }
fi
+# Extract the first word of "alembic", so it can be a program name with args.
+set dummy alembic; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ALEMBIC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ALEMBIC in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ALEMBIC="$ALEMBIC" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_ALEMBIC="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_ALEMBIC" && ac_cv_path_ALEMBIC=":"
+ ;;
+esac
+fi
+ALEMBIC=$ac_cv_path_ALEMBIC
+if test -n "$ALEMBIC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ALEMBIC" >&5
+$as_echo "$ALEMBIC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
if test "${WGET}" != ":" ; then
DOWNLOAD=${WGET}
else if test "${CURL}" != ":" ; then
@@ -13477,7 +13519,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13523,7 +13565,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13547,7 +13589,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13592,7 +13634,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13616,7 +13658,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
diff --git a/configure.ac b/configure.ac
index 329280924..54cbab211 100644
--- a/configure.ac
+++ b/configure.ac
@@ -283,6 +283,7 @@ AC_PATH_PROG([KPATHSEA], [kpsewhich], :)
AC_PATH_PROG([XMLLINT], [xmllint], :)
AC_PATH_PROG([XMLSTARLET], [xmlstarlet], :)
AC_PATH_PROG([GIT], [git], :)
+AC_PATH_PROG([ALEMBIC], [alembic], :)
if test "${WGET}" != ":" ; then
DOWNLOAD=${WGET}
else if test "${CURL}" != ":" ; then
diff --git a/funcs/func_cdr.c b/funcs/func_cdr.c
index 95d047eb2..dc865934f 100644
--- a/funcs/func_cdr.c
+++ b/funcs/func_cdr.c
@@ -221,6 +221,26 @@ STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_read_message_type);
STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_write_message_type);
STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_prop_write_message_type);
+static struct timeval cdr_retrieve_time(struct ast_channel *chan, const char *time_name)
+{
+ struct timeval time;
+ char *value = NULL;
+ char tempbuf[128];
+
+ if (ast_strlen_zero(ast_channel_name(chan))) {
+ /* Format request on a dummy channel */
+ ast_cdr_format_var(ast_channel_cdr(chan), time_name, &value, tempbuf, sizeof(tempbuf), 1);
+ } else {
+ ast_cdr_getvar(ast_channel_name(chan), time_name, tempbuf, sizeof(tempbuf));
+ }
+
+ if (sscanf(tempbuf, "%ld.%ld", &time.tv_sec, &time.tv_usec) != 2) {
+ ast_log(AST_LOG_WARNING, "Failed to fully extract '%s' from CDR\n", time_name);
+ }
+
+ return time;
+}
+
static void cdr_read_callback(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{
struct cdr_func_payload *payload = stasis_message_data(message);
@@ -268,16 +288,21 @@ static void cdr_read_callback(void *data, struct stasis_subscription *sub, struc
if (ast_test_flag(&flags, OPT_FLOAT)
&& (!strcasecmp("billsec", args.variable) || !strcasecmp("duration", args.variable))) {
- long ms;
- double dtime;
+ struct timeval start = cdr_retrieve_time(payload->chan, !strcasecmp("billsec", args.variable) ? "answer" : "start");
+ struct timeval finish = cdr_retrieve_time(payload->chan, "end");
+ double delta;
- if (sscanf(tempbuf, "%30ld", &ms) != 1) {
- ast_log(AST_LOG_WARNING, "Unable to parse %s (%s) from the CDR for channel %s\n",
- args.variable, tempbuf, ast_channel_name(payload->chan));
- return;
+ if (ast_tvzero(finish)) {
+ finish = ast_tvnow();
}
- dtime = (double)(ms / 1000.0);
- snprintf(tempbuf, sizeof(tempbuf), "%lf", dtime);
+
+ if (ast_tvzero(start)) {
+ delta = 0.0;
+ } else {
+ delta = (double)(ast_tvdiff_us(finish, start) / 1000000.0);
+ }
+ snprintf(tempbuf, sizeof(tempbuf), "%lf", delta);
+
} else if (!ast_test_flag(&flags, OPT_UNPARSED)) {
if (!strcasecmp("start", args.variable)
|| !strcasecmp("end", args.variable)
diff --git a/include/asterisk/res_pjproject.h b/include/asterisk/res_pjproject.h
new file mode 100644
index 000000000..8828b340c
--- /dev/null
+++ b/include/asterisk/res_pjproject.h
@@ -0,0 +1,96 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Fairview 5 Engineering, LLC
+ *
+ * George Joseph <george.joseph@fairview5.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef _RES_PJPROJECT_H
+#define _RES_PJPROJECT_H
+
+/*! \brief Determines whether the res_pjproject module is loaded */
+#define CHECK_PJPROJECT_MODULE_LOADED() \
+ do { \
+ if (!ast_module_check("res_pjproject.so")) { \
+ return AST_MODULE_LOAD_DECLINE; \
+ } \
+ } while(0)
+
+/*!
+ * \brief Retrieve a pjproject build option
+ *
+ * \param option The build option requested
+ * \param format_string A scanf-style format string to parse the option value into
+ * \param ... Pointers to variables to receive the values parsed
+ *
+ * \retval The number of values parsed
+ *
+ * \since 13.8.0
+ *
+ * \note The option requested must be from those returned by pj_dump_config()
+ * which can be displayed with the 'pjsip show buildopts' CLI command.
+ *
+ * <b>Sample Usage:</b>
+ * \code
+ *
+ * int max_hostname;
+ *
+ * ast_sip_get_pjproject_buildopt("PJ_MAX_HOSTNAME", "%d", &max_hostname);
+ *
+ * \endcode
+ *
+ */
+int ast_pjproject_get_buildopt(char *option, char *format_string, ...) __attribute__((format(scanf, 2, 3)));
+
+/*!
+ * \brief Begin PJPROJECT log interception for CLI output.
+ * \since 13.8.0
+ *
+ * \param fd CLI file descriptior to send intercepted output.
+ *
+ * \note ast_pjproject_log_intercept_begin() and
+ * ast_pjproject_log_intercept_end() must always be called
+ * in pairs.
+ *
+ * \return Nothing
+ */
+void ast_pjproject_log_intercept_begin(int fd);
+
+/*!
+ * \brief End PJPROJECT log interception for CLI output.
+ * \since 13.8.0
+ *
+ * \note ast_pjproject_log_intercept_begin() and
+ * ast_pjproject_log_intercept_end() must always be called
+ * in pairs.
+ *
+ * \return Nothing
+ */
+void ast_pjproject_log_intercept_end(void);
+
+/*!
+ * \brief Increment the res_pjproject reference count.
+ *
+ * This ensures graceful shutdown happens in the proper order.
+ */
+void ast_pjproject_ref(void);
+
+/*!
+ * \brief Decrement the res_pjproject reference count.
+ *
+ * This ensures graceful shutdown happens in the proper order.
+ */
+void ast_pjproject_unref(void);
+
+#endif /* _RES_PJPROJECT_H */
diff --git a/main/asterisk.c b/main/asterisk.c
index c576c16f6..d99aaed18 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -2808,11 +2808,12 @@ static int ast_el_read_char(EditLine *editline, char *cp)
console_print(buf, 0);
- if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (buf[res-2] == '\n'))) {
+ if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (res >= 2 && buf[res-2] == '\n'))) {
*cp = CC_REFRESH;
return(1);
- } else
+ } else {
lastpos = 1;
+ }
}
}
diff --git a/main/logger.c b/main/logger.c
index fb9e8ed4f..46d9cbb57 100644
--- a/main/logger.c
+++ b/main/logger.c
@@ -464,7 +464,7 @@ static int init_logger_chain(int locked, const char *altconf)
/* If no config file, we're fine, set default options. */
if (!cfg) {
- if (!(chan = ast_calloc(1, sizeof(*chan)))) {
+ if (!(chan = ast_calloc(1, sizeof(*chan) + 1))) {
fprintf(stderr, "Failed to initialize default logging\n");
return -1;
}
diff --git a/main/taskprocessor.c b/main/taskprocessor.c
index 05d646128..1ba0c8a2f 100644
--- a/main/taskprocessor.c
+++ b/main/taskprocessor.c
@@ -332,10 +332,11 @@ static void *tps_task_free(struct tps_task *task)
}
/* taskprocessor tab completion */
-static char *tps_taskprocessor_tab_complete(struct ast_taskprocessor *p, struct ast_cli_args *a)
+static char *tps_taskprocessor_tab_complete(struct ast_cli_args *a)
{
int tklen;
int wordnum = 0;
+ struct ast_taskprocessor *p;
char *name = NULL;
struct ao2_iterator i;
@@ -347,10 +348,10 @@ static char *tps_taskprocessor_tab_complete(struct ast_taskprocessor *p, struct
while ((p = ao2_iterator_next(&i))) {
if (!strncasecmp(a->word, p->name, tklen) && ++wordnum > a->n) {
name = ast_strdup(p->name);
- ao2_ref(p, -1);
+ ast_taskprocessor_unreference(p);
break;
}
- ao2_ref(p, -1);
+ ast_taskprocessor_unreference(p);
}
ao2_iterator_destroy(&i);
return name;
@@ -372,7 +373,7 @@ static char *cli_tps_ping(struct ast_cli_entry *e, int cmd, struct ast_cli_args
const char *name;
struct timeval when;
struct timespec ts;
- struct ast_taskprocessor *tps = NULL;
+ struct ast_taskprocessor *tps;
switch (cmd) {
case CLI_INIT:
@@ -382,7 +383,7 @@ static char *cli_tps_ping(struct ast_cli_entry *e, int cmd, struct ast_cli_args
" Displays the time required for a task to be processed\n";
return NULL;
case CLI_GENERATE:
- return tps_taskprocessor_tab_complete(tps, a);
+ return tps_taskprocessor_tab_complete(a);
}
if (a->argc != 4)
@@ -394,22 +395,32 @@ static char *cli_tps_ping(struct ast_cli_entry *e, int cmd, struct ast_cli_args
return CLI_SUCCESS;
}
ast_cli(a->fd, "\npinging %s ...", name);
- when = ast_tvadd((begin = ast_tvnow()), ast_samp2tv(1000, 1000));
+
+ /*
+ * Wait up to 5 seconds for a ping reply.
+ *
+ * On a very busy system it could take awhile to get a
+ * ping response from some taskprocessors.
+ */
+ begin = ast_tvnow();
+ when = ast_tvadd(begin, ast_samp2tv(5000, 1000));
ts.tv_sec = when.tv_sec;
ts.tv_nsec = when.tv_usec * 1000;
+
ast_mutex_lock(&cli_ping_cond_lock);
if (ast_taskprocessor_push(tps, tps_ping_handler, 0) < 0) {
ast_mutex_unlock(&cli_ping_cond_lock);
ast_cli(a->fd, "\nping failed: could not push task to %s\n\n", name);
- ao2_ref(tps, -1);
+ ast_taskprocessor_unreference(tps);
return CLI_FAILURE;
}
ast_cond_timedwait(&cli_ping_cond, &cli_ping_cond_lock, &ts);
ast_mutex_unlock(&cli_ping_cond_lock);
+
end = ast_tvnow();
delta = ast_tvsub(end, begin);
ast_cli(a->fd, "\n\t%24s ping time: %.1ld.%.6ld sec\n\n", name, (long)delta.tv_sec, (long int)delta.tv_usec);
- ao2_ref(tps, -1);
+ ast_taskprocessor_unreference(tps);
return CLI_SUCCESS;
}
@@ -663,6 +674,8 @@ static struct ast_taskprocessor *__allocate_taskprocessor(const char *name, stru
if (!(ao2_link(tps_singletons, p))) {
ast_log(LOG_ERROR, "Failed to add taskprocessor '%s' to container\n", p->name);
+ listener->tps = NULL;
+ ao2_ref(p, -1);
return NULL;
}
diff --git a/makeopts.in b/makeopts.in
index 4922c2fca..943bc3a51 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -38,6 +38,7 @@ SHA1SUM=@SHA1SUM@
OPENSSL=@OPENSSL@
LDCONFIG=@LDCONFIG@
GIT=@GIT@
+ALEMBIC=@ALEMBIC@
BUILD_PLATFORM=@BUILD_PLATFORM@
BUILD_CPU=@BUILD_CPU@
diff --git a/res/res_pjproject.c b/res/res_pjproject.c
new file mode 100644
index 000000000..2e092d76e
--- /dev/null
+++ b/res/res_pjproject.c
@@ -0,0 +1,256 @@
+/*
+ * 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 Bridge PJPROJECT logging to Asterisk logging.
+ * \author David M. Lee, II <dlee@digium.com>
+ *
+ * PJPROJECT logging doesn't exactly match Asterisk logging, but mapping the two is
+ * not too bad. PJPROJECT log levels are identified by a single int. Limits are
+ * not specified by PJPROJECT, but their implementation used 1 through 6.
+ *
+ * The mapping is as follows:
+ * - 0: LOG_ERROR
+ * - 1: LOG_ERROR
+ * - 2: LOG_WARNING
+ * - 3 and above: equivalent to ast_debug(level, ...) for res_pjproject.so
+ */
+
+/*** MODULEINFO
+ <depend>pjproject</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include <stdarg.h>
+#include <pjlib.h>
+#include <pjsip.h>
+#include <pj/log.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/res_pjproject.h"
+#include "asterisk/vector.h"
+
+static pj_log_func *log_cb_orig;
+static unsigned decor_orig;
+
+static AST_VECTOR(buildopts, char *) buildopts;
+
+/*! Protection from other log intercept instances. There can be only one at a time. */
+AST_MUTEX_DEFINE_STATIC(pjproject_log_intercept_lock);
+
+struct pjproject_log_intercept_data {
+ pthread_t thread;
+ int fd;
+};
+
+static struct pjproject_log_intercept_data pjproject_log_intercept = {
+ .thread = AST_PTHREADT_NULL,
+ .fd = -1,
+};
+
+static void log_forwarder(int level, const char *data, int len)
+{
+ int ast_level;
+ /* PJPROJECT doesn't provide much in the way of source info */
+ const char * log_source = "pjproject";
+ int log_line = 0;
+ const char *log_func = "<?>";
+ int mod_level;
+
+ if (pjproject_log_intercept.fd != -1
+ && pjproject_log_intercept.thread == pthread_self()) {
+ /*
+ * We are handling a CLI command intercepting PJPROJECT
+ * log output.
+ */
+ ast_cli(pjproject_log_intercept.fd, "%s\n", data);
+ return;
+ }
+
+ /* Lower number indicates higher importance */
+ switch (level) {
+ case 0: /* level zero indicates fatal error, according to docs */
+ case 1: /* 1 seems to be used for errors */
+ ast_level = __LOG_ERROR;
+ break;
+ case 2: /* 2 seems to be used for warnings and errors */
+ ast_level = __LOG_WARNING;
+ break;
+ default:
+ ast_level = __LOG_DEBUG;
+
+ /* For levels 3 and up, obey the debug level for res_pjproject */
+ mod_level = ast_opt_dbg_module ?
+ ast_debug_get_by_module("res_pjproject") : 0;
+ if (option_debug < level && mod_level < level) {
+ return;
+ }
+ break;
+ }
+
+ /* PJPROJECT uses indention to indicate function call depth. We'll prepend
+ * log statements with a tab so they'll have a better shot at lining
+ * up */
+ ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data);
+}
+
+static void capture_buildopts_cb(int level, const char *data, int len)
+{
+ if (strstr(data, "Teluu") || strstr(data, "Dumping")) {
+ return;
+ }
+
+ AST_VECTOR_ADD_SORTED(&buildopts, ast_strdup(ast_skip_blanks(data)), strcmp);
+}
+
+int ast_pjproject_get_buildopt(char *option, char *format_string, ...)
+{
+ int res = 0;
+ char *format_temp;
+ int i;
+
+ format_temp = ast_alloca(strlen(option) + strlen(" : ") + strlen(format_string) + 1);
+ sprintf(format_temp, "%s : %s", option, format_string);
+
+ for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
+ va_list arg_ptr;
+ va_start(arg_ptr, format_string);
+ res = vsscanf(AST_VECTOR_GET(&buildopts, i), format_temp, arg_ptr);
+ va_end(arg_ptr);
+ if (res) {
+ break;
+ }
+ }
+
+ return res;
+}
+
+void ast_pjproject_log_intercept_begin(int fd)
+{
+ /* Protect from other CLI instances trying to do this at the same time. */
+ ast_mutex_lock(&pjproject_log_intercept_lock);
+
+ pjproject_log_intercept.thread = pthread_self();
+ pjproject_log_intercept.fd = fd;
+}
+
+void ast_pjproject_log_intercept_end(void)
+{
+ pjproject_log_intercept.fd = -1;
+ pjproject_log_intercept.thread = AST_PTHREADT_NULL;
+
+ ast_mutex_unlock(&pjproject_log_intercept_lock);
+}
+
+void ast_pjproject_ref(void)
+{
+ ast_module_ref(ast_module_info->self);
+}
+
+void ast_pjproject_unref(void)
+{
+ ast_module_unref(ast_module_info->self);
+}
+
+static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "pjproject show buildopts";
+ e->usage =
+ "Usage: pjproject show buildopts\n"
+ " Show the compile time config of the pjproject that Asterisk is\n"
+ " running against.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "PJPROJECT compile time config currently running against:\n");
+
+ for (i = 0; i < AST_VECTOR_SIZE(&buildopts); i++) {
+ ast_cli(a->fd, "%s\n", AST_VECTOR_GET(&buildopts, i));
+ }
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry pjproject_cli[] = {
+ AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"),
+};
+
+static int load_module(void)
+{
+ ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
+
+ pj_init();
+
+ decor_orig = pj_log_get_decor();
+ log_cb_orig = pj_log_get_log_func();
+
+ if (AST_VECTOR_INIT(&buildopts, 64)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ /*
+ * On startup, we want to capture the dump once and store it.
+ */
+ pj_log_set_log_func(capture_buildopts_cb);
+ pj_log_set_decor(0);
+ pj_dump_config();
+ pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT);
+ pj_log_set_log_func(log_forwarder);
+
+ ast_cli_register_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+#define NOT_EQUALS(a, b) (a != b)
+
+static int unload_module(void)
+{
+ ast_cli_unregister_multiple(pjproject_cli, ARRAY_LEN(pjproject_cli));
+ pj_log_set_log_func(log_cb_orig);
+ pj_log_set_decor(decor_orig);
+
+ AST_VECTOR_REMOVE_CMP_UNORDERED(&buildopts, NULL, NOT_EQUALS, ast_free);
+ AST_VECTOR_FREE(&buildopts);
+
+ ast_debug(3, "Stopped PJPROJECT logging to Asterisk logger\n");
+
+ pj_shutdown();
+
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
+ );
diff --git a/res/res_pjproject.exports.in b/res/res_pjproject.exports.in
new file mode 100644
index 000000000..f1821a6a2
--- /dev/null
+++ b/res/res_pjproject.exports.in
@@ -0,0 +1,6 @@
+{
+ global:
+ LINKER_SYMBOL_PREFIXast_pjproject_*;
+ local:
+ *;
+};
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index b51d6dcb3..c94f6a9eb 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -42,9 +42,11 @@
#include "asterisk/res_pjsip_cli.h"
#include "asterisk/test.h"
#include "asterisk/res_pjsip_presence_xml.h"
+#include "asterisk/res_pjproject.h"
/*** MODULEINFO
<depend>pjproject</depend>
+ <depend>res_pjproject</depend>
<depend>res_sorcery_config</depend>
<depend>res_sorcery_memory</depend>
<depend>res_sorcery_astdb</depend>
@@ -2220,6 +2222,57 @@ struct ast_sip_endpoint *ast_sip_identify_endpoint(pjsip_rx_data *rdata)
return endpoint;
}
+static int do_cli_dump_endpt(void *v_a)
+{
+ struct ast_cli_args *a = v_a;
+
+ ast_pjproject_log_intercept_begin(a->fd);
+ pjsip_endpt_dump(ast_sip_get_pjsip_endpoint(), a->argc == 4 ? PJ_TRUE : PJ_FALSE);
+ ast_pjproject_log_intercept_end();
+
+ return 0;
+}
+
+static char *cli_dump_endpt(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+#ifdef AST_DEVMODE
+ e->command = "pjsip dump endpt [details]";
+ e->usage =
+ "Usage: pjsip dump endpt [details]\n"
+ " Dump the res_pjsip endpt internals.\n"
+ "\n"
+ "Warning: PJPROJECT documents that the function used by this\n"
+ "CLI command may cause a crash when asking for details because\n"
+ "it tries to access all active memory pools.\n";
+#else
+ /*
+ * In non-developer mode we will not document or make easily accessible
+ * the details option even though it is still available. The user has
+ * to know it exists to use it. Presumably they would also be aware of
+ * the potential crash warning.
+ */
+ e->command = "pjsip dump endpt";
+ e->usage =
+ "Usage: pjsip dump endpt\n"
+ " Dump the res_pjsip endpt internals.\n";
+#endif /* AST_DEVMODE */
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (4 < a->argc
+ || (a->argc == 4 && strcasecmp(a->argv[3], "details"))) {
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_sip_push_task_synchronous(NULL, do_cli_dump_endpt, a);
+
+ return CLI_SUCCESS;
+}
+
static char *cli_show_endpoint_identifiers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
#define ENDPOINT_IDENTIFIER_FORMAT "%-20.20s\n"
@@ -2283,8 +2336,9 @@ static char *cli_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
}
static struct ast_cli_entry cli_commands[] = {
- AST_CLI_DEFINE(cli_show_settings, "Show global and system configuration options"),
- AST_CLI_DEFINE(cli_show_endpoint_identifiers, "List registered endpoint identifiers")
+ AST_CLI_DEFINE(cli_dump_endpt, "Dump the res_pjsip endpt internals"),
+ AST_CLI_DEFINE(cli_show_settings, "Show global and system configuration options"),
+ AST_CLI_DEFINE(cli_show_endpoint_identifiers, "List registered endpoint identifiers")
};
AST_RWLIST_HEAD_STATIC(endpoint_formatters, ast_sip_endpoint_formatter);
@@ -3861,6 +3915,8 @@ static int load_module(void)
pj_status_t status;
struct ast_threadpool_options options;
+ CHECK_PJPROJECT_MODULE_LOADED();
+
if (pj_init() != PJ_SUCCESS) {
return AST_MODULE_LOAD_DECLINE;
}
@@ -4022,6 +4078,8 @@ static int load_module(void)
AST_TEST_REGISTER(xml_sanitization_end_null);
AST_TEST_REGISTER(xml_sanitization_exceeds_buffer);
+ ast_pjproject_ref();
+
return AST_MODULE_LOAD_SUCCESS;
}
@@ -4076,6 +4134,8 @@ static int unload_module(void)
ast_threadpool_shutdown(sip_threadpool);
ast_sip_destroy_cli();
+ ast_pjproject_unref();
+
return 0;
}
diff --git a/res/res_pjsip/presence_xml.c b/res/res_pjsip/presence_xml.c
index b98ea0237..c991a0d68 100644
--- a/res/res_pjsip/presence_xml.c
+++ b/res/res_pjsip/presence_xml.c
@@ -91,6 +91,12 @@ void ast_sip_presence_exten_state_to_str(int state, char **statestring, char **p
*pidfstate = "busy";
*pidfnote = "Ringing";
break;
+ case (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING):
+ *statestring = "confirmed";
+ *local_state = NOTIFY_INUSE;
+ *pidfstate = "busy";
+ *pidfnote = "Ringing";
+ break;
case AST_EXTENSION_INUSE:
*statestring = "confirmed";
*local_state = NOTIFY_INUSE;
@@ -121,7 +127,7 @@ void ast_sip_presence_exten_state_to_str(int state, char **statestring, char **p
*statestring = "terminated";
*local_state = NOTIFY_OPEN;
*pidfstate = "--";
- *pidfnote ="Ready";
+ *pidfnote = "Ready";
break;
}
}
diff --git a/res/res_pjsip_log_forwarder.c b/res/res_pjsip_log_forwarder.c
deleted file mode 100644
index 7a7128054..000000000
--- a/res/res_pjsip_log_forwarder.c
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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 Bridge PJSIP logging to Asterisk logging.
- * \author David M. Lee, II <dlee@digium.com>
- *
- * PJSIP logging doesn't exactly match Asterisk logging, but mapping the two is
- * not too bad. PJSIP log levels are identified by a single int. Limits are
- * not specified by PJSIP, but their implementation used 1 through 6.
- *
- * The mapping is as follows:
- * - 0: LOG_ERROR
- * - 1: LOG_ERROR
- * - 2: LOG_WARNING
- * - 3 and above: equivalent to ast_debug(level, ...) for res_pjsip.so
- */
-
-/*** MODULEINFO
- <depend>pjproject</depend>
- <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <pjsip.h>
-#include <pj/log.h>
-
-#include "asterisk/logger.h"
-#include "asterisk/module.h"
-#include "asterisk/cli.h"
-
-static pj_log_func *log_cb_orig;
-static unsigned decor_orig;
-
-/*! Protection from other CLI instances. */
-AST_MUTEX_DEFINE_STATIC(show_buildopts_lock);
-
-struct pjsip_show_buildopts {
- pthread_t thread;
- int fd;
-};
-
-static struct pjsip_show_buildopts show_buildopts = {
- .thread = AST_PTHREADT_NULL,
- .fd = -1,
-};
-
-static void log_cb(int level, const char *data, int len)
-{
- int ast_level;
- /* PJSIP doesn't provide much in the way of source info */
- const char * log_source = "pjsip";
- int log_line = 0;
- const char *log_func = "<?>";
- int mod_level;
-
- if (show_buildopts.fd != -1 && show_buildopts.thread == pthread_self()) {
- /*
- * We are handling the CLI command dumping the
- * PJPROJECT compile time config option settings.
- */
- ast_cli(show_buildopts.fd, "%s\n", data);
- return;
- }
-
- /* Lower number indicates higher importance */
- switch (level) {
- case 0: /* level zero indicates fatal error, according to docs */
- case 1: /* 1 seems to be used for errors */
- ast_level = __LOG_ERROR;
- break;
- case 2: /* 2 seems to be used for warnings and errors */
- ast_level = __LOG_WARNING;
- break;
- default:
- ast_level = __LOG_DEBUG;
-
- /* For levels 3 and up, obey the debug level for res_pjsip */
- mod_level = ast_opt_dbg_module ?
- ast_debug_get_by_module("res_pjsip") : 0;
- if (option_debug < level && mod_level < level) {
- return;
- }
- break;
- }
-
- /* PJSIP uses indention to indicate function call depth. We'll prepend
- * log statements with a tab so they'll have a better shot at lining
- * up */
- ast_log(ast_level, log_source, log_line, log_func, "\t%s\n", data);
-}
-
-static char *handle_pjsip_show_buildopts(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
- switch(cmd) {
- case CLI_INIT:
- e->command = "pjsip show buildopts";
- e->usage =
- "Usage: pjsip show buildopts\n"
- " Show the compile time config of pjproject that res_pjsip is\n"
- " running against.\n";
- return NULL;
- case CLI_GENERATE:
- return NULL;
- }
-
- ast_cli(a->fd, "PJPROJECT compile time config currently running against:\n");
-
- /* Protect from other CLI instances trying to do this at the same time. */
- ast_mutex_lock(&show_buildopts_lock);
-
- show_buildopts.thread = pthread_self();
- show_buildopts.fd = a->fd;
- pj_dump_config();
- show_buildopts.fd = -1;
- show_buildopts.thread = AST_PTHREADT_NULL;
-
- ast_mutex_unlock(&show_buildopts_lock);
-
- return CLI_SUCCESS;
-}
-
-static struct ast_cli_entry pjsip_cli[] = {
- AST_CLI_DEFINE(handle_pjsip_show_buildopts, "Show the compiled config of pjproject in use"),
-};
-
-static int load_module(void)
-{
- pj_init();
-
- decor_orig = pj_log_get_decor();
- log_cb_orig = pj_log_get_log_func();
-
- ast_debug(3, "Forwarding PJSIP logger to Asterisk logger\n");
- /* SENDER prepends the source to the log message. This could be a
- * filename, object reference, or simply a string
- *
- * INDENT is assumed to be on by most log statements in PJSIP itself.
- */
- pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_INDENT);
- pj_log_set_log_func(log_cb);
-
- ast_cli_register_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
-
- return AST_MODULE_LOAD_SUCCESS;
-}
-
-static int unload_module(void)
-{
- ast_cli_unregister_multiple(pjsip_cli, ARRAY_LEN(pjsip_cli));
-
- pj_log_set_log_func(log_cb_orig);
- pj_log_set_decor(decor_orig);
-
- pj_shutdown();
-
- return 0;
-}
-
-/* While we don't really export global symbols, we want to load before other
- * modules that do */
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP Log Forwarder",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
- );
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 936e5e143..57b5e9964 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -790,6 +790,14 @@ static int app_send_command_on_condition(struct stasis_app_control *control,
RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
if (control == NULL || control->is_done) {
+ /* If exec_command_on_condition fails, it calls the data_destructor.
+ * In order to provide consistent behavior, we'll also call the data_destructor
+ * on this error path. This way, callers never have to call the
+ * data_destructor themselves.
+ */
+ if (data_destructor) {
+ data_destructor(data);
+ }
return -1;
}
@@ -815,6 +823,14 @@ int stasis_app_send_command_async(struct stasis_app_control *control,
RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
if (control == NULL || control->is_done) {
+ /* If exec_command fails, it calls the data_destructor. In order to
+ * provide consistent behavior, we'll also call the data_destructor
+ * on this error path. This way, callers never have to call the
+ * data_destructor themselves.
+ */
+ if (data_destructor) {
+ data_destructor(data);
+ }
return -1;
}