mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-01 19:43:03 +00:00
Fix extension state callback references in chan_sip.
Chan_sip gives a dialog reference to the extension state callback and assumes that when ast_extension_state_del() returns, the callback cannot happen anymore. Chan_sip then reduces the dialog reference count associated with the callback. Recent changes (ASTERISK-17760) have resulted in the potential for the callback to happen after ast_extension_state_del() has returned. For chan_sip, this could be very bad because the dialog pointer could have already been destroyed. * Added ast_extension_state_add_destroy() so chan_sip can account for the sip_pvt reference given to the extension state callback when the extension state callback is deleted. * Fix pbx.c awkward statecbs handling in ast_extension_state_add_destroy() and handle_statechange() now that the struct ast_state_cb has a destructor to call. * Ensure that ast_extension_state_add_destroy() will never return -1 or 0 for a successful registration. * Fixed pbx.c statecbs_cmp() to compare the correct information. The passed in value to compare is a change_cb function pointer not an object pointer. * Make pbx.c ast_merge_contexts_and_delete() not perform callbacks with AST_EXTENSION_REMOVED with locks held. Chan_sip is notorious for deadlocking when those locks are held during the callback. * Removed unused lock declaration for the pbx.c store_hints list. (closes issue ASTERISK-18844) Reported by: rmudgett Review: https://reviewboard.asterisk.org/r/1635/ git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@348940 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -2958,10 +2958,9 @@ void dialog_unlink_all(struct sip_pvt *dialog)
|
|||||||
}
|
}
|
||||||
dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
|
dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
|
||||||
}
|
}
|
||||||
if (dialog->stateid > -1) {
|
if (dialog->stateid != -1) {
|
||||||
ast_extension_state_del(dialog->stateid, NULL);
|
ast_extension_state_del(dialog->stateid, cb_extensionstate);
|
||||||
dialog_unref(dialog, "removing extension_state, should unref the associated dialog ptr that was stored there.");
|
dialog->stateid = -1;
|
||||||
dialog->stateid = -1; /* shouldn't we 'zero' this out? */
|
|
||||||
}
|
}
|
||||||
/* Remove link from peer to subscription of MWI */
|
/* Remove link from peer to subscription of MWI */
|
||||||
if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
|
if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
|
||||||
@@ -14327,6 +14326,13 @@ static void network_change_event_cb(const struct ast_event *event, void *userdat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cb_extensionstate_destroy(int id, void *data)
|
||||||
|
{
|
||||||
|
struct sip_pvt *p = data;
|
||||||
|
|
||||||
|
dialog_unref(p, "the extensionstate containing this dialog ptr was destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
|
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
|
||||||
\note If you add an "hint" priority to the extension in the dial plan,
|
\note If you add an "hint" priority to the extension in the dial plan,
|
||||||
you will get notifications on device state changes */
|
you will get notifications on device state changes */
|
||||||
@@ -14341,7 +14347,6 @@ static int cb_extensionstate(char *context, char* exten, int state, void *data)
|
|||||||
case AST_EXTENSION_REMOVED: /* Extension is gone */
|
case AST_EXTENSION_REMOVED: /* Extension is gone */
|
||||||
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
|
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
|
||||||
ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
|
ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
|
||||||
p->stateid = -1;
|
|
||||||
p->subscribed = NONE;
|
p->subscribed = NONE;
|
||||||
append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
|
append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
|
||||||
break;
|
break;
|
||||||
@@ -24346,7 +24351,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
|
|||||||
{
|
{
|
||||||
int gotdest = 0;
|
int gotdest = 0;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
int firststate = AST_EXTENSION_REMOVED;
|
int firststate;
|
||||||
struct sip_peer *authpeer = NULL;
|
struct sip_peer *authpeer = NULL;
|
||||||
const char *eventheader = get_header(req, "Event"); /* Get Event package name */
|
const char *eventheader = get_header(req, "Event"); /* Get Event package name */
|
||||||
int resubscribe = (p->subscribed != NONE) && !req->ignore;
|
int resubscribe = (p->subscribed != NONE) && !req->ignore;
|
||||||
@@ -24638,12 +24643,15 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
|
|||||||
|
|
||||||
/* Add subscription for extension state from the PBX core */
|
/* Add subscription for extension state from the PBX core */
|
||||||
if (p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION && !resubscribe) {
|
if (p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION && !resubscribe) {
|
||||||
if (p->stateid > -1) {
|
if (p->stateid != -1) {
|
||||||
ast_extension_state_del(p->stateid, cb_extensionstate);
|
ast_extension_state_del(p->stateid, cb_extensionstate);
|
||||||
/* we need to dec the refcount, now that the extensionstate is removed */
|
|
||||||
dialog_unref(p, "the extensionstate containing this dialog ptr was deleted");
|
|
||||||
}
|
}
|
||||||
p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, dialog_ref(p,"copying dialog ptr into extension state struct"));
|
dialog_ref(p, "copying dialog ptr into extension state struct");
|
||||||
|
p->stateid = ast_extension_state_add_destroy(p->context, p->exten,
|
||||||
|
cb_extensionstate, cb_extensionstate_destroy, p);
|
||||||
|
if (p->stateid == -1) {
|
||||||
|
dialog_unref(p, "copying dialog ptr into extension state struct failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req->ignore && p)
|
if (!req->ignore && p)
|
||||||
|
|||||||
@@ -76,7 +76,10 @@ struct ast_ignorepat;
|
|||||||
struct ast_sw;
|
struct ast_sw;
|
||||||
|
|
||||||
/*! \brief Typedef for devicestate and hint callbacks */
|
/*! \brief Typedef for devicestate and hint callbacks */
|
||||||
typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
|
typedef int (*ast_state_cb_type)(char *context, char *id, enum ast_extension_states state, void *data);
|
||||||
|
|
||||||
|
/*! \brief Typedef for devicestate and hint callback removal indication callback */
|
||||||
|
typedef void (*ast_state_cb_destroy_type)(int id, void *data);
|
||||||
|
|
||||||
/*! \brief Data structure associated with a custom dialplan function */
|
/*! \brief Data structure associated with a custom dialplan function */
|
||||||
struct ast_custom_function {
|
struct ast_custom_function {
|
||||||
@@ -407,34 +410,55 @@ int ast_extension_state(struct ast_channel *c, const char *context, const char *
|
|||||||
*/
|
*/
|
||||||
const char *ast_extension_state2str(int extension_state);
|
const char *ast_extension_state2str(int extension_state);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Registers a state change callback with destructor.
|
||||||
|
* \since 1.8.9
|
||||||
|
* \since 10.1.0
|
||||||
|
*
|
||||||
|
* \param context which context to look in
|
||||||
|
* \param exten which extension to get state
|
||||||
|
* \param change_cb callback to call if state changed
|
||||||
|
* \param destroy_cb callback to call when registration destroyed.
|
||||||
|
* \param data to pass to callback
|
||||||
|
*
|
||||||
|
* \note The change_cb is called if the state of an extension is changed.
|
||||||
|
*
|
||||||
|
* \note The destroy_cb is called when the registration is
|
||||||
|
* deleted so the registerer can release any associated
|
||||||
|
* resources.
|
||||||
|
*
|
||||||
|
* \retval -1 on failure
|
||||||
|
* \retval ID on success
|
||||||
|
*/
|
||||||
|
int ast_extension_state_add_destroy(const char *context, const char *exten,
|
||||||
|
ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Registers a state change callback
|
* \brief Registers a state change callback
|
||||||
*
|
*
|
||||||
* \param context which context to look in
|
* \param context which context to look in
|
||||||
* \param exten which extension to get state
|
* \param exten which extension to get state
|
||||||
* \param callback callback to call if state changed
|
* \param change_cb callback to call if state changed
|
||||||
* \param data to pass to callback
|
* \param data to pass to callback
|
||||||
*
|
*
|
||||||
* The callback is called if the state of an extension is changed.
|
* \note The change_cb is called if the state of an extension is changed.
|
||||||
*
|
*
|
||||||
* \retval -1 on failure
|
* \retval -1 on failure
|
||||||
* \retval ID on success
|
* \retval ID on success
|
||||||
*/
|
*/
|
||||||
int ast_extension_state_add(const char *context, const char *exten,
|
int ast_extension_state_add(const char *context, const char *exten,
|
||||||
ast_state_cb_type callback, void *data);
|
ast_state_cb_type change_cb, void *data);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Deletes a registered state change callback by ID
|
* \brief Deletes a registered state change callback by ID
|
||||||
*
|
*
|
||||||
* \param id of the callback to delete
|
* \param id of the registered state callback to delete
|
||||||
* \param callback callback
|
* \param change_cb callback to call if state changed (Used if id == 0 (global))
|
||||||
*
|
|
||||||
* Removes the callback from list of callbacks
|
|
||||||
*
|
*
|
||||||
* \retval 0 success
|
* \retval 0 success
|
||||||
* \retval -1 failure
|
* \retval -1 failure
|
||||||
*/
|
*/
|
||||||
int ast_extension_state_del(int id, ast_state_cb_type callback);
|
int ast_extension_state_del(int id, ast_state_cb_type change_cb);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief If an extension hint exists, return non-zero
|
* \brief If an extension hint exists, return non-zero
|
||||||
|
|||||||
139
main/pbx.c
139
main/pbx.c
@@ -918,9 +918,14 @@ struct ast_app {
|
|||||||
|
|
||||||
/*! \brief ast_state_cb: An extension state notify register item */
|
/*! \brief ast_state_cb: An extension state notify register item */
|
||||||
struct ast_state_cb {
|
struct ast_state_cb {
|
||||||
|
/*! Watcher ID returned when registered. */
|
||||||
int id;
|
int id;
|
||||||
|
/*! Arbitrary data passed for callbacks. */
|
||||||
void *data;
|
void *data;
|
||||||
ast_state_cb_type callback;
|
/*! Callback when state changes. */
|
||||||
|
ast_state_cb_type change_cb;
|
||||||
|
/*! Callback when destroyed so any resources given by the registerer can be freed. */
|
||||||
|
ast_state_cb_destroy_type destroy_cb;
|
||||||
/*! \note Only used by ast_merge_contexts_and_delete */
|
/*! \note Only used by ast_merge_contexts_and_delete */
|
||||||
AST_LIST_ENTRY(ast_state_cb) entry;
|
AST_LIST_ENTRY(ast_state_cb) entry;
|
||||||
};
|
};
|
||||||
@@ -949,9 +954,9 @@ struct ast_hint {
|
|||||||
|
|
||||||
/* --- Hash tables of various objects --------*/
|
/* --- Hash tables of various objects --------*/
|
||||||
#ifdef LOW_MEMORY
|
#ifdef LOW_MEMORY
|
||||||
static const int HASH_EXTENHINT_SIZE = 17;
|
#define HASH_EXTENHINT_SIZE 17
|
||||||
#else
|
#else
|
||||||
static const int HASH_EXTENHINT_SIZE = 563;
|
#define HASH_EXTENHINT_SIZE 563
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct cfextension_states {
|
static const struct cfextension_states {
|
||||||
@@ -4465,27 +4470,16 @@ static int handle_statechange(void *datap)
|
|||||||
hint->laststate = state; /* record we saw the change */
|
hint->laststate = state; /* record we saw the change */
|
||||||
|
|
||||||
/* For general callbacks */
|
/* For general callbacks */
|
||||||
ao2_lock(statecbs);
|
|
||||||
cb_iter = ao2_iterator_init(statecbs, 0);
|
cb_iter = ao2_iterator_init(statecbs, 0);
|
||||||
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
|
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
|
||||||
void *data;
|
state_cb->change_cb(context_name, exten_name, state, state_cb->data);
|
||||||
|
|
||||||
/*
|
|
||||||
* Protect the data ptr because it could get updated by
|
|
||||||
* ast_extension_state_add().
|
|
||||||
*/
|
|
||||||
data = state_cb->data;
|
|
||||||
ao2_unlock(statecbs);
|
|
||||||
state_cb->callback(context_name, exten_name, state, data);
|
|
||||||
ao2_lock(statecbs);
|
|
||||||
}
|
}
|
||||||
ao2_unlock(statecbs);
|
|
||||||
ao2_iterator_destroy(&cb_iter);
|
ao2_iterator_destroy(&cb_iter);
|
||||||
|
|
||||||
/* For extension callbacks */
|
/* For extension callbacks */
|
||||||
cb_iter = ao2_iterator_init(hint->callbacks, 0);
|
cb_iter = ao2_iterator_init(hint->callbacks, 0);
|
||||||
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
|
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
|
||||||
state_cb->callback(context_name, exten_name, state, state_cb->data);
|
state_cb->change_cb(context_name, exten_name, state, state_cb->data);
|
||||||
}
|
}
|
||||||
ao2_iterator_destroy(&cb_iter);
|
ao2_iterator_destroy(&cb_iter);
|
||||||
}
|
}
|
||||||
@@ -4497,9 +4491,26 @@ static int handle_statechange(void *datap)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Add watcher for extension states */
|
/*!
|
||||||
int ast_extension_state_add(const char *context, const char *exten,
|
* \internal
|
||||||
ast_state_cb_type callback, void *data)
|
* \brief Destroy the given state callback object.
|
||||||
|
*
|
||||||
|
* \param doomed State callback to destroy.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void destroy_state_cb(void *doomed)
|
||||||
|
{
|
||||||
|
struct ast_state_cb *state_cb = doomed;
|
||||||
|
|
||||||
|
if (state_cb->destroy_cb) {
|
||||||
|
state_cb->destroy_cb(state_cb->id, state_cb->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Add watcher for extension states with destructor */
|
||||||
|
int ast_extension_state_add_destroy(const char *context, const char *exten,
|
||||||
|
ast_state_cb_type change_cb, ast_state_cb_destroy_type destroy_cb, void *data)
|
||||||
{
|
{
|
||||||
struct ast_hint *hint;
|
struct ast_hint *hint;
|
||||||
struct ast_state_cb *state_cb;
|
struct ast_state_cb *state_cb;
|
||||||
@@ -4508,24 +4519,20 @@ int ast_extension_state_add(const char *context, const char *exten,
|
|||||||
|
|
||||||
/* If there's no context and extension: add callback to statecbs list */
|
/* If there's no context and extension: add callback to statecbs list */
|
||||||
if (!context && !exten) {
|
if (!context && !exten) {
|
||||||
/* Prevent multiple adds from adding the same callback at the same time. */
|
/* Prevent multiple adds from adding the same change_cb at the same time. */
|
||||||
ao2_lock(statecbs);
|
ao2_lock(statecbs);
|
||||||
|
|
||||||
state_cb = ao2_find(statecbs, callback, 0);
|
/* Remove any existing change_cb. */
|
||||||
if (state_cb) {
|
ao2_find(statecbs, change_cb, OBJ_UNLINK | OBJ_NODATA);
|
||||||
state_cb->data = data;
|
|
||||||
ao2_ref(state_cb, -1);
|
|
||||||
ao2_unlock(statecbs);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now insert the callback */
|
/* Now insert the change_cb */
|
||||||
if (!(state_cb = ao2_alloc(sizeof(*state_cb), NULL))) {
|
if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
|
||||||
ao2_unlock(statecbs);
|
ao2_unlock(statecbs);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
state_cb->id = 0;
|
state_cb->id = 0;
|
||||||
state_cb->callback = callback;
|
state_cb->change_cb = change_cb;
|
||||||
|
state_cb->destroy_cb = destroy_cb;
|
||||||
state_cb->data = data;
|
state_cb->data = data;
|
||||||
ao2_link(statecbs, state_cb);
|
ao2_link(statecbs, state_cb);
|
||||||
|
|
||||||
@@ -4566,14 +4573,18 @@ int ast_extension_state_add(const char *context, const char *exten,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Now insert the callback in the callback list */
|
/* Now insert the callback in the callback list */
|
||||||
if (!(state_cb = ao2_alloc(sizeof(*state_cb), NULL))) {
|
if (!(state_cb = ao2_alloc(sizeof(*state_cb), destroy_state_cb))) {
|
||||||
ao2_ref(hint, -1);
|
ao2_ref(hint, -1);
|
||||||
ao2_unlock(hints);
|
ao2_unlock(hints);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
id = stateid++; /* Unique ID for this callback */
|
do {
|
||||||
|
id = stateid++; /* Unique ID for this callback */
|
||||||
|
/* Do not allow id to ever be -1 or 0. */
|
||||||
|
} while (id == -1 || id == 0);
|
||||||
state_cb->id = id;
|
state_cb->id = id;
|
||||||
state_cb->callback = callback; /* Pointer to callback routine */
|
state_cb->change_cb = change_cb; /* Pointer to callback routine */
|
||||||
|
state_cb->destroy_cb = destroy_cb;
|
||||||
state_cb->data = data; /* Data for the callback */
|
state_cb->data = data; /* Data for the callback */
|
||||||
ao2_link(hint->callbacks, state_cb);
|
ao2_link(hint->callbacks, state_cb);
|
||||||
|
|
||||||
@@ -4584,6 +4595,13 @@ int ast_extension_state_add(const char *context, const char *exten,
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \brief Add watcher for extension states */
|
||||||
|
int ast_extension_state_add(const char *context, const char *exten,
|
||||||
|
ast_state_cb_type change_cb, void *data)
|
||||||
|
{
|
||||||
|
return ast_extension_state_add_destroy(context, exten, change_cb, NULL, data);
|
||||||
|
}
|
||||||
|
|
||||||
/*! \brief Remove a watcher from the callback list */
|
/*! \brief Remove a watcher from the callback list */
|
||||||
static int find_hint_by_cb_id(void *obj, void *arg, int flags)
|
static int find_hint_by_cb_id(void *obj, void *arg, int flags)
|
||||||
{
|
{
|
||||||
@@ -4600,16 +4618,16 @@ static int find_hint_by_cb_id(void *obj, void *arg, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief ast_extension_state_del: Remove a watcher from the callback list */
|
/*! \brief ast_extension_state_del: Remove a watcher from the callback list */
|
||||||
int ast_extension_state_del(int id, ast_state_cb_type callback)
|
int ast_extension_state_del(int id, ast_state_cb_type change_cb)
|
||||||
{
|
{
|
||||||
struct ast_state_cb *p_cur = NULL;
|
struct ast_state_cb *p_cur;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
if (!id) { /* id == 0 is a callback without extension */
|
if (!id) { /* id == 0 is a callback without extension */
|
||||||
if (!callback) {
|
if (!change_cb) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
p_cur = ao2_find(statecbs, callback, OBJ_UNLINK);
|
p_cur = ao2_find(statecbs, change_cb, OBJ_UNLINK);
|
||||||
if (p_cur) {
|
if (p_cur) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
ao2_ref(p_cur, -1);
|
ao2_ref(p_cur, -1);
|
||||||
@@ -4671,7 +4689,7 @@ static void destroy_hint(void *obj)
|
|||||||
while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
|
while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
|
||||||
/* Notify with -1 and remove all callbacks */
|
/* Notify with -1 and remove all callbacks */
|
||||||
/* NOTE: The casts will not be needed for v1.10 and later */
|
/* NOTE: The casts will not be needed for v1.10 and later */
|
||||||
state_cb->callback((char *) context_name, (char *) exten_name,
|
state_cb->change_cb((char *) context_name, (char *) exten_name,
|
||||||
AST_EXTENSION_DEACTIVATED, state_cb->data);
|
AST_EXTENSION_DEACTIVATED, state_cb->data);
|
||||||
ao2_ref(state_cb, -1);
|
ao2_ref(state_cb, -1);
|
||||||
}
|
}
|
||||||
@@ -7229,7 +7247,7 @@ struct store_hint {
|
|||||||
char data[1];
|
char data[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
AST_LIST_HEAD(store_hints, store_hint);
|
AST_LIST_HEAD_NOLOCK(store_hints, store_hint);
|
||||||
|
|
||||||
static void context_merge_incls_swits_igps_other_registrars(struct ast_context *new, struct ast_context *old, const char *registrar)
|
static void context_merge_incls_swits_igps_other_registrars(struct ast_context *new, struct ast_context *old, const char *registrar)
|
||||||
{
|
{
|
||||||
@@ -7354,7 +7372,8 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
struct ast_context *tmp;
|
struct ast_context *tmp;
|
||||||
struct ast_context *oldcontextslist;
|
struct ast_context *oldcontextslist;
|
||||||
struct ast_hashtab *oldtable;
|
struct ast_hashtab *oldtable;
|
||||||
struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
|
struct store_hints hints_stored = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
|
||||||
|
struct store_hints hints_removed = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
|
||||||
struct store_hint *saved_hint;
|
struct store_hint *saved_hint;
|
||||||
struct ast_hint *hint;
|
struct ast_hint *hint;
|
||||||
struct ast_exten *exten;
|
struct ast_exten *exten;
|
||||||
@@ -7424,7 +7443,7 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
saved_hint->exten = saved_hint->data + strlen(saved_hint->context) + 1;
|
saved_hint->exten = saved_hint->data + strlen(saved_hint->context) + 1;
|
||||||
strcpy(saved_hint->exten, hint->exten->exten);
|
strcpy(saved_hint->exten, hint->exten->exten);
|
||||||
ao2_unlock(hint);
|
ao2_unlock(hint);
|
||||||
AST_LIST_INSERT_HEAD(&store, saved_hint, list);
|
AST_LIST_INSERT_HEAD(&hints_stored, saved_hint, list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7440,7 +7459,7 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
* Restore the watchers for hints that can be found; notify
|
* Restore the watchers for hints that can be found; notify
|
||||||
* those that cannot be restored.
|
* those that cannot be restored.
|
||||||
*/
|
*/
|
||||||
while ((saved_hint = AST_LIST_REMOVE_HEAD(&store, list))) {
|
while ((saved_hint = AST_LIST_REMOVE_HEAD(&hints_stored, list))) {
|
||||||
struct pbx_find_info q = { .stacklen = 0 };
|
struct pbx_find_info q = { .stacklen = 0 };
|
||||||
|
|
||||||
exten = pbx_find_extension(NULL, NULL, &q, saved_hint->context, saved_hint->exten,
|
exten = pbx_find_extension(NULL, NULL, &q, saved_hint->context, saved_hint->exten,
|
||||||
@@ -7462,13 +7481,11 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
/* Find the hint in the hints container */
|
/* Find the hint in the hints container */
|
||||||
hint = exten ? ao2_find(hints, exten, 0) : NULL;
|
hint = exten ? ao2_find(hints, exten, 0) : NULL;
|
||||||
if (!hint) {
|
if (!hint) {
|
||||||
/* this hint has been removed, notify the watchers */
|
/*
|
||||||
while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) {
|
* Notify watchers of this removed hint later when we aren't
|
||||||
thiscb->callback(saved_hint->context, saved_hint->exten,
|
* encumberd by so many locks.
|
||||||
AST_EXTENSION_REMOVED, thiscb->data);
|
*/
|
||||||
/* Ref that we added when putting into saved_hint->callbacks */
|
AST_LIST_INSERT_HEAD(&hints_removed, saved_hint, list);
|
||||||
ao2_ref(thiscb, -1);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ao2_lock(hint);
|
ao2_lock(hint);
|
||||||
while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) {
|
while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) {
|
||||||
@@ -7479,12 +7496,28 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
hint->laststate = saved_hint->laststate;
|
hint->laststate = saved_hint->laststate;
|
||||||
ao2_unlock(hint);
|
ao2_unlock(hint);
|
||||||
ao2_ref(hint, -1);
|
ao2_ref(hint, -1);
|
||||||
|
ast_free(saved_hint);
|
||||||
}
|
}
|
||||||
ast_free(saved_hint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ao2_unlock(hints);
|
ao2_unlock(hints);
|
||||||
ast_unlock_contexts();
|
ast_unlock_contexts();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify watchers of all removed hints with the same lock
|
||||||
|
* environment as handle_statechange().
|
||||||
|
*/
|
||||||
|
while ((saved_hint = AST_LIST_REMOVE_HEAD(&hints_removed, list))) {
|
||||||
|
/* this hint has been removed, notify the watchers */
|
||||||
|
while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) {
|
||||||
|
thiscb->change_cb(saved_hint->context, saved_hint->exten,
|
||||||
|
AST_EXTENSION_REMOVED, thiscb->data);
|
||||||
|
/* Ref that we added when putting into saved_hint->callbacks */
|
||||||
|
ao2_ref(thiscb, -1);
|
||||||
|
}
|
||||||
|
ast_free(saved_hint);
|
||||||
|
}
|
||||||
|
|
||||||
ast_mutex_unlock(&context_merge_lock);
|
ast_mutex_unlock(&context_merge_lock);
|
||||||
endlocktime = ast_tvnow();
|
endlocktime = ast_tvnow();
|
||||||
|
|
||||||
@@ -10630,15 +10663,15 @@ static int hint_cmp(void *obj, void *arg, int flags)
|
|||||||
static int statecbs_cmp(void *obj, void *arg, int flags)
|
static int statecbs_cmp(void *obj, void *arg, int flags)
|
||||||
{
|
{
|
||||||
const struct ast_state_cb *state_cb = obj;
|
const struct ast_state_cb *state_cb = obj;
|
||||||
const struct ast_state_cb *callback = arg;
|
ast_state_cb_type change_cb = arg;
|
||||||
|
|
||||||
return (state_cb == callback) ? CMP_MATCH | CMP_STOP : 0;
|
return (state_cb->change_cb == change_cb) ? CMP_MATCH | CMP_STOP : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ast_pbx_init(void)
|
int ast_pbx_init(void)
|
||||||
{
|
{
|
||||||
hints = ao2_container_alloc(HASH_EXTENHINT_SIZE, hint_hash, hint_cmp);
|
hints = ao2_container_alloc(HASH_EXTENHINT_SIZE, hint_hash, hint_cmp);
|
||||||
statecbs = ao2_container_alloc(HASH_EXTENHINT_SIZE, NULL, statecbs_cmp);
|
statecbs = ao2_container_alloc(1, NULL, statecbs_cmp);
|
||||||
|
|
||||||
return (hints && statecbs) ? 0 : -1;
|
return (hints && statecbs) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user