git migration: Refactor the ASTERISK_FILE_VERSION macro
[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_REGISTER_FILE()
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/stasis_channels.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         <managerEvent language="en_US" name="VarSet">
87                 <managerEventInstance class="EVENT_FLAG_DIALPLAN">
88                         <synopsis>Raised when a variable is shared between channels.</synopsis>
89                         <syntax>
90                                 <channel_snapshot/>
91                                 <parameter name="Variable">
92                                         <para>The SHARED variable being set.</para>
93                                         <note><para>The variable name will always be enclosed with
94                                         <literal>SHARED()</literal></para></note>
95                                 </parameter>
96                                 <parameter name="Value">
97                                         <para>The new value of the variable.</para>
98                                 </parameter>
99                         </syntax>
100                         <see-also>
101                                 <ref type="function">SHARED</ref>
102                         </see-also>
103                 </managerEventInstance>
104         </managerEvent>
105  ***/
106
107 static void shared_variable_free(void *data);
108
109 static const struct ast_datastore_info shared_variable_info = {
110         .type = "SHARED_VARIABLES",
111         .destroy = shared_variable_free,
112 };
113
114 static void shared_variable_free(void *data)
115 {
116         struct varshead *varshead = data;
117         struct ast_var_t *var;
118
119         while ((var = AST_LIST_REMOVE_HEAD(varshead, entries))) {
120                 ast_var_delete(var);
121         }
122         ast_free(varshead);
123 }
124
125 static int global_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
126 {
127         const char *var = pbx_builtin_getvar_helper(NULL, data);
128
129         *buf = '\0';
130
131         if (var)
132                 ast_copy_string(buf, var, len);
133
134         return 0;
135 }
136
137 static int global_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
138 {
139         pbx_builtin_setvar_helper(NULL, data, value);
140
141         return 0;
142 }
143
144 static struct ast_custom_function global_function = {
145         .name = "GLOBAL",
146         .read = global_read,
147         .write = global_write,
148 };
149
150 static int shared_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
151 {
152         struct ast_datastore *varstore;
153         struct varshead *varshead;
154         struct ast_var_t *var;
155         AST_DECLARE_APP_ARGS(args,
156                 AST_APP_ARG(var);
157                 AST_APP_ARG(chan);
158         );
159         struct ast_channel *c_ref = NULL;
160
161         if (ast_strlen_zero(data)) {
162                 ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
163                 return -1;
164         }
165
166         AST_STANDARD_APP_ARGS(args, data);
167
168         if (!ast_strlen_zero(args.chan)) {
169                 char *prefix = ast_alloca(strlen(args.chan) + 2);
170                 sprintf(prefix, "%s-", args.chan);
171                 if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
172                         ast_log(LOG_ERROR, "Channel '%s' not found!  Variable '%s' will be blank.\n", args.chan, args.var);
173                         return -1;
174                 }
175                 chan = c_ref;
176         } else if (!chan) {
177                 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
178                 return -1;
179         }
180
181         ast_channel_lock(chan);
182
183         if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
184                 ast_channel_unlock(chan);
185                 if (c_ref) {
186                         c_ref = ast_channel_unref(c_ref);
187                 }
188                 return -1;
189         }
190
191         varshead = varstore->data;
192         *buf = '\0';
193
194         /* Protected by the channel lock */
195         AST_LIST_TRAVERSE(varshead, var, entries) {
196                 if (!strcmp(args.var, ast_var_name(var))) {
197                         ast_copy_string(buf, ast_var_value(var), len);
198                         break;
199                 }
200         }
201
202         ast_channel_unlock(chan);
203
204         if (c_ref) {
205                 c_ref = ast_channel_unref(c_ref);
206         }
207
208         return 0;
209 }
210
211 static int shared_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
212 {
213         struct ast_datastore *varstore;
214         struct varshead *varshead;
215         struct ast_var_t *var;
216         AST_DECLARE_APP_ARGS(args,
217                 AST_APP_ARG(var);
218                 AST_APP_ARG(chan);
219         );
220         struct ast_channel *c_ref = NULL;
221         int len;
222         RAII_VAR(char *, shared_buffer, NULL, ast_free);
223
224         if (ast_strlen_zero(data)) {
225                 ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
226                 return -1;
227         }
228
229         AST_STANDARD_APP_ARGS(args, data);
230
231         if (!ast_strlen_zero(args.chan)) {
232                 char *prefix = ast_alloca(strlen(args.chan) + 2);
233                 sprintf(prefix, "%s-", args.chan);
234                 if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
235                         ast_log(LOG_ERROR, "Channel '%s' not found!  Variable '%s' not set to '%s'.\n", args.chan, args.var, value);
236                         return -1;
237                 }
238                 chan = c_ref;
239         } else if (!chan) {
240                 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
241                 return -1;
242         }
243
244         len = 9 + strlen(args.var); /* SHARED() + var */
245         shared_buffer = ast_malloc(len);
246         if (!shared_buffer) {
247                 if (c_ref) {
248                         ast_channel_unref(c_ref);
249                 }
250                 return -1;
251         }
252
253         ast_channel_lock(chan);
254
255         if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
256                 if (!(varstore = ast_datastore_alloc(&shared_variable_info, NULL))) {
257                         ast_log(LOG_ERROR, "Unable to allocate new datastore.  Shared variable not set.\n");
258                         ast_channel_unlock(chan);
259                         if (c_ref) {
260                                 c_ref = ast_channel_unref(c_ref);
261                         }
262                         return -1;
263                 }
264
265                 if (!(varshead = ast_calloc(1, sizeof(*varshead)))) {
266                         ast_log(LOG_ERROR, "Unable to allocate variable structure.  Shared variable not set.\n");
267                         ast_datastore_free(varstore);
268                         ast_channel_unlock(chan);
269                         if (c_ref) {
270                                 c_ref = ast_channel_unref(c_ref);
271                         }
272                         return -1;
273                 }
274
275                 varstore->data = varshead;
276                 ast_channel_datastore_add(chan, varstore);
277         }
278         varshead = varstore->data;
279
280         /* Protected by the channel lock */
281         AST_LIST_TRAVERSE_SAFE_BEGIN(varshead, var, entries) {
282                 /* If there's a previous value, remove it */
283                 if (!strcmp(args.var, ast_var_name(var))) {
284                         AST_LIST_REMOVE_CURRENT(entries);
285                         ast_var_delete(var);
286                         break;
287                 }
288         }
289         AST_LIST_TRAVERSE_SAFE_END;
290
291         if ((var = ast_var_assign(args.var, S_OR(value, "")))) {
292                 AST_LIST_INSERT_HEAD(varshead, var, entries);
293
294                 sprintf(shared_buffer, "SHARED(%s)", args.var);
295                 ast_channel_publish_varset(chan, shared_buffer, value);
296         }
297
298         ast_channel_unlock(chan);
299
300         if (c_ref) {
301                 c_ref = ast_channel_unref(c_ref);
302         }
303
304         return 0;
305 }
306
307 static struct ast_custom_function shared_function = {
308         .name = "SHARED",
309         .read = shared_read,
310         .write = shared_write,
311 };
312
313 static int unload_module(void)
314 {
315         int res = 0;
316
317         res |= ast_custom_function_unregister(&global_function);
318         res |= ast_custom_function_unregister(&shared_function);
319
320         return res;
321 }
322
323 static int load_module(void)
324 {
325         int res = 0;
326
327         res |= ast_custom_function_register(&global_function);
328         res |= ast_custom_function_register(&shared_function);
329
330         return res;
331 }
332
333 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Variable dialplan functions");