mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-13 16:21:01 +00:00
add MacroExclusive application, a Macro that only one call can executed at
a time (issue #7366, Steve Davies, with mods by me as discussed in the report) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@39681 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -70,6 +70,13 @@ static char *if_descrip =
|
||||
"(otherwise <macroname_b> if provided)\n"
|
||||
"Arguments and return values as in application macro()\n";
|
||||
|
||||
static char *exclusive_descrip =
|
||||
" MacroExclusive(macroname|arg1|arg2...):\n"
|
||||
"Executes macro defined in the context 'macro-macroname'\n"
|
||||
"Only one call at a time may run the macro.\n"
|
||||
"(we'll wait if another call is busy executing in the Macro)\n"
|
||||
"Arguments and return values as in application Macro()\n";
|
||||
|
||||
static char *exit_descrip =
|
||||
" MacroExit():\n"
|
||||
"Causes the currently running macro to exit as if it had\n"
|
||||
@@ -79,15 +86,17 @@ static char *exit_descrip =
|
||||
|
||||
static char *app = "Macro";
|
||||
static char *if_app = "MacroIf";
|
||||
static char *exclusive_app = "MacroExclusive";
|
||||
static char *exit_app = "MacroExit";
|
||||
|
||||
static char *synopsis = "Macro Implementation";
|
||||
static char *if_synopsis = "Conditional Macro Implementation";
|
||||
static char *exclusive_synopsis = "Exclusive Macro Implementation";
|
||||
static char *exit_synopsis = "Exit From Macro";
|
||||
|
||||
LOCAL_USER_DECL;
|
||||
|
||||
static int macro_exec(struct ast_channel *chan, void *data)
|
||||
static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
@@ -140,6 +149,7 @@ static int macro_exec(struct ast_channel *chan, void *data)
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
|
||||
if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
|
||||
if (!ast_context_find(fullmacro))
|
||||
@@ -150,6 +160,19 @@ static int macro_exec(struct ast_channel *chan, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we are to run the macro exclusively, take the mutex */
|
||||
if (exclusive) {
|
||||
ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
|
||||
ast_autoservice_start(chan);
|
||||
if (ast_context_lockmacro(fullmacro)) {
|
||||
ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
|
||||
ast_autoservice_stop(chan);
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return 0;
|
||||
}
|
||||
ast_autoservice_stop(chan);
|
||||
}
|
||||
|
||||
/* Save old info */
|
||||
oldpriority = chan->priority;
|
||||
ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
|
||||
@@ -243,7 +266,6 @@ static int macro_exec(struct ast_channel *chan, void *data)
|
||||
snprintf(depthc, sizeof(depthc), "%d", depth);
|
||||
if (!dead) {
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
|
||||
|
||||
ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
|
||||
}
|
||||
|
||||
@@ -302,10 +324,30 @@ static int macro_exec(struct ast_channel *chan, void *data)
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
|
||||
if (save_macro_offset)
|
||||
free(save_macro_offset);
|
||||
|
||||
/* Unlock the macro */
|
||||
if (exclusive) {
|
||||
ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
|
||||
if (ast_context_unlockmacro(fullmacro)) {
|
||||
ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL_USER_REMOVE(u);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int macro_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
_macro_exec(chan, data, 0);
|
||||
}
|
||||
|
||||
static int macroexclusive_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
_macro_exec(chan, data, 1);
|
||||
}
|
||||
|
||||
static int macroif_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
char *expr = NULL, *label_a = NULL, *label_b = NULL;
|
||||
@@ -350,6 +392,7 @@ static int unload_module(void *mod)
|
||||
res = ast_unregister_application(if_app);
|
||||
res |= ast_unregister_application(exit_app);
|
||||
res |= ast_unregister_application(app);
|
||||
res |= ast_unregister_application(exclusive_app);
|
||||
|
||||
STANDARD_HANGUP_LOCALUSERS;
|
||||
|
||||
@@ -362,6 +405,7 @@ static int load_module(void *mod)
|
||||
|
||||
res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
|
||||
res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
|
||||
res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
|
||||
res |= ast_register_application(app, macro_exec, synopsis, descrip);
|
||||
|
||||
return res;
|
||||
|
@@ -681,6 +681,29 @@ int ast_lock_context(struct ast_context *con);
|
||||
*/
|
||||
int ast_unlock_context(struct ast_context *con);
|
||||
|
||||
/*!
|
||||
* \brief locks the macrolock in the given given context
|
||||
*
|
||||
* \param macrocontext name of the macro-context to lock
|
||||
*
|
||||
* Locks the given macro-context to ensure only one thread (call) can execute it at a time
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int ast_context_lockmacro(const char *macrocontext);
|
||||
|
||||
/*!
|
||||
* \brief Unlocks the macrolock in the given context
|
||||
*
|
||||
* \param macrocontext name of the macro-context to unlock
|
||||
*
|
||||
* Unlocks the given macro-context so that another thread (call) can execute it
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int ast_context_unlockmacro(const char *macrocontext);
|
||||
|
||||
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
|
||||
|
||||
|
58
pbx.c
58
pbx.c
@@ -165,6 +165,7 @@ struct ast_context {
|
||||
struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
|
||||
const char *registrar; /*!< Registrar */
|
||||
AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
|
||||
ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
|
||||
char name[0]; /*!< Name of the context */
|
||||
};
|
||||
|
||||
@@ -2742,6 +2743,62 @@ int ast_context_remove_extension2(struct ast_context *con, const char *extension
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* \note This function locks contexts list by &conlist, searches for the right context
|
||||
* structure, and locks the macrolock mutex in that context.
|
||||
* macrolock is used to limit a macro to be executed by one call at a time.
|
||||
*/
|
||||
int ast_context_lockmacro(const char *context)
|
||||
{
|
||||
struct ast_context *c = NULL;
|
||||
int ret = -1;
|
||||
|
||||
ast_lock_contexts();
|
||||
|
||||
while ((c = ast_walk_contexts(c))) {
|
||||
if (!strcmp(ast_get_context_name(c), context)) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ast_unlock_contexts();
|
||||
|
||||
/* if we found context, lock macrolock */
|
||||
if (ret == 0)
|
||||
ret = ast_mutex_lock(&c->macrolock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \note This function locks contexts list by &conlist, searches for the right context
|
||||
* structure, and unlocks the macrolock mutex in that context.
|
||||
* macrolock is used to limit a macro to be executed by one call at a time.
|
||||
*/
|
||||
int ast_context_unlockmacro(const char *context)
|
||||
{
|
||||
struct ast_context *c = NULL;
|
||||
int ret = -1;
|
||||
|
||||
ast_lock_contexts();
|
||||
|
||||
while ((c = ast_walk_contexts(c))) {
|
||||
if (!strcmp(ast_get_context_name(c), context)) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ast_unlock_contexts();
|
||||
|
||||
/* if we found context, unlock macrolock */
|
||||
if (ret == 0)
|
||||
ret = ast_mutex_unlock(&c->macrolock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*! \brief Dynamically register a new dial plan application */
|
||||
int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
|
||||
{
|
||||
@@ -3451,6 +3508,7 @@ struct ast_context *ast_context_create(struct ast_context **extcontexts, const c
|
||||
}
|
||||
if ((tmp = ast_calloc(1, length))) {
|
||||
ast_mutex_init(&tmp->lock);
|
||||
ast_mutex_init(&tmp->macrolock);
|
||||
strcpy(tmp->name, name);
|
||||
tmp->root = NULL;
|
||||
tmp->registrar = registrar;
|
||||
|
Reference in New Issue
Block a user