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

Generated on Thu Jul 9 13:40:18 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7