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