Asterisk - The Open Source Telephony Project  21.4.1
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 <markster@digium.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 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"
53 #include "asterisk/stasis_channels.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 
61 enum option_flags {
62  OPTION_PATTERNS_DISABLED = (1 << 0),
63 };
64 
65 AST_APP_OPTIONS(switch_opts, {
66  AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED),
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 
77 struct ao2_container *cache;
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;
114  ast_variables_destroy(e->var);
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();
136  ao2_callback(cache, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, purge_old_fn, &now);
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 */
202  ast_config_sort_categories(cfg, 1, extension_length_comparator);
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) {
219  var = ast_category_detach_variables(ast_category_get(cfg, cat, NULL));
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",
339  ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan),
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 
392 static struct ast_switch realtime_switch =
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 {
416  cache_hash, NULL, cache_cmp);
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 
430 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Realtime Switch");
struct ast_variable * next
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.
#define EXT_DATA_SIZE
Definition: pbx.c:216
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.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
Channel Variables.
#define OBJ_POINTER
Definition: astobj2.h:1150
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
Structure for variables, used for configurations and for channel variables.
void ast_unregister_switch(struct ast_switch *sw)
Unregister an alternative switch.
Definition: pbx_switch.c:76
Definition: pbx_realtime.c:69
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
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:3326
I/O Management (derived from Cheops-NG)
Utility functions.
const char * name
Definition: pbx.h:162
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Configuration File Parser.
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition: res_stasis.c:327
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define AST_MAX_EXTENSION
Definition: channel.h:134
Scheduler Routines (derived from cheops)
Asterisk internal frame definitions.
A set of macros to manage forward-linked lists.
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:3066
Core PBX routines and definitions.
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:235
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:3622
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
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:4295
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
Support for logging to various files, console and syslog Configuration in file logger.conf.
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:824
struct stasis_message_type * ast_channel_snapshot_type(void)
Message type for ast_channel_snapshot_update.
Structure used to handle boolean flags.
Definition: utils.h:199
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:1260
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: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
ast_app: A registered application
Definition: pbx_app.c:45
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.
static struct sorcery_test_caching cache
Global scope caching structure for testing.
Definition: test_sorcery.c:178
#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:1111
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
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
Asterisk module definitions.
Persistent data storage (akin to *doze registry)
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
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:1303
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532