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: 295790 $")
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 "WARNING: Because of the way Macro is implemented (it executes the priorities\n"
00065 " contained within it via sub-engine), and a fixed per-thread\n"
00066 " memory stack allowance, macros are limited to 7 levels\n"
00067 " of nesting (macro calling macro calling macro, etc.); It\n"
00068 " may be possible that stack-intensive applications in deeply nested\n"
00069 " macros could cause asterisk to crash earlier than this limit.\n"
00070 "NOTE: a bug existed in earlier versions of Asterisk that caused Macro not\n"
00071 "to reset its context and extension correctly upon exit. This meant that\n"
00072 "the 'h' extension within a Macro sometimes would execute, when the dialplan\n"
00073 "exited while that Macro was running. However, since this bug has been in\n"
00074 "Asterisk for so long, users started to depend upon this behavior. Therefore,\n"
00075 "when a channel hangs up when in the midst of executing a Macro, the macro\n"
00076 "context will first be checked for an 'h' extension, followed by the main\n"
00077 "context from which the Macro was originally called. This behavior in 1.4\n"
00078 "exists only for compatibility with earlier versions. You are strongly\n"
00079 "encouraged to make use of the 'h' extension only in the context from which\n"
00080 "Macro was originally called.\n";
00081
00082 static char *if_descrip =
00083 " MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
00084 "Executes macro defined in <macroname_a> if <expr> is true\n"
00085 "(otherwise <macroname_b> if provided)\n"
00086 "Arguments and return values as in application macro()\n";
00087
00088 static char *exclusive_descrip =
00089 " MacroExclusive(macroname|arg1|arg2...):\n"
00090 "Executes macro defined in the context 'macro-macroname'\n"
00091 "Only one call at a time may run the macro.\n"
00092 "(we'll wait if another call is busy executing in the Macro)\n"
00093 "Arguments and return values as in application Macro()\n";
00094
00095 static char *exit_descrip =
00096 " MacroExit():\n"
00097 "Causes the currently running macro to exit as if it had\n"
00098 "ended normally by running out of priorities to execute.\n"
00099 "If used outside a macro, will likely cause unexpected\n"
00100 "behavior.\n";
00101
00102 static char *app = "Macro";
00103 static char *if_app = "MacroIf";
00104 static char *exclusive_app = "MacroExclusive";
00105 static char *exit_app = "MacroExit";
00106
00107 static char *synopsis = "Macro Implementation";
00108 static char *if_synopsis = "Conditional Macro Implementation";
00109 static char *exclusive_synopsis = "Exclusive Macro Implementation";
00110 static char *exit_synopsis = "Exit From Macro";
00111
00112 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
00113
00114 struct ast_datastore_info macro_ds_info = {
00115 .type = "MACRO",
00116 .chan_fixup = macro_fixup,
00117 };
00118
00119 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00120 {
00121 int i;
00122 char varname[10];
00123 pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
00124 pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
00125 pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
00126 pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
00127 pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
00128 for (i = 1; i < 100; i++) {
00129 snprintf(varname, sizeof(varname), "ARG%d", i);
00130 while (pbx_builtin_getvar_helper(new_chan, varname)) {
00131
00132 pbx_builtin_setvar_helper(new_chan, varname, NULL);
00133 }
00134 }
00135 }
00136
00137 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
00138 {
00139 struct ast_exten *e;
00140 struct ast_include *i;
00141 struct ast_context *c2;
00142
00143 for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
00144 if (ast_extension_match(ast_get_extension_name(e), exten)) {
00145 int needmatch = ast_get_extension_matchcid(e);
00146 if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
00147 (!needmatch)) {
00148
00149 struct ast_exten *p;
00150 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
00151 if (priority != ast_get_extension_priority(p))
00152 continue;
00153 return p;
00154 }
00155 }
00156 }
00157 }
00158
00159
00160 for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
00161 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
00162 if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
00163 e = find_matching_priority(c2, exten, priority, callerid);
00164 if (e)
00165 return e;
00166 }
00167 }
00168 }
00169 return NULL;
00170 }
00171
00172 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
00173 {
00174 const char *s;
00175 char *tmp;
00176 char *cur, *rest;
00177 char *macro;
00178 char fullmacro[80];
00179 char varname[80];
00180 char runningapp[80], runningdata[1024];
00181 char *oldargs[MAX_ARGS + 1] = { NULL, };
00182 int argc, x;
00183 int res=0;
00184 char oldexten[256]="";
00185 int oldpriority, gosub_level = 0;
00186 char pc[80], depthc[12];
00187 char oldcontext[AST_MAX_CONTEXT] = "";
00188 const char *inhangupc;
00189 int offset, depth = 0, maxdepth = 7;
00190 int setmacrocontext=0;
00191 int autoloopflag, inhangup = 0;
00192
00193 char *save_macro_exten;
00194 char *save_macro_context;
00195 char *save_macro_priority;
00196 char *save_macro_offset;
00197 struct ast_module_user *u;
00198 struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
00199
00200 if (ast_strlen_zero(data)) {
00201 ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
00202 return -1;
00203 }
00204
00205 u = ast_module_user_add(chan);
00206
00207 do {
00208 if (macro_store) {
00209 break;
00210 }
00211 if (!(macro_store = ast_channel_datastore_alloc(¯o_ds_info, NULL))) {
00212 ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
00213 break;
00214 }
00215
00216 macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
00217 ast_channel_datastore_add(chan, macro_store);
00218 } while (0);
00219
00220
00221 s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
00222 if (s)
00223 sscanf(s, "%30d", &maxdepth);
00224
00225
00226 s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
00227 if (s)
00228 sscanf(s, "%30d", &depth);
00229
00230 if (strcmp(chan->exten, "h") == 0)
00231 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
00232 inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP");
00233 if (!ast_strlen_zero(inhangupc))
00234 sscanf(inhangupc, "%30d", &inhangup);
00235
00236 if (depth >= maxdepth) {
00237 ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
00238 ast_module_user_remove(u);
00239 return 0;
00240 }
00241 snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00242 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00243
00244 tmp = ast_strdupa(data);
00245 rest = tmp;
00246 macro = strsep(&rest, "|");
00247 if (ast_strlen_zero(macro)) {
00248 ast_log(LOG_WARNING, "Invalid macro name specified\n");
00249 ast_module_user_remove(u);
00250 return 0;
00251 }
00252
00253 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00254 if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
00255 if (!ast_context_find(fullmacro))
00256 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00257 else
00258 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00259 ast_module_user_remove(u);
00260 return 0;
00261 }
00262
00263
00264 if (exclusive) {
00265 ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
00266 ast_autoservice_start(chan);
00267 if (ast_context_lockmacro(fullmacro)) {
00268 ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
00269 ast_autoservice_stop(chan);
00270 ast_module_user_remove(u);
00271
00272 return 0;
00273 }
00274 ast_autoservice_stop(chan);
00275 }
00276
00277
00278 oldpriority = chan->priority;
00279 ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00280 ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00281 if (ast_strlen_zero(chan->macrocontext)) {
00282 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00283 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00284 chan->macropriority = chan->priority;
00285 setmacrocontext=1;
00286 }
00287 argc = 1;
00288
00289 save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
00290 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00291
00292 save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
00293 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00294
00295 save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
00296 snprintf(pc, sizeof(pc), "%d", oldpriority);
00297 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00298
00299 save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
00300 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00301
00302
00303 chan->exten[0] = 's';
00304 chan->exten[1] = '\0';
00305 ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00306 chan->priority = 1;
00307
00308 while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
00309 const char *s;
00310
00311
00312 snprintf(varname, sizeof(varname), "ARG%d", argc);
00313 s = pbx_builtin_getvar_helper(chan, varname);
00314 if (s)
00315 oldargs[argc] = ast_strdup(s);
00316 pbx_builtin_setvar_helper(chan, varname, cur);
00317 argc++;
00318 }
00319 autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00320 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00321 while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
00322 struct ast_context *c;
00323 struct ast_exten *e;
00324 runningapp[0] = '\0';
00325 runningdata[0] = '\0';
00326
00327
00328 if (ast_rdlock_contexts()) {
00329 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00330 } else {
00331 for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00332 if (!strcmp(ast_get_context_name(c), chan->context)) {
00333 if (ast_lock_context(c)) {
00334 ast_log(LOG_WARNING, "Unable to lock context?\n");
00335 } else {
00336 e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
00337 if (e) {
00338 ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00339 ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00340 }
00341 ast_unlock_context(c);
00342 }
00343 break;
00344 }
00345 }
00346 }
00347 ast_unlock_contexts();
00348
00349
00350 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00351
00352 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
00353
00354 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00355 (res == '*') || (res == '#')) {
00356
00357 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
00358 break;
00359 }
00360 switch(res) {
00361 case MACRO_EXIT_RESULT:
00362 res = 0;
00363 goto out;
00364 case AST_PBX_KEEPALIVE:
00365 if (option_debug)
00366 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);
00367 else if (option_verbose > 1)
00368 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);
00369 goto out;
00370 default:
00371 if (option_debug)
00372 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);
00373 else if (option_verbose > 1)
00374 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);
00375 goto out;
00376 }
00377 }
00378
00379 ast_log(LOG_DEBUG, "Executed application: %s\n", runningapp);
00380
00381 if (!strcasecmp(runningapp, "GOSUB")) {
00382 gosub_level++;
00383 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00384 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00385 char tmp2[1024] = "", *cond, *app, *app2 = tmp2;
00386 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00387 cond = strsep(&app2, "?");
00388 app = strsep(&app2, ":");
00389 if (pbx_checkcondition(cond)) {
00390 if (!ast_strlen_zero(app)) {
00391 gosub_level++;
00392 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00393 }
00394 } else {
00395 if (!ast_strlen_zero(app2)) {
00396 gosub_level++;
00397 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00398 }
00399 }
00400 } else if (!strcasecmp(runningapp, "RETURN")) {
00401 gosub_level--;
00402 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00403 } else if (!strcasecmp(runningapp, "STACKPOP")) {
00404 gosub_level--;
00405 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00406 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00407
00408 char tmp2[1024] = "", *tmp3 = NULL;
00409 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00410 if (!strcasecmp(runningapp, "EXECIF")) {
00411 tmp3 = strchr(tmp2, '|');
00412 if (tmp3)
00413 *tmp3++ = '\0';
00414 if (!pbx_checkcondition(tmp2))
00415 tmp3 = NULL;
00416 } else
00417 tmp3 = tmp2;
00418
00419 if (tmp3)
00420 ast_log(LOG_DEBUG, "Last app: %s\n", tmp3);
00421
00422 if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00423 gosub_level++;
00424 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
00425 } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00426 gosub_level--;
00427 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00428 } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00429 gosub_level--;
00430 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
00431 }
00432 }
00433
00434 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00435 if (option_verbose > 1)
00436 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00437 break;
00438 }
00439
00440
00441 if (chan->_softhangup && !inhangup) {
00442 ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
00443 chan->exten, chan->macroexten, chan->priority);
00444 goto out;
00445 }
00446 chan->priority++;
00447 }
00448 out:
00449
00450
00451 ast_channel_lock(chan);
00452
00453
00454 snprintf(depthc, sizeof(depthc), "%d", depth);
00455 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00456 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00457
00458 for (x = 1; x < argc; x++) {
00459
00460 snprintf(varname, sizeof(varname), "ARG%d", x);
00461 if (oldargs[x]) {
00462 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00463 free(oldargs[x]);
00464 } else {
00465 pbx_builtin_setvar_helper(chan, varname, NULL);
00466 }
00467 }
00468
00469
00470 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00471 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00472 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00473 if (save_macro_exten)
00474 free(save_macro_exten);
00475 if (save_macro_context)
00476 free(save_macro_context);
00477 if (save_macro_priority)
00478 free(save_macro_priority);
00479
00480 if (setmacrocontext) {
00481 chan->macrocontext[0] = '\0';
00482 chan->macroexten[0] = '\0';
00483 chan->macropriority = 0;
00484 }
00485
00486
00487
00488
00489
00490
00491
00492
00493 if (ast_check_hangup(chan) || res < 0) {
00494
00495
00496
00497 const char *emc = pbx_builtin_getvar_helper(chan, "EXIT_MACRO_CONTEXT");
00498 if (!emc) {
00499 pbx_builtin_setvar_helper(chan, "EXIT_MACRO_CONTEXT", fullmacro);
00500 }
00501 }
00502
00503 if (!strcasecmp(chan->context, fullmacro)) {
00504 const char *offsets;
00505
00506
00507 chan->priority = oldpriority;
00508 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00509 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00510 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00511
00512
00513 if (sscanf(offsets, "%30d", &offset) == 1) {
00514 if (ast_exists_extension(chan, chan->context, chan->exten,
00515 chan->priority + offset + 1,
00516 chan->cid.cid_num)) {
00517 chan->priority += offset;
00518 }
00519 }
00520 }
00521 }
00522
00523 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00524 if (save_macro_offset)
00525 free(save_macro_offset);
00526
00527
00528 if (exclusive) {
00529 ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
00530 if (ast_context_unlockmacro(fullmacro)) {
00531 ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00532 res = 0;
00533 }
00534 }
00535 ast_channel_unlock(chan);
00536
00537 ast_module_user_remove(u);
00538
00539 return res;
00540 }
00541
00542 static int macro_exec(struct ast_channel *chan, void *data)
00543 {
00544 return _macro_exec(chan, data, 0);
00545 }
00546
00547 static int macroexclusive_exec(struct ast_channel *chan, void *data)
00548 {
00549 return _macro_exec(chan, data, 1);
00550 }
00551
00552 static int macroif_exec(struct ast_channel *chan, void *data)
00553 {
00554 char *expr = NULL, *label_a = NULL, *label_b = NULL;
00555 int res = 0;
00556 struct ast_module_user *u;
00557
00558 u = ast_module_user_add(chan);
00559
00560 if (!(expr = ast_strdupa(data))) {
00561 ast_module_user_remove(u);
00562 return -1;
00563 }
00564
00565 if ((label_a = strchr(expr, '?'))) {
00566 *label_a = '\0';
00567 label_a++;
00568 if ((label_b = strchr(label_a, ':'))) {
00569 *label_b = '\0';
00570 label_b++;
00571 }
00572 if (pbx_checkcondition(expr))
00573 res = macro_exec(chan, label_a);
00574 else if (label_b)
00575 res = macro_exec(chan, label_b);
00576 } else
00577 ast_log(LOG_WARNING, "Invalid Syntax.\n");
00578
00579 ast_module_user_remove(u);
00580
00581 return res;
00582 }
00583
00584 static int macro_exit_exec(struct ast_channel *chan, void *data)
00585 {
00586 return MACRO_EXIT_RESULT;
00587 }
00588
00589 static int unload_module(void)
00590 {
00591 int res;
00592
00593 res = ast_unregister_application(if_app);
00594 res |= ast_unregister_application(exit_app);
00595 res |= ast_unregister_application(app);
00596 res |= ast_unregister_application(exclusive_app);
00597
00598 ast_module_user_hangup_all();
00599
00600 return res;
00601 }
00602
00603 static int load_module(void)
00604 {
00605 int res;
00606
00607 res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
00608 res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
00609 res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
00610 res |= ast_register_application(app, macro_exec, synopsis, descrip);
00611
00612 return res;
00613 }
00614
00615 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");