Asterisk - The Open Source Telephony Project  21.4.1
func_global.c
Go to the documentation of this file.
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="GLOBAL_DELETE" language="en_US">
57  <synopsis>
58  Deletes a specified global variable.
59  </synopsis>
60  <syntax>
61  <parameter name="varname" required="true">
62  <para>Global variable name</para>
63  </parameter>
64  </syntax>
65  <description>
66  <para>Delete the global variable specified in <replaceable>varname</replaceable>.
67  Will succeed if the global variable exists or not.</para>
68  </description>
69  <see-also>
70  <ref type="function">GLOBAL</ref>
71  <ref type="function">DELETE</ref>
72  </see-also>
73  </function>
74  <function name="GLOBAL_EXISTS" language="en_US">
75  <synopsis>
76  Check if a global variable exists or not.
77  </synopsis>
78  <syntax>
79  <parameter name="varname" required="true">
80  <para>Global variable name</para>
81  </parameter>
82  </syntax>
83  <description>
84  <para>Returns <literal>1</literal> if global variable exists or <literal>0</literal> otherwise.</para>
85  </description>
86  <see-also>
87  <ref type="function">VARIABLE_EXISTS</ref>
88  </see-also>
89  </function>
90  <function name="SHARED" language="en_US">
91  <synopsis>
92  Gets or sets the shared variable specified.
93  </synopsis>
94  <syntax>
95  <parameter name="varname" required="true">
96  <para>Variable name</para>
97  </parameter>
98  <parameter name="channel">
99  <para>If not specified will default to current channel. It is the complete
100  channel name: <literal>SIP/12-abcd1234</literal> or the prefix only <literal>SIP/12</literal>.</para>
101  </parameter>
102  </syntax>
103  <description>
104  <para>Implements a shared variable area, in which you may share variables between
105  channels.</para>
106  <para>The variables used in this space are separate from the general namespace of
107  the channel and thus <variable>SHARED(foo)</variable> and <variable>foo</variable>
108  represent two completely different variables, despite sharing the same name.</para>
109  <para>Finally, realize that there is an inherent race between channels operating
110  at the same time, fiddling with each others' internal variables, which is why
111  this special variable namespace exists; it is to remind you that variables in
112  the SHARED namespace may change at any time, without warning. You should
113  therefore take special care to ensure that when using the SHARED namespace,
114  you retrieve the variable and store it in a regular channel variable before
115  using it in a set of calculations (or you might be surprised by the result).</para>
116  </description>
117  </function>
118  <managerEvent language="en_US" name="VarSet">
119  <managerEventInstance class="EVENT_FLAG_DIALPLAN">
120  <synopsis>Raised when a variable is shared between channels.</synopsis>
121  <syntax>
122  <channel_snapshot/>
123  <parameter name="Variable">
124  <para>The SHARED variable being set.</para>
125  <note><para>The variable name will always be enclosed with
126  <literal>SHARED()</literal></para></note>
127  </parameter>
128  <parameter name="Value">
129  <para>The new value of the variable.</para>
130  </parameter>
131  </syntax>
132  <see-also>
133  <ref type="function">SHARED</ref>
134  </see-also>
135  </managerEventInstance>
136  </managerEvent>
137  ***/
138 
139 static void shared_variable_free(void *data);
140 
141 static const struct ast_datastore_info shared_variable_info = {
142  .type = "SHARED_VARIABLES",
143  .destroy = shared_variable_free,
144 };
145 
146 static void shared_variable_free(void *data)
147 {
148  struct varshead *varshead = data;
149  struct ast_var_t *var;
150 
151  while ((var = AST_LIST_REMOVE_HEAD(varshead, entries))) {
152  ast_var_delete(var);
153  }
154  ast_free(varshead);
155 }
156 
157 static int global_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
158 {
159  const char *var = pbx_builtin_getvar_helper(NULL, data);
160 
161  *buf = '\0';
162 
163  if (var)
164  ast_copy_string(buf, var, len);
165 
166  return 0;
167 }
168 
169 static int global_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
170 {
171  pbx_builtin_setvar_helper(NULL, data, value);
172 
173  return 0;
174 }
175 
176 static struct ast_custom_function global_function = {
177  .name = "GLOBAL",
178  .read = global_read,
179  .write = global_write,
180 };
181 
182 static int global_delete_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
183 {
184  pbx_builtin_setvar_helper(NULL, data, NULL);
185 
186  return 0;
187 }
188 
189 static struct ast_custom_function global_delete_function = {
190  .name = "GLOBAL_DELETE",
191  .write = global_delete_write,
192 };
193 
194 static int global_exists_read(struct ast_channel *chan, const char *cmd, char *data,
195  char *buf, size_t len)
196 {
197  const char *var = pbx_builtin_getvar_helper(NULL, data);
198 
199  strcpy(buf, var ? "1" : "0");
200 
201  return 0;
202 }
203 
204 static struct ast_custom_function global_exists_function = {
205  .name = "GLOBAL_EXISTS",
206  .read = global_exists_read,
207 };
208 
209 static int shared_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
210 {
211  struct ast_datastore *varstore;
212  struct varshead *varshead;
213  struct ast_var_t *var;
215  AST_APP_ARG(var);
216  AST_APP_ARG(chan);
217  );
218  struct ast_channel *c_ref = NULL;
219 
220  if (ast_strlen_zero(data)) {
221  ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
222  return -1;
223  }
224 
225  AST_STANDARD_APP_ARGS(args, data);
226 
227  if (!ast_strlen_zero(args.chan)) {
228  char *prefix = ast_alloca(strlen(args.chan) + 2);
229  sprintf(prefix, "%s-", args.chan);
230  if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
231  ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' will be blank.\n", args.chan, args.var);
232  return -1;
233  }
234  chan = c_ref;
235  } else if (!chan) {
236  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
237  return -1;
238  }
239 
240  ast_channel_lock(chan);
241 
242  if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
243  ast_channel_unlock(chan);
244  if (c_ref) {
245  c_ref = ast_channel_unref(c_ref);
246  }
247  return -1;
248  }
249 
250  varshead = varstore->data;
251  *buf = '\0';
252 
253  /* Protected by the channel lock */
254  AST_LIST_TRAVERSE(varshead, var, entries) {
255  if (!strcmp(args.var, ast_var_name(var))) {
256  ast_copy_string(buf, ast_var_value(var), len);
257  break;
258  }
259  }
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 int shared_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
271 {
272  struct ast_datastore *varstore;
273  struct varshead *varshead;
274  struct ast_var_t *var;
276  AST_APP_ARG(var);
277  AST_APP_ARG(chan);
278  );
279  struct ast_channel *c_ref = NULL;
280  int len;
281  RAII_VAR(char *, shared_buffer, NULL, ast_free);
282 
283  if (ast_strlen_zero(data)) {
284  ast_log(LOG_WARNING, "SHARED() requires an argument: SHARED(<var>[,<chan>])\n");
285  return -1;
286  }
287 
288  AST_STANDARD_APP_ARGS(args, data);
289 
290  if (!ast_strlen_zero(args.chan)) {
291  char *prefix = ast_alloca(strlen(args.chan) + 2);
292  sprintf(prefix, "%s-", args.chan);
293  if (!(c_ref = ast_channel_get_by_name(args.chan)) && !(c_ref = ast_channel_get_by_name_prefix(prefix, strlen(prefix)))) {
294  ast_log(LOG_ERROR, "Channel '%s' not found! Variable '%s' not set to '%s'.\n", args.chan, args.var, value);
295  return -1;
296  }
297  chan = c_ref;
298  } else if (!chan) {
299  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
300  return -1;
301  }
302 
303  len = 9 + strlen(args.var); /* SHARED() + var */
304  shared_buffer = ast_malloc(len);
305  if (!shared_buffer) {
306  if (c_ref) {
307  ast_channel_unref(c_ref);
308  }
309  return -1;
310  }
311 
312  ast_channel_lock(chan);
313 
314  if (!(varstore = ast_channel_datastore_find(chan, &shared_variable_info, NULL))) {
315  if (!(varstore = ast_datastore_alloc(&shared_variable_info, NULL))) {
316  ast_log(LOG_ERROR, "Unable to allocate new datastore. Shared variable not set.\n");
317  ast_channel_unlock(chan);
318  if (c_ref) {
319  c_ref = ast_channel_unref(c_ref);
320  }
321  return -1;
322  }
323 
324  if (!(varshead = ast_calloc(1, sizeof(*varshead)))) {
325  ast_log(LOG_ERROR, "Unable to allocate variable structure. Shared variable not set.\n");
326  ast_datastore_free(varstore);
327  ast_channel_unlock(chan);
328  if (c_ref) {
329  c_ref = ast_channel_unref(c_ref);
330  }
331  return -1;
332  }
333 
334  varstore->data = varshead;
335  ast_channel_datastore_add(chan, varstore);
336  }
337  varshead = varstore->data;
338 
339  /* Protected by the channel lock */
340  AST_LIST_TRAVERSE_SAFE_BEGIN(varshead, var, entries) {
341  /* If there's a previous value, remove it */
342  if (!strcmp(args.var, ast_var_name(var))) {
343  AST_LIST_REMOVE_CURRENT(entries);
344  ast_var_delete(var);
345  break;
346  }
347  }
349 
350  if ((var = ast_var_assign(args.var, S_OR(value, "")))) {
351  AST_LIST_INSERT_HEAD(varshead, var, entries);
352 
353  sprintf(shared_buffer, "SHARED(%s)", args.var);
354  ast_channel_publish_varset(chan, shared_buffer, value);
355  }
356 
357  ast_channel_unlock(chan);
358 
359  if (c_ref) {
360  c_ref = ast_channel_unref(c_ref);
361  }
362 
363  return 0;
364 }
365 
366 static struct ast_custom_function shared_function = {
367  .name = "SHARED",
368  .read = shared_read,
369  .write = shared_write,
370 };
371 
372 static int unload_module(void)
373 {
374  int res = 0;
375 
376  res |= ast_custom_function_unregister(&global_function);
377  res |= ast_custom_function_unregister(&global_delete_function);
378  res |= ast_custom_function_unregister(&global_exists_function);
379  res |= ast_custom_function_unregister(&shared_function);
380 
381  return res;
382 }
383 
384 static int load_module(void)
385 {
386  int res = 0;
387 
388  res |= ast_custom_function_register(&global_function);
389  res |= ast_custom_function_register(&global_delete_function);
390  res |= ast_custom_function_register(&global_exists_function);
391  res |= ast_custom_function_register(&shared_function);
392 
393  return res;
394 }
395 
396 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Variable dialplan functions");
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Structure for a data store type.
Definition: datastore.h:31
Structure for a data store object.
Definition: datastore.h:64
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_channel * ast_channel_get_by_name_prefix(const char *name, size_t name_len)
Find a channel by a name prefix.
Definition: channel.c:1434
General Asterisk PBX channel definitions.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
Core PBX routines and definitions.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
void ast_channel_publish_varset(struct ast_channel *chan, const char *variable, const char *value)
Publish a ast_channel_publish_varset for a channel.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
void * data
Definition: datastore.h:66
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
struct varshead varshead
#define AST_APP_ARG(name)
Define an application argument.