Blocked revisions 375247
[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 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <sys/stat.h>
37
38 #include "asterisk/module.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/app.h"
42 #include "asterisk/manager.h"
43
44 /*** DOCUMENTATION
45         <function name="GLOBAL" language="en_US">
46                 <synopsis>
47                         Gets or sets the global variable specified.
48                 </synopsis>
49                 <syntax>
50                         <parameter name="varname" required="true">
51                                 <para>Global variable name</para>
52                         </parameter>
53                 </syntax>
54                 <description>
55                         <para>Set or get the value of a global variable specified in <replaceable>varname</replaceable></para>
56                 </description>
57         </function>
58         <function name="SHARED" language="en_US">
59                 <synopsis>
60                         Gets or sets the shared variable specified.
61                 </synopsis>
62                 <syntax>
63                         <parameter name="varname" required="true">
64                                 <para>Variable name</para>
65                         </parameter>
66                         <parameter name="channel">
67                                 <para>If not specified will default to current channel. It is the complete
68                                 channel name: <literal>SIP/12-abcd1234</literal> or the prefix only <literal>SIP/12</literal>.</para>
69                         </parameter>
70                 </syntax>
71                 <description>
72                         <para>Implements a shared variable area, in which you may share variables between
73                         channels.</para>
74                         <para>The variables used in this space are separate from the general namespace of
75                         the channel and thus <variable>SHARED(foo)</variable> and <variable>foo</variable> 
76                         represent two completely different variables, despite sharing the same name.</para>
77                         <para>Finally, realize that there is an inherent race between channels operating
78                         at the same time, fiddling with each others' internal variables, which is why
79                         this special variable namespace exists; it is to remind you that variables in
80                         the SHARED namespace may change at any time, without warning.  You should
81                         therefore take special care to ensure that when using the SHARED namespace,
82                         you retrieve the variable and store it in a regular channel variable before
83                         using it in a set of calculations (or you might be surprised by the result).</para>
84                 </description>
85         </function>
86
87  ***/
88
89 static void shared_variable_free(void *data);
90
91 static const struct ast_datastore_info shared_variable_info = {
92         .type = "SHARED_VARIABLES",
93         .destroy = shared_variable_free,
94 };
95
96 static void shared_variable_free(void *data)
97 {
98         struct varshead *varshead = data;
99         struct ast_var_t *var;
100
101         while ((var = AST_LIST_REMOVE_HEAD(varshead, entries))) {
102                 ast_var_delete(var);
103         }
104         ast_free(varshead);
105 }
106
107 static int global_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
108 {
109         const char *var = pbx_builtin_getvar_helper(NULL, data);
110
111         *buf = '\0';
112
113         if (var)
114                 ast_copy_string(buf, var, len);
115
116         return 0;
117 }
118
119 static int global_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
120 {
121         pbx_builtin_setvar_helper(NULL, data, value);
122
123         return 0;
124 }
125
126 static struct ast_custom_function global_function = {
127         .name = "GLOBAL",
128         .read = global_read,
129         .write = global_write,
130 };
131
132 static int shared_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
133 {
134         struct ast_datastore *varstore;
135         struct varshead *varshead;
136         struct ast_var_t *var;
137         AST_DECLARE_APP_ARGS(args,
138                 AST_APP_ARG(var);
139                 AST_APP_ARG(chan);
140         );
141         struct ast_channel *c_ref = NULL;
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 = ast_alloca(strlen(args.chan) + 2);
152                 sprintf(prefix, "%s-", args.chan);
153                 if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
154                         ast_log(LOG_ERROR, "Channel '%s' not found!  Variable '%s' will be blank.\n", args.chan, args.var);
155                         return -1;
156                 }
157                 chan = c_ref;
158         }
159
160         ast_channel_lock(chan);
161
162         if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
163                 ast_channel_unlock(chan);
164                 if (c_ref) {
165                         c_ref = ast_channel_unref(c_ref);
166                 }
167                 return -1;
168         }
169
170         varshead = varstore->data;
171         *buf = '\0';
172
173         /* Protected by the channel lock */
174         AST_LIST_TRAVERSE(varshead, var, entries) {
175                 if (!strcmp(args.var, ast_var_name(var))) {
176                         ast_copy_string(buf, ast_var_value(var), len);
177                         break;
178                 }
179         }
180
181         ast_channel_unlock(chan);
182
183         if (c_ref) {
184                 c_ref = ast_channel_unref(c_ref);
185         }
186
187         return 0;
188 }
189
190 static int shared_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
191 {
192         struct ast_datastore *varstore;
193         struct varshead *varshead;
194         struct ast_var_t *var;
195         AST_DECLARE_APP_ARGS(args,
196                 AST_APP_ARG(var);
197                 AST_APP_ARG(chan);
198         );
199         struct ast_channel *c_ref = NULL;
200
201         if (ast_strlen_zero(data)) {
202                 ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
203                 return -1;
204         }
205
206         AST_STANDARD_APP_ARGS(args, data);
207
208         if (!ast_strlen_zero(args.chan)) {
209                 char *prefix = ast_alloca(strlen(args.chan) + 2);
210                 sprintf(prefix, "%s-", args.chan);
211                 if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
212                         ast_log(LOG_ERROR, "Channel '%s' not found!  Variable '%s' not set to '%s'.\n", args.chan, args.var, value);
213                         return -1;
214                 }
215                 chan = c_ref;
216         }
217
218         ast_channel_lock(chan);
219
220         if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
221                 if (!(varstore = ast_datastore_alloc(&shared_variable_info, NULL))) {
222                         ast_log(LOG_ERROR, "Unable to allocate new datastore.  Shared variable not set.\n");
223                         ast_channel_unlock(chan);
224                         if (c_ref) {
225                                 c_ref = ast_channel_unref(c_ref);
226                         }
227                         return -1;
228                 }
229
230                 if (!(varshead = ast_calloc(1, sizeof(*varshead)))) {
231                         ast_log(LOG_ERROR, "Unable to allocate variable structure.  Shared variable not set.\n");
232                         ast_datastore_free(varstore);
233                         ast_channel_unlock(chan);
234                         if (c_ref) {
235                                 c_ref = ast_channel_unref(c_ref);
236                         }
237                         return -1;
238                 }
239
240                 varstore->data = varshead;
241                 ast_channel_datastore_add(chan, varstore);
242         }
243         varshead = varstore->data;
244
245         /* Protected by the channel lock */
246         AST_LIST_TRAVERSE_SAFE_BEGIN(varshead, var, entries) {
247                 /* If there's a previous value, remove it */
248                 if (!strcmp(args.var, ast_var_name(var))) {
249                         AST_LIST_REMOVE_CURRENT(entries);
250                         ast_var_delete(var);
251                         break;
252                 }
253         }
254         AST_LIST_TRAVERSE_SAFE_END;
255
256         var = ast_var_assign(args.var, S_OR(value, ""));
257         AST_LIST_INSERT_HEAD(varshead, var, entries);
258         manager_event(EVENT_FLAG_DIALPLAN, "VarSet", 
259                 "Channel: %s\r\n"
260                 "Variable: SHARED(%s)\r\n"
261                 "Value: %s\r\n"
262                 "Uniqueid: %s\r\n", 
263                 chan ? ast_channel_name(chan) : "none", args.var, value, 
264                 chan ? ast_channel_uniqueid(chan) : "none");
265
266         ast_channel_unlock(chan);
267
268         if (c_ref) {
269                 c_ref = ast_channel_unref(c_ref);
270         }
271
272         return 0;
273 }
274
275 static struct ast_custom_function shared_function = {
276         .name = "SHARED",
277         .read = shared_read,
278         .write = shared_write,
279 };
280
281 static int unload_module(void)
282 {
283         int res = 0;
284
285         res |= ast_custom_function_unregister(&global_function);
286         res |= ast_custom_function_unregister(&shared_function);
287
288         return res;
289 }
290
291 static int load_module(void)
292 {
293         int res = 0;
294
295         res |= ast_custom_function_register(&global_function);
296         res |= ast_custom_function_register(&shared_function);
297
298         return res;
299 }
300
301 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Variable dialplan functions");