diff options
-rw-r--r-- | include/asterisk/res_pjproject.h | 36 | ||||
-rw-r--r-- | res/res_pjproject.c | 40 | ||||
-rw-r--r-- | res/res_pjsip.c | 64 |
3 files changed, 137 insertions, 3 deletions
diff --git a/include/asterisk/res_pjproject.h b/include/asterisk/res_pjproject.h index 2095caed2..8828b340c 100644 --- a/include/asterisk/res_pjproject.h +++ b/include/asterisk/res_pjproject.h @@ -19,6 +19,14 @@ #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 * @@ -43,7 +51,33 @@ * \endcode * */ -int ast_pjproject_get_buildopt(char *option, char *format_string, ...) __attribute__((format(scanf, 2,3))); +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. diff --git a/res/res_pjproject.c b/res/res_pjproject.c index de1cecbc8..e9f0d15c4 100644 --- a/res/res_pjproject.c +++ b/res/res_pjproject.c @@ -57,6 +57,19 @@ 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; @@ -66,6 +79,16 @@ static void log_forwarder(int level, const char *data, int len) 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 */ @@ -124,6 +147,23 @@ int ast_pjproject_get_buildopt(char *option, char *format_string, ...) 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); diff --git a/res/res_pjsip.c b/res/res_pjsip.c index b3c6773b4..2b7625aba 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> @@ -2216,6 +2218,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" @@ -2279,8 +2332,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); @@ -3895,6 +3949,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; } @@ -4041,6 +4097,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; } @@ -4094,6 +4152,8 @@ static int unload_module(void) ast_threadpool_shutdown(sip_threadpool); ast_sip_destroy_cli(); + ast_pjproject_unref(); + return 0; } |