Mon Jun 27 16:50:55 2011

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

Generated on Mon Jun 27 16:50:55 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7