mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-08 19:08:14 +00:00
move devices from hints into an ao2_container
by splitting up devices from hints into an own ao2_container the callback to get these devices for statechange handling is faster. with this changes the length of a device used in a hint isnt longer restricted to 80 characters. Tests showed that calling handle_statechange is 40 times faster if no hints are used and 25 times faster if there are any hints. (closes issue #17928) Reported by: mdu113 Tested by: schmidts Review: https://reviewboard.asterisk.org/r/1003/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@296752 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -78,6 +78,7 @@ int __ast_fdleak_dup(int oldfd, const char *file, int line, const char *func);
|
|||||||
|
|
||||||
int ast_set_priority(int); /*!< Provided by asterisk.c */
|
int ast_set_priority(int); /*!< Provided by asterisk.c */
|
||||||
int ast_fd_init(void); /*!< Provided by astfd.c */
|
int ast_fd_init(void); /*!< Provided by astfd.c */
|
||||||
|
int ast_pbx_init(void); /*!< Provided by pbx.c */
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Register a function to be executed before Asterisk exits.
|
* \brief Register a function to be executed before Asterisk exits.
|
||||||
|
|||||||
@@ -3207,6 +3207,7 @@ int main(int argc, char *argv[])
|
|||||||
tdd_init();
|
tdd_init();
|
||||||
ast_tps_init();
|
ast_tps_init();
|
||||||
ast_fd_init();
|
ast_fd_init();
|
||||||
|
ast_pbx_init();
|
||||||
|
|
||||||
if (getenv("HOME"))
|
if (getenv("HOME"))
|
||||||
snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
|
snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
|
||||||
|
|||||||
473
main/pbx.c
473
main/pbx.c
@@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "asterisk/indications.h"
|
#include "asterisk/indications.h"
|
||||||
#include "asterisk/taskprocessor.h"
|
#include "asterisk/taskprocessor.h"
|
||||||
#include "asterisk/xmldoc.h"
|
#include "asterisk/xmldoc.h"
|
||||||
|
#include "asterisk/astobj2.h"
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \note I M P O R T A N T :
|
* \note I M P O R T A N T :
|
||||||
@@ -933,9 +934,107 @@ struct ast_hint {
|
|||||||
struct ast_exten *exten; /*!< Extension */
|
struct ast_exten *exten; /*!< Extension */
|
||||||
int laststate; /*!< Last known state */
|
int laststate; /*!< Last known state */
|
||||||
AST_LIST_HEAD_NOLOCK(, ast_state_cb) callbacks; /*!< Callback list for this extension */
|
AST_LIST_HEAD_NOLOCK(, ast_state_cb) callbacks; /*!< Callback list for this extension */
|
||||||
AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define HINTDEVICE_DATA_LENGTH 16
|
||||||
|
AST_THREADSTORAGE(hintdevice_data);
|
||||||
|
|
||||||
|
/* --- Hash tables of various objects --------*/
|
||||||
|
#ifdef LOW_MEMORY
|
||||||
|
static const int HASH_EXTENHINT_SIZE = 17;
|
||||||
|
#else
|
||||||
|
static const int HASH_EXTENHINT_SIZE = 563;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Container for hint devices
|
||||||
|
*/
|
||||||
|
static struct ao2_container *hintdevices;
|
||||||
|
|
||||||
|
/*! \brief Structure for dial plan hint devices
|
||||||
|
|
||||||
|
\note hintdevice is one device pointing to a hint.
|
||||||
|
*/
|
||||||
|
struct ast_hintdevice {
|
||||||
|
|
||||||
|
struct ast_hint *hint;
|
||||||
|
char hintdevice[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \note Using the device for hash
|
||||||
|
*/
|
||||||
|
static int hintdevice_hash_cb(const void *obj, const int flags)
|
||||||
|
{
|
||||||
|
const struct ast_hintdevice *ext = obj;
|
||||||
|
|
||||||
|
return ast_str_case_hash(ext->hintdevice);
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
* \note Devices on hints are not unique so no CMP_STOP is returned
|
||||||
|
* Dont use ao2_find against hintdevices container cause there allways
|
||||||
|
* could be more than one result.
|
||||||
|
*/
|
||||||
|
static int hintdevice_cmp_multiple(void *obj, void *arg, int flags)
|
||||||
|
{
|
||||||
|
struct ast_hintdevice *ext = obj, *ext2 = arg;
|
||||||
|
|
||||||
|
return !strcasecmp(ext->hintdevice, ext2->hintdevice) ? CMP_MATCH : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \details This is used with ao2_callback to remove old devices
|
||||||
|
* when they are linked to the hint
|
||||||
|
*/
|
||||||
|
static int hintdevice_remove_cb(void *deviceobj, void *arg, int flags)
|
||||||
|
{
|
||||||
|
struct ast_hintdevice *device = deviceobj;
|
||||||
|
struct ast_hint *hint = arg;
|
||||||
|
|
||||||
|
return (device->hint == hint) ? CMP_MATCH : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int remove_hintdevice(struct ast_hint *hint)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* iterate through all devices and remove the devices which are linked to this hint */
|
||||||
|
ao2_t_callback(hintdevices, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, hintdevice_remove_cb, hint ,
|
||||||
|
"callback to remove all devices which are linked to a hint");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief add hintdevice structure and link it into the container.
|
||||||
|
*/
|
||||||
|
static int add_hintdevice(struct ast_hint *hint, const char *devicelist)
|
||||||
|
{
|
||||||
|
struct ast_str *str;
|
||||||
|
char *cur=NULL,*parse;
|
||||||
|
struct ast_hintdevice *device;
|
||||||
|
int devicelength = 0;
|
||||||
|
|
||||||
|
if (!(str = ast_str_thread_get(&hintdevice_data, 16))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ast_str_set(&str, 0, "%s", devicelist);
|
||||||
|
parse = ast_str_buffer(str);
|
||||||
|
|
||||||
|
while ((cur = strsep(&parse, "&"))) {
|
||||||
|
devicelength = strlen(cur);
|
||||||
|
if (!(device = ao2_t_alloc(sizeof(*device) + devicelength, NULL, "allocating a hintdevice structure"))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
strcpy(device->hintdevice, cur);
|
||||||
|
device->hint = hint;
|
||||||
|
ao2_t_link(hintdevices, device, "Linking device into hintdevice container.");
|
||||||
|
ao2_t_ref(device, -1, "hintdevice is linked so we can unref");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const struct cfextension_states {
|
static const struct cfextension_states {
|
||||||
int extension_state;
|
int extension_state;
|
||||||
const char * const text;
|
const char * const text;
|
||||||
@@ -1175,14 +1274,15 @@ static AST_RWLIST_HEAD_STATIC(apps, ast_app);
|
|||||||
static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
|
static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
|
||||||
|
|
||||||
static int stateid = 1;
|
static int stateid = 1;
|
||||||
/* WARNING:
|
/*!
|
||||||
When holding this list's lock, do _not_ do anything that will cause conlock
|
* \brief When holding this container's lock, do _not_ do anything that will cause conlock
|
||||||
to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
|
* to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
|
||||||
function will take the locks in conlock/hints order, so any other
|
* function will take the locks in conlock/hints order, so any other
|
||||||
paths that require both locks must also take them in that order.
|
* paths that require both locks must also take them in that order.
|
||||||
*/
|
*/
|
||||||
static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
|
static struct ao2_container *hints;
|
||||||
|
|
||||||
|
/* XXX TODO Convert this to an astobj2 container, too. */
|
||||||
static AST_LIST_HEAD_NOLOCK_STATIC(statecbs, ast_state_cb);
|
static AST_LIST_HEAD_NOLOCK_STATIC(statecbs, ast_state_cb);
|
||||||
|
|
||||||
#ifdef CONTEXT_DEBUG
|
#ifdef CONTEXT_DEBUG
|
||||||
@@ -4222,43 +4322,55 @@ int ast_extension_state(struct ast_channel *c, const char *context, const char *
|
|||||||
static int handle_statechange(void *datap)
|
static int handle_statechange(void *datap)
|
||||||
{
|
{
|
||||||
struct ast_hint *hint;
|
struct ast_hint *hint;
|
||||||
struct ast_str *str;
|
struct ast_hintdevice *device, *cmpdevice;
|
||||||
struct statechange *sc = datap;
|
struct statechange *sc = datap;
|
||||||
|
struct ast_state_cb *cblist;
|
||||||
|
int state;
|
||||||
|
struct ao2_iterator *iterator;
|
||||||
|
|
||||||
if (!(str = ast_str_create(1024))) {
|
if (ao2_container_count(hintdevices) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!(cmpdevice = ast_malloc(sizeof(*cmpdevice) + strlen(sc->dev)))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
strcpy(cmpdevice->hintdevice, sc->dev);
|
||||||
|
|
||||||
ast_rdlock_contexts();
|
|
||||||
AST_RWLIST_RDLOCK(&hints);
|
|
||||||
|
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
iterator = ao2_t_callback(hintdevices,
|
||||||
struct ast_state_cb *cblist;
|
OBJ_POINTER | OBJ_MULTIPLE,
|
||||||
char *cur, *parse;
|
hintdevice_cmp_multiple,
|
||||||
int state;
|
cmpdevice,
|
||||||
|
"find devices in container");
|
||||||
ast_str_set(&str, 0, "%s", ast_get_extension_app(hint->exten));
|
while (iterator && (device = ao2_iterator_next(iterator))) {
|
||||||
parse = str->str;
|
if (!device->hint) {
|
||||||
|
ao2_t_ref(device, -1, "no hint linked to device");
|
||||||
while ( (cur = strsep(&parse, "&")) ) {
|
|
||||||
if (!strcasecmp(cur, sc->dev)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cur) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
hint = device->hint;
|
||||||
/* Get device state for this hint */
|
/* Get device state for this hint */
|
||||||
state = ast_extension_state2(hint->exten);
|
state = ast_extension_state2(hint->exten);
|
||||||
|
|
||||||
if ((state == -1) || (state == hint->laststate)) {
|
if ((state == -1) || (state == hint->laststate)) {
|
||||||
|
ao2_t_ref(device, -1, "no statechange for device");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Device state changed since last check - notify the watchers */
|
/* Device state changed since last check - notify the watchers */
|
||||||
|
|
||||||
|
ast_rdlock_contexts();
|
||||||
|
ao2_lock(hints);
|
||||||
|
ao2_lock(hint);
|
||||||
|
|
||||||
|
if (hint->exten == NULL) {
|
||||||
|
/* the extension has been destroyed */
|
||||||
|
ao2_unlock(hint);
|
||||||
|
ao2_t_ref(device, -1, "linked hint from device has no exten");
|
||||||
|
ao2_unlock(hints);
|
||||||
|
ast_unlock_contexts();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* For general callbacks */
|
/* For general callbacks */
|
||||||
AST_LIST_TRAVERSE(&statecbs, cblist, entry) {
|
AST_LIST_TRAVERSE(&statecbs, cblist, entry) {
|
||||||
cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
|
cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
|
||||||
@@ -4270,11 +4382,17 @@ static int handle_statechange(void *datap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
hint->laststate = state; /* record we saw the change */
|
hint->laststate = state; /* record we saw the change */
|
||||||
}
|
ao2_unlock(hint);
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_t_ref(device, -1, "finished callbacks for device");
|
||||||
|
ao2_unlock(hints);
|
||||||
ast_unlock_contexts();
|
ast_unlock_contexts();
|
||||||
ast_free(str);
|
}
|
||||||
ast_free(sc);
|
if (iterator) {
|
||||||
|
ao2_iterator_destroy(iterator);
|
||||||
|
}
|
||||||
|
if (cmpdevice) {
|
||||||
|
ast_free(cmpdevice);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4288,19 +4406,19 @@ 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) {
|
||||||
AST_RWLIST_WRLOCK(&hints);
|
ao2_lock(hints);
|
||||||
|
|
||||||
AST_LIST_TRAVERSE(&statecbs, cblist, entry) {
|
AST_LIST_TRAVERSE(&statecbs, cblist, entry) {
|
||||||
if (cblist->callback == callback) {
|
if (cblist->callback == callback) {
|
||||||
cblist->data = data;
|
cblist->data = data;
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_unlock(hints);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now insert the callback */
|
/* Now insert the callback */
|
||||||
if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
|
if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_unlock(hints);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
cblist->id = 0;
|
cblist->id = 0;
|
||||||
@@ -4309,8 +4427,7 @@ int ast_extension_state_add(const char *context, const char *exten,
|
|||||||
|
|
||||||
AST_LIST_INSERT_HEAD(&statecbs, cblist, entry);
|
AST_LIST_INSERT_HEAD(&statecbs, cblist, entry);
|
||||||
|
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_unlock(hints);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4338,22 +4455,15 @@ int ast_extension_state_add(const char *context, const char *exten,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find the hint in the list of hints */
|
/* Find the hint in the list of hints */
|
||||||
AST_RWLIST_WRLOCK(&hints);
|
hint = ao2_find(hints, e, 0);
|
||||||
|
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
|
||||||
if (hint->exten == e)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hint) {
|
if (!hint) {
|
||||||
/* We have no hint, sorry */
|
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now insert the callback in the callback list */
|
/* Now insert the callback in the callback list */
|
||||||
if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
|
if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_ref(hint, -1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4361,25 +4471,43 @@ int ast_extension_state_add(const char *context, const char *exten,
|
|||||||
cblist->callback = callback; /* Pointer to callback routine */
|
cblist->callback = callback; /* Pointer to callback routine */
|
||||||
cblist->data = data; /* Data for the callback */
|
cblist->data = data; /* Data for the callback */
|
||||||
|
|
||||||
|
ao2_lock(hint);
|
||||||
AST_LIST_INSERT_HEAD(&hint->callbacks, cblist, entry);
|
AST_LIST_INSERT_HEAD(&hint->callbacks, cblist, entry);
|
||||||
|
ao2_unlock(hint);
|
||||||
|
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_ref(hint, -1);
|
||||||
|
|
||||||
return cblist->id;
|
return cblist->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Remove a watcher from the callback list */
|
/*! \brief Find Hint by callback id */
|
||||||
|
static int find_hint_by_cb_id(void *obj, void *arg, int flags)
|
||||||
|
{
|
||||||
|
const struct ast_hint *hint = obj;
|
||||||
|
int *id = arg;
|
||||||
|
struct ast_state_cb *cb;
|
||||||
|
|
||||||
|
AST_LIST_TRAVERSE(&hint->callbacks, cb, entry) {
|
||||||
|
if (cb->id == *id) {
|
||||||
|
return CMP_MATCH | CMP_STOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \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 callback)
|
||||||
{
|
{
|
||||||
struct ast_state_cb *p_cur = NULL;
|
struct ast_state_cb *p_cur = NULL;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
if (!id && !callback)
|
if (!id && !callback) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
AST_RWLIST_WRLOCK(&hints);
|
|
||||||
|
|
||||||
if (!id) { /* id == 0 is a callback without extension */
|
if (!id) { /* id == 0 is a callback without extension */
|
||||||
|
ao2_lock(hints);
|
||||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&statecbs, p_cur, entry) {
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&statecbs, p_cur, entry) {
|
||||||
if (p_cur->callback == callback) {
|
if (p_cur->callback == callback) {
|
||||||
AST_LIST_REMOVE_CURRENT(entry);
|
AST_LIST_REMOVE_CURRENT(entry);
|
||||||
@@ -4387,19 +4515,25 @@ int ast_extension_state_del(int id, ast_state_cb_type callback)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
AST_LIST_TRAVERSE_SAFE_END;
|
AST_LIST_TRAVERSE_SAFE_END;
|
||||||
|
ao2_unlock(hints);
|
||||||
} else { /* callback with extension, find the callback based on ID */
|
} else { /* callback with extension, find the callback based on ID */
|
||||||
struct ast_hint *hint;
|
struct ast_hint *hint;
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
|
||||||
|
hint = ao2_callback(hints, 0, find_hint_by_cb_id, &id);
|
||||||
|
|
||||||
|
if (hint) {
|
||||||
|
ao2_lock(hint);
|
||||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&hint->callbacks, p_cur, entry) {
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&hint->callbacks, p_cur, entry) {
|
||||||
if (p_cur->id == id) {
|
if (p_cur->id == id) {
|
||||||
AST_LIST_REMOVE_CURRENT(entry);
|
AST_LIST_REMOVE_CURRENT(entry);
|
||||||
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AST_LIST_TRAVERSE_SAFE_END;
|
AST_LIST_TRAVERSE_SAFE_END;
|
||||||
|
|
||||||
if (p_cur)
|
ao2_unlock(hint);
|
||||||
break;
|
ao2_ref(hint, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4407,70 +4541,73 @@ int ast_extension_state_del(int id, ast_state_cb_type callback)
|
|||||||
ast_free(p_cur);
|
ast_free(p_cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Add hint to hint list, check initial extension state; the hints had better be WRLOCKED already! */
|
|
||||||
static int ast_add_hint_nolock(struct ast_exten *e)
|
|
||||||
{
|
|
||||||
struct ast_hint *hint;
|
|
||||||
|
|
||||||
if (!e)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* Search if hint exists, do nothing */
|
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
|
||||||
if (hint->exten == e) {
|
|
||||||
ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
|
|
||||||
|
|
||||||
if (!(hint = ast_calloc(1, sizeof(*hint)))) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
/* Initialize and insert new item at the top */
|
|
||||||
hint->exten = e;
|
|
||||||
hint->laststate = ast_extension_state2(e);
|
|
||||||
AST_RWLIST_INSERT_HEAD(&hints, hint, list);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! \brief Add hint to hint list, check initial extension state */
|
/*! \brief Add hint to hint list, check initial extension state */
|
||||||
static int ast_add_hint(struct ast_exten *e)
|
static int ast_add_hint(struct ast_exten *e)
|
||||||
{
|
{
|
||||||
int ret;
|
struct ast_hint *hint;
|
||||||
|
|
||||||
AST_RWLIST_WRLOCK(&hints);
|
if (!e) {
|
||||||
ret = ast_add_hint_nolock(e);
|
return -1;
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Search if hint exists, do nothing */
|
||||||
|
hint = ao2_find(hints, e, 0);
|
||||||
|
if (hint) {
|
||||||
|
ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
|
||||||
|
ao2_ref(hint, -1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
|
||||||
|
|
||||||
|
if (!(hint = ao2_alloc(sizeof(*hint), NULL))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize and insert new item at the top */
|
||||||
|
hint->exten = e;
|
||||||
|
hint->laststate = ast_extension_state2(e);
|
||||||
|
|
||||||
|
ao2_link(hints, hint);
|
||||||
|
|
||||||
|
if (add_hintdevice(hint, ast_get_extension_app(e))) {
|
||||||
|
ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
|
||||||
|
ast_get_extension_name(e),
|
||||||
|
ast_get_context_name(ast_get_extension_context(e)));
|
||||||
|
}
|
||||||
|
ao2_ref(hint, -1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Change hint for an extension */
|
/*! \brief Change hint for an extension */
|
||||||
static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
|
static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
|
||||||
{
|
{
|
||||||
struct ast_hint *hint;
|
struct ast_hint *hint;
|
||||||
int res = -1;
|
|
||||||
|
|
||||||
AST_RWLIST_WRLOCK(&hints);
|
hint = ao2_find(hints, oe, 0);
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
|
||||||
if (hint->exten == oe) {
|
if (!hint) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ao2_lock(hint);
|
||||||
hint->exten = ne;
|
hint->exten = ne;
|
||||||
res = 0;
|
remove_hintdevice(hint);
|
||||||
break;
|
if (add_hintdevice(hint, ast_get_extension_app(ne))) {
|
||||||
|
ast_log(LOG_WARNING, "Could not add devices for hint: %s@%s.\n",
|
||||||
|
ast_get_extension_name(ne),
|
||||||
|
ast_get_context_name(ast_get_extension_context(ne)));
|
||||||
}
|
}
|
||||||
}
|
ao2_unlock(hint);
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_ref(hint, -1);
|
||||||
|
|
||||||
return res;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Remove hint from extension */
|
/*! \brief Remove hint from extension */
|
||||||
@@ -4479,14 +4616,17 @@ static int ast_remove_hint(struct ast_exten *e)
|
|||||||
/* Cleanup the Notifys if hint is removed */
|
/* Cleanup the Notifys if hint is removed */
|
||||||
struct ast_hint *hint;
|
struct ast_hint *hint;
|
||||||
struct ast_state_cb *cblist;
|
struct ast_state_cb *cblist;
|
||||||
int res = -1;
|
|
||||||
|
|
||||||
if (!e)
|
if (!e) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
|
hint = ao2_find(hints, e, 0);
|
||||||
if (hint->exten != e)
|
|
||||||
continue;
|
if (!hint) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ao2_lock(hint);
|
||||||
|
|
||||||
while ((cblist = AST_LIST_REMOVE_HEAD(&hint->callbacks, entry))) {
|
while ((cblist = AST_LIST_REMOVE_HEAD(&hint->callbacks, entry))) {
|
||||||
/* Notify with -1 and remove all callbacks */
|
/* Notify with -1 and remove all callbacks */
|
||||||
@@ -4494,17 +4634,13 @@ static int ast_remove_hint(struct ast_exten *e)
|
|||||||
AST_EXTENSION_DEACTIVATED, cblist->data);
|
AST_EXTENSION_DEACTIVATED, cblist->data);
|
||||||
ast_free(cblist);
|
ast_free(cblist);
|
||||||
}
|
}
|
||||||
|
remove_hintdevice(hint);
|
||||||
|
hint->exten = NULL;
|
||||||
|
ao2_unlink(hints, hint);
|
||||||
|
ao2_unlock(hint);
|
||||||
|
ao2_ref(hint, -1);
|
||||||
|
|
||||||
AST_RWLIST_REMOVE_CURRENT(list);
|
return 0;
|
||||||
ast_free(hint);
|
|
||||||
|
|
||||||
res = 0;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -5721,6 +5857,7 @@ static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_
|
|||||||
int num = 0;
|
int num = 0;
|
||||||
int watchers;
|
int watchers;
|
||||||
struct ast_state_cb *watcher;
|
struct ast_state_cb *watcher;
|
||||||
|
struct ao2_iterator i;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CLI_INIT:
|
case CLI_INIT:
|
||||||
@@ -5733,15 +5870,16 @@ static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_RWLIST_RDLOCK(&hints);
|
if (ao2_container_count(hints) == 0) {
|
||||||
if (AST_RWLIST_EMPTY(&hints)) {
|
|
||||||
ast_cli(a->fd, "There are no registered dialplan hints\n");
|
ast_cli(a->fd, "There are no registered dialplan hints\n");
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
}
|
}
|
||||||
/* ... we have hints ... */
|
/* ... we have hints ... */
|
||||||
ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
|
ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
|
||||||
|
i = ao2_iterator_init(hints, 0);
|
||||||
|
for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
|
||||||
|
|
||||||
watchers = 0;
|
watchers = 0;
|
||||||
AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
|
AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
|
||||||
watchers++;
|
watchers++;
|
||||||
@@ -5753,9 +5891,10 @@ static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_
|
|||||||
ast_extension_state2str(hint->laststate), watchers);
|
ast_extension_state2str(hint->laststate), watchers);
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ao2_iterator_destroy(&i);
|
||||||
ast_cli(a->fd, "----------------\n");
|
ast_cli(a->fd, "----------------\n");
|
||||||
ast_cli(a->fd, "- %d hints registered\n", num);
|
ast_cli(a->fd, "- %d hints registered\n", num);
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5766,21 +5905,24 @@ static char *complete_core_show_hint(const char *line, const char *word, int pos
|
|||||||
char *ret = NULL;
|
char *ret = NULL;
|
||||||
int which = 0;
|
int which = 0;
|
||||||
int wordlen;
|
int wordlen;
|
||||||
|
struct ao2_iterator i;
|
||||||
|
|
||||||
if (pos != 3)
|
if (pos != 3)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
wordlen = strlen(word);
|
wordlen = strlen(word);
|
||||||
|
|
||||||
AST_RWLIST_RDLOCK(&hints);
|
|
||||||
/* walk through all hints */
|
/* walk through all hints */
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
i = ao2_iterator_init(hints, 0);
|
||||||
if (!strncasecmp(word, ast_get_extension_name(hint->exten), wordlen) && ++which > state) {
|
for (hint = ao2_iterator_next(&i); hint; ao2_unlock(hint), ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
|
||||||
|
ao2_lock(hint);
|
||||||
|
if (!strncasecmp(word, hint->exten ? ast_get_extension_name(hint->exten) : "", wordlen) && ++which > state) {
|
||||||
ret = ast_strdup(ast_get_extension_name(hint->exten));
|
ret = ast_strdup(ast_get_extension_name(hint->exten));
|
||||||
|
ao2_unlock(hint);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_iterator_destroy(&i);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -5792,6 +5934,7 @@ static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_a
|
|||||||
int watchers;
|
int watchers;
|
||||||
int num = 0, extenlen;
|
int num = 0, extenlen;
|
||||||
struct ast_state_cb *watcher;
|
struct ast_state_cb *watcher;
|
||||||
|
struct ao2_iterator i;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CLI_INIT:
|
case CLI_INIT:
|
||||||
@@ -5807,15 +5950,16 @@ static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_a
|
|||||||
if (a->argc < 4)
|
if (a->argc < 4)
|
||||||
return CLI_SHOWUSAGE;
|
return CLI_SHOWUSAGE;
|
||||||
|
|
||||||
AST_RWLIST_RDLOCK(&hints);
|
if (ao2_container_count(hints) == 0) {
|
||||||
if (AST_RWLIST_EMPTY(&hints)) {
|
|
||||||
ast_cli(a->fd, "There are no registered dialplan hints\n");
|
ast_cli(a->fd, "There are no registered dialplan hints\n");
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
extenlen = strlen(a->argv[3]);
|
extenlen = strlen(a->argv[3]);
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
i = ao2_iterator_init(hints, 0);
|
||||||
if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) {
|
for (hint = ao2_iterator_next(&i); hint; ao2_unlock(hint), ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
|
||||||
|
ao2_lock(hint);
|
||||||
|
if (!strncasecmp(hint->exten ? ast_get_extension_name(hint->exten) : "", a->argv[3], extenlen)) {
|
||||||
watchers = 0;
|
watchers = 0;
|
||||||
AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
|
AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
|
||||||
watchers++;
|
watchers++;
|
||||||
@@ -5828,7 +5972,7 @@ static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_a
|
|||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_iterator_destroy(&i);
|
||||||
if (!num)
|
if (!num)
|
||||||
ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]);
|
ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]);
|
||||||
else
|
else
|
||||||
@@ -7020,6 +7164,7 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
int length;
|
int length;
|
||||||
struct ast_state_cb *thiscb;
|
struct ast_state_cb *thiscb;
|
||||||
struct ast_hashtab_iter *iter;
|
struct ast_hashtab_iter *iter;
|
||||||
|
struct ao2_iterator i;
|
||||||
|
|
||||||
/* it is very important that this function hold the hint list lock _and_ the conlock
|
/* it is very important that this function hold the hint list lock _and_ the conlock
|
||||||
during its operation; not only do we need to ensure that the list of contexts
|
during its operation; not only do we need to ensure that the list of contexts
|
||||||
@@ -7040,15 +7185,23 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
}
|
}
|
||||||
ast_hashtab_end_traversal(iter);
|
ast_hashtab_end_traversal(iter);
|
||||||
|
|
||||||
AST_RWLIST_WRLOCK(&hints);
|
ao2_lock(hints);
|
||||||
writelocktime = ast_tvnow();
|
writelocktime = ast_tvnow();
|
||||||
|
|
||||||
/* preserve all watchers for hints */
|
/* preserve all watchers for hints */
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
i = ao2_iterator_init(hints, AO2_ITERATOR_DONTLOCK);
|
||||||
|
for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
|
||||||
if (!AST_LIST_EMPTY(&hint->callbacks)) {
|
if (!AST_LIST_EMPTY(&hint->callbacks)) {
|
||||||
length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
|
length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
|
||||||
if (!(this = ast_calloc(1, length)))
|
if (!(this = ast_calloc(1, length))) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
ao2_lock(hint);
|
||||||
|
|
||||||
|
if (hint->exten == NULL) {
|
||||||
|
ao2_unlock(hint);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
/* this removes all the callbacks from the hint into this. */
|
/* this removes all the callbacks from the hint into this. */
|
||||||
AST_LIST_APPEND_LIST(&this->callbacks, &hint->callbacks, entry);
|
AST_LIST_APPEND_LIST(&this->callbacks, &hint->callbacks, entry);
|
||||||
this->laststate = hint->laststate;
|
this->laststate = hint->laststate;
|
||||||
@@ -7056,6 +7209,7 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
strcpy(this->data, hint->exten->parent->name);
|
strcpy(this->data, hint->exten->parent->name);
|
||||||
this->exten = this->data + strlen(this->context) + 1;
|
this->exten = this->data + strlen(this->context) + 1;
|
||||||
strcpy(this->exten, hint->exten->exten);
|
strcpy(this->exten, hint->exten->exten);
|
||||||
|
ao2_unlock(hint);
|
||||||
AST_LIST_INSERT_HEAD(&store, this, list);
|
AST_LIST_INSERT_HEAD(&store, this, list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7086,10 +7240,7 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find the hint in the list of hints */
|
/* Find the hint in the list of hints */
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
hint = ao2_find(hints, exten, 0);
|
||||||
if (hint->exten == exten)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!exten || !hint) {
|
if (!exten || !hint) {
|
||||||
/* this hint has been removed, notify the watchers */
|
/* this hint has been removed, notify the watchers */
|
||||||
while ((thiscb = AST_LIST_REMOVE_HEAD(&this->callbacks, entry))) {
|
while ((thiscb = AST_LIST_REMOVE_HEAD(&this->callbacks, entry))) {
|
||||||
@@ -7097,13 +7248,18 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
|
|||||||
ast_free(thiscb);
|
ast_free(thiscb);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
ao2_lock(hint);
|
||||||
AST_LIST_APPEND_LIST(&hint->callbacks, &this->callbacks, entry);
|
AST_LIST_APPEND_LIST(&hint->callbacks, &this->callbacks, entry);
|
||||||
hint->laststate = this->laststate;
|
hint->laststate = this->laststate;
|
||||||
|
ao2_unlock(hint);
|
||||||
}
|
}
|
||||||
ast_free(this);
|
ast_free(this);
|
||||||
|
if (hint) {
|
||||||
|
ao2_ref(hint, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_unlock(hints);
|
||||||
ast_unlock_contexts();
|
ast_unlock_contexts();
|
||||||
endlocktime = ast_tvnow();
|
endlocktime = ast_tvnow();
|
||||||
|
|
||||||
@@ -7973,7 +8129,7 @@ static int add_pri_lockopt(struct ast_context *con, struct ast_exten *tmp,
|
|||||||
if (lockhints) {
|
if (lockhints) {
|
||||||
ast_add_hint(tmp);
|
ast_add_hint(tmp);
|
||||||
} else {
|
} else {
|
||||||
ast_add_hint_nolock(tmp);
|
ast_add_hint(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8199,7 +8355,7 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
|
|||||||
if (lockhints) {
|
if (lockhints) {
|
||||||
ast_add_hint(tmp);
|
ast_add_hint(tmp);
|
||||||
} else {
|
} else {
|
||||||
ast_add_hint_nolock(tmp);
|
ast_add_hint(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9773,14 +9929,14 @@ static int hints_data_provider_get(const struct ast_data_search *search,
|
|||||||
struct ast_hint *hint;
|
struct ast_hint *hint;
|
||||||
int watchers;
|
int watchers;
|
||||||
struct ast_state_cb *watcher;
|
struct ast_state_cb *watcher;
|
||||||
|
struct ao2_iterator i;
|
||||||
|
|
||||||
AST_RWLIST_RDLOCK(&hints);
|
if (ao2_container_count(hints) == 0) {
|
||||||
if (AST_RWLIST_EMPTY(&hints)) {
|
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_RWLIST_TRAVERSE(&hints, hint, list) {
|
i = ao2_iterator_init(hints, 0);
|
||||||
|
for (hint = ao2_iterator_next(&i); hint; ao2_ref(hint, -1), hint = ao2_iterator_next(&i)) {
|
||||||
watchers = 0;
|
watchers = 0;
|
||||||
AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
|
AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) {
|
||||||
watchers++;
|
watchers++;
|
||||||
@@ -9799,7 +9955,7 @@ static int hints_data_provider_get(const struct ast_data_search *search,
|
|||||||
ast_data_remove_node(data_root, data_hint);
|
ast_data_remove_node(data_root, data_hint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AST_RWLIST_UNLOCK(&hints);
|
ao2_iterator_destroy(&i);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -10169,3 +10325,32 @@ char *ast_complete_applications(const char *line, const char *word, int state)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hint_hash(const void *obj, const int flags)
|
||||||
|
{
|
||||||
|
const struct ast_hint *hint = obj;
|
||||||
|
|
||||||
|
int res = -1;
|
||||||
|
|
||||||
|
if (ast_get_extension_name(hint->exten)) {
|
||||||
|
res = ast_str_case_hash(ast_get_extension_name(hint->exten));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hint_cmp(void *obj, void *arg, int flags)
|
||||||
|
{
|
||||||
|
const struct ast_hint *hint = obj;
|
||||||
|
const struct ast_exten *exten = arg;
|
||||||
|
|
||||||
|
return (hint->exten == exten) ? CMP_MATCH | CMP_STOP : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ast_pbx_init(void)
|
||||||
|
{
|
||||||
|
hints = ao2_container_alloc(HASH_EXTENHINT_SIZE, hint_hash, hint_cmp);
|
||||||
|
hintdevices = ao2_container_alloc(HASH_EXTENHINT_SIZE, hintdevice_hash_cb, hintdevice_cmp_multiple);
|
||||||
|
|
||||||
|
return (hints && hintdevices) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user