diff options
-rw-r--r-- | CHANGES | 8 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rwxr-xr-x | build_tools/make_check_alembic | 29 | ||||
-rwxr-xr-x | configure | 52 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | funcs/func_cdr.c | 41 | ||||
-rw-r--r-- | include/asterisk/res_pjproject.h | 96 | ||||
-rw-r--r-- | main/asterisk.c | 5 | ||||
-rw-r--r-- | main/logger.c | 2 | ||||
-rw-r--r-- | main/taskprocessor.c | 29 | ||||
-rw-r--r-- | makeopts.in | 1 | ||||
-rw-r--r-- | res/res_pjproject.c | 256 | ||||
-rw-r--r-- | res/res_pjproject.exports.in | 6 | ||||
-rw-r--r-- | res/res_pjsip.c | 64 | ||||
-rw-r--r-- | res/res_pjsip/presence_xml.c | 8 | ||||
-rw-r--r-- | res/res_pjsip_log_forwarder.c | 186 | ||||
-rw-r--r-- | res/stasis/control.c | 16 |
17 files changed, 592 insertions, 213 deletions
@@ -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 ------------------ @@ -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 @@ -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; } |