Wed Jan 8 2020 09:49:47

Asterisk developer's documentation


func_realtime.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005-2006, BJ Weschke. All rights reserved.
5  *
6  * BJ Weschke <bweschke@btwtech.com>
7  *
8  * This code is released by the author with no restrictions on usage.
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  */
17 
18 /*** MODULEINFO
19  <support_level>core</support_level>
20  ***/
21 
22 /*! \file
23  *
24  * \brief REALTIME dialplan function
25  *
26  * \author BJ Weschke <bweschke@btwtech.com>
27  *
28  * \ingroup functions
29  */
30 
31 #include "asterisk.h"
32 
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 403913 $")
34 
35 #include "asterisk/file.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/config.h"
39 #include "asterisk/module.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/app.h"
43 
44 /*** DOCUMENTATION
45  <function name="REALTIME" language="en_US">
46  <synopsis>
47  RealTime Read/Write Functions.
48  </synopsis>
49  <syntax>
50  <parameter name="family" required="true" />
51  <parameter name="fieldmatch" required="true" />
52  <parameter name="matchvalue" />
53  <parameter name="delim1|field">
54  <para>Use <replaceable>delim1</replaceable> with <replaceable>delim2</replaceable> on
55  read and <replaceable>field</replaceable> without <replaceable>delim2</replaceable> on
56  write</para>
57  <para>If we are reading and <replaceable>delim1</replaceable> is not specified, defaults
58  to <literal>,</literal></para>
59  </parameter>
60  <parameter name="delim2">
61  <para>Parameter only used when reading, if not specified defaults to <literal>=</literal></para>
62  </parameter>
63  </syntax>
64  <description>
65  <para>This function will read or write values from/to a RealTime repository.
66  REALTIME(....) will read names/values from the repository, and
67  REALTIME(....)= will write a new value/field to the repository. On a
68  read, this function returns a delimited text string. The name/value
69  pairs are delimited by <replaceable>delim1</replaceable>, and the name and value are delimited
70  between each other with delim2.
71  If there is no match, NULL will be returned by the function.
72  On a write, this function will always return NULL.</para>
73  </description>
74  <see-also>
75  <ref type="function">REALTIME_STORE</ref>
76  <ref type="function">REALTIME_DESTROY</ref>
77  <ref type="function">REALTIME_FIELD</ref>
78  <ref type="function">REALTIME_HASH</ref>
79  </see-also>
80  </function>
81  <function name="REALTIME_STORE" language="en_US">
82  <synopsis>
83  RealTime Store Function.
84  </synopsis>
85  <syntax>
86  <parameter name="family" required="true" />
87  <parameter name="field1" required="true" />
88  <parameter name="fieldN" required="true" multiple="true" />
89  <parameter name="field30" required="true" />
90  </syntax>
91  <description>
92  <para>This function will insert a new set of values into the RealTime repository.
93  If RT engine provides an unique ID of the stored record, REALTIME_STORE(...)=..
94  creates channel variable named RTSTOREID, which contains value of unique ID.
95  Currently, a maximum of 30 field/value pairs is supported.</para>
96  </description>
97  <see-also>
98  <ref type="function">REALTIME</ref>
99  <ref type="function">REALTIME_DESTROY</ref>
100  <ref type="function">REALTIME_FIELD</ref>
101  <ref type="function">REALTIME_HASH</ref>
102  </see-also>
103  </function>
104  <function name="REALTIME_DESTROY" language="en_US">
105  <synopsis>
106  RealTime Destroy Function.
107  </synopsis>
108  <syntax>
109  <parameter name="family" required="true" />
110  <parameter name="fieldmatch" required="true" />
111  <parameter name="matchvalue" />
112  <parameter name="delim1" />
113  <parameter name="delim2" />
114  </syntax>
115  <description>
116  <para>This function acts in the same way as REALTIME(....) does, except that
117  it destroys the matched record in the RT engine.</para>
118  <note>
119  <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
120  is set to <literal>no</literal>, this function can only be read from the
121  dialplan, and not directly from external protocols. It can, however, be
122  executed as a write operation (<literal>REALTIME_DESTROY(family, fieldmatch)=ignored</literal>)</para>
123  </note>
124  </description>
125  <see-also>
126  <ref type="function">REALTIME</ref>
127  <ref type="function">REALTIME_STORE</ref>
128  <ref type="function">REALTIME_FIELD</ref>
129  <ref type="function">REALTIME_HASH</ref>
130  </see-also>
131  </function>
132  <function name="REALTIME_FIELD" language="en_US">
133  <synopsis>
134  RealTime query function.
135  </synopsis>
136  <syntax>
137  <parameter name="family" required="true" />
138  <parameter name="fieldmatch" required="true" />
139  <parameter name="matchvalue" required="true" />
140  <parameter name="fieldname" required="true" />
141  </syntax>
142  <description>
143  <para>This function retrieves a single item, <replaceable>fieldname</replaceable>
144  from the RT engine, where <replaceable>fieldmatch</replaceable> contains the value
145  <replaceable>matchvalue</replaceable>. When written to, the REALTIME_FIELD() function
146  performs identically to the REALTIME() function.</para>
147  </description>
148  <see-also>
149  <ref type="function">REALTIME</ref>
150  <ref type="function">REALTIME_STORE</ref>
151  <ref type="function">REALTIME_DESTROY</ref>
152  <ref type="function">REALTIME_HASH</ref>
153  </see-also>
154  </function>
155  <function name="REALTIME_HASH" language="en_US">
156  <synopsis>
157  RealTime query function.
158  </synopsis>
159  <syntax>
160  <parameter name="family" required="true" />
161  <parameter name="fieldmatch" required="true" />
162  <parameter name="matchvalue" required="true" />
163  </syntax>
164  <description>
165  <para>This function retrieves a single record from the RT engine, where
166  <replaceable>fieldmatch</replaceable> contains the value
167  <replaceable>matchvalue</replaceable> and formats the output suitably, such that
168  it can be assigned to the HASH() function. The HASH() function then provides
169  a suitable method for retrieving each field value of the record.</para>
170  </description>
171  <see-also>
172  <ref type="function">REALTIME</ref>
173  <ref type="function">REALTIME_STORE</ref>
174  <ref type="function">REALTIME_DESTROY</ref>
175  <ref type="function">REALTIME_FIELD</ref>
176  </see-also>
177  </function>
178  ***/
179 
183 
184 static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
185 {
186  struct ast_variable *var, *head;
187  struct ast_str *out;
188  size_t resultslen;
189  int n;
191  AST_APP_ARG(family);
192  AST_APP_ARG(fieldmatch);
194  AST_APP_ARG(delim1);
195  AST_APP_ARG(delim2);
196  );
197 
198  if (ast_strlen_zero(data)) {
199  ast_log(LOG_WARNING, "Syntax: REALTIME(family,fieldmatch[,matchvalue[,delim1[,delim2]]]) - missing argument!\n");
200  return -1;
201  }
202 
204 
205  if (!args.delim1)
206  args.delim1 = ",";
207  if (!args.delim2)
208  args.delim2 = "=";
209 
210  if (chan)
211  ast_autoservice_start(chan);
212 
213  head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL);
214 
215  if (!head) {
216  if (chan)
217  ast_autoservice_stop(chan);
218  return -1;
219  }
220 
221  resultslen = 0;
222  n = 0;
223  for (var = head; var; n++, var = var->next)
224  resultslen += strlen(var->name) + strlen(var->value);
225  /* add space for delimiters and final '\0' */
226  resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
227 
228  if (resultslen > len) {
229  ast_log(LOG_WARNING, "Failed to fetch. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
230  return -1;
231  }
232 
233  /* len is going to be sensible, so we don't need to check for stack
234  * overflows here. */
235  out = ast_str_alloca(resultslen);
236  for (var = head; var; var = var->next)
237  ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
238  ast_copy_string(buf, ast_str_buffer(out), len);
239 
240  ast_variables_destroy(head);
241 
242  if (chan)
243  ast_autoservice_stop(chan);
244 
245  return 0;
246 }
247 
248 static int function_realtime_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
249 {
250  int res = 0;
252  AST_APP_ARG(family);
253  AST_APP_ARG(fieldmatch);
254  AST_APP_ARG(value);
255  AST_APP_ARG(field);
256  );
257 
258  if (ast_strlen_zero(data)) {
259  ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue,updatecol) - missing argument!\n", cmd);
260  return -1;
261  }
262 
264 
265  if (ast_strlen_zero(args.fieldmatch) || ast_strlen_zero(args.field)) {
266  ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue,updatecol) - missing argument!\n", cmd);
267  return -1;
268  }
269 
270  if (chan) {
271  ast_autoservice_start(chan);
272  }
273 
274  res = ast_update_realtime(args.family, args.fieldmatch, args.value, args.field, (char *)value, SENTINEL);
275 
276  if (res < 0) {
277  ast_log(LOG_WARNING, "Failed to update. Check the debug log for possible data repository related entries.\n");
278  }
279 
280  if (chan) {
281  ast_autoservice_stop(chan);
282  }
283 
284  return res;
285 }
286 
287 static int realtimefield_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
288 {
289  struct ast_variable *var, *head;
290  struct ast_str *escapebuf = ast_str_thread_get(&buf1, 16);
291  struct ast_str *fields = ast_str_thread_get(&buf2, 16);
292  struct ast_str *values = ast_str_thread_get(&buf3, 16);
293  int first = 0;
294  enum { rtfield, rthash } which;
296  AST_APP_ARG(family);
297  AST_APP_ARG(fieldmatch);
299  AST_APP_ARG(fieldname);
300  );
301 
302  if (!strcmp(cmd, "REALTIME_FIELD")) {
303  which = rtfield;
304  } else {
305  which = rthash;
306  }
307 
308  if (ast_strlen_zero(data)) {
309  ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
310  return -1;
311  }
312 
314 
315  if ((which == rtfield && args.argc != 4) || (which == rthash && args.argc != 3)) {
316  ast_log(LOG_WARNING, "Syntax: %s(family,fieldmatch,matchvalue%s) - missing argument!\n", cmd, which == rtfield ? ",fieldname" : "");
317  return -1;
318  }
319 
320  if (chan) {
321  ast_autoservice_start(chan);
322  }
323 
324  if (!(head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL))) {
325  if (chan) {
326  ast_autoservice_stop(chan);
327  }
328  return -1;
329  }
330 
331  ast_str_reset(fields);
332  ast_str_reset(values);
333 
334  for (var = head; var; var = var->next) {
335  if (which == rtfield) {
336  ast_debug(1, "Comparing %s to %s\n", var->name, args.fieldname);
337  if (!strcasecmp(var->name, args.fieldname)) {
338  ast_debug(1, "Match! Value is %s\n", var->value);
339  ast_copy_string(buf, var->value, len);
340  break;
341  }
342  } else if (which == rthash) {
343  ast_debug(1, "Setting hash key %s to value %s\n", var->name, var->value);
344  ast_str_append(&fields, 0, "%s%s", first ? "" : ",", ast_str_set_escapecommas(&escapebuf, 0, var->name, INT_MAX));
345  ast_str_append(&values, 0, "%s%s", first ? "" : ",", ast_str_set_escapecommas(&escapebuf, 0, var->value, INT_MAX));
346  first = 0;
347  }
348  }
349  ast_variables_destroy(head);
350 
351  if (which == rthash) {
352  pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
353  ast_copy_string(buf, ast_str_buffer(values), len);
354  }
355 
356  if (chan) {
357  ast_autoservice_stop(chan);
358  }
359 
360  return 0;
361 }
362 
363 static int function_realtime_store(struct ast_channel *chan, const char *cmd, char *data, const char *value)
364 {
365  int res = 0;
366  char storeid[32];
367  char *valcopy;
369  AST_APP_ARG(family);
370  AST_APP_ARG(f)[30]; /* fields */
371  );
372 
374  AST_APP_ARG(v)[30]; /* values */
375  );
376 
377  if (ast_strlen_zero(data)) {
378  ast_log(LOG_WARNING, "Syntax: REALTIME_STORE(family,field1,field2,...,field30) - missing argument!\n");
379  return -1;
380  }
381 
382  if (chan)
383  ast_autoservice_start(chan);
384 
385  valcopy = ast_strdupa(value);
386  AST_STANDARD_APP_ARGS(a, data);
387  AST_STANDARD_APP_ARGS(v, valcopy);
388 
389  res = ast_store_realtime(a.family,
390  a.f[0], v.v[0], a.f[1], v.v[1], a.f[2], v.v[2], a.f[3], v.v[3], a.f[4], v.v[4],
391  a.f[5], v.v[5], a.f[6], v.v[6], a.f[7], v.v[7], a.f[8], v.v[8], a.f[9], v.v[9],
392  a.f[10], v.v[10], a.f[11], v.v[11], a.f[12], v.v[12], a.f[13], v.v[13], a.f[14], v.v[14],
393  a.f[15], v.v[15], a.f[16], v.v[16], a.f[17], v.v[17], a.f[18], v.v[18], a.f[19], v.v[19],
394  a.f[20], v.v[20], a.f[21], v.v[21], a.f[22], v.v[22], a.f[23], v.v[23], a.f[24], v.v[24],
395  a.f[25], v.v[25], a.f[26], v.v[26], a.f[27], v.v[27], a.f[28], v.v[28], a.f[29], v.v[29], SENTINEL
396  );
397 
398  if (res < 0) {
399  ast_log(LOG_WARNING, "Failed to store. Check the debug log for possible data repository related entries.\n");
400  } else {
401  snprintf(storeid, sizeof(storeid), "%d", res);
402  pbx_builtin_setvar_helper(chan, "RTSTOREID", storeid);
403  }
404 
405  if (chan)
406  ast_autoservice_stop(chan);
407 
408  return 0;
409 }
410 
411 static int function_realtime_readdestroy(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
412 {
413  struct ast_variable *var, *head;
414  struct ast_str *out;
415  size_t resultslen;
416  int n;
418  AST_APP_ARG(family);
419  AST_APP_ARG(fieldmatch);
421  AST_APP_ARG(delim1);
422  AST_APP_ARG(delim2);
423  );
424 
425  if (ast_strlen_zero(data)) {
426  ast_log(LOG_WARNING, "Syntax: REALTIME_DESTROY(family,fieldmatch[,matchvalue[,delim1[,delim2]]]) - missing argument!\n");
427  return -1;
428  }
429 
431 
432  if (!args.delim1)
433  args.delim1 = ",";
434  if (!args.delim2)
435  args.delim2 = "=";
436 
437  if (chan)
438  ast_autoservice_start(chan);
439 
440  head = ast_load_realtime_all(args.family, args.fieldmatch, args.value, SENTINEL);
441 
442  if (!head) {
443  if (chan)
444  ast_autoservice_stop(chan);
445  return -1;
446  }
447 
448  if (len > 0) {
449  resultslen = 0;
450  n = 0;
451  for (var = head; var; n++, var = var->next) {
452  resultslen += strlen(var->name) + strlen(var->value);
453  }
454  /* add space for delimiters and final '\0' */
455  resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
456 
457  if (resultslen > len) {
458  /* Unfortunately this does mean that we cannot destroy
459  * the row anymore. But OTOH, we're not destroying
460  * someones data without giving him the chance to look
461  * at it. */
462  ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
463  return -1;
464  }
465 
466  /* len is going to be sensible, so we don't need to check for
467  * stack overflows here. */
468  out = ast_str_alloca(resultslen);
469  for (var = head; var; var = var->next) {
470  ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
471  }
472  ast_copy_string(buf, ast_str_buffer(out), len);
473  }
474 
475  ast_destroy_realtime(args.family, args.fieldmatch, args.value, SENTINEL);
476  ast_variables_destroy(head);
477 
478  if (chan)
479  ast_autoservice_stop(chan);
480 
481  return 0;
482 }
483 
484 /*!
485  * \brief Wrapper to execute REALTIME_DESTROY from a write operation. Allows
486  * execution even if live_dangerously is disabled.
487  */
488 static int function_realtime_writedestroy(struct ast_channel *chan, const char *cmd, char *data, const char *value)
489 {
490  return function_realtime_readdestroy(chan, cmd, data, NULL, 0);
491 }
492 
494  .name = "REALTIME",
495  .read = function_realtime_read,
496  .write = function_realtime_write,
497 };
498 
500  .name = "REALTIME_FIELD",
501  .read = realtimefield_read,
502  .write = function_realtime_write,
503 };
504 
506  .name = "REALTIME_HASH",
507  .read = realtimefield_read,
508 };
509 
511  .name = "REALTIME_STORE",
512  .write = function_realtime_store,
513 };
514 
516  .name = "REALTIME_DESTROY",
519 };
520 
521 static int unload_module(void)
522 {
523  int res = 0;
524  res |= ast_custom_function_unregister(&realtime_function);
525  res |= ast_custom_function_unregister(&realtime_store_function);
526  res |= ast_custom_function_unregister(&realtime_destroy_function);
527  res |= ast_custom_function_unregister(&realtimefield_function);
528  res |= ast_custom_function_unregister(&realtimehash_function);
529  return res;
530 }
531 
532 static int load_module(void)
533 {
534  int res = 0;
535  res |= ast_custom_function_register(&realtime_function);
536  res |= ast_custom_function_register(&realtime_store_function);
537  res |= ast_custom_function_register_escalating(&realtime_destroy_function, AST_CFE_READ);
538  res |= ast_custom_function_register(&realtimefield_function);
539  res |= ast_custom_function_register(&realtimehash_function);
540  return res;
541 }
542 
543 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Read/Write/Store/Destroy values from a RealTime repository");
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:396
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: config.c:2726
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:179
static int load_module(void)
static struct ast_custom_function realtime_store_function
#define LOG_WARNING
Definition: logger.h:144
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
static struct ast_custom_function realtimehash_function
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
static int function_realtime_writedestroy(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Wrapper to execute REALTIME_DESTROY from a write operation. Allows execution even if live_dangerously...
struct ast_variable * ast_load_realtime_all(const char *family,...) attribute_sentinel
Definition: config.c:2536
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
#define var
Definition: ast_expr2f.c:606
Configuration File Parser.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
static struct ast_threadstorage buf2
#define ast_str_alloca(init_len)
Definition: strings.h:608
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: config.c:586
static int unload_module(void)
int value
Definition: syslog.c:39
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
static struct ast_custom_function realtime_function
Utility functions.
static struct ast_custom_function realtime_destroy_function
static int function_realtime_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: config.c:2679
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition: pbx.h:1173
static struct ast_custom_function realtimefield_function
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
#define SENTINEL
Definition: compiler.h:75
const char * value
Definition: config.h:79
General Asterisk PBX channel definitions.
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Destroy realtime configuration.
Definition: config.c:2750
static struct ast_threadstorage buf3
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
static int realtimefield_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
const char * name
Definition: config.h:77
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:238
static int function_realtime_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
static struct @350 args
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct sla_ringing_trunk * first
Definition: app_meetme.c:965
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static struct ast_threadstorage buf1
static int function_realtime_readdestroy(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static struct ast_format f[]
Definition: format_g726.c:181
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...
Definition: pbx.c:10546
char * ast_str_set_escapecommas(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Set a dynamic string to a non-NULL terminated substring, with escaping of commas. ...
Definition: strings.h:830
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:436
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
const char * name
Definition: pbx.h:96
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
struct ast_variable * next
Definition: config.h:82
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:669
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
Asterisk module definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
static int function_realtime_store(struct ast_channel *chan, const char *cmd, char *data, const char *value)
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180