Mon May 14 04:42:52 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 
00283       /* What application will execute? */
00284       if (ast_lock_contexts()) {
00285          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00286          e = NULL;
00287       } else {
00288          for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00289             if (!strcmp(ast_get_context_name(c), chan->context)) {
00290                if (ast_lock_context(c)) {
00291                   ast_log(LOG_WARNING, "Unable to lock context?\n");
00292                   runningapp[0] = '\0';
00293                   runningdata[0] = '\0';
00294                } else {
00295                   e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
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                   ast_unlock_context(c);
00299                }
00300                break;
00301             }
00302          }
00303       }
00304       ast_unlock_contexts();
00305 
00306       /* Reset the macro depth, if it was changed in the last iteration */
00307       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00308 
00309       if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
00310          /* Something bad happened, or a hangup has been requested. */
00311          if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00312             (res == '*') || (res == '#')) {
00313             /* Just return result as to the previous application as if it had been dialed */
00314             ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
00315             break;
00316          }
00317          switch(res) {
00318          case MACRO_EXIT_RESULT:
00319             res = 0;
00320             goto out;
00321          case AST_PBX_KEEPALIVE:
00322             if (option_debug)
00323                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);
00324             else if (option_verbose > 1)
00325                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);
00326             goto out;
00327             break;
00328          default:
00329             if (option_debug)
00330                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);
00331             else if (option_verbose > 1)
00332                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);
00333             dead = 1;
00334             goto out;
00335          }
00336       }
00337 
00338       ast_log(LOG_DEBUG, "Executed application: %s\n", runningapp);
00339 
00340       if (!strcasecmp(runningapp, "GOSUB")) {
00341          gosub_level++;
00342          ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00343       } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00344          char tmp2[1024] = "", *cond, *app, *app2 = tmp2;
00345          pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00346          cond = strsep(&app2, "?");
00347          app = strsep(&app2, ":");
00348          if (pbx_checkcondition(cond)) {
00349             if (!ast_strlen_zero(app)) {
00350                gosub_level++;
00351                ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00352             }
00353          } else {
00354             if (!ast_strlen_zero(app2)) {
00355                gosub_level++;
00356                ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00357             }
00358          }
00359       } else if (!strcasecmp(runningapp, "RETURN")) {
00360          gosub_level--;
00361          ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00362       } else if (!strcasecmp(runningapp, "STACKPOP")) {
00363          gosub_level--;
00364          ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00365       } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00366          /* Must evaluate args to find actual app */
00367          char tmp2[1024] = "", *tmp3 = NULL;
00368          pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00369          if (!strcasecmp(runningapp, "EXECIF")) {
00370             tmp3 = strchr(tmp2, '|');
00371             if (tmp3)
00372                *tmp3++ = '\0';
00373             if (!pbx_checkcondition(tmp2))
00374                tmp3 = NULL;
00375          } else
00376             tmp3 = tmp2;
00377 
00378          if (tmp3)
00379             ast_log(LOG_DEBUG, "Last app: %s\n", tmp3);
00380 
00381          if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00382             gosub_level++;
00383             ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00384          } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00385             gosub_level--;
00386             ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00387          } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00388             gosub_level--;
00389             ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00390          }
00391       }
00392 
00393       if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00394          if (option_verbose > 1)
00395             ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00396          break;
00397       }
00398 
00399       /* don't stop executing extensions when we're in "h" */
00400       if (chan->_softhangup && !inhangup) {
00401          ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
00402             chan->exten, chan->macroexten, chan->priority);
00403          goto out;
00404       }
00405       chan->priority++;
00406    }
00407    out:
00408    /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
00409    snprintf(depthc, sizeof(depthc), "%d", depth);
00410    if (!dead) {
00411       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00412       ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00413    }
00414 
00415    for (x = 1; x < argc; x++) {
00416       /* Restore old arguments and delete ours */
00417       snprintf(varname, sizeof(varname), "ARG%d", x);
00418       if (oldargs[x]) {
00419          if (!dead)
00420             pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00421          free(oldargs[x]);
00422       } else if (!dead) {
00423          pbx_builtin_setvar_helper(chan, varname, NULL);
00424       }
00425    }
00426 
00427    /* Restore macro variables */
00428    if (!dead) {
00429       pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00430       pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00431       pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00432    }
00433    if (save_macro_exten)
00434       free(save_macro_exten);
00435    if (save_macro_context)
00436       free(save_macro_context);
00437    if (save_macro_priority)
00438       free(save_macro_priority);
00439 
00440    if (!dead && setmacrocontext) {
00441       chan->macrocontext[0] = '\0';
00442       chan->macroexten[0] = '\0';
00443       chan->macropriority = 0;
00444    }
00445 
00446    if (!dead && !strcasecmp(chan->context, fullmacro)) {
00447       /* If we're leaving the macro normally, restore original information */
00448       chan->priority = oldpriority;
00449       ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00450       if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
00451          /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
00452          const char *offsets;
00453          ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00454          if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00455             /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
00456                normally if there is any problem */
00457             if (sscanf(offsets, "%d", &offset) == 1) {
00458                if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
00459                   chan->priority += offset;
00460                }
00461             }
00462          }
00463       }
00464    }
00465 
00466    if (!dead)
00467       pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00468    if (save_macro_offset)
00469       free(save_macro_offset);
00470 
00471    /* Unlock the macro */
00472    if (exclusive) {
00473       ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
00474       if (ast_context_unlockmacro(fullmacro)) {
00475          ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00476          res = 0;
00477       }
00478    }
00479    
00480    ast_module_user_remove(u);
00481 
00482    return res;
00483 }
00484 
00485 static int macro_exec(struct ast_channel *chan, void *data)
00486 {
00487    return _macro_exec(chan, data, 0);
00488 }
00489 
00490 static int macroexclusive_exec(struct ast_channel *chan, void *data)
00491 {
00492    return _macro_exec(chan, data, 1);
00493 }
00494 
00495 static int macroif_exec(struct ast_channel *chan, void *data) 
00496 {
00497    char *expr = NULL, *label_a = NULL, *label_b = NULL;
00498    int res = 0;
00499    struct ast_module_user *u;
00500 
00501    u = ast_module_user_add(chan);
00502 
00503    if (!(expr = ast_strdupa(data))) {
00504       ast_module_user_remove(u);
00505       return -1;
00506    }
00507 
00508    if ((label_a = strchr(expr, '?'))) {
00509       *label_a = '\0';
00510       label_a++;
00511       if ((label_b = strchr(label_a, ':'))) {
00512          *label_b = '\0';
00513          label_b++;
00514       }
00515       if (pbx_checkcondition(expr))
00516          macro_exec(chan, label_a);
00517       else if (label_b) 
00518          macro_exec(chan, label_b);
00519    } else
00520       ast_log(LOG_WARNING, "Invalid Syntax.\n");
00521 
00522    ast_module_user_remove(u);
00523 
00524    return res;
00525 }
00526          
00527 static int macro_exit_exec(struct ast_channel *chan, void *data)
00528 {
00529    return MACRO_EXIT_RESULT;
00530 }
00531 
00532 static int unload_module(void)
00533 {
00534    int res;
00535 
00536    res = ast_unregister_application(if_app);
00537    res |= ast_unregister_application(exit_app);
00538    res |= ast_unregister_application(app);
00539    res |= ast_unregister_application(exclusive_app);
00540 
00541    ast_module_user_hangup_all();
00542 
00543    return res;
00544 }
00545 
00546 static int load_module(void)
00547 {
00548    int res;
00549 
00550    res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
00551    res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
00552    res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
00553    res |= ast_register_application(app, macro_exec, synopsis, descrip);
00554 
00555    return res;
00556 }
00557 
00558 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");

Generated on Mon May 14 04:42:52 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1