Fri Aug 24 02:22:09 2007

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

Generated on Fri Aug 24 02:22:09 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1