From e323ca23a7084a4df0298b814a6e3f5c8249b031 Mon Sep 17 00:00:00 2001 From: tilghman Date: Mon, 25 Feb 2008 21:56:19 +0000 Subject: Shared space for variables (instead of letting other channels muck with your own) (closes issue #11943) Reported by: ramonpeek Patches: 20080208__bug11943__2.diff.txt uploaded by Corydon76 (license 14) Tested by: jmls git-svn-id: http://svn.digium.com/svn/asterisk/trunk@104098 f38db490-d61c-443f-a65b-d21fe96a405b --- funcs/func_global.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 1 deletion(-) (limited to 'funcs/func_global.c') diff --git a/funcs/func_global.c b/funcs/func_global.c index 1f0053d94..1f88d0017 100644 --- a/funcs/func_global.c +++ b/funcs/func_global.c @@ -33,6 +33,27 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/pbx.h" +#include "asterisk/channel.h" +#include "asterisk/app.h" +#include "asterisk/manager.h" + +static void shared_variable_free(void *data); + +static struct ast_datastore_info shared_variable_info = { + .type = "SHARED_VARIABLES", + .destroy = shared_variable_free, +}; + +static void shared_variable_free(void *data) +{ + struct varshead *varshead = data; + struct ast_var_t *var; + + while ((var = AST_LIST_REMOVE_HEAD(varshead, entries))) { + ast_var_delete(var); + } + ast_free(varshead); +} static int global_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { @@ -61,11 +82,151 @@ static struct ast_custom_function global_function = { .write = global_write, }; +static int shared_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + struct ast_datastore *varstore; + struct varshead *varshead; + struct ast_var_t *var; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(var); + AST_APP_ARG(chan); + ); + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED([,])\n"); + return -1; + } + + AST_STANDARD_APP_ARGS(args, data); + + if (!ast_strlen_zero(args.chan)) { + char *prefix = alloca(strlen(args.chan) + 2); + sprintf(prefix, "%s-", args.chan); + if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) { + ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' will be blank.\n", args.chan, args.var); + return -1; + } + } else + ast_channel_lock(chan); + + if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) + return -1; + varshead = varstore->data; + *buf = '\0'; + + /* Protected by the channel lock */ + AST_LIST_TRAVERSE(varshead, var, entries) { + if (!strcmp(args.var, ast_var_name(var))) { + ast_copy_string(buf, ast_var_value(var), len); + break; + } + } + + return 0; +} + +static int shared_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) +{ + struct ast_datastore *varstore; + struct varshead *varshead; + struct ast_var_t *var; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(var); + AST_APP_ARG(chan); + ); + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED([,])\n"); + return -1; + } + + AST_STANDARD_APP_ARGS(args, data); + + if (!ast_strlen_zero(args.chan)) { + char *prefix = alloca(strlen(args.chan) + 2); + sprintf(prefix, "%s-", args.chan); + if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) { + ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' not set to '%s'.\n", args.chan, args.var, value); + return -1; + } + } else + ast_channel_lock(chan); + + if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) { + if (!(varstore = ast_channel_datastore_alloc(&shared_variable_info, NULL))) { + ast_log(LOG_ERROR, "Unable to allocate new datastore. Shared variable not set.\n"); + ast_channel_unlock(chan); + return -1; + } + + if (!(varshead = ast_calloc(1, sizeof(*varshead)))) { + ast_log(LOG_ERROR, "Unable to allocate variable structure. Shared variable not set.\n"); + ast_channel_datastore_free(varstore); + ast_channel_unlock(chan); + return -1; + } + + varstore->data = varshead; + ast_channel_datastore_add(chan, varstore); + } + varshead = varstore->data; + + /* Protected by the channel lock */ + AST_LIST_TRAVERSE(varshead, var, entries) { + /* If there's a previous value, remove it */ + if (!strcmp(args.var, ast_var_name(var))) { + AST_LIST_REMOVE(varshead, var, entries); + ast_var_delete(var); + break; + } + } + + var = ast_var_assign(args.var, S_OR(value, "")); + AST_LIST_INSERT_HEAD(varshead, var, entries); + manager_event(EVENT_FLAG_DIALPLAN, "VarSet", + "Channel: %s\r\n" + "Variable: SHARED(%s)\r\n" + "Value: %s\r\n" + "Uniqueid: %s\r\n", + chan ? chan->name : "none", args.var, value, + chan ? chan->uniqueid : "none"); + + ast_channel_unlock(chan); + + return 0; +} + +static struct ast_custom_function shared_function = { + .name = "SHARED", + .synopsis = "Gets or sets the shared variable specified", + .syntax = "SHARED([,])", + .desc = +"Implements a shared variable area, in which you may share variables between\n" +"channels. If channel is unspecified, defaults to the current channel. Note\n" +"that the channel name may be the complete name (i.e. SIP/12-abcd1234) or the\n" +"prefix only (i.e. SIP/12).\n" +"\n" +"The variables used in this space are separate from the general namespace of\n" +"the channel and thus ${SHARED(foo)} and ${foo} represent two completely\n" +"different variables, despite sharing the same name.\n" +"\n" +"Finally, realize that there is an inherent race between channels operating\n" +"at the same time, fiddling with each others' internal variables, which is why\n" +"this special variable namespace exists; it is to remind you that variables in\n" +"the SHARED namespace may change at any time, without warning. You should\n" +"therefore take special care to ensure that when using the SHARED namespace,\n" +"you retrieve the variable and store it in a regular channel variable before\n" +"using it in a set of calculations (or you might be surprised by the result).\n", + .read = shared_read, + .write = shared_write, +}; + static int unload_module(void) { int res = 0; res |= ast_custom_function_unregister(&global_function); + res |= ast_custom_function_unregister(&shared_function); return res; } @@ -75,8 +236,9 @@ static int load_module(void) int res = 0; res |= ast_custom_function_register(&global_function); + res |= ast_custom_function_register(&shared_function); return res; } -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Global variable dialplan functions"); +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Variable dialplan functions"); -- cgit v1.2.3