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