Fri Jul 24 00:40:38 2009

Asterisk developer's documentation


app_macro.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 Dial plan macro Implementation
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  * 
00025  * \ingroup applications
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 186806 $")
00031 
00032 #include "asterisk/file.h"
00033 #include "asterisk/channel.h"
00034 #include "asterisk/pbx.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/config.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/lock.h"
00039 
00040 #define MAX_ARGS 80
00041 
00042 /* special result value used to force macro exit */
00043 #define MACRO_EXIT_RESULT 1024
00044 
00045 static char *descrip =
00046 "  Macro(macroname,arg1,arg2...): Executes a macro using the context\n"
00047 "'macro-<macroname>', jumping to the 's' extension of that context and\n"
00048 "executing each step, then returning when the steps end. \n"
00049 "The calling extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
00050 "${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively.  Arguments become\n"
00051 "${ARG1}, ${ARG2}, etc in the macro context.\n"
00052 "If you Goto out of the Macro context, the Macro will terminate and control\n"
00053 "will be returned at the location of the Goto.\n"
00054 "If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
00055 "at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n"
00056 "WARNING: Because of the way Macro is implemented (it executes the priorities\n"
00057 "         contained within it via sub-engine), and a fixed per-thread memory\n"
00058 "         stack allowance, macros are limited to 7 levels of nesting (macro\n"
00059 "         calling macro calling macro, etc.); It may be possible that\n"
00060 "         stack-intensive applications in deeply nested macros could cause\n"
00061 "         Asterisk to crash earlier than this limit. It is advised that if you\n"
00062 "         need to deeply nest macro calls, that you use the Gosub application\n"
00063 "         (now allows arguments like a Macro) with explict Return() calls\n"
00064 "         instead.\n";
00065 
00066 static char *if_descrip =
00067 "  MacroIf(<expr>?macroname_a[,arg1][:macroname_b[,arg1]])\n"
00068 "Executes macro defined in <macroname_a> if <expr> is true\n"
00069 "(otherwise <macroname_b> if provided)\n"
00070 "Arguments and return values as in application Macro()\n";
00071 
00072 static char *exclusive_descrip =
00073 "  MacroExclusive(macroname,arg1,arg2...):\n"
00074 "Executes macro defined in the context 'macro-macroname'\n"
00075 "Only one call at a time may run the macro.\n"
00076 "(we'll wait if another call is busy executing in the Macro)\n"
00077 "Arguments and return values as in application Macro()\n";
00078 
00079 static char *exit_descrip =
00080 "  MacroExit():\n"
00081 "Causes the currently running macro to exit as if it had\n"
00082 "ended normally by running out of priorities to execute.\n"
00083 "If used outside a macro, will likely cause unexpected\n"
00084 "behavior.\n";
00085 
00086 static char *app = "Macro";
00087 static char *if_app = "MacroIf";
00088 static char *exclusive_app = "MacroExclusive";
00089 static char *exit_app = "MacroExit";
00090 
00091 static char *synopsis = "Macro Implementation";
00092 static char *if_synopsis = "Conditional Macro Implementation";
00093 static char *exclusive_synopsis = "Exclusive Macro Implementation";
00094 static char *exit_synopsis = "Exit From Macro";
00095 
00096 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
00097 
00098 struct ast_datastore_info macro_ds_info = {
00099    .type = "MACRO",
00100    .chan_fixup = macro_fixup,
00101 };
00102 
00103 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00104 {
00105    int i;
00106    char varname[10];
00107    pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
00108    pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
00109    pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
00110    pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
00111    pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
00112    for (i = 1; i < 100; i++) {
00113       snprintf(varname, sizeof(varname), "ARG%d", i);
00114       while (pbx_builtin_getvar_helper(new_chan, varname)) {
00115          /* Kill all levels of arguments */
00116          pbx_builtin_setvar_helper(new_chan, varname, NULL);
00117       }
00118    }
00119 }
00120 
00121 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
00122 {
00123    struct ast_exten *e;
00124    struct ast_include *i;
00125    struct ast_context *c2;
00126 
00127    for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
00128       if (ast_extension_match(ast_get_extension_name(e), exten)) {
00129          int needmatch = ast_get_extension_matchcid(e);
00130          if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
00131             (!needmatch)) {
00132             /* This is the matching extension we want */
00133             struct ast_exten *p;
00134             for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
00135                if (priority != ast_get_extension_priority(p))
00136                   continue;
00137                return p;
00138             }
00139          }
00140       }
00141    }
00142 
00143    /* No match; run through includes */
00144    for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
00145       for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
00146          if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
00147             e = find_matching_priority(c2, exten, priority, callerid);
00148             if (e)
00149                return e;
00150          }
00151       }
00152    }
00153    return NULL;
00154 }
00155 
00156 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
00157 {
00158    const char *s;
00159    char *tmp;
00160    char *cur, *rest;
00161    char *macro;
00162    char fullmacro[80];
00163    char varname[80];
00164    char runningapp[80], runningdata[1024];
00165    char *oldargs[MAX_ARGS + 1] = { NULL, };
00166    int argc, x;
00167    int res=0;
00168    char oldexten[256]="";
00169    int oldpriority, gosub_level = 0;
00170    char pc[80], depthc[12];
00171    char oldcontext[AST_MAX_CONTEXT] = "";
00172    const char *inhangupc;
00173    int offset, depth = 0, maxdepth = 7;
00174    int setmacrocontext=0;
00175    int autoloopflag, inhangup = 0;
00176   
00177    char *save_macro_exten;
00178    char *save_macro_context;
00179    char *save_macro_priority;
00180    char *save_macro_offset;
00181    struct ast_datastore *macro_store = ast_channel_datastore_find(chan, &macro_ds_info, NULL);
00182  
00183    if (ast_strlen_zero(data)) {
00184       ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
00185       return -1;
00186    }
00187 
00188    do {
00189       if (macro_store) {
00190          break;
00191       }
00192       if (!(macro_store = ast_datastore_alloc(&macro_ds_info, NULL))) {
00193          ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
00194          break;
00195       }
00196       /* Just the existence of this datastore is enough. */
00197       macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
00198       ast_channel_datastore_add(chan, macro_store);
00199    } while (0);
00200 
00201    /* does the user want a deeper rabbit hole? */
00202    ast_channel_lock(chan);
00203    if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
00204       sscanf(s, "%d", &maxdepth);
00205    }
00206    
00207    /* Count how many levels deep the rabbit hole goes */
00208    if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
00209       sscanf(s, "%d", &depth);
00210    }
00211    
00212    /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
00213    if (strcmp(chan->exten, "h") == 0)
00214       pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
00215    
00216    if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
00217       sscanf(inhangupc, "%d", &inhangup);
00218    }
00219    ast_channel_unlock(chan);
00220 
00221    if (depth >= maxdepth) {
00222       ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
00223       return 0;
00224    }
00225    snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00226    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00227 
00228    tmp = ast_strdupa(data);
00229    rest = tmp;
00230    macro = strsep(&rest, ",");
00231    if (ast_strlen_zero(macro)) {
00232       ast_log(LOG_WARNING, "Invalid macro name specified\n");
00233       return 0;
00234    }
00235 
00236    snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00237    if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
00238       if (!ast_context_find(fullmacro)) 
00239          ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00240       else
00241          ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00242       return 0;
00243    }
00244 
00245    /* If we are to run the macro exclusively, take the mutex */
00246    if (exclusive) {
00247       ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
00248       ast_autoservice_start(chan);
00249       if (ast_context_lockmacro(fullmacro)) {
00250          ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
00251          ast_autoservice_stop(chan);
00252          return 0;
00253       }
00254       ast_autoservice_stop(chan);
00255    }
00256    
00257    /* Save old info */
00258    oldpriority = chan->priority;
00259    ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00260    ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00261    if (ast_strlen_zero(chan->macrocontext)) {
00262       ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00263       ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00264       chan->macropriority = chan->priority;
00265       setmacrocontext=1;
00266    }
00267    argc = 1;
00268    /* Save old macro variables */
00269    save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
00270    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00271 
00272    save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
00273    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00274 
00275    save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
00276    snprintf(pc, sizeof(pc), "%d", oldpriority);
00277    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00278   
00279    save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
00280    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00281 
00282    /* Setup environment for new run */
00283    chan->exten[0] = 's';
00284    chan->exten[1] = '\0';
00285    ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00286    chan->priority = 1;
00287 
00288    ast_channel_lock(chan);
00289    while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
00290       const char *argp;
00291       /* Save copy of old arguments if we're overwriting some, otherwise
00292          let them pass through to the other macro */
00293       snprintf(varname, sizeof(varname), "ARG%d", argc);
00294       if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
00295          oldargs[argc] = ast_strdup(argp);
00296       }
00297       pbx_builtin_setvar_helper(chan, varname, cur);
00298       argc++;
00299    }
00300    ast_channel_unlock(chan);
00301    autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00302    ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00303    while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
00304       struct ast_context *c;
00305       struct ast_exten *e;
00306       int foundx;
00307       runningapp[0] = '\0';
00308       runningdata[0] = '\0';
00309 
00310       /* What application will execute? */
00311       if (ast_rdlock_contexts()) {
00312          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00313       } else {
00314          for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00315             if (!strcmp(ast_get_context_name(c), chan->context)) {
00316                if (ast_rdlock_context(c)) {
00317                   ast_log(LOG_WARNING, "Unable to lock context?\n");
00318                } else {
00319                   e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
00320                   if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
00321                      ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00322                      ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00323                   }
00324                   ast_unlock_context(c);
00325                }
00326                break;
00327             }
00328          }
00329       }
00330       ast_unlock_contexts();
00331 
00332       /* Reset the macro depth, if it was changed in the last iteration */
00333       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00334 
00335       if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num, &foundx,1))) {
00336          /* Something bad happened, or a hangup has been requested. */
00337          if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00338             (res == '*') || (res == '#')) {
00339             /* Just return result as to the previous application as if it had been dialed */
00340             ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
00341             break;
00342          }
00343          switch(res) {
00344          case MACRO_EXIT_RESULT:
00345             res = 0;
00346             goto out;
00347          default:
00348             ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
00349             ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
00350             goto out;
00351          }
00352       }
00353 
00354       ast_debug(1, "Executed application: %s\n", runningapp);
00355 
00356       if (!strcasecmp(runningapp, "GOSUB")) {
00357          gosub_level++;
00358          ast_debug(1, "Incrementing gosub_level\n");
00359       } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00360          char tmp2[1024], *cond, *app_arg, *app2 = tmp2;
00361          pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00362          cond = strsep(&app2, "?");
00363          app_arg = strsep(&app2, ":");
00364          if (pbx_checkcondition(cond)) {
00365             if (!ast_strlen_zero(app_arg)) {
00366                gosub_level++;
00367                ast_debug(1, "Incrementing gosub_level\n");
00368             }
00369          } else {
00370             if (!ast_strlen_zero(app2)) {
00371                gosub_level++;
00372                ast_debug(1, "Incrementing gosub_level\n");
00373             }
00374          }
00375       } else if (!strcasecmp(runningapp, "RETURN")) {
00376          gosub_level--;
00377          ast_debug(1, "Decrementing gosub_level\n");
00378       } else if (!strcasecmp(runningapp, "STACKPOP")) {
00379          gosub_level--;
00380          ast_debug(1, "Decrementing gosub_level\n");
00381       } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00382          /* Must evaluate args to find actual app */
00383          char tmp2[1024], *tmp3 = NULL;
00384          pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00385          if (!strcasecmp(runningapp, "EXECIF")) {
00386             tmp3 = strchr(tmp2, '|');
00387             if (tmp3)
00388                *tmp3++ = '\0';
00389             if (!pbx_checkcondition(tmp2))
00390                tmp3 = NULL;
00391          } else
00392             tmp3 = tmp2;
00393 
00394          if (tmp3)
00395             ast_debug(1, "Last app: %s\n", tmp3);
00396 
00397          if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00398             gosub_level++;
00399             ast_debug(1, "Incrementing gosub_level\n");
00400          } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00401             gosub_level--;
00402             ast_debug(1, "Decrementing gosub_level\n");
00403          } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00404             gosub_level--;
00405             ast_debug(1, "Decrementing gosub_level\n");
00406          }
00407       }
00408 
00409       if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00410          ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00411          break;
00412       }
00413 
00414       /* don't stop executing extensions when we're in "h" */
00415       if (ast_check_hangup(chan) && !inhangup) {
00416          ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
00417          goto out;
00418       }
00419       chan->priority++;
00420    }
00421    out:
00422 
00423    /* Don't let the channel change now. */
00424    ast_channel_lock(chan);
00425 
00426    /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
00427    snprintf(depthc, sizeof(depthc), "%d", depth);
00428    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00429    ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00430 
00431    for (x = 1; x < argc; x++) {
00432       /* Restore old arguments and delete ours */
00433       snprintf(varname, sizeof(varname), "ARG%d", x);
00434       if (oldargs[x]) {
00435          pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00436          ast_free(oldargs[x]);
00437       } else {
00438          pbx_builtin_setvar_helper(chan, varname, NULL);
00439       }
00440    }
00441 
00442    /* Restore macro variables */
00443    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00444    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00445    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00446    if (save_macro_exten)
00447       ast_free(save_macro_exten);
00448    if (save_macro_context)
00449       ast_free(save_macro_context);
00450    if (save_macro_priority)
00451       ast_free(save_macro_priority);
00452 
00453    if (setmacrocontext) {
00454       chan->macrocontext[0] = '\0';
00455       chan->macroexten[0] = '\0';
00456       chan->macropriority = 0;
00457    }
00458 
00459    if (!strcasecmp(chan->context, fullmacro)) {
00460       /* If we're leaving the macro normally, restore original information */
00461       chan->priority = oldpriority;
00462       ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00463       if (!(ast_check_hangup(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
00464          /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
00465          const char *offsets;
00466          ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00467          if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00468             /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
00469                normally if there is any problem */
00470             if (sscanf(offsets, "%d", &offset) == 1) {
00471                if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
00472                   chan->priority += offset;
00473                }
00474             }
00475          }
00476       }
00477    }
00478 
00479    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00480    if (save_macro_offset)
00481       ast_free(save_macro_offset);
00482 
00483    /* Unlock the macro */
00484    if (exclusive) {
00485       ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
00486       if (ast_context_unlockmacro(fullmacro)) {
00487          ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00488          res = 0;
00489       }
00490    }
00491    ast_channel_unlock(chan);
00492 
00493    return res;
00494 }
00495 
00496 static int macro_exec(struct ast_channel *chan, void *data)
00497 {
00498    return _macro_exec(chan, data, 0);
00499 }
00500 
00501 static int macroexclusive_exec(struct ast_channel *chan, void *data)
00502 {
00503    return _macro_exec(chan, data, 1);
00504 }
00505 
00506 static int macroif_exec(struct ast_channel *chan, void *data) 
00507 {
00508    char *expr = NULL, *label_a = NULL, *label_b = NULL;
00509    int res = 0;
00510 
00511    if (!(expr = ast_strdupa(data)))
00512       return -1;
00513 
00514    if ((label_a = strchr(expr, '?'))) {
00515       *label_a = '\0';
00516       label_a++;
00517       if ((label_b = strchr(label_a, ':'))) {
00518          *label_b = '\0';
00519          label_b++;
00520       }
00521       if (pbx_checkcondition(expr))
00522          res = macro_exec(chan, label_a);
00523       else if (label_b) 
00524          res = macro_exec(chan, label_b);
00525    } else
00526       ast_log(LOG_WARNING, "Invalid Syntax.\n");
00527 
00528    return res;
00529 }
00530          
00531 static int macro_exit_exec(struct ast_channel *chan, void *data)
00532 {
00533    return MACRO_EXIT_RESULT;
00534 }
00535 
00536 static int unload_module(void)
00537 {
00538    int res;
00539 
00540    res = ast_unregister_application(if_app);
00541    res |= ast_unregister_application(exit_app);
00542    res |= ast_unregister_application(app);
00543    res |= ast_unregister_application(exclusive_app);
00544 
00545    return res;
00546 }
00547 
00548 static int load_module(void)
00549 {
00550    int res;
00551 
00552    res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
00553    res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
00554    res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
00555    res |= ast_register_application(app, macro_exec, synopsis, descrip);
00556 
00557    return res;
00558 }
00559 
00560 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");

Generated on Fri Jul 24 00:40:38 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7