00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 174885 $")
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
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 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
00107
00108 struct ast_datastore_info macro_ds_info = {
00109 .type = "MACRO",
00110 .chan_fixup = macro_fixup,
00111 };
00112
00113 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00114 {
00115 int i;
00116 char varname[10];
00117 pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
00118 pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
00119 pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
00120 pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
00121 pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
00122 for (i = 1; i < 100; i++) {
00123 snprintf(varname, sizeof(varname), "ARG%d", i);
00124 while (pbx_builtin_getvar_helper(new_chan, varname)) {
00125
00126 pbx_builtin_setvar_helper(new_chan, varname, NULL);
00127 }
00128 }
00129 }
00130
00131 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
00132 {
00133 struct ast_exten *e;
00134 struct ast_include *i;
00135 struct ast_context *c2;
00136
00137 for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
00138 if (ast_extension_match(ast_get_extension_name(e), exten)) {
00139 int needmatch = ast_get_extension_matchcid(e);
00140 if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
00141 (!needmatch)) {
00142
00143 struct ast_exten *p;
00144 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
00145 if (priority != ast_get_extension_priority(p))
00146 continue;
00147 return p;
00148 }
00149 }
00150 }
00151 }
00152
00153
00154 for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
00155 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
00156 if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
00157 e = find_matching_priority(c2, exten, priority, callerid);
00158 if (e)
00159 return e;
00160 }
00161 }
00162 }
00163 return NULL;
00164 }
00165
00166 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
00167 {
00168 const char *s;
00169 char *tmp;
00170 char *cur, *rest;
00171 char *macro;
00172 char fullmacro[80];
00173 char varname[80];
00174 char runningapp[80], runningdata[1024];
00175 char *oldargs[MAX_ARGS + 1] = { NULL, };
00176 int argc, x;
00177 int res=0;
00178 char oldexten[256]="";
00179 int oldpriority, gosub_level = 0;
00180 char pc[80], depthc[12];
00181 char oldcontext[AST_MAX_CONTEXT] = "";
00182 const char *inhangupc;
00183 int offset, depth = 0, maxdepth = 7;
00184 int setmacrocontext=0;
00185 int autoloopflag, inhangup = 0;
00186
00187 char *save_macro_exten;
00188 char *save_macro_context;
00189 char *save_macro_priority;
00190 char *save_macro_offset;
00191 struct ast_module_user *u;
00192 struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
00193
00194 if (ast_strlen_zero(data)) {
00195 ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
00196 return -1;
00197 }
00198
00199 u = ast_module_user_add(chan);
00200
00201 do {
00202 if (macro_store) {
00203 break;
00204 }
00205 if (!(macro_store = ast_channel_datastore_alloc(¯o_ds_info, NULL))) {
00206 ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
00207 break;
00208 }
00209
00210 macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
00211 ast_channel_datastore_add(chan, macro_store);
00212 } while (0);
00213
00214
00215 s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
00216 if (s)
00217 sscanf(s, "%d", &maxdepth);
00218
00219
00220 s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
00221 if (s)
00222 sscanf(s, "%d", &depth);
00223
00224 if (strcmp(chan->exten, "h") == 0)
00225 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
00226 inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP");
00227 if (!ast_strlen_zero(inhangupc))
00228 sscanf(inhangupc, "%d", &inhangup);
00229
00230 if (depth >= maxdepth) {
00231 ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
00232 ast_module_user_remove(u);
00233 return 0;
00234 }
00235 snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00236 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00237
00238 tmp = ast_strdupa(data);
00239 rest = tmp;
00240 macro = strsep(&rest, "|");
00241 if (ast_strlen_zero(macro)) {
00242 ast_log(LOG_WARNING, "Invalid macro name specified\n");
00243 ast_module_user_remove(u);
00244 return 0;
00245 }
00246
00247 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00248 if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
00249 if (!ast_context_find(fullmacro))
00250 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00251 else
00252 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00253 ast_module_user_remove(u);
00254 return 0;
00255 }
00256
00257
00258 if (exclusive) {
00259 ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
00260 ast_autoservice_start(chan);
00261 if (ast_context_lockmacro(fullmacro)) {
00262 ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
00263 ast_autoservice_stop(chan);
00264 ast_module_user_remove(u);
00265
00266 return 0;
00267 }
00268 ast_autoservice_stop(chan);
00269 }
00270
00271
00272 oldpriority = chan->priority;
00273 ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00274 ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00275 if (ast_strlen_zero(chan->macrocontext)) {
00276 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00277 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00278 chan->macropriority = chan->priority;
00279 setmacrocontext=1;
00280 }
00281 argc = 1;
00282
00283 save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
00284 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00285
00286 save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
00287 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00288
00289 save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
00290 snprintf(pc, sizeof(pc), "%d", oldpriority);
00291 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00292
00293 save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
00294 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00295
00296
00297 chan->exten[0] = 's';
00298 chan->exten[1] = '\0';
00299 ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00300 chan->priority = 1;
00301
00302 while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
00303 const char *s;
00304
00305
00306 snprintf(varname, sizeof(varname), "ARG%d", argc);
00307 s = pbx_builtin_getvar_helper(chan, varname);
00308 if (s)
00309 oldargs[argc] = ast_strdup(s);
00310 pbx_builtin_setvar_helper(chan, varname, cur);
00311 argc++;
00312 }
00313 autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00314 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00315 while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
00316 struct ast_context *c;
00317 struct ast_exten *e;
00318 runningapp[0] = '\0';
00319 runningdata[0] = '\0';
00320
00321
00322 if (ast_rdlock_contexts()) {
00323 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00324 } else {
00325 for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00326 if (!strcmp(ast_get_context_name(c), chan->context)) {
00327 if (ast_lock_context(c)) {
00328 ast_log(LOG_WARNING, "Unable to lock context?\n");
00329 } else {
00330 e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
00331 if (e) {
00332 ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00333 ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00334 }
00335 ast_unlock_context(c);
00336 }
00337 break;
00338 }
00339 }
00340 }
00341 ast_unlock_contexts();
00342
00343
00344 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00345
00346 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
00347
00348 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00349 (res == '*') || (res == '#')) {
00350
00351 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
00352 break;
00353 }
00354 switch(res) {
00355 case MACRO_EXIT_RESULT:
00356 res = 0;
00357 goto out;
00358 case AST_PBX_KEEPALIVE:
00359 if (option_debug)
00360 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);
00361 else if (option_verbose > 1)
00362 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);
00363 goto out;
00364 default:
00365 if (option_debug)
00366 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);
00367 else if (option_verbose > 1)
00368 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);
00369 goto out;
00370 }
00371 }
00372
00373 ast_log(LOG_DEBUG, "Executed application: %s\n", runningapp);
00374
00375 if (!strcasecmp(runningapp, "GOSUB")) {
00376 gosub_level++;
00377 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00378 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00379 char tmp2[1024] = "", *cond, *app, *app2 = tmp2;
00380 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00381 cond = strsep(&app2, "?");
00382 app = strsep(&app2, ":");
00383 if (pbx_checkcondition(cond)) {
00384 if (!ast_strlen_zero(app)) {
00385 gosub_level++;
00386 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00387 }
00388 } else {
00389 if (!ast_strlen_zero(app2)) {
00390 gosub_level++;
00391 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00392 }
00393 }
00394 } else if (!strcasecmp(runningapp, "RETURN")) {
00395 gosub_level--;
00396 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00397 } else if (!strcasecmp(runningapp, "STACKPOP")) {
00398 gosub_level--;
00399 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00400 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00401
00402 char tmp2[1024] = "", *tmp3 = NULL;
00403 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00404 if (!strcasecmp(runningapp, "EXECIF")) {
00405 tmp3 = strchr(tmp2, '|');
00406 if (tmp3)
00407 *tmp3++ = '\0';
00408 if (!pbx_checkcondition(tmp2))
00409 tmp3 = NULL;
00410 } else
00411 tmp3 = tmp2;
00412
00413 if (tmp3)
00414 ast_log(LOG_DEBUG, "Last app: %s\n", tmp3);
00415
00416 if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00417 gosub_level++;
00418 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00419 } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00420 gosub_level--;
00421 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00422 } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00423 gosub_level--;
00424 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00425 }
00426 }
00427
00428 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00429 if (option_verbose > 1)
00430 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00431 break;
00432 }
00433
00434
00435 if (chan->_softhangup && !inhangup) {
00436 ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
00437 chan->exten, chan->macroexten, chan->priority);
00438 goto out;
00439 }
00440 chan->priority++;
00441 }
00442 out:
00443
00444
00445 ast_channel_lock(chan);
00446
00447
00448 snprintf(depthc, sizeof(depthc), "%d", depth);
00449 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00450 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00451
00452 for (x = 1; x < argc; x++) {
00453
00454 snprintf(varname, sizeof(varname), "ARG%d", x);
00455 if (oldargs[x]) {
00456 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00457 free(oldargs[x]);
00458 } else {
00459 pbx_builtin_setvar_helper(chan, varname, NULL);
00460 }
00461 }
00462
00463
00464 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00465 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00466 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00467 if (save_macro_exten)
00468 free(save_macro_exten);
00469 if (save_macro_context)
00470 free(save_macro_context);
00471 if (save_macro_priority)
00472 free(save_macro_priority);
00473
00474 if (setmacrocontext) {
00475 chan->macrocontext[0] = '\0';
00476 chan->macroexten[0] = '\0';
00477 chan->macropriority = 0;
00478 }
00479
00480
00481
00482
00483
00484
00485
00486
00487 if (ast_check_hangup(chan) || res < 0) {
00488
00489
00490
00491 const char *emc = pbx_builtin_getvar_helper(chan, "EXIT_MACRO_CONTEXT");
00492 if (!emc) {
00493 pbx_builtin_setvar_helper(chan, "EXIT_MACRO_CONTEXT", fullmacro);
00494 }
00495 }
00496
00497 if (!strcasecmp(chan->context, fullmacro)) {
00498
00499 chan->priority = oldpriority;
00500 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00501 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
00502
00503 const char *offsets;
00504 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00505 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00506
00507
00508 if (sscanf(offsets, "%d", &offset) == 1) {
00509 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
00510 chan->priority += offset;
00511 }
00512 }
00513 }
00514 }
00515 }
00516
00517 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00518 if (save_macro_offset)
00519 free(save_macro_offset);
00520
00521
00522 if (exclusive) {
00523 ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
00524 if (ast_context_unlockmacro(fullmacro)) {
00525 ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00526 res = 0;
00527 }
00528 }
00529 ast_channel_unlock(chan);
00530
00531 ast_module_user_remove(u);
00532
00533 return res;
00534 }
00535
00536 static int macro_exec(struct ast_channel *chan, void *data)
00537 {
00538 return _macro_exec(chan, data, 0);
00539 }
00540
00541 static int macroexclusive_exec(struct ast_channel *chan, void *data)
00542 {
00543 return _macro_exec(chan, data, 1);
00544 }
00545
00546 static int macroif_exec(struct ast_channel *chan, void *data)
00547 {
00548 char *expr = NULL, *label_a = NULL, *label_b = NULL;
00549 int res = 0;
00550 struct ast_module_user *u;
00551
00552 u = ast_module_user_add(chan);
00553
00554 if (!(expr = ast_strdupa(data))) {
00555 ast_module_user_remove(u);
00556 return -1;
00557 }
00558
00559 if ((label_a = strchr(expr, '?'))) {
00560 *label_a = '\0';
00561 label_a++;
00562 if ((label_b = strchr(label_a, ':'))) {
00563 *label_b = '\0';
00564 label_b++;
00565 }
00566 if (pbx_checkcondition(expr))
00567 res = macro_exec(chan, label_a);
00568 else if (label_b)
00569 res = macro_exec(chan, label_b);
00570 } else
00571 ast_log(LOG_WARNING, "Invalid Syntax.\n");
00572
00573 ast_module_user_remove(u);
00574
00575 return res;
00576 }
00577
00578 static int macro_exit_exec(struct ast_channel *chan, void *data)
00579 {
00580 return MACRO_EXIT_RESULT;
00581 }
00582
00583 static int unload_module(void)
00584 {
00585 int res;
00586
00587 res = ast_unregister_application(if_app);
00588 res |= ast_unregister_application(exit_app);
00589 res |= ast_unregister_application(app);
00590 res |= ast_unregister_application(exclusive_app);
00591
00592 ast_module_user_hangup_all();
00593
00594 return res;
00595 }
00596
00597 static int load_module(void)
00598 {
00599 int res;
00600
00601 res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
00602 res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
00603 res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
00604 res |= ast_register_application(app, macro_exec, synopsis, descrip);
00605
00606 return res;
00607 }
00608
00609 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");