Merge "res_pjsip: Refactor load_module/unload_module"
[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 ASTERISK_REGISTER_FILE()
33
34 #include <signal.h>
35
36 #include "asterisk/file.h"
37 #include "asterisk/logger.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/config.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/frame.h"
43 #include "asterisk/term.h"
44 #include "asterisk/manager.h"
45 #include "asterisk/cli.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/linkedlists.h"
48 #include "asterisk/chanvars.h"
49 #include "asterisk/sched.h"
50 #include "asterisk/io.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/astdb.h"
53 #include "asterisk/app.h"
54 #include "asterisk/astobj2.h"
55 #include "asterisk/stasis_channels.h"
56
57 #define MODE_MATCH              0
58 #define MODE_MATCHMORE  1
59 #define MODE_CANMATCH   2
60
61 #define EXT_DATA_SIZE 256
62
63 enum option_flags {
64         OPTION_PATTERNS_DISABLED = (1 << 0),
65 };
66
67 AST_APP_OPTIONS(switch_opts, {
68         AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED),
69 });
70
71 struct cache_entry {
72         struct timeval when;
73         struct ast_variable *var;
74         int priority;
75         char *context;
76         char exten[2];
77 };
78
79 struct ao2_container *cache;
80 pthread_t cleanup_thread = 0;
81
82 static int cache_hash(const void *obj, const int flags)
83 {
84         const struct cache_entry *e = obj;
85         return ast_str_case_hash(e->exten) + e->priority;
86 }
87
88 static int cache_cmp(void *obj, void *arg, int flags)
89 {
90         struct cache_entry *e = obj, *f = arg;
91         return e->priority != f->priority ? 0 :
92                 strcmp(e->exten, f->exten) ? 0 :
93                 strcmp(e->context, f->context) ? 0 :
94                 CMP_MATCH;
95 }
96
97 static struct ast_variable *dup_vars(struct ast_variable *v)
98 {
99         struct ast_variable *new, *list = NULL;
100         for (; v; v = v->next) {
101                 if (!(new = ast_variable_new(v->name, v->value, v->file))) {
102                         ast_variables_destroy(list);
103                         return NULL;
104                 }
105                 /* Reversed list in cache, but when we duplicate out of the cache,
106                  * it's back to correct order. */
107                 new->next = list;
108                 list = new;
109         }
110         return list;
111 }
112
113 static void free_entry(void *obj)
114 {
115         struct cache_entry *e = obj;
116         ast_variables_destroy(e->var);
117 }
118
119 static int purge_old_fn(void *obj, void *arg, int flags)
120 {
121         struct cache_entry *e = obj;
122         struct timeval *now = arg;
123         return ast_tvdiff_ms(*now, e->when) >= 1000 ? CMP_MATCH : 0;
124 }
125
126 static void *cleanup(void *unused)
127 {
128         struct timespec forever = { 999999999, 0 }, one_second = { 1, 0 };
129         struct timeval now;
130
131         for (;;) {
132                 pthread_testcancel();
133                 if (ao2_container_count(cache) == 0) {
134                         nanosleep(&forever, NULL);
135                 }
136                 pthread_testcancel();
137                 now = ast_tvnow();
138                 ao2_callback(cache, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, purge_old_fn, &now);
139                 pthread_testcancel();
140                 nanosleep(&one_second, NULL);
141         }
142
143         return NULL;
144 }
145
146
147 /* Realtime switch looks up extensions in the supplied realtime table.
148
149         [context@][realtimetable][/options]
150
151         If the realtimetable is omitted it is assumed to be "extensions".  If no context is 
152         specified the context is assumed to be whatever is the container.
153
154         The realtime table should have entries for context,exten,priority,app,args
155         
156         The realtime table currently does not support callerid fields.
157
158 */
159
160
161 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
162 {
163         struct ast_variable *var;
164         struct ast_config *cfg;
165         char pri[20];
166         char *ematch;
167         char rexten[AST_MAX_EXTENSION + 20]="";
168         int match;
169         /* Optimization: since we don't support hints in realtime, it's silly to
170          * query for a hint here, since we won't actually do anything with it.
171          * This just wastes CPU time and resources. */
172         if (priority < 0) {
173                 return NULL;
174         }
175         snprintf(pri, sizeof(pri), "%d", priority);
176         switch(mode) {
177         case MODE_MATCHMORE:
178                 ematch = "exten LIKE";
179                 snprintf(rexten, sizeof(rexten), "%s_%%", exten);
180                 break;
181         case MODE_CANMATCH:
182                 ematch = "exten LIKE";
183                 snprintf(rexten, sizeof(rexten), "%s%%", exten);
184                 break;
185         case MODE_MATCH:
186         default:
187                 ematch = "exten";
188                 ast_copy_string(rexten, exten, sizeof(rexten));
189         }
190         var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, SENTINEL);
191         if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
192                 cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL); 
193                 if (cfg) {
194                         char *cat = ast_category_browse(cfg, NULL);
195
196                         while(cat) {
197                                 switch(mode) {
198                                 case MODE_MATCHMORE:
199                                         match = ast_extension_close(cat, exten, 1);
200                                         break;
201                                 case MODE_CANMATCH:
202                                         match = ast_extension_close(cat, exten, 0);
203                                         break;
204                                 case MODE_MATCH:
205                                 default:
206                                         match = ast_extension_match(cat, exten);
207                                 }
208                                 if (match) {
209                                         var = ast_category_detach_variables(ast_category_get(cfg, cat, NULL));
210                                         break;
211                                 }
212                                 cat = ast_category_browse(cfg, cat);
213                         }
214                         ast_config_destroy(cfg);
215                 }
216         }
217         return var;
218 }
219
220 static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
221 {
222         const char *ctx = NULL;
223         char *table;
224         struct ast_variable *var=NULL;
225         struct ast_flags flags = { 0, };
226         struct cache_entry *ce;
227         struct {
228                 struct cache_entry ce;
229                 char exten[AST_MAX_EXTENSION];
230         } cache_search = { { .priority = priority, .context = (char *) context }, };
231         char *buf = ast_strdupa(data);
232         /* "Realtime" prefix is stripped off in the parent engine.  The
233          * remaining string is: [[context@]table][/opts] */
234         char *opts = strchr(buf, '/');
235         if (opts)
236                 *opts++ = '\0';
237         table = strchr(buf, '@');
238         if (table) {
239                 *table++ = '\0';
240                 ctx = buf;
241         }
242         ctx = S_OR(ctx, context);
243         table = S_OR(table, "extensions");
244         if (!ast_strlen_zero(opts)) {
245                 ast_app_parse_options(switch_opts, &flags, NULL, opts);
246         }
247         ast_copy_string(cache_search.exten, exten, sizeof(cache_search.exten));
248         if (mode == MODE_MATCH && (ce = ao2_find(cache, &cache_search, OBJ_POINTER))) {
249                 var = dup_vars(ce->var);
250                 ao2_ref(ce, -1);
251         } else {
252                 var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
253                 do {
254                         struct ast_variable *new;
255                         /* Only cache matches */
256                         if (mode != MODE_MATCH) {
257                                 break;
258                         }
259                         if (!(new = dup_vars(var))) {
260                                 break;
261                         }
262                         if (!(ce = ao2_alloc(sizeof(*ce) + strlen(exten) + strlen(context), free_entry))) {
263                                 ast_variables_destroy(new);
264                                 break;
265                         }
266                         ce->context = ce->exten + strlen(exten) + 1;
267                         strcpy(ce->exten, exten); /* SAFE */
268                         strcpy(ce->context, context); /* SAFE */
269                         ce->priority = priority;
270                         ce->var = new;
271                         ce->when = ast_tvnow();
272                         ao2_link(cache, ce);
273                         pthread_kill(cleanup_thread, SIGURG);
274                         ao2_ref(ce, -1);
275                 } while (0);
276         }
277         return var;
278 }
279
280 static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
281 {
282         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
283         if (var) {
284                 ast_variables_destroy(var);
285                 return 1;
286         }
287         return 0;
288 }
289
290 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
291 {
292         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
293         if (var) {
294                 ast_variables_destroy(var);
295                 return 1;
296         }
297         return 0;
298 }
299
300 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
301 {
302         int res = -1;
303         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
304
305         if (var) {
306                 char *appdata_tmp = "";
307                 char *app = NULL;
308                 struct ast_variable *v;
309
310                 for (v = var; v ; v = v->next) {
311                         if (!strcasecmp(v->name, "app"))
312                                 app = ast_strdupa(v->value);
313                         else if (!strcasecmp(v->name, "appdata")) {
314                                 appdata_tmp = ast_strdupa(v->value);
315                         }
316                 }
317                 ast_variables_destroy(var);
318                 if (!ast_strlen_zero(app)) {
319                         struct ast_app *a = pbx_findapp(app);
320                         if (a) {
321                                 char appdata[512];
322                                 char tmp1[80];
323                                 char tmp2[80];
324                                 char tmp3[EXT_DATA_SIZE];
325                                 RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
326                                 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
327
328                                 appdata[0] = 0; /* just in case the substitute var func isn't called */
329                                 if(!ast_strlen_zero(appdata_tmp))
330                                         pbx_substitute_variables_helper(chan, appdata_tmp, appdata, sizeof(appdata) - 1);
331                                 ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n",
332                                                 ast_channel_exten(chan), ast_channel_context(chan), ast_channel_priority(chan),
333                                                  term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
334                                                  term_color(tmp2, ast_channel_name(chan), COLOR_BRMAGENTA, 0, sizeof(tmp2)),
335                                                  term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
336                                 if (ast_channel_snapshot_type()) {
337                                         ast_channel_lock(chan);
338                                         snapshot = ast_channel_snapshot_create(chan);
339                                         ast_channel_unlock(chan);
340                                 }
341                                 if (snapshot) {
342                                         /* pbx_exec sets application name and data, but we don't want to log
343                                          * every exec. Just update the snapshot here instead.
344                                          */
345                                         ast_string_field_set(snapshot, appl, app);
346                                         ast_string_field_set(snapshot, data, !ast_strlen_zero(appdata) ? appdata : "(NULL)");
347                                         msg = stasis_message_create(ast_channel_snapshot_type(), snapshot);
348                                         if (msg) {
349                                                 stasis_publish(ast_channel_topic(chan), msg);
350                                         }
351                                 }
352                                 res = pbx_exec(chan, a, appdata);
353                         } else
354                                 ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
355                 } else {
356                         ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
357                 }
358         }
359         return res;
360 }
361
362 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
363 {
364         struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
365         if (var) {
366                 ast_variables_destroy(var);
367                 return 1;
368         }
369         return 0;
370 }
371
372 static struct ast_switch realtime_switch =
373 {
374         .name                   = "Realtime",
375         .description            = "Realtime Dialplan Switch",
376         .exists                 = realtime_exists,
377         .canmatch               = realtime_canmatch,
378         .exec                   = realtime_exec,
379         .matchmore              = realtime_matchmore,
380 };
381
382 static int unload_module(void)
383 {
384         ast_unregister_switch(&realtime_switch);
385         pthread_cancel(cleanup_thread);
386         pthread_kill(cleanup_thread, SIGURG);
387         pthread_join(cleanup_thread, NULL);
388         /* Destroy all remaining entries */
389         ao2_ref(cache, -1);
390         return 0;
391 }
392
393 static int load_module(void)
394 {
395         if (!(cache = ao2_container_alloc(573, cache_hash, cache_cmp))) {
396                 return AST_MODULE_LOAD_FAILURE;
397         }
398
399         if (ast_pthread_create(&cleanup_thread, NULL, cleanup, NULL)) {
400                 return AST_MODULE_LOAD_FAILURE;
401         }
402
403         if (ast_register_switch(&realtime_switch))
404                 return AST_MODULE_LOAD_FAILURE;
405         return AST_MODULE_LOAD_SUCCESS;
406 }
407
408 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Realtime Switch");
409