d97379ce9161ba37a9aeac35dbb0a6c821c289f4
[asterisk/asterisk.git] / funcs / func_global.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Tilghman Lesher
5  *
6  * Tilghman Lesher <func_global__200605@the-tilghman.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Global variable dialplan functions
22  *
23  * \author Tilghman Lesher <func_global__200605@the-tilghman.com>
24  *
25  * \ingroup functions
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <sys/stat.h>
33
34 #include "asterisk/module.h"
35 #include "asterisk/pbx.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/app.h"
38 #include "asterisk/manager.h"
39
40 static void shared_variable_free(void *data);
41
42 static struct ast_datastore_info shared_variable_info = {
43         .type = "SHARED_VARIABLES",
44         .destroy = shared_variable_free,
45 };
46
47 static void shared_variable_free(void *data)
48 {
49         struct varshead *varshead = data;
50         struct ast_var_t *var;
51
52         while ((var = AST_LIST_REMOVE_HEAD(varshead, entries))) {
53                 ast_var_delete(var);
54         }
55         ast_free(varshead);
56 }
57
58 static int global_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
59 {
60         const char *var = pbx_builtin_getvar_helper(NULL, data);
61
62         *buf = '\0';
63
64         if (var)
65                 ast_copy_string(buf, var, len);
66
67         return 0;
68 }
69
70 static int global_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
71 {
72         pbx_builtin_setvar_helper(NULL, data, value);
73
74         return 0;
75 }
76
77 static struct ast_custom_function global_function = {
78         .name = "GLOBAL",
79         .synopsis = "Gets or sets the global variable specified",
80         .syntax = "GLOBAL(<varname>)",
81         .read = global_read,
82         .write = global_write,
83 };
84
85 static int shared_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
86 {
87         struct ast_datastore *varstore;
88         struct varshead *varshead;
89         struct ast_var_t *var;
90         AST_DECLARE_APP_ARGS(args,
91                 AST_APP_ARG(var);
92                 AST_APP_ARG(chan);
93         );
94
95         if (ast_strlen_zero(data)) {
96                 ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
97                 return -1;
98         }
99
100         AST_STANDARD_APP_ARGS(args, data);
101
102         if (!ast_strlen_zero(args.chan)) {
103                 char *prefix = alloca(strlen(args.chan) + 2);
104                 sprintf(prefix, "%s-", args.chan);
105                 if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) {
106                         ast_log(LOG_ERROR, "Channel '%s' not found!  Variable '%s' will be blank.\n", args.chan, args.var);
107                         return -1;
108                 }
109         } else
110                 ast_channel_lock(chan);
111
112         if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
113                 ast_channel_unlock(chan);
114                 return -1;
115         }
116
117         varshead = varstore->data;
118         *buf = '\0';
119
120         /* Protected by the channel lock */
121         AST_LIST_TRAVERSE(varshead, var, entries) {
122                 if (!strcmp(args.var, ast_var_name(var))) {
123                         ast_copy_string(buf, ast_var_value(var), len);
124                         break;
125                 }
126         }
127
128         ast_channel_unlock(chan);
129
130         return 0;
131 }
132
133 static int shared_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
134 {
135         struct ast_datastore *varstore;
136         struct varshead *varshead;
137         struct ast_var_t *var;
138         AST_DECLARE_APP_ARGS(args,
139                 AST_APP_ARG(var);
140                 AST_APP_ARG(chan);
141         );
142
143         if (ast_strlen_zero(data)) {
144                 ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
145                 return -1;
146         }
147
148         AST_STANDARD_APP_ARGS(args, data);
149
150         if (!ast_strlen_zero(args.chan)) {
151                 char *prefix = alloca(strlen(args.chan) + 2);
152                 sprintf(prefix, "%s-", args.chan);
153                 if (!(chan = ast_get_channel_by_name_locked(args.chan)) && !(chan = ast_get_channel_by_name_prefix_locked(prefix, strlen(prefix)))) {
154                         ast_log(LOG_ERROR, "Channel '%s' not found!  Variable '%s' not set to '%s'.\n", args.chan, args.var, value);
155                         return -1;
156                 }
157         } else
158                 ast_channel_lock(chan);
159
160         if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
161                 if (!(varstore = ast_channel_datastore_alloc(&shared_variable_info, NULL))) {
162                         ast_log(LOG_ERROR, "Unable to allocate new datastore.  Shared variable not set.\n");
163                         ast_channel_unlock(chan);
164                         return -1;
165                 }
166
167                 if (!(varshead = ast_calloc(1, sizeof(*varshead)))) {
168                         ast_log(LOG_ERROR, "Unable to allocate variable structure.  Shared variable not set.\n");
169                         ast_channel_datastore_free(varstore);
170                         ast_channel_unlock(chan);
171                         return -1;
172                 }
173
174                 varstore->data = varshead;
175                 ast_channel_datastore_add(chan, varstore);
176         }
177         varshead = varstore->data;
178
179         /* Protected by the channel lock */
180         AST_LIST_TRAVERSE(varshead, var, entries) {
181                 /* If there's a previous value, remove it */
182                 if (!strcmp(args.var, ast_var_name(var))) {
183                         AST_LIST_REMOVE(varshead, var, entries);
184                         ast_var_delete(var);
185                         break;
186                 }
187         }
188
189         var = ast_var_assign(args.var, S_OR(value, ""));
190         AST_LIST_INSERT_HEAD(varshead, var, entries);
191         manager_event(EVENT_FLAG_DIALPLAN, "VarSet", 
192                 "Channel: %s\r\n"
193                 "Variable: SHARED(%s)\r\n"
194                 "Value: %s\r\n"
195                 "Uniqueid: %s\r\n", 
196                 chan ? chan->name : "none", args.var, value, 
197                 chan ? chan->uniqueid : "none");
198
199         ast_channel_unlock(chan);
200
201         return 0;
202 }
203
204 static struct ast_custom_function shared_function = {
205         .name = "SHARED",
206         .synopsis = "Gets or sets the shared variable specified",
207         .syntax = "SHARED(<varname>[,<channel>])",
208         .desc =
209 "Implements a shared variable area, in which you may share variables between\n"
210 "channels.  If channel is unspecified, defaults to the current channel.  Note\n"
211 "that the channel name may be the complete name (i.e. SIP/12-abcd1234) or the\n"
212 "prefix only (i.e. SIP/12).\n"
213 "\n"
214 "The variables used in this space are separate from the general namespace of\n"
215 "the channel and thus ${SHARED(foo)} and ${foo} represent two completely\n"
216 "different variables, despite sharing the same name.\n"
217 "\n"
218 "Finally, realize that there is an inherent race between channels operating\n"
219 "at the same time, fiddling with each others' internal variables, which is why\n"
220 "this special variable namespace exists; it is to remind you that variables in\n"
221 "the SHARED namespace may change at any time, without warning.  You should\n"
222 "therefore take special care to ensure that when using the SHARED namespace,\n"
223 "you retrieve the variable and store it in a regular channel variable before\n"
224 "using it in a set of calculations (or you might be surprised by the result).\n",
225         .read = shared_read,
226         .write = shared_write,
227 };
228
229 static int unload_module(void)
230 {
231         int res = 0;
232
233         res |= ast_custom_function_unregister(&global_function);
234         res |= ast_custom_function_unregister(&shared_function);
235
236         return res;
237 }
238
239 static int load_module(void)
240 {
241         int res = 0;
242
243         res |= ast_custom_function_register(&global_function);
244         res |= ast_custom_function_register(&shared_function);
245
246         return res;
247 }
248
249 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Variable dialplan functions");