Asterisk.org: Developer Documentation (28 Oct 2009)

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