[Asterisk-code-review] main/pbx: Move custom function routines to pbx functions.c. (asterisk[master])

Joshua Colp asteriskteam at digium.com
Mon Jan 4 08:01:01 CST 2016


Joshua Colp has submitted this change and it was merged.

Change subject: main/pbx: Move custom function routines to pbx_functions.c.
......................................................................


main/pbx: Move custom function routines to pbx_functions.c.

This is the second patch in a series meant to reduce the bulk of pbx.c.
This moves custom function management routines to their own source.

Change-Id: I34a6190282f781cdbbd3ce9d3adeac3c3805e177
---
M include/asterisk/_private.h
M main/asterisk.c
M main/pbx.c
A main/pbx_functions.c
4 files changed, 729 insertions(+), 646 deletions(-)

Approvals:
  Anonymous Coward #1000019: Verified
  Joshua Colp: Looks good to me, approved
  George Joseph: Looks good to me, but someone else must approve



diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index e58828c..fb80434 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -18,6 +18,7 @@
 int load_modules(unsigned int);		/*!< Provided by loader.c */
 int load_pbx(void);			/*!< Provided by pbx.c */
 int load_pbx_builtins(void);	/*!< Provided by pbx_builtins.c */
+int load_pbx_functions_cli(void);	/*!< Provided by pbx_functions.c */
 int init_logger(void);			/*!< Provided by logger.c */
 void close_logger(void);		/*!< Provided by logger.c */
 void logger_queue_start(void);		/*!< Provided by logger.c */
diff --git a/main/asterisk.c b/main/asterisk.c
index a72e249..ec49c65 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -4600,6 +4600,11 @@
 		exit(1);
 	}
 
+	if (load_pbx_functions_cli()) {
+		printf("Failed: load_pbx_functions_cli\n%s", term_quit());
+		exit(1);
+	}
+
 	if (ast_local_init()) {
 		printf("Failed: ast_local_init\n%s", term_quit());
 		exit(1);
diff --git a/main/pbx.c b/main/pbx.c
index c54eca8..cfc3f62 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -229,17 +229,6 @@
 
 AST_THREADSTORAGE(switch_data);
 AST_THREADSTORAGE(extensionstate_buf);
-/*!
- * \brief A thread local indicating whether the current thread can run
- * 'dangerous' dialplan functions.
- */
-AST_THREADSTORAGE(thread_inhibit_escalations_tl);
-
-/*!
- * \brief Set to true (non-zero) to globally allow all dangerous dialplan
- * functions to run.
- */
-static int live_dangerously;
 
 /*!
    \brief ast_exten: An extension
@@ -758,13 +747,6 @@
 AST_MUTEX_DEFINE_STATIC(maxcalllock);
 static int countcalls;
 static int totalcalls;
-
-/*!
- * \brief Registered functions container.
- *
- * It is sorted by function name.
- */
-static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
 
 static struct ast_context *contexts;
 static struct ast_hashtab *contexts_table = NULL;
@@ -3159,632 +3141,6 @@
 	.name = "EXCEPTION",
 	.read = acf_exception_read,
 };
-
-static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-	struct ast_custom_function *acf;
-	int count_acf = 0;
-	int like = 0;
-
-	switch (cmd) {
-	case CLI_INIT:
-		e->command = "core show functions [like]";
-		e->usage =
-			"Usage: core show functions [like <text>]\n"
-			"       List builtin functions, optionally only those matching a given string\n";
-		return NULL;
-	case CLI_GENERATE:
-		return NULL;
-	}
-
-	if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
-		like = 1;
-	} else if (a->argc != 3) {
-		return CLI_SHOWUSAGE;
-	}
-
-	ast_cli(a->fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
-
-	AST_RWLIST_RDLOCK(&acf_root);
-	AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
-		if (!like || strstr(acf->name, a->argv[4])) {
-			count_acf++;
-			ast_cli(a->fd, "%-20.20s  %-35.35s  %s\n",
-				S_OR(acf->name, ""),
-				S_OR(acf->syntax, ""),
-				S_OR(acf->synopsis, ""));
-		}
-	}
-	AST_RWLIST_UNLOCK(&acf_root);
-
-	ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
-
-	return CLI_SUCCESS;
-}
-
-static char *complete_functions(const char *word, int pos, int state)
-{
-	struct ast_custom_function *cur;
-	char *ret = NULL;
-	int which = 0;
-	int wordlen;
-	int cmp;
-
-	if (pos != 3) {
-		return NULL;
-	}
-
-	wordlen = strlen(word);
-	AST_RWLIST_RDLOCK(&acf_root);
-	AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
-		/*
-		 * Do a case-insensitive search for convenience in this
-		 * 'complete' function.
-		 *
-		 * We must search the entire container because the functions are
-		 * sorted and normally found case sensitively.
-		 */
-		cmp = strncasecmp(word, cur->name, wordlen);
-		if (!cmp) {
-			/* Found match. */
-			if (++which <= state) {
-				/* Not enough matches. */
-				continue;
-			}
-			ret = ast_strdup(cur->name);
-			break;
-		}
-	}
-	AST_RWLIST_UNLOCK(&acf_root);
-
-	return ret;
-}
-
-static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-	struct ast_custom_function *acf;
-	/* Maximum number of characters added by terminal coloring is 22 */
-	char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40], argtitle[40], seealsotitle[40];
-	char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL, *seealso = NULL;
-	char stxtitle[40], *syntax = NULL, *arguments = NULL;
-	int syntax_size, description_size, synopsis_size, arguments_size, seealso_size;
-
-	switch (cmd) {
-	case CLI_INIT:
-		e->command = "core show function";
-		e->usage =
-			"Usage: core show function <function>\n"
-			"       Describe a particular dialplan function.\n";
-		return NULL;
-	case CLI_GENERATE:
-		return complete_functions(a->word, a->pos, a->n);
-	}
-
-	if (a->argc != 4) {
-		return CLI_SHOWUSAGE;
-	}
-
-	if (!(acf = ast_custom_function_find(a->argv[3]))) {
-		ast_cli(a->fd, "No function by that name registered.\n");
-		return CLI_FAILURE;
-	}
-
-	syntax_size = strlen(S_OR(acf->syntax, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-	if (!(syntax = ast_malloc(syntax_size))) {
-		ast_cli(a->fd, "Memory allocation failure!\n");
-		return CLI_FAILURE;
-	}
-
-	snprintf(info, sizeof(info), "\n  -= Info about function '%s' =- \n\n", acf->name);
-	term_color(infotitle, info, COLOR_MAGENTA, 0, sizeof(infotitle));
-	term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
-	term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
-	term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
-	term_color(argtitle, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
-	term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, 40);
-	term_color(syntax, S_OR(acf->syntax, "Not available"), COLOR_CYAN, 0, syntax_size);
-#ifdef AST_XML_DOCS
-	if (acf->docsrc == AST_XML_DOC) {
-		arguments = ast_xmldoc_printable(S_OR(acf->arguments, "Not available"), 1);
-		synopsis = ast_xmldoc_printable(S_OR(acf->synopsis, "Not available"), 1);
-		description = ast_xmldoc_printable(S_OR(acf->desc, "Not available"), 1);
-		seealso = ast_xmldoc_printable(S_OR(acf->seealso, "Not available"), 1);
-	} else
-#endif
-	{
-		synopsis_size = strlen(S_OR(acf->synopsis, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-		synopsis = ast_malloc(synopsis_size);
-
-		description_size = strlen(S_OR(acf->desc, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-		description = ast_malloc(description_size);
-
-		arguments_size = strlen(S_OR(acf->arguments, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-		arguments = ast_malloc(arguments_size);
-
-		seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
-		seealso = ast_malloc(seealso_size);
-
-		/* check allocated memory. */
-		if (!synopsis || !description || !arguments || !seealso) {
-			ast_free(synopsis);
-			ast_free(description);
-			ast_free(arguments);
-			ast_free(seealso);
-			ast_free(syntax);
-			return CLI_FAILURE;
-		}
-
-		term_color(arguments, S_OR(acf->arguments, "Not available"), COLOR_CYAN, 0, arguments_size);
-		term_color(synopsis, S_OR(acf->synopsis, "Not available"), COLOR_CYAN, 0, synopsis_size);
-		term_color(description, S_OR(acf->desc, "Not available"), COLOR_CYAN, 0, description_size);
-		term_color(seealso, S_OR(acf->seealso, "Not available"), COLOR_CYAN, 0, seealso_size);
-	}
-
-	ast_cli(a->fd, "%s%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n",
-			infotitle, syntitle, synopsis, destitle, description,
-			stxtitle, syntax, argtitle, arguments, seealsotitle, seealso);
-
-	ast_free(arguments);
-	ast_free(synopsis);
-	ast_free(description);
-	ast_free(seealso);
-	ast_free(syntax);
-
-	return CLI_SUCCESS;
-}
-
-static struct ast_custom_function *ast_custom_function_find_nolock(const char *name)
-{
-	struct ast_custom_function *cur;
-	int cmp;
-
-	AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
-		cmp = strcmp(name, cur->name);
-		if (cmp > 0) {
-			continue;
-		}
-		if (!cmp) {
-			/* Found it. */
-			break;
-		}
-		/* Not in container. */
-		cur = NULL;
-		break;
-	}
-
-	return cur;
-}
-
-struct ast_custom_function *ast_custom_function_find(const char *name)
-{
-	struct ast_custom_function *acf;
-
-	AST_RWLIST_RDLOCK(&acf_root);
-	acf = ast_custom_function_find_nolock(name);
-	AST_RWLIST_UNLOCK(&acf_root);
-
-	return acf;
-}
-
-int ast_custom_function_unregister(struct ast_custom_function *acf)
-{
-	struct ast_custom_function *cur;
-
-	if (!acf) {
-		return -1;
-	}
-
-	AST_RWLIST_WRLOCK(&acf_root);
-	if ((cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist))) {
-#ifdef AST_XML_DOCS
-		if (cur->docsrc == AST_XML_DOC) {
-			ast_string_field_free_memory(acf);
-		}
-#endif
-		ast_verb(2, "Unregistered custom function %s\n", cur->name);
-	}
-	AST_RWLIST_UNLOCK(&acf_root);
-
-	return cur ? 0 : -1;
-}
-
-/*!
- * \brief Returns true if given custom function escalates privileges on read.
- *
- * \param acf Custom function to query.
- * \return True (non-zero) if reads escalate privileges.
- * \return False (zero) if reads just read.
- */
-static int read_escalates(const struct ast_custom_function *acf) {
-	return acf->read_escalates;
-}
-
-/*!
- * \brief Returns true if given custom function escalates privileges on write.
- *
- * \param acf Custom function to query.
- * \return True (non-zero) if writes escalate privileges.
- * \return False (zero) if writes just write.
- */
-static int write_escalates(const struct ast_custom_function *acf) {
-	return acf->write_escalates;
-}
-
-/*! \internal
- *  \brief Retrieve the XML documentation of a specified ast_custom_function,
- *         and populate ast_custom_function string fields.
- *  \param acf ast_custom_function structure with empty 'desc' and 'synopsis'
- *             but with a function 'name'.
- *  \retval -1 On error.
- *  \retval 0 On succes.
- */
-static int acf_retrieve_docs(struct ast_custom_function *acf)
-{
-#ifdef AST_XML_DOCS
-	char *tmpxml;
-
-	/* Let's try to find it in the Documentation XML */
-	if (!ast_strlen_zero(acf->desc) || !ast_strlen_zero(acf->synopsis)) {
-		return 0;
-	}
-
-	if (ast_string_field_init(acf, 128)) {
-		return -1;
-	}
-
-	/* load synopsis */
-	tmpxml = ast_xmldoc_build_synopsis("function", acf->name, ast_module_name(acf->mod));
-	ast_string_field_set(acf, synopsis, tmpxml);
-	ast_free(tmpxml);
-
-	/* load description */
-	tmpxml = ast_xmldoc_build_description("function", acf->name, ast_module_name(acf->mod));
-	ast_string_field_set(acf, desc, tmpxml);
-	ast_free(tmpxml);
-
-	/* load syntax */
-	tmpxml = ast_xmldoc_build_syntax("function", acf->name, ast_module_name(acf->mod));
-	ast_string_field_set(acf, syntax, tmpxml);
-	ast_free(tmpxml);
-
-	/* load arguments */
-	tmpxml = ast_xmldoc_build_arguments("function", acf->name, ast_module_name(acf->mod));
-	ast_string_field_set(acf, arguments, tmpxml);
-	ast_free(tmpxml);
-
-	/* load seealso */
-	tmpxml = ast_xmldoc_build_seealso("function", acf->name, ast_module_name(acf->mod));
-	ast_string_field_set(acf, seealso, tmpxml);
-	ast_free(tmpxml);
-
-	acf->docsrc = AST_XML_DOC;
-#endif
-
-	return 0;
-}
-
-int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
-{
-	struct ast_custom_function *cur;
-
-	if (!acf) {
-		return -1;
-	}
-
-	acf->mod = mod;
-#ifdef AST_XML_DOCS
-	acf->docsrc = AST_STATIC_DOC;
-#endif
-
-	if (acf_retrieve_docs(acf)) {
-		return -1;
-	}
-
-	AST_RWLIST_WRLOCK(&acf_root);
-
-	cur = ast_custom_function_find_nolock(acf->name);
-	if (cur) {
-		ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
-		AST_RWLIST_UNLOCK(&acf_root);
-		return -1;
-	}
-
-	/* Store in alphabetical order */
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
-		if (strcmp(acf->name, cur->name) < 0) {
-			AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
-			break;
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END;
-	if (!cur) {
-		AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
-	}
-
-	AST_RWLIST_UNLOCK(&acf_root);
-
-	ast_verb(2, "Registered custom function '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, acf->name));
-
-	return 0;
-}
-
-int __ast_custom_function_register_escalating(struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod)
-{
-	int res;
-
-	res = __ast_custom_function_register(acf, mod);
-	if (res != 0) {
-		return -1;
-	}
-
-	switch (escalation) {
-	case AST_CFE_NONE:
-		break;
-	case AST_CFE_READ:
-		acf->read_escalates = 1;
-		break;
-	case AST_CFE_WRITE:
-		acf->write_escalates = 1;
-		break;
-	case AST_CFE_BOTH:
-		acf->read_escalates = 1;
-		acf->write_escalates = 1;
-		break;
-	}
-
-	return 0;
-}
-
-/*! \brief return a pointer to the arguments of the function,
- * and terminates the function name with '\\0'
- */
-static char *func_args(char *function)
-{
-	char *args = strchr(function, '(');
-
-	if (!args) {
-		ast_log(LOG_WARNING, "Function '%s' doesn't contain parentheses.  Assuming null argument.\n", function);
-	} else {
-		char *p;
-		*args++ = '\0';
-		if ((p = strrchr(args, ')'))) {
-			*p = '\0';
-		} else {
-			ast_log(LOG_WARNING, "Can't find trailing parenthesis for function '%s(%s'?\n", function, args);
-		}
-	}
-	return args;
-}
-
-void pbx_live_dangerously(int new_live_dangerously)
-{
-	if (new_live_dangerously && !live_dangerously) {
-		ast_log(LOG_WARNING, "Privilege escalation protection disabled!\n"
-			"See https://wiki.asterisk.org/wiki/x/1gKfAQ for more details.\n");
-	}
-
-	if (!new_live_dangerously && live_dangerously) {
-		ast_log(LOG_NOTICE, "Privilege escalation protection enabled.\n");
-	}
-	live_dangerously = new_live_dangerously;
-}
-
-int ast_thread_inhibit_escalations(void)
-{
-	int *thread_inhibit_escalations;
-
-	thread_inhibit_escalations = ast_threadstorage_get(
-		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
-
-	if (thread_inhibit_escalations == NULL) {
-		ast_log(LOG_ERROR, "Error inhibiting privilege escalations for current thread\n");
-		return -1;
-	}
-
-	*thread_inhibit_escalations = 1;
-	return 0;
-}
-
-/*!
- * \brief Indicates whether the current thread inhibits the execution of
- * dangerous functions.
- *
- * \return True (non-zero) if dangerous function execution is inhibited.
- * \return False (zero) if dangerous function execution is allowed.
- */
-static int thread_inhibits_escalations(void)
-{
-	int *thread_inhibit_escalations;
-
-	thread_inhibit_escalations = ast_threadstorage_get(
-		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
-
-	if (thread_inhibit_escalations == NULL) {
-		ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
-		/* On error, assume that we are inhibiting */
-		return 1;
-	}
-
-	return *thread_inhibit_escalations;
-}
-
-/*!
- * \brief Determines whether execution of a custom function's read function
- * is allowed.
- *
- * \param acfptr Custom function to check
- * \return True (non-zero) if reading is allowed.
- * \return False (zero) if reading is not allowed.
- */
-static int is_read_allowed(struct ast_custom_function *acfptr)
-{
-	if (!acfptr) {
-		return 1;
-	}
-
-	if (!read_escalates(acfptr)) {
-		return 1;
-	}
-
-	if (!thread_inhibits_escalations()) {
-		return 1;
-	}
-
-	if (live_dangerously) {
-		/* Global setting overrides the thread's preference */
-		ast_debug(2, "Reading %s from a dangerous context\n",
-			acfptr->name);
-		return 1;
-	}
-
-	/* We have no reason to allow this function to execute */
-	return 0;
-}
-
-/*!
- * \brief Determines whether execution of a custom function's write function
- * is allowed.
- *
- * \param acfptr Custom function to check
- * \return True (non-zero) if writing is allowed.
- * \return False (zero) if writing is not allowed.
- */
-static int is_write_allowed(struct ast_custom_function *acfptr)
-{
-	if (!acfptr) {
-		return 1;
-	}
-
-	if (!write_escalates(acfptr)) {
-		return 1;
-	}
-
-	if (!thread_inhibits_escalations()) {
-		return 1;
-	}
-
-	if (live_dangerously) {
-		/* Global setting overrides the thread's preference */
-		ast_debug(2, "Writing %s from a dangerous context\n",
-			acfptr->name);
-		return 1;
-	}
-
-	/* We have no reason to allow this function to execute */
-	return 0;
-}
-
-int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
-{
-	char *copy = ast_strdupa(function);
-	char *args = func_args(copy);
-	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
-	int res;
-	struct ast_module_user *u = NULL;
-
-	if (acfptr == NULL) {
-		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
-	} else if (!acfptr->read && !acfptr->read2) {
-		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
-	} else if (!is_read_allowed(acfptr)) {
-		ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
-	} else if (acfptr->read) {
-		if (acfptr->mod) {
-			u = __ast_module_user_add(acfptr->mod, chan);
-		}
-		res = acfptr->read(chan, copy, args, workspace, len);
-		if (acfptr->mod && u) {
-			__ast_module_user_remove(acfptr->mod, u);
-		}
-		return res;
-	} else {
-		struct ast_str *str = ast_str_create(16);
-		if (acfptr->mod) {
-			u = __ast_module_user_add(acfptr->mod, chan);
-		}
-		res = acfptr->read2(chan, copy, args, &str, 0);
-		if (acfptr->mod && u) {
-			__ast_module_user_remove(acfptr->mod, u);
-		}
-		ast_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
-		ast_free(str);
-		return res;
-	}
-	return -1;
-}
-
-int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
-{
-	char *copy = ast_strdupa(function);
-	char *args = func_args(copy);
-	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
-	int res;
-	struct ast_module_user *u = NULL;
-
-	if (acfptr == NULL) {
-		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
-	} else if (!acfptr->read && !acfptr->read2) {
-		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
-	} else if (!is_read_allowed(acfptr)) {
-		ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
-	} else {
-		if (acfptr->mod) {
-			u = __ast_module_user_add(acfptr->mod, chan);
-		}
-		ast_str_reset(*str);
-		if (acfptr->read2) {
-			/* ast_str enabled */
-			res = acfptr->read2(chan, copy, args, str, maxlen);
-		} else {
-			/* Legacy function pointer, allocate buffer for result */
-			int maxsize = ast_str_size(*str);
-			if (maxlen > -1) {
-				if (maxlen == 0) {
-					if (acfptr->read_max) {
-						maxsize = acfptr->read_max;
-					} else {
-						maxsize = VAR_BUF_SIZE;
-					}
-				} else {
-					maxsize = maxlen;
-				}
-				ast_str_make_space(str, maxsize);
-			}
-			res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxsize);
-		}
-		if (acfptr->mod && u) {
-			__ast_module_user_remove(acfptr->mod, u);
-		}
-		return res;
-	}
-	return -1;
-}
-
-int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
-{
-	char *copy = ast_strdupa(function);
-	char *args = func_args(copy);
-	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
-
-	if (acfptr == NULL) {
-		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
-	} else if (!acfptr->write) {
-		ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
-	} else if (!is_write_allowed(acfptr)) {
-		ast_log(LOG_ERROR, "Dangerous function %s write blocked\n", copy);
-	} else {
-		int res;
-		struct ast_module_user *u = NULL;
-		if (acfptr->mod)
-			u = __ast_module_user_add(acfptr->mod, chan);
-		res = acfptr->write(chan, copy, args, value);
-		if (acfptr->mod && u)
-			__ast_module_user_remove(acfptr->mod, u);
-		return res;
-	}
-
-	return -1;
-}
 
 void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
 {
@@ -8054,7 +7410,6 @@
 	AST_CLI_DEFINE(handle_eat_memory, "Eats all available memory"),
 #endif
 	AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),
-	AST_CLI_DEFINE(handle_show_functions, "Shows registered dialplan functions"),
 	AST_CLI_DEFINE(handle_show_switches, "Show alternative switches"),
 	AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
 	AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
@@ -8063,7 +7418,6 @@
 	AST_CLI_DEFINE(handle_show_device2extenstate, "Show expected exten state from multiple device states"),
 #endif
 	AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"),
-	AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
 	AST_CLI_DEFINE(handle_show_hangup_all, "Show hangup handlers of all channels"),
 	AST_CLI_DEFINE(handle_show_hangup_channel, "Show hangup handlers of a specified channel"),
 	AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
diff --git a/main/pbx_functions.c b/main/pbx_functions.c
new file mode 100644
index 0000000..bc738b0
--- /dev/null
+++ b/main/pbx_functions.c
@@ -0,0 +1,723 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, CFWare, LLC
+ *
+ * Corey Farrell <git at cfware.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 Custom function management routines.
+ *
+ * \author Corey Farrell <git at cfware.com>
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/_private.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/term.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/xmldoc.h"
+#include "pbx_private.h"
+
+/*!
+ * \brief A thread local indicating whether the current thread can run
+ * 'dangerous' dialplan functions.
+ */
+AST_THREADSTORAGE(thread_inhibit_escalations_tl);
+
+/*!
+ * \brief Set to true (non-zero) to globally allow all dangerous dialplan
+ * functions to run.
+ */
+static int live_dangerously;
+
+/*!
+ * \brief Registered functions container.
+ *
+ * It is sorted by function name.
+ */
+static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
+
+static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_custom_function *acf;
+	int count_acf = 0;
+	int like = 0;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "core show functions [like]";
+		e->usage =
+			"Usage: core show functions [like <text>]\n"
+			"       List builtin functions, optionally only those matching a given string\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
+		like = 1;
+	} else if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	ast_cli(a->fd, "%s Custom Functions:\n"
+		"--------------------------------------------------------------------------------\n",
+		like ? "Matching" : "Installed");
+
+	AST_RWLIST_RDLOCK(&acf_root);
+	AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+		if (!like || strstr(acf->name, a->argv[4])) {
+			count_acf++;
+			ast_cli(a->fd, "%-20.20s  %-35.35s  %s\n",
+				S_OR(acf->name, ""),
+				S_OR(acf->syntax, ""),
+				S_OR(acf->synopsis, ""));
+		}
+	}
+	AST_RWLIST_UNLOCK(&acf_root);
+
+	ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
+
+	return CLI_SUCCESS;
+}
+
+static char *complete_functions(const char *word, int pos, int state)
+{
+	struct ast_custom_function *cur;
+	char *ret = NULL;
+	int which = 0;
+	int wordlen;
+	int cmp;
+
+	if (pos != 3) {
+		return NULL;
+	}
+
+	wordlen = strlen(word);
+	AST_RWLIST_RDLOCK(&acf_root);
+	AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
+		/*
+		 * Do a case-insensitive search for convenience in this
+		 * 'complete' function.
+		 *
+		 * We must search the entire container because the functions are
+		 * sorted and normally found case sensitively.
+		 */
+		cmp = strncasecmp(word, cur->name, wordlen);
+		if (!cmp) {
+			/* Found match. */
+			if (++which <= state) {
+				/* Not enough matches. */
+				continue;
+			}
+			ret = ast_strdup(cur->name);
+			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&acf_root);
+
+	return ret;
+}
+
+static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_custom_function *acf;
+	/* Maximum number of characters added by terminal coloring is 22 */
+	char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40], argtitle[40], seealsotitle[40];
+	char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL, *seealso = NULL;
+	char stxtitle[40], *syntax = NULL, *arguments = NULL;
+	int syntax_size, description_size, synopsis_size, arguments_size, seealso_size;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "core show function";
+		e->usage =
+			"Usage: core show function <function>\n"
+			"       Describe a particular dialplan function.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return complete_functions(a->word, a->pos, a->n);
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if (!(acf = ast_custom_function_find(a->argv[3]))) {
+		ast_cli(a->fd, "No function by that name registered.\n");
+
+		return CLI_FAILURE;
+	}
+
+	syntax_size = strlen(S_OR(acf->syntax, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
+	syntax = ast_malloc(syntax_size);
+	if (!syntax) {
+		ast_cli(a->fd, "Memory allocation failure!\n");
+
+		return CLI_FAILURE;
+	}
+
+	snprintf(info, sizeof(info), "\n  -= Info about function '%s' =- \n\n", acf->name);
+	term_color(infotitle, info, COLOR_MAGENTA, 0, sizeof(infotitle));
+	term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
+	term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
+	term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
+	term_color(argtitle, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
+	term_color(seealsotitle, "[See Also]\n", COLOR_MAGENTA, 0, 40);
+	term_color(syntax, S_OR(acf->syntax, "Not available"), COLOR_CYAN, 0, syntax_size);
+#ifdef AST_XML_DOCS
+	if (acf->docsrc == AST_XML_DOC) {
+		arguments = ast_xmldoc_printable(S_OR(acf->arguments, "Not available"), 1);
+		synopsis = ast_xmldoc_printable(S_OR(acf->synopsis, "Not available"), 1);
+		description = ast_xmldoc_printable(S_OR(acf->desc, "Not available"), 1);
+		seealso = ast_xmldoc_printable(S_OR(acf->seealso, "Not available"), 1);
+	} else
+#endif
+	{
+		synopsis_size = strlen(S_OR(acf->synopsis, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
+		synopsis = ast_malloc(synopsis_size);
+
+		description_size = strlen(S_OR(acf->desc, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
+		description = ast_malloc(description_size);
+
+		arguments_size = strlen(S_OR(acf->arguments, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
+		arguments = ast_malloc(arguments_size);
+
+		seealso_size = strlen(S_OR(acf->seealso, "Not Available")) + AST_TERM_MAX_ESCAPE_CHARS;
+		seealso = ast_malloc(seealso_size);
+
+		/* check allocated memory. */
+		if (!synopsis || !description || !arguments || !seealso) {
+			ast_free(synopsis);
+			ast_free(description);
+			ast_free(arguments);
+			ast_free(seealso);
+			ast_free(syntax);
+
+			return CLI_FAILURE;
+		}
+
+		term_color(arguments, S_OR(acf->arguments, "Not available"), COLOR_CYAN, 0, arguments_size);
+		term_color(synopsis, S_OR(acf->synopsis, "Not available"), COLOR_CYAN, 0, synopsis_size);
+		term_color(description, S_OR(acf->desc, "Not available"), COLOR_CYAN, 0, description_size);
+		term_color(seealso, S_OR(acf->seealso, "Not available"), COLOR_CYAN, 0, seealso_size);
+	}
+
+	ast_cli(a->fd, "%s%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n",
+			infotitle, syntitle, synopsis, destitle, description,
+			stxtitle, syntax, argtitle, arguments, seealsotitle, seealso);
+
+	ast_free(arguments);
+	ast_free(synopsis);
+	ast_free(description);
+	ast_free(seealso);
+	ast_free(syntax);
+
+	return CLI_SUCCESS;
+}
+
+static struct ast_custom_function *ast_custom_function_find_nolock(const char *name)
+{
+	struct ast_custom_function *cur;
+	int cmp;
+
+	AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
+		cmp = strcmp(name, cur->name);
+		if (cmp > 0) {
+			continue;
+		}
+		if (!cmp) {
+			/* Found it. */
+			break;
+		}
+		/* Not in container. */
+		cur = NULL;
+		break;
+	}
+
+	return cur;
+}
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+	struct ast_custom_function *acf;
+
+	AST_RWLIST_RDLOCK(&acf_root);
+	acf = ast_custom_function_find_nolock(name);
+	AST_RWLIST_UNLOCK(&acf_root);
+
+	return acf;
+}
+
+int ast_custom_function_unregister(struct ast_custom_function *acf)
+{
+	struct ast_custom_function *cur;
+
+	if (!acf) {
+		return -1;
+	}
+
+	AST_RWLIST_WRLOCK(&acf_root);
+	cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist);
+	if (cur) {
+#ifdef AST_XML_DOCS
+		if (cur->docsrc == AST_XML_DOC) {
+			ast_string_field_free_memory(acf);
+		}
+#endif
+		ast_verb(2, "Unregistered custom function %s\n", cur->name);
+	}
+	AST_RWLIST_UNLOCK(&acf_root);
+
+	return cur ? 0 : -1;
+}
+
+/*!
+ * \brief Returns true if given custom function escalates privileges on read.
+ *
+ * \param acf Custom function to query.
+ * \return True (non-zero) if reads escalate privileges.
+ * \return False (zero) if reads just read.
+ */
+static int read_escalates(const struct ast_custom_function *acf) {
+	return acf->read_escalates;
+}
+
+/*!
+ * \brief Returns true if given custom function escalates privileges on write.
+ *
+ * \param acf Custom function to query.
+ * \return True (non-zero) if writes escalate privileges.
+ * \return False (zero) if writes just write.
+ */
+static int write_escalates(const struct ast_custom_function *acf) {
+	return acf->write_escalates;
+}
+
+/*! \internal
+ *  \brief Retrieve the XML documentation of a specified ast_custom_function,
+ *         and populate ast_custom_function string fields.
+ *  \param acf ast_custom_function structure with empty 'desc' and 'synopsis'
+ *             but with a function 'name'.
+ *  \retval -1 On error.
+ *  \retval 0 On succes.
+ */
+static int acf_retrieve_docs(struct ast_custom_function *acf)
+{
+#ifdef AST_XML_DOCS
+	char *tmpxml;
+
+	/* Let's try to find it in the Documentation XML */
+	if (!ast_strlen_zero(acf->desc) || !ast_strlen_zero(acf->synopsis)) {
+		return 0;
+	}
+
+	if (ast_string_field_init(acf, 128)) {
+		return -1;
+	}
+
+	/* load synopsis */
+	tmpxml = ast_xmldoc_build_synopsis("function", acf->name, ast_module_name(acf->mod));
+	ast_string_field_set(acf, synopsis, tmpxml);
+	ast_free(tmpxml);
+
+	/* load description */
+	tmpxml = ast_xmldoc_build_description("function", acf->name, ast_module_name(acf->mod));
+	ast_string_field_set(acf, desc, tmpxml);
+	ast_free(tmpxml);
+
+	/* load syntax */
+	tmpxml = ast_xmldoc_build_syntax("function", acf->name, ast_module_name(acf->mod));
+	ast_string_field_set(acf, syntax, tmpxml);
+	ast_free(tmpxml);
+
+	/* load arguments */
+	tmpxml = ast_xmldoc_build_arguments("function", acf->name, ast_module_name(acf->mod));
+	ast_string_field_set(acf, arguments, tmpxml);
+	ast_free(tmpxml);
+
+	/* load seealso */
+	tmpxml = ast_xmldoc_build_seealso("function", acf->name, ast_module_name(acf->mod));
+	ast_string_field_set(acf, seealso, tmpxml);
+	ast_free(tmpxml);
+
+	acf->docsrc = AST_XML_DOC;
+#endif
+
+	return 0;
+}
+
+int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
+{
+	struct ast_custom_function *cur;
+
+	if (!acf) {
+		return -1;
+	}
+
+	acf->mod = mod;
+#ifdef AST_XML_DOCS
+	acf->docsrc = AST_STATIC_DOC;
+#endif
+
+	if (acf_retrieve_docs(acf)) {
+		return -1;
+	}
+
+	AST_RWLIST_WRLOCK(&acf_root);
+
+	cur = ast_custom_function_find_nolock(acf->name);
+	if (cur) {
+		ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
+		AST_RWLIST_UNLOCK(&acf_root);
+		return -1;
+	}
+
+	/* Store in alphabetical order */
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
+		if (strcmp(acf->name, cur->name) < 0) {
+			AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+	if (!cur) {
+		AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
+	}
+
+	AST_RWLIST_UNLOCK(&acf_root);
+
+	ast_verb(2, "Registered custom function '" COLORIZE_FMT "'\n", COLORIZE(COLOR_BRCYAN, 0, acf->name));
+
+	return 0;
+}
+
+int __ast_custom_function_register_escalating(struct ast_custom_function *acf, enum ast_custom_function_escalation escalation, struct ast_module *mod)
+{
+	int res;
+
+	res = __ast_custom_function_register(acf, mod);
+	if (res != 0) {
+		return -1;
+	}
+
+	switch (escalation) {
+	case AST_CFE_NONE:
+		break;
+	case AST_CFE_READ:
+		acf->read_escalates = 1;
+		break;
+	case AST_CFE_WRITE:
+		acf->write_escalates = 1;
+		break;
+	case AST_CFE_BOTH:
+		acf->read_escalates = 1;
+		acf->write_escalates = 1;
+		break;
+	}
+
+	return 0;
+}
+
+/*! \brief return a pointer to the arguments of the function,
+ * and terminates the function name with '\\0'
+ */
+static char *func_args(char *function)
+{
+	char *args = strchr(function, '(');
+
+	if (!args) {
+		ast_log(LOG_WARNING, "Function '%s' doesn't contain parentheses.  Assuming null argument.\n", function);
+	} else {
+		char *p;
+		*args++ = '\0';
+		if ((p = strrchr(args, ')'))) {
+			*p = '\0';
+		} else {
+			ast_log(LOG_WARNING, "Can't find trailing parenthesis for function '%s(%s'?\n", function, args);
+		}
+	}
+	return args;
+}
+
+void pbx_live_dangerously(int new_live_dangerously)
+{
+	if (new_live_dangerously && !live_dangerously) {
+		ast_log(LOG_WARNING, "Privilege escalation protection disabled!\n"
+			"See https://wiki.asterisk.org/wiki/x/1gKfAQ for more details.\n");
+	}
+
+	if (!new_live_dangerously && live_dangerously) {
+		ast_log(LOG_NOTICE, "Privilege escalation protection enabled.\n");
+	}
+	live_dangerously = new_live_dangerously;
+}
+
+int ast_thread_inhibit_escalations(void)
+{
+	int *thread_inhibit_escalations;
+
+	thread_inhibit_escalations = ast_threadstorage_get(
+		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
+
+	if (thread_inhibit_escalations == NULL) {
+		ast_log(LOG_ERROR, "Error inhibiting privilege escalations for current thread\n");
+		return -1;
+	}
+
+	*thread_inhibit_escalations = 1;
+	return 0;
+}
+
+/*!
+ * \brief Indicates whether the current thread inhibits the execution of
+ * dangerous functions.
+ *
+ * \return True (non-zero) if dangerous function execution is inhibited.
+ * \return False (zero) if dangerous function execution is allowed.
+ */
+static int thread_inhibits_escalations(void)
+{
+	int *thread_inhibit_escalations;
+
+	thread_inhibit_escalations = ast_threadstorage_get(
+		&thread_inhibit_escalations_tl, sizeof(*thread_inhibit_escalations));
+
+	if (thread_inhibit_escalations == NULL) {
+		ast_log(LOG_ERROR, "Error checking thread's ability to run dangerous functions\n");
+		/* On error, assume that we are inhibiting */
+		return 1;
+	}
+
+	return *thread_inhibit_escalations;
+}
+
+/*!
+ * \brief Determines whether execution of a custom function's read function
+ * is allowed.
+ *
+ * \param acfptr Custom function to check
+ * \return True (non-zero) if reading is allowed.
+ * \return False (zero) if reading is not allowed.
+ */
+static int is_read_allowed(struct ast_custom_function *acfptr)
+{
+	if (!acfptr) {
+		return 1;
+	}
+
+	if (!read_escalates(acfptr)) {
+		return 1;
+	}
+
+	if (!thread_inhibits_escalations()) {
+		return 1;
+	}
+
+	if (live_dangerously) {
+		/* Global setting overrides the thread's preference */
+		ast_debug(2, "Reading %s from a dangerous context\n",
+			acfptr->name);
+		return 1;
+	}
+
+	/* We have no reason to allow this function to execute */
+	return 0;
+}
+
+/*!
+ * \brief Determines whether execution of a custom function's write function
+ * is allowed.
+ *
+ * \param acfptr Custom function to check
+ * \return True (non-zero) if writing is allowed.
+ * \return False (zero) if writing is not allowed.
+ */
+static int is_write_allowed(struct ast_custom_function *acfptr)
+{
+	if (!acfptr) {
+		return 1;
+	}
+
+	if (!write_escalates(acfptr)) {
+		return 1;
+	}
+
+	if (!thread_inhibits_escalations()) {
+		return 1;
+	}
+
+	if (live_dangerously) {
+		/* Global setting overrides the thread's preference */
+		ast_debug(2, "Writing %s from a dangerous context\n",
+			acfptr->name);
+		return 1;
+	}
+
+	/* We have no reason to allow this function to execute */
+	return 0;
+}
+
+int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
+{
+	char *copy = ast_strdupa(function);
+	char *args = func_args(copy);
+	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+	int res;
+	struct ast_module_user *u = NULL;
+
+	if (acfptr == NULL) {
+		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+	} else if (!acfptr->read && !acfptr->read2) {
+		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
+	} else if (!is_read_allowed(acfptr)) {
+		ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
+	} else if (acfptr->read) {
+		if (acfptr->mod) {
+			u = __ast_module_user_add(acfptr->mod, chan);
+		}
+		res = acfptr->read(chan, copy, args, workspace, len);
+		if (acfptr->mod && u) {
+			__ast_module_user_remove(acfptr->mod, u);
+		}
+
+		return res;
+	} else {
+		struct ast_str *str = ast_str_create(16);
+
+		if (acfptr->mod) {
+			u = __ast_module_user_add(acfptr->mod, chan);
+		}
+		res = acfptr->read2(chan, copy, args, &str, 0);
+		if (acfptr->mod && u) {
+			__ast_module_user_remove(acfptr->mod, u);
+		}
+		ast_copy_string(workspace, ast_str_buffer(str), len > ast_str_size(str) ? ast_str_size(str) : len);
+		ast_free(str);
+
+		return res;
+	}
+
+	return -1;
+}
+
+int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, ssize_t maxlen)
+{
+	char *copy = ast_strdupa(function);
+	char *args = func_args(copy);
+	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+	int res;
+	struct ast_module_user *u = NULL;
+
+	if (acfptr == NULL) {
+		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+	} else if (!acfptr->read && !acfptr->read2) {
+		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
+	} else if (!is_read_allowed(acfptr)) {
+		ast_log(LOG_ERROR, "Dangerous function %s read blocked\n", copy);
+	} else {
+		if (acfptr->mod) {
+			u = __ast_module_user_add(acfptr->mod, chan);
+		}
+		ast_str_reset(*str);
+		if (acfptr->read2) {
+			/* ast_str enabled */
+			res = acfptr->read2(chan, copy, args, str, maxlen);
+		} else {
+			/* Legacy function pointer, allocate buffer for result */
+			int maxsize = ast_str_size(*str);
+
+			if (maxlen > -1) {
+				if (maxlen == 0) {
+					if (acfptr->read_max) {
+						maxsize = acfptr->read_max;
+					} else {
+						maxsize = VAR_BUF_SIZE;
+					}
+				} else {
+					maxsize = maxlen;
+				}
+				ast_str_make_space(str, maxsize);
+			}
+			res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxsize);
+		}
+		if (acfptr->mod && u) {
+			__ast_module_user_remove(acfptr->mod, u);
+		}
+
+		return res;
+	}
+
+	return -1;
+}
+
+int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+{
+	char *copy = ast_strdupa(function);
+	char *args = func_args(copy);
+	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+
+	if (acfptr == NULL) {
+		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+	} else if (!acfptr->write) {
+		ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
+	} else if (!is_write_allowed(acfptr)) {
+		ast_log(LOG_ERROR, "Dangerous function %s write blocked\n", copy);
+	} else {
+		int res;
+		struct ast_module_user *u = NULL;
+
+		if (acfptr->mod) {
+			u = __ast_module_user_add(acfptr->mod, chan);
+		}
+		res = acfptr->write(chan, copy, args, value);
+		if (acfptr->mod && u) {
+			__ast_module_user_remove(acfptr->mod, u);
+		}
+
+		return res;
+	}
+
+	return -1;
+}
+
+static struct ast_cli_entry acf_cli[] = {
+	AST_CLI_DEFINE(handle_show_functions, "Shows registered dialplan functions"),
+	AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
+};
+
+static void unload_pbx_functions_cli(void)
+{
+	ast_cli_unregister_multiple(acf_cli, ARRAY_LEN(acf_cli));
+}
+
+int load_pbx_functions_cli(void)
+{
+	ast_cli_register_multiple(acf_cli, ARRAY_LEN(acf_cli));
+	ast_register_cleanup(unload_pbx_functions_cli);
+
+	return 0;
+}

-- 
To view, visit https://gerrit.asterisk.org/1895
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I34a6190282f781cdbbd3ce9d3adeac3c3805e177
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Corey Farrell <git at cfware.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: George Joseph <george.joseph at fairview5.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>



More information about the asterisk-code-review mailing list