Asterisk - The Open Source Telephony Project  18.5.0
pbx_realtime.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <[email protected]>
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 Realtime PBX Module
22  *
23  * \arg See also: \ref AstARA
24  */
25 
26 /*** MODULEINFO
27  <support_level>extended</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include <signal.h>
33 
34 #include "asterisk/file.h"
35 #include "asterisk/logger.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/config.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/module.h"
40 #include "asterisk/frame.h"
41 #include "asterisk/term.h"
42 #include "asterisk/manager.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/linkedlists.h"
46 #include "asterisk/chanvars.h"
47 #include "asterisk/sched.h"
48 #include "asterisk/io.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/astdb.h"
51 #include "asterisk/app.h"
52 #include "asterisk/astobj2.h"
54 
55 #define MODE_MATCH 0
56 #define MODE_MATCHMORE 1
57 #define MODE_CANMATCH 2
58 
59 #define EXT_DATA_SIZE 256
60 
63 };
64 
67 });
68 
69 struct cache_entry {
70  struct timeval when;
71  struct ast_variable *var;
72  int priority;
73  char *context;
74  char exten[2];
75 };
76 
78 pthread_t cleanup_thread = 0;
79 
80 static int cache_hash(const void *obj, const int flags)
81 {
82  const struct cache_entry *e = obj;
83  return ast_str_case_hash(e->exten) + e->priority;
84 }
85 
86 static int cache_cmp(void *obj, void *arg, int flags)
87 {
88  struct cache_entry *e = obj, *f = arg;
89  return e->priority != f->priority ? 0 :
90  strcmp(e->exten, f->exten) ? 0 :
91  strcmp(e->context, f->context) ? 0 :
92  CMP_MATCH;
93 }
94 
95 static struct ast_variable *dup_vars(struct ast_variable *v)
96 {
97  struct ast_variable *new, *list = NULL;
98  for (; v; v = v->next) {
99  if (!(new = ast_variable_new(v->name, v->value, v->file))) {
100  ast_variables_destroy(list);
101  return NULL;
102  }
103  /* Reversed list in cache, but when we duplicate out of the cache,
104  * it's back to correct order. */
105  new->next = list;
106  list = new;
107  }
108  return list;
109 }
110 
111 static void free_entry(void *obj)
112 {
113  struct cache_entry *e = obj;
115 }
116 
117 static int purge_old_fn(void *obj, void *arg, int flags)
118 {
119  struct cache_entry *e = obj;
120  struct timeval *now = arg;
121  return ast_tvdiff_ms(*now, e->when) >= 1000 ? CMP_MATCH : 0;
122 }
123 
124 static void *cleanup(void *unused)
125 {
126  struct timespec forever = { 999999999, 0 }, one_second = { 1, 0 };
127  struct timeval now;
128 
129  for (;;) {
130  pthread_testcancel();
131  if (ao2_container_count(cache) == 0) {
132  nanosleep(&forever, NULL);
133  }
134  pthread_testcancel();
135  now = ast_tvnow();
137  pthread_testcancel();
138  nanosleep(&one_second, NULL);
139  }
140 
141  return NULL;
142 }
143 
144 static int extension_length_comparator(struct ast_category *p, struct ast_category *q)
145 {
146  const char *extenp = S_OR(ast_variable_find(p, "exten"), "");
147  const char *extenq = S_OR(ast_variable_find(q, "exten"), "");
148 
149  return strlen(extenp) - strlen(extenq);
150 }
151 
152 /* Realtime switch looks up extensions in the supplied realtime table.
153 
154  [context@][realtimetable][/options]
155 
156  If the realtimetable is omitted it is assumed to be "extensions". If no context is
157  specified the context is assumed to be whatever is the container.
158 
159  The realtime table should have entries for context,exten,priority,app,args
160 
161  The realtime table currently does not support callerid fields.
162 
163 */
164 
165 
166 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
167 {
168  struct ast_variable *var;
169  struct ast_config *cfg;
170  char pri[20];
171  char *ematch;
172  char rexten[AST_MAX_EXTENSION + 20]="";
173  int match;
174  /* Optimization: since we don't support hints in realtime, it's silly to
175  * query for a hint here, since we won't actually do anything with it.
176  * This just wastes CPU time and resources. */
177  if (priority < 0) {
178  return NULL;
179  }
180  snprintf(pri, sizeof(pri), "%d", priority);
181  switch(mode) {
182  case MODE_MATCHMORE:
183  ematch = "exten LIKE";
184  snprintf(rexten, sizeof(rexten), "%s_%%", exten);
185  break;
186  case MODE_CANMATCH:
187  ematch = "exten LIKE";
188  snprintf(rexten, sizeof(rexten), "%s%%", exten);
189  break;
190  case MODE_MATCH:
191  default:
192  ematch = "exten";
193  ast_copy_string(rexten, exten, sizeof(rexten));
194  }
195  var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, SENTINEL);
196  if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
197  cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL);
198  if (cfg) {
199  char *cat = NULL;
200 
201  /* Sort so that longer patterns are checked first */
203 
204  while ((cat = ast_category_browse(cfg, cat))) {
205  const char *realtime_exten = ast_variable_retrieve(cfg, cat, "exten");
206 
207  switch(mode) {
208  case MODE_MATCHMORE:
209  match = ast_extension_close(realtime_exten, exten, 1);
210  break;
211  case MODE_CANMATCH:
212  match = ast_extension_close(realtime_exten, exten, 0);
213  break;
214  case MODE_MATCH:
215  default:
216  match = ast_extension_match(realtime_exten, exten);
217  }
218  if (match) {
220  break;
221  }
222  }
223  ast_config_destroy(cfg);
224  }
225  }
226  return var;
227 }
228 
229 static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
230 {
231  const char *ctx = NULL;
232  char *table;
233  struct ast_variable *var=NULL;
234  struct ast_flags flags = { 0, };
235  struct cache_entry *ce;
236  struct {
237  struct cache_entry ce;
238  char exten[AST_MAX_EXTENSION];
239  } cache_search = { { .priority = priority, .context = (char *) context }, };
240  char *buf = ast_strdupa(data);
241  /* "Realtime" prefix is stripped off in the parent engine. The
242  * remaining string is: [[context@]table][/opts] */
243  char *opts = strchr(buf, '/');
244  if (opts)
245  *opts++ = '\0';
246  table = strchr(buf, '@');
247  if (table) {
248  *table++ = '\0';
249  ctx = buf;
250  }
251  ctx = S_OR(ctx, context);
252  table = S_OR(table, "extensions");
253  if (!ast_strlen_zero(opts)) {
254  ast_app_parse_options(switch_opts, &flags, NULL, opts);
255  }
256  ast_copy_string(cache_search.exten, exten, sizeof(cache_search.exten));
257  if (mode == MODE_MATCH && (ce = ao2_find(cache, &cache_search, OBJ_POINTER))) {
258  var = dup_vars(ce->var);
259  ao2_ref(ce, -1);
260  } else {
261  var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
262  do {
263  struct ast_variable *new;
264  /* Only cache matches */
265  if (mode != MODE_MATCH) {
266  break;
267  }
268  if (!(new = dup_vars(var))) {
269  break;
270  }
271  if (!(ce = ao2_alloc(sizeof(*ce) + strlen(exten) + strlen(context), free_entry))) {
273  break;
274  }
275  ce->context = ce->exten + strlen(exten) + 1;
276  strcpy(ce->exten, exten); /* SAFE */
277  strcpy(ce->context, context); /* SAFE */
278  ce->priority = priority;
279  ce->var = new;
280  ce->when = ast_tvnow();
281  ao2_link(cache, ce);
282  pthread_kill(cleanup_thread, SIGURG);
283  ao2_ref(ce, -1);
284  } while (0);
285  }
286  return var;
287 }
288 
289 static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
290 {
291  struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
292  if (var) {
294  return 1;
295  }
296  return 0;
297 }
298 
299 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
300 {
301  struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
302  if (var) {
304  return 1;
305  }
306  return 0;
307 }
308 
309 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
310 {
311  int res = -1;
312  struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
313 
314  if (var) {
315  char *appdata_tmp = "";
316  char *app = NULL;
317  struct ast_variable *v;
318 
319  for (v = var; v ; v = v->next) {
320  if (!strcasecmp(v->name, "app"))
321  app = ast_strdupa(v->value);
322  else if (!strcasecmp(v->name, "appdata")) {
323  appdata_tmp = ast_strdupa(v->value);
324  }
325  }
327  if (!ast_strlen_zero(app)) {
328  struct ast_app *a = pbx_findapp(app);
329  if (a) {
330  char appdata[512];
331  char tmp1[80];
332  char tmp2[80];
333  char tmp3[EXT_DATA_SIZE];
334 
335  appdata[0] = 0; /* just in case the substitute var func isn't called */
336  if(!ast_strlen_zero(appdata_tmp))
337  pbx_substitute_variables_helper(chan, appdata_tmp, appdata, sizeof(appdata) - 1);
338  ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n",
340  term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
341  term_color(tmp2, ast_channel_name(chan), COLOR_BRMAGENTA, 0, sizeof(tmp2)),
342  term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
344  char *tmp_appl;
345  char *tmp_data;
346 
347  ast_channel_lock(chan);
348  /* Force a new dialplan segment that will be unique to use so we can update it with the
349  * information we want. In the future when a channel snapshot is published this will
350  * occur again and unset this flag.
351  */
353 
354  /* pbx_exec sets application name and data, but we don't want to log
355  * every exec. Just update the snapshot here instead. Publishing the
356  * snapshot retrieves data from the channel object directly, so save
357  * current values prior to publishing so they can be restored after.
358  */
359  tmp_appl = ast_channel_appl(chan) ? ast_strdupa(ast_channel_appl(chan)) : NULL;
360  tmp_data = ast_channel_data(chan) ? ast_strdupa(ast_channel_data(chan)) : NULL;
361 
362  ast_channel_appl_set(chan, app);
363  ast_channel_data_set(chan, !ast_strlen_zero(appdata) ? appdata : "(NULL)");
364 
366 
367  ast_channel_appl_set(chan, tmp_appl);
368  ast_channel_data_set(chan, tmp_data);
369 
370  ast_channel_unlock(chan);
371  }
372  res = pbx_exec(chan, a, appdata);
373  } else
374  ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
375  } else {
376  ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
377  }
378  }
379  return res;
380 }
381 
382 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
383 {
384  struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
385  if (var) {
387  return 1;
388  }
389  return 0;
390 }
391 
393 {
394  .name = "Realtime",
395  .description = "Realtime Dialplan Switch",
396  .exists = realtime_exists,
397  .canmatch = realtime_canmatch,
398  .exec = realtime_exec,
399  .matchmore = realtime_matchmore,
400 };
401 
402 static int unload_module(void)
403 {
404  ast_unregister_switch(&realtime_switch);
405  pthread_cancel(cleanup_thread);
406  pthread_kill(cleanup_thread, SIGURG);
407  pthread_join(cleanup_thread, NULL);
408  /* Destroy all remaining entries */
409  ao2_ref(cache, -1);
410  return 0;
411 }
412 
413 static int load_module(void)
414 {
417  if (!cache) {
419  }
420 
421  if (ast_pthread_create(&cleanup_thread, NULL, cleanup, NULL)) {
423  }
424 
425  if (ast_register_switch(&realtime_switch))
428 }
429 
static int cache_cmp(void *obj, void *arg, int flags)
Definition: pbx_realtime.c:86
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3339
struct ast_variable * next
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
int priority
Definition: pbx_realtime.c:72
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
void ast_channel_snapshot_invalidate_segment(struct ast_channel *chan, enum ast_channel_snapshot_segment_invalidation segment)
Invalidate a channel snapshot segment from being reused.
struct timeval when
Definition: pbx_realtime.c:70
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
#define ast_test_flag(p, flag)
Definition: utils.h:63
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_channel_appl_set(struct ast_channel *chan, const char *value)
Channel Variables.
#define OBJ_POINTER
Definition: astobj2.h:1154
#define LOG_WARNING
Definition: logger.h:274
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
Structure for variables, used for configurations and for channel variables.
static struct ast_variable * dup_vars(struct ast_variable *v)
Definition: pbx_realtime.c:95
static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_realtime.c:289
void ast_unregister_switch(struct ast_switch *sw)
Unregister an alternative switch.
Definition: pbx_switch.c:76
static struct ast_variable * realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
Definition: pbx_realtime.c:166
Definition: pbx_realtime.c:69
static int cache_hash(const void *obj, const int flags)
Definition: pbx_realtime.c:80
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2315
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
#define NULL
Definition: resample.c:96
I/O Management (derived from Cheops-NG)
static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_realtime.c:299
int ast_channel_priority(const struct ast_channel *chan)
#define ast_verb(level,...)
Definition: logger.h:463
pthread_t cleanup_thread
Definition: pbx_realtime.c:78
#define COLOR_BRCYAN
Definition: term.h:60
Utility functions.
#define ast_strlen_zero(foo)
Definition: strings.h:52
const char * name
Definition: pbx.h:162
static int purge_old_fn(void *obj, void *arg, int flags)
Definition: pbx_realtime.c:117
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
static int load_module(void)
Definition: pbx_realtime.c:413
static char * table
Definition: cdr_odbc.c:58
Configuration File Parser.
#define ast_log
Definition: astobj2.c:42
#define SENTINEL
Definition: compiler.h:87
static int unload_module(void)
Definition: pbx_realtime.c:402
General Asterisk PBX channel definitions.
#define MODE_CANMATCH
Definition: pbx_realtime.c:57
#define AST_MAX_EXTENSION
Definition: channel.h:135
Scheduler Routines (derived from cheops)
Asterisk internal frame definitions.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
A set of macros to manage forward-linked lists.
#define ast_variable_new(name, value, filename)
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:2906
const char * ast_channel_exten(const struct ast_channel *chan)
Core PBX routines and definitions.
static const struct ast_app_option switch_opts[128]
Definition: pbx_realtime.c:67
char * term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
Colorize a specified string by adding terminal color codes.
Definition: term.c:184
#define COLOR_BRMAGENTA
Definition: term.h:58
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3452
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
char exten[2]
Definition: pbx_realtime.c:74
static struct ast_variable * realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
Definition: pbx_realtime.c:229
struct ast_variable * var
Definition: pbx_realtime.c:71
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define LOG_NOTICE
Definition: logger.h:263
const char * ast_channel_appl(const struct ast_channel *chan)
#define MODE_MATCH
Definition: pbx_realtime.c:55
#define ast_channel_unlock(chan)
Definition: channel.h:2946
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:559
int ast_extension_match(const char *pattern, const char *extension)
Determine if a given extension matches a given pattern (in NXX format)
Definition: extconf.c:4297
int ast_register_switch(struct ast_switch *sw)
Register an alternative dialplan switch.
Definition: pbx_switch.c:58
Module could not be loaded properly.
Definition: module.h:102
int ast_extension_close(const char *pattern, const char *data, int needmore)
Definition: pbx.c:2418
const char * ast_variable_find(const struct ast_category *category, const char *variable)
Gets a variable value from a specific category structure by name.
Definition: main/config.c:735
struct ast_variable * ast_category_detach_variables(struct ast_category *cat)
Definition: main/config.c:1351
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
struct stasis_message_type * ast_channel_snapshot_type(void)
Message type for ast_channel_snapshot_update.
static void free_entry(void *obj)
Definition: pbx_realtime.c:111
static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_realtime.c:309
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
Structure used to handle boolean flags.
Definition: utils.h:199
Support for logging to various files, console and syslog Configuration in file logger.conf.
struct ao2_container * cache
Definition: pbx_realtime.c:77
const char * ast_channel_data(const struct ast_channel *chan)
#define EXT_DATA_SIZE
Definition: pbx_realtime.c:59
static int extension_length_comparator(struct ast_category *p, struct ast_category *q)
Definition: pbx_realtime.c:144
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
#define MODE_MATCHMORE
Definition: pbx_realtime.c:56
void ast_config_sort_categories(struct ast_config *config, int descending, int(*comparator)(struct ast_category *p, struct ast_category *q))
Sorts categories in a config in the order of a numerical value contained within them.
Definition: main/config.c:1171
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
ast_app: A registered application
Definition: pbx_app.c:45
const char * ast_channel_name(const struct ast_channel *chan)
option_flags
Definition: app_skel.c:136
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: ael_main.c:211
const char * ast_channel_context(const struct ast_channel *chan)
Handy terminal functions for vt* terms.
void ast_channel_publish_snapshot(struct ast_channel *chan)
Publish a ast_channel_snapshot for a channel.
Generic container type.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1022
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
static const char app[]
Definition: app_mysql.c:62
Asterisk module definitions.
Persistant data storage (akin to *doze registry)
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
void ast_channel_data_set(struct ast_channel *chan, const char *value)
static struct ast_switch realtime_switch
Definition: pbx_realtime.c:392
char * context
Definition: pbx_realtime.c:73
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition: strings.h:1250
static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_realtime.c:382
static struct test_val a
#define ao2_link(container, obj)
Definition: astobj2.h:1549
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Realtime Switch")