pjproject_bundled: Fix missing inclusion of symbols
[asterisk/asterisk.git] / pbx / pbx_realtime.c
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
145 /* Realtime switch looks up extensions in the supplied realtime table.
146
147         [context@][realtimetable][/options]
148
149         If the realtimetable is omitted it is assumed to be "extensions".  If no context is 
150         specified the context is assumed to be whatever is the container.
151
152         The realtime table should have entries for context,exten,priority,app,args
153         
154         The realtime table currently does not support callerid fields.
155
156 */
157
158
159 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
160 {
161         struct ast_variable *var;
162         struct ast_config *cfg;
163         char pri[20];
164         char *ematch;
165         char rexten[AST_MAX_EXTENSION + 20]="";
166         int match;
167         /* Optimization: since we don't support hints in realtime, it's silly to
168          * query for a hint here, since we won't actually do anything with it.
169          * This just wastes CPU time and resources. */
170         if (priority < 0) {
171                 return NULL;
172         }
173         snprintf(pri, sizeof(pri), "%d", priority);
174         switch(mode) {
175         case MODE_MATCHMORE:
176                 ematch = "exten LIKE";
177                 snprintf(rexten, sizeof(rexten), "%s_%%", exten);
178                 break;
179         case MODE_CANMATCH:
180                 ematch = "exten LIKE";
181                 snprintf(rexten, sizeof(rexten), "%s%%", exten);
182                 break;
183         case MODE_MATCH:
184         default:
185                 ematch = "exten";
186                 ast_copy_string(rexten, exten, sizeof(rexten));
187         }
188         var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, SENTINEL);
189         if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
190                 cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL); 
191                 if (cfg) {
192                         char *cat = ast_category_browse(cfg, NULL);
193
194                         while(cat) {
195                                 switch(mode) {
196                                 case MODE_MATCHMORE:
197                                         match = ast_extension_close(cat, exten, 1);
198                                         break;
199                                 case MODE_CANMATCH:
200                                         match = ast_extension_close(cat, exten, 0);
201                                         break;
202                                 case MODE_MATCH:
203                                 default:
204                                         match = ast_extension_match(cat, exten);
205                                 }
206                                 if (match) {
207                                         var = ast_category_detach_variables(ast_category_get(cfg, cat, NULL));
208                                         break;
209                                 }
210                                 cat = ast_category_browse(cfg, cat);
211                         }
212                         ast_config_destroy(cfg);
213                 }
214         }
215         return var;
216 }
217
218 static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
219 {
220         const char *ctx = NULL;
221         char *table;
222         struct ast_variable *var=NULL;
223         struct ast_flags flags = { 0, };
224         struct cache_entry *ce;
225         struct {
226                 struct cache_entry ce;
227                 char exten[AST_MAX_EXTENSION];
228         } cache_search = { { .priority = priority, .context = (char *) context }, };
229         char *buf = ast_strdupa(data);
230         /* "Realtime" prefix is stripped off in the parent engine.  The
231          * remaining string is: [[context@]table][/opts] */
232         char *opts = strchr(buf, '/');
233         if (opts)
234                 *opts++ = '\0';
235         table = strchr(buf, '@');
236         if (table) {
237                 *table++ = '\0';
238                 ctx = buf;
239         }
240         ctx = S_OR(ctx, context);
241         table = S_OR(table, "extensions");
242         if (!ast_strlen_zero(opts)) {
243                 ast_app_parse_options(switch_opts, &flags, NULL, opts);
244         }
245         ast_copy_string(cache_search.exten, exten, sizeof(cache_search.exten));
246         if (mode == MODE_MATCH && (ce = ao2_find(cache, &cache_search, OBJ_POINTER))) {
247                 var = dup_vars(ce->var);
248                 ao2_ref(ce, -1);
249         } else {
250                 var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
251                 do {
252                         struct ast_variable *new;
253                         /* Only cache matches */
254                         if (mode != MODE_MATCH) {
255                                 break;
256                         }
257                         if (!(new = dup_vars(var))) {
258                                 break;
259                         }
260                         if (!(ce = ao2_alloc(sizeof(*ce) + strlen(exten) + strlen(context), free_entry))) {
261                                 ast_variables_destroy(new);
262                                 break;
263                         }
264                         ce->context = ce->exten + strlen(exten) + 1;
265                         strcpy(ce->exten, exten); /* SAFE */
266                         strcpy(ce->context, context); /* SAFE */
267                         ce->priority = priority;
268                         ce->var = new;
269                         ce->when = ast_tvnow();
270                         ao2_link(cache, ce);
271                         pthread_kill(cleanup_thread, SIGURG);
272                         ao2_ref(ce, -1);
273                 } while (0);
274         }
275         return var;
276 }
277
278 static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
279 {
280         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
281         if (var) {
282                 ast_variables_destroy(var);
283                 return 1;
284         }
285         return 0;
286 }
287
288 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
289 {
290         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
291         if (var) {
292                 ast_variables_destroy(var);
293                 return 1;
294         }
295         return 0;
296 }
297
298 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
299 {
300         int res = -1;
301         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
302
303         if (var) {
304                 char *appdata_tmp = "";
305                 char *app = NULL;
306                 struct ast_variable *v;
307
308                 for (v = var; v ; v = v->next) {
309                         if (!strcasecmp(v->name, "app"))
310                                 app = ast_strdupa(v->value);
311                         else if (!strcasecmp(v->name, "appdata")) {
312                                 appdata_tmp = ast_strdupa(v->value);
313                         }
314                 }
315                 ast_variables_destroy(var);
316                 if (!ast_strlen_zero(app)) {
317                         struct ast_app *a = pbx_findapp(app);
318                         if (a) {
319                                 char appdata[512];
320                                 char tmp1[80];
321                                 char tmp2[80];
322                                 char tmp3[EXT_DATA_SIZE];
323                                 RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
324                                 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
325
326                                 appdata[0] = 0; /* just in case the substitute var func isn't called */
327                                 if(!ast_strlen_zero(appdata_tmp))
328                                         pbx_substitute_variables_helper(chan, appdata_tmp, appdata, sizeof(appdata) - 1);
329                                 ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n",
330                                                 ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan),
331                                                  term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
332                                                  term_color(tmp2, ast_channel_name(chan), COLOR_BRMAGENTA, 0, sizeof(tmp2)),
333                                                  term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
334                                 if (ast_channel_snapshot_type()) {
335                                         ast_channel_lock(chan);
336                                         snapshot = ast_channel_snapshot_create(chan);
337                                         ast_channel_unlock(chan);
338                                 }
339                                 if (snapshot) {
340                                         /* pbx_exec sets application name and data, but we don't want to log
341                                          * every exec. Just update the snapshot here instead.
342                                          */
343                                         ast_string_field_set(snapshot, appl, app);
344                                         ast_string_field_set(snapshot, data, !ast_strlen_zero(appdata) ? appdata : "(NULL)");
345                                         msg = stasis_message_create(ast_channel_snapshot_type(), snapshot);
346                                         if (msg) {
347                                                 stasis_publish(ast_channel_topic(chan), msg);
348                                         }
349                                 }
350                                 res = pbx_exec(chan, a, appdata);
351                         } else
352                                 ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
353                 } else {
354                         ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
355                 }
356         }
357         return res;
358 }
359
360 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
361 {
362         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
363         if (var) {
364                 ast_variables_destroy(var);
365                 return 1;
366         }
367         return 0;
368 }
369
370 static struct ast_switch realtime_switch =
371 {
372         .name                   = "Realtime",
373         .description            = "Realtime Dialplan Switch",
374         .exists                 = realtime_exists,
375         .canmatch               = realtime_canmatch,
376         .exec                   = realtime_exec,
377         .matchmore              = realtime_matchmore,
378 };
379
380 static int unload_module(void)
381 {
382         ast_unregister_switch(&realtime_switch);
383         pthread_cancel(cleanup_thread);
384         pthread_kill(cleanup_thread, SIGURG);
385         pthread_join(cleanup_thread, NULL);
386         /* Destroy all remaining entries */
387         ao2_ref(cache, -1);
388         return 0;
389 }
390
391 static int load_module(void)
392 {
393         if (!(cache = ao2_container_alloc(573, cache_hash, cache_cmp))) {
394                 return AST_MODULE_LOAD_FAILURE;
395         }
396
397         if (ast_pthread_create(&cleanup_thread, NULL, cleanup, NULL)) {
398                 return AST_MODULE_LOAD_FAILURE;
399         }
400
401         if (ast_register_switch(&realtime_switch))
402                 return AST_MODULE_LOAD_FAILURE;
403         return AST_MODULE_LOAD_SUCCESS;
404 }
405
406 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Realtime Switch");
407