Sun Jun 12 16:37:43 2011

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

Generated on Sun Jun 12 16:37:43 2011 for Asterisk - the Open Source PBX by  doxygen 1.5.6