Sun Oct 16 2011 08:41:43

Asterisk developer's documentation


pbx_realtime.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Realtime PBX Module
00022  *
00023  * \arg See also: \ref AstARA
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>extended</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00033 
00034 #include <signal.h>
00035 
00036 #include "asterisk/file.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/config.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/module.h"
00042 #include "asterisk/frame.h"
00043 #include "asterisk/term.h"
00044 #include "asterisk/manager.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/lock.h"
00047 #include "asterisk/md5.h"
00048 #include "asterisk/linkedlists.h"
00049 #include "asterisk/chanvars.h"
00050 #include "asterisk/sched.h"
00051 #include "asterisk/io.h"
00052 #include "asterisk/utils.h"
00053 #include "asterisk/astdb.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/astobj2.h"
00056 
00057 #define MODE_MATCH      0
00058 #define MODE_MATCHMORE  1
00059 #define MODE_CANMATCH   2
00060 
00061 #define EXT_DATA_SIZE 256
00062 
00063 enum option_flags {
00064    OPTION_PATTERNS_DISABLED = (1 << 0),
00065 };
00066 
00067 AST_APP_OPTIONS(switch_opts, {
00068    AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED),
00069 });
00070 
00071 struct cache_entry {
00072    struct timeval when;
00073    struct ast_variable *var;
00074    int priority;
00075    char *context;
00076    char exten[2];
00077 };
00078 
00079 struct ao2_container *cache;
00080 pthread_t cleanup_thread = 0;
00081 
00082 static int cache_hash(const void *obj, const int flags)
00083 {
00084    const struct cache_entry *e = obj;
00085    return ast_str_case_hash(e->exten) + e->priority;
00086 }
00087 
00088 static int cache_cmp(void *obj, void *arg, int flags)
00089 {
00090    struct cache_entry *e = obj, *f = arg;
00091    return e->priority != f->priority ? 0 :
00092       strcmp(e->exten, f->exten) ? 0 :
00093       strcmp(e->context, f->context) ? 0 :
00094       CMP_MATCH;
00095 }
00096 
00097 static struct ast_variable *dup_vars(struct ast_variable *v)
00098 {
00099    struct ast_variable *new, *list = NULL;
00100    for (; v; v = v->next) {
00101       if (!(new = ast_variable_new(v->name, v->value, v->file))) {
00102          ast_variables_destroy(list);
00103          return NULL;
00104       }
00105       /* Reversed list in cache, but when we duplicate out of the cache,
00106        * it's back to correct order. */
00107       new->next = list;
00108       list = new;
00109    }
00110    return list;
00111 }
00112 
00113 static void free_entry(void *obj)
00114 {
00115    struct cache_entry *e = obj;
00116    ast_variables_destroy(e->var);
00117 }
00118 
00119 static int purge_old_fn(void *obj, void *arg, int flags)
00120 {
00121    struct cache_entry *e = obj;
00122    struct timeval *now = arg;
00123    return ast_tvdiff_ms(*now, e->when) >= 1000 ? CMP_MATCH : 0;
00124 }
00125 
00126 static void *cleanup(void *unused)
00127 {
00128    struct timespec forever = { 999999999, 0 }, one_second = { 1, 0 };
00129    struct timeval now;
00130 
00131    for (;;) {
00132       pthread_testcancel();
00133       if (ao2_container_count(cache) == 0) {
00134          nanosleep(&forever, NULL);
00135       }
00136       pthread_testcancel();
00137       now = ast_tvnow();
00138       ao2_callback(cache, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, purge_old_fn, &now);
00139       pthread_testcancel();
00140       nanosleep(&one_second, NULL);
00141    }
00142 
00143    return NULL;
00144 }
00145 
00146 
00147 /* Realtime switch looks up extensions in the supplied realtime table.
00148 
00149    [context@][realtimetable][/options]
00150 
00151    If the realtimetable is omitted it is assumed to be "extensions".  If no context is 
00152    specified the context is assumed to be whatever is the container.
00153 
00154    The realtime table should have entries for context,exten,priority,app,args
00155    
00156    The realtime table currently does not support callerid fields.
00157 
00158 */
00159 
00160 
00161 static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode, struct ast_flags flags)
00162 {
00163    struct ast_variable *var;
00164    struct ast_config *cfg;
00165    char pri[20];
00166    char *ematch;
00167    char rexten[AST_MAX_EXTENSION + 20]="";
00168    int match;
00169    /* Optimization: since we don't support hints in realtime, it's silly to
00170     * query for a hint here, since we won't actually do anything with it.
00171     * This just wastes CPU time and resources. */
00172    if (priority < 0) {
00173       return NULL;
00174    }
00175    snprintf(pri, sizeof(pri), "%d", priority);
00176    switch(mode) {
00177    case MODE_MATCHMORE:
00178       ematch = "exten LIKE";
00179       snprintf(rexten, sizeof(rexten), "%s_%%", exten);
00180       break;
00181    case MODE_CANMATCH:
00182       ematch = "exten LIKE";
00183       snprintf(rexten, sizeof(rexten), "%s%%", exten);
00184       break;
00185    case MODE_MATCH:
00186    default:
00187       ematch = "exten";
00188       ast_copy_string(rexten, exten, sizeof(rexten));
00189    }
00190    var = ast_load_realtime(table, ematch, rexten, "context", context, "priority", pri, SENTINEL);
00191    if (!var && !ast_test_flag(&flags, OPTION_PATTERNS_DISABLED)) {
00192       cfg = ast_load_realtime_multientry(table, "exten LIKE", "\\_%", "context", context, "priority", pri, SENTINEL);   
00193       if (cfg) {
00194          char *cat = ast_category_browse(cfg, NULL);
00195 
00196          while(cat) {
00197             switch(mode) {
00198             case MODE_MATCHMORE:
00199                match = ast_extension_close(cat, exten, 1);
00200                break;
00201             case MODE_CANMATCH:
00202                match = ast_extension_close(cat, exten, 0);
00203                break;
00204             case MODE_MATCH:
00205             default:
00206                match = ast_extension_match(cat, exten);
00207             }
00208             if (match) {
00209                var = ast_category_detach_variables(ast_category_get(cfg, cat));
00210                break;
00211             }
00212             cat = ast_category_browse(cfg, cat);
00213          }
00214          ast_config_destroy(cfg);
00215       }
00216    }
00217    return var;
00218 }
00219 
00220 static struct ast_variable *realtime_common(const char *context, const char *exten, int priority, const char *data, int mode)
00221 {
00222    const char *ctx = NULL;
00223    char *table;
00224    struct ast_variable *var=NULL;
00225    struct ast_flags flags = { 0, };
00226    struct cache_entry *ce;
00227    struct {
00228       struct cache_entry ce;
00229       char exten[AST_MAX_EXTENSION];
00230    } cache_search = { { .priority = priority, .context = (char *) context }, };
00231    char *buf = ast_strdupa(data);
00232    if (buf) {
00233       /* "Realtime" prefix is stripped off in the parent engine.  The
00234        * remaining string is: [[context@]table][/opts] */
00235       char *opts = strchr(buf, '/');
00236       if (opts)
00237          *opts++ = '\0';
00238       table = strchr(buf, '@');
00239       if (table) {
00240          *table++ = '\0';
00241          ctx = buf;
00242       }
00243       ctx = S_OR(ctx, context);
00244       table = S_OR(table, "extensions");
00245       if (!ast_strlen_zero(opts)) {
00246          ast_app_parse_options(switch_opts, &flags, NULL, opts);
00247       }
00248       ast_copy_string(cache_search.exten, exten, sizeof(cache_search.exten));
00249       if (mode == MODE_MATCH && (ce = ao2_find(cache, &cache_search, OBJ_POINTER))) {
00250          var = dup_vars(ce->var);
00251          ao2_ref(ce, -1);
00252       } else {
00253          var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
00254          do {
00255             struct ast_variable *new;
00256             /* Only cache matches */
00257             if (mode != MODE_MATCH) {
00258                break;
00259             }
00260             if (!(new = dup_vars(var))) {
00261                break;
00262             }
00263             if (!(ce = ao2_alloc(sizeof(*ce) + strlen(exten) + strlen(context), free_entry))) {
00264                ast_variables_destroy(new);
00265                break;
00266             }
00267             ce->context = ce->exten + strlen(exten) + 1;
00268             strcpy(ce->exten, exten); /* SAFE */
00269             strcpy(ce->context, context); /* SAFE */
00270             ce->priority = priority;
00271             ce->var = new;
00272             ce->when = ast_tvnow();
00273             ao2_link(cache, ce);
00274             pthread_kill(cleanup_thread, SIGURG);
00275             ao2_ref(ce, -1);
00276          } while (0);
00277       }
00278    }
00279    return var;
00280 }
00281 
00282 static int realtime_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00283 {
00284    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
00285    if (var) {
00286       ast_variables_destroy(var);
00287       return 1;
00288    }
00289    return 0;
00290 }
00291 
00292 static int realtime_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00293 {
00294    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_CANMATCH);
00295    if (var) {
00296       ast_variables_destroy(var);
00297       return 1;
00298    }
00299    return 0;
00300 }
00301 
00302 static int realtime_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00303 {
00304    int res = -1;
00305    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCH);
00306 
00307    if (var) {
00308       char *tmp="";
00309       char *app = NULL;
00310       struct ast_variable *v;
00311 
00312       for (v = var; v ; v = v->next) {
00313          if (!strcasecmp(v->name, "app"))
00314             app = ast_strdupa(v->value);
00315          else if (!strcasecmp(v->name, "appdata")) {
00316             if (ast_compat_pbx_realtime) {
00317                char *ptr;
00318                int in = 0;
00319                tmp = alloca(strlen(v->value) * 2 + 1);
00320                for (ptr = tmp; *v->value; v->value++) {
00321                   if (*v->value == ',') {
00322                      *ptr++ = '\\';
00323                      *ptr++ = ',';
00324                   } else if (*v->value == '|' && !in) {
00325                      *ptr++ = ',';
00326                   } else {
00327                      *ptr++ = *v->value;
00328                   }
00329 
00330                   /* Don't escape '|', meaning 'or', inside expressions ($[ ]) */
00331                   if (v->value[0] == '[' && v->value[-1] == '$') {
00332                      in++;
00333                   } else if (v->value[0] == ']' && in) {
00334                      in--;
00335                   }
00336                }
00337                *ptr = '\0';
00338             } else {
00339                tmp = ast_strdupa(v->value);
00340             }
00341          }
00342       }
00343       ast_variables_destroy(var);
00344       if (!ast_strlen_zero(app)) {
00345          struct ast_app *a = pbx_findapp(app);
00346          if (a) {
00347             char appdata[512];
00348             char tmp1[80];
00349             char tmp2[80];
00350             char tmp3[EXT_DATA_SIZE];
00351 
00352             appdata[0] = 0; /* just in case the substitute var func isn't called */
00353             if(!ast_strlen_zero(tmp))
00354                pbx_substitute_variables_helper(chan, tmp, appdata, sizeof(appdata) - 1);
00355             ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\")\n",
00356                   chan->exten, chan->context, chan->priority,
00357                    term_color(tmp1, app, COLOR_BRCYAN, 0, sizeof(tmp1)),
00358                    term_color(tmp2, chan->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
00359                    term_color(tmp3, S_OR(appdata, ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)));
00360             manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
00361                        "Channel: %s\r\n"
00362                        "Context: %s\r\n"
00363                        "Extension: %s\r\n"
00364                        "Priority: %d\r\n"
00365                        "Application: %s\r\n"
00366                        "AppData: %s\r\n"
00367                        "Uniqueid: %s\r\n",
00368                        chan->name, chan->context, chan->exten, chan->priority, app, !ast_strlen_zero(appdata) ? appdata : "(NULL)", chan->uniqueid);
00369             
00370             res = pbx_exec(chan, a, appdata);
00371          } else
00372             ast_log(LOG_NOTICE, "No such application '%s' for extension '%s' in context '%s'\n", app, exten, context);
00373       } else {
00374          ast_log(LOG_WARNING, "No application specified for realtime extension '%s' in context '%s'\n", exten, context);
00375       }
00376    }
00377    return res;
00378 }
00379 
00380 static int realtime_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
00381 {
00382    struct ast_variable *var = realtime_common(context, exten, priority, data, MODE_MATCHMORE);
00383    if (var) {
00384       ast_variables_destroy(var);
00385       return 1;
00386    }
00387    return 0;
00388 }
00389 
00390 static struct ast_switch realtime_switch =
00391 {
00392         name:                   "Realtime",
00393         description:       "Realtime Dialplan Switch",
00394         exists:                 realtime_exists,
00395         canmatch:               realtime_canmatch,
00396         exec:                   realtime_exec,
00397         matchmore:              realtime_matchmore,
00398 };
00399 
00400 static int unload_module(void)
00401 {
00402    ast_unregister_switch(&realtime_switch);
00403    pthread_cancel(cleanup_thread);
00404    pthread_kill(cleanup_thread, SIGURG);
00405    pthread_join(cleanup_thread, NULL);
00406    /* Destroy all remaining entries */
00407    ao2_ref(cache, -1);
00408    return 0;
00409 }
00410 
00411 static int load_module(void)
00412 {
00413    if (!(cache = ao2_container_alloc(573, cache_hash, cache_cmp))) {
00414       return AST_MODULE_LOAD_FAILURE;
00415    }
00416 
00417    if (ast_pthread_create(&cleanup_thread, NULL, cleanup, NULL)) {
00418       return AST_MODULE_LOAD_FAILURE;
00419    }
00420 
00421    if (ast_register_switch(&realtime_switch))
00422       return AST_MODULE_LOAD_FAILURE;
00423    return AST_MODULE_LOAD_SUCCESS;
00424 }
00425 
00426 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Switch");