queue device state changes and handle them serially in a background thread

optimize device state related functions
add ast_get_channel_by_name_prefix to allow searching for matching channels in O(1) operation


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6062 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.2-netsec
Kevin P. Fleming 20 years ago
parent 0111a38e4b
commit c13d601fb4

@ -254,7 +254,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \
cdr.o tdd.o acl.o rtp.o manager.o asterisk.o \ cdr.o tdd.o acl.o rtp.o manager.o asterisk.o \
dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \ dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
utils.o config_old.o plc.o jitterbuf.o dnsmgr.o utils.o config_old.o plc.o jitterbuf.o dnsmgr.o devicestate.o
ifeq (${OSARCH},Darwin) ifeq (${OSARCH},Darwin)
OBJS+=poll.o dlfcn.o OBJS+=poll.o dlfcn.o
ASTLINK=-Wl,-dynamic ASTLINK=-Wl,-dynamic

@ -31,6 +31,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h" #include "asterisk/pbx.h"
#include "asterisk/module.h" #include "asterisk/module.h"
#include "asterisk/app.h" #include "asterisk/app.h"
#include "asterisk/devicestate.h"
static char *tdesc = "Check if channel is available"; static char *tdesc = "Check if channel is available";

@ -71,6 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/causes.h" #include "asterisk/causes.h"
#include "asterisk/astdb.h" #include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#define QUEUE_STRATEGY_RINGALL 0 #define QUEUE_STRATEGY_RINGALL 0
#define QUEUE_STRATEGY_ROUNDROBIN 1 #define QUEUE_STRATEGY_ROUNDROBIN 1

@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/version.h" #include "asterisk/version.h"
#include "asterisk/build.h" #include "asterisk/build.h"
#include "asterisk/linkedlists.h" #include "asterisk/linkedlists.h"
#include "asterisk/devicestate.h"
#include "defaults.h" #include "defaults.h"
@ -2095,6 +2096,10 @@ int main(int argc, char *argv[])
printf(term_quit()); printf(term_quit());
exit(1); exit(1);
} }
if (ast_device_state_engine_init()) {
printf(term_quit());
exit(1);
}
ast_rtp_init(); ast_rtp_init();
if (ast_image_init()) { if (ast_image_init()) {
printf(term_quit()); printf(term_quit());

@ -57,6 +57,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/lock.h" #include "asterisk/lock.h"
#include "asterisk/app.h" #include "asterisk/app.h"
#include "asterisk/transcap.h" #include "asterisk/transcap.h"
#include "asterisk/devicestate.h"
/* uncomment if you have problems with 'monitoring' synchronized files */ /* uncomment if you have problems with 'monitoring' synchronized files */
#if 0 #if 0
@ -295,6 +296,58 @@ int ast_channel_register(const struct ast_channel_tech *tech)
return 0; return 0;
} }
void ast_channel_unregister(const struct ast_channel_tech *tech)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
ast_mutex_lock(&chlock);
chan = backends;
while (chan) {
if (chan->tech == tech) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
ast_mutex_unlock(&chlock);
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
return;
}
last = chan;
chan = chan->next;
}
ast_mutex_unlock(&chlock);
}
const struct ast_channel_tech *ast_get_channel_tech(const char *name)
{
struct chanlist *chanls;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel tech list\n");
return NULL;
}
for (chanls = backends; chanls; chanls = chanls->next) {
if (strcasecmp(name, chanls->tech->type))
continue;
ast_mutex_unlock(&chlock);
return chanls->tech;
}
ast_mutex_unlock(&chlock);
return NULL;
}
/*--- ast_cause2str: Gives the string form of a given hangup cause */ /*--- ast_cause2str: Gives the string form of a given hangup cause */
const char *ast_cause2str(int cause) const char *ast_cause2str(int cause)
{ {
@ -605,7 +658,7 @@ void ast_channel_undefer_dtmf(struct ast_channel *chan)
* We should definitely go for a better scheme that is deadlock-free. * We should definitely go for a better scheme that is deadlock-free.
*/ */
static struct ast_channel *channel_find_locked(const struct ast_channel *prev, static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
const char *name) const char *name, const int namelen)
{ {
const char *msg = prev ? "deadlock" : "initial deadlock"; const char *msg = prev ? "deadlock" : "initial deadlock";
int retries, done; int retries, done;
@ -615,8 +668,14 @@ static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
ast_mutex_lock(&chlock); ast_mutex_lock(&chlock);
for (c = channels; c; c = c->next) { for (c = channels; c; c = c->next) {
if (prev == NULL) { if (prev == NULL) {
/* want either head of list or match by name */ /* want head of list */
if (name == NULL || !strcasecmp(name, c->name)) if (!name)
break;
/* want match by full name */
if (!namelen && !strcasecmp(c->name, name))
break;
/* want match by name prefix */
if (!strncasecmp(c->name, name, namelen))
break; break;
} else if (c == prev) { /* found, return c->next */ } else if (c == prev) { /* found, return c->next */
c = c->next; c = c->next;
@ -646,13 +705,19 @@ static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
/*--- ast_channel_walk_locked: Browse channels in use */ /*--- ast_channel_walk_locked: Browse channels in use */
struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev) struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
{ {
return channel_find_locked(prev, NULL); return channel_find_locked(prev, NULL, 0);
} }
/*--- ast_get_channel_by_name_locked: Get channel by name and lock it */ /*--- ast_get_channel_by_name_locked: Get channel by name and lock it */
struct ast_channel *ast_get_channel_by_name_locked(const char *name) struct ast_channel *ast_get_channel_by_name_locked(const char *name)
{ {
return channel_find_locked(NULL, name); return channel_find_locked(NULL, name, 0);
}
/*--- ast_get_channel_by_name_prefix_locked: Get channel by name prefix and lock it */
struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen)
{
return channel_find_locked(NULL, name, namelen);
} }
/*--- ast_safe_sleep_conditional: Wait, look for hangups and condition arg */ /*--- ast_safe_sleep_conditional: Wait, look for hangups and condition arg */
@ -964,37 +1029,6 @@ int ast_hangup(struct ast_channel *chan)
return res; return res;
} }
void ast_channel_unregister(const struct ast_channel_tech *tech)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
ast_mutex_lock(&chlock);
chan = backends;
while (chan) {
if (chan->tech == tech) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
ast_mutex_unlock(&chlock);
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
return;
}
last = chan;
chan = chan->next;
}
ast_mutex_unlock(&chlock);
}
int ast_answer(struct ast_channel *chan) int ast_answer(struct ast_channel *chan)
{ {
int res = 0; int res = 0;
@ -2167,70 +2201,6 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *c
return c; return c;
} }
int ast_parse_device_state(char *device)
{
char name[AST_CHANNEL_NAME] = "";
char *cut;
struct ast_channel *chan;
chan = ast_channel_walk_locked(NULL);
while (chan) {
ast_copy_string(name, chan->name, sizeof(name));
ast_mutex_unlock(&chan->lock);
cut = strchr(name,'-');
if (cut)
*cut = 0;
if (!strcmp(name, device)) {
if (chan->_state == AST_STATE_RINGING) {
return AST_DEVICE_RINGING;
} else {
return AST_DEVICE_INUSE;
}
}
chan = ast_channel_walk_locked(chan);
}
return AST_DEVICE_UNKNOWN;
}
int ast_device_state(char *device)
{
char tech[AST_MAX_EXTENSION] = "";
char *number;
struct chanlist *chanls;
int res = 0;
ast_copy_string(tech, device, sizeof(tech));
number = strchr(tech, '/');
if (!number) {
return AST_DEVICE_INVALID;
}
*number = 0;
number++;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chanls = backends;
while(chanls) {
if (!strcasecmp(tech, chanls->tech->type)) {
ast_mutex_unlock(&chlock);
if (!chanls->tech->devicestate)
return ast_parse_device_state(device);
else {
res = chanls->tech->devicestate(number);
if (res == AST_DEVICE_UNKNOWN)
return ast_parse_device_state(device);
else
return res;
}
}
chanls = chanls->next;
}
ast_mutex_unlock(&chlock);
return AST_DEVICE_INVALID;
}
int ast_call(struct ast_channel *chan, char *addr, int timeout) int ast_call(struct ast_channel *chan, char *addr, int timeout)
{ {
/* Place an outgoing call, but don't wait any longer than timeout ms before returning. /* Place an outgoing call, but don't wait any longer than timeout ms before returning.

@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/causes.h" #include "asterisk/causes.h"
#include "asterisk/astdb.h" #include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
static const char desc[] = "Agent Proxy Channel"; static const char desc[] = "Agent Proxy Channel";
static const char channeltype[] = "Agent"; static const char channeltype[] = "Agent";

@ -42,6 +42,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/localtime.h" #include "asterisk/localtime.h"
#include "asterisk/aes.h" #include "asterisk/aes.h"
#include "asterisk/dnsmgr.h" #include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#include <sys/mman.h> #include <sys/mman.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <dirent.h> #include <dirent.h>
@ -73,7 +75,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "iax2.h" #include "iax2.h"
#include "iax2-parser.h" #include "iax2-parser.h"
#include "iax2-provision.h" #include "iax2-provision.h"
#include "asterisk.h"
/* Define NEWJB to use the new channel independent jitterbuffer, /* Define NEWJB to use the new channel independent jitterbuffer,
* otherwise, use the old jitterbuffer */ * otherwise, use the old jitterbuffer */

@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/file.h" #include "asterisk/file.h"
#include "asterisk/astobj.h" #include "asterisk/astobj.h"
#include "asterisk/dnsmgr.h" #include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#ifdef OSP_SUPPORT #ifdef OSP_SUPPORT
#include "asterisk/astosp.h" #include "asterisk/astosp.h"
#endif #endif

@ -444,21 +444,6 @@ struct outgoing_helper {
/*! Do not transmit voice data */ /*! Do not transmit voice data */
#define AST_STATE_MUTE (1 << 16) #define AST_STATE_MUTE (1 << 16)
/*! Device is valid but channel didn't know state */
#define AST_DEVICE_UNKNOWN 0
/*! Device is not used */
#define AST_DEVICE_NOT_INUSE 1
/*! Device is in use */
#define AST_DEVICE_INUSE 2
/*! Device is busy */
#define AST_DEVICE_BUSY 3
/*! Device is invalid */
#define AST_DEVICE_INVALID 4
/*! Device is unavailable */
#define AST_DEVICE_UNAVAILABLE 5
/*! Device is ringing */
#define AST_DEVICE_RINGING 6
/*! Create a channel structure */ /*! Create a channel structure */
/*! Returns NULL on failure to allocate. New channels are /*! Returns NULL on failure to allocate. New channels are
by default set to the "default" context and by default set to the "default" context and
@ -494,27 +479,6 @@ void ast_channel_free(struct ast_channel *);
*/ */
struct ast_channel *ast_request(const char *type, int format, void *data, int *status); struct ast_channel *ast_request(const char *type, int format, void *data, int *status);
/*! Search the Channels by Name */
/*!
* \param device like a dialstring
* Search the Device in active channels by compare the channelname against
* the devicename. Compared are only the first chars to the first '-' char.
* Returns an AST_DEVICE_UNKNOWN if no channel found or
* AST_DEVICE_INUSE if a channel is found
*/
int ast_parse_device_state(char *device);
/*! Asks a channel for device state */
/*!
* \param device like a dialstring
* Asks a channel for device state, data is normaly a number from dialstring
* used by the low level module
* Trys the channel devicestate callback if not supported search in the
* active channels list for the device.
* Returns an AST_DEVICE_??? state -1 on failure
*/
int ast_device_state(char *device);
/*! /*!
* \param type type of channel to request * \param type type of channel to request
* \param format requested channel format * \param format requested channel format
@ -545,6 +509,12 @@ int ast_channel_register(const struct ast_channel_tech *tech);
*/ */
void ast_channel_unregister(const struct ast_channel_tech *tech); void ast_channel_unregister(const struct ast_channel_tech *tech);
/*! Get a channel technology structure by name
* \param name name of technology to find
* \return a pointer to the structure, or NULL if no matching technology found
*/
const struct ast_channel_tech *ast_get_channel_tech(const char *name);
/*! Hang up a channel */ /*! Hang up a channel */
/*! /*!
* \param chan channel to hang up * \param chan channel to hang up
@ -759,6 +729,9 @@ struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev);
/*! Get channel by name (locks channel) */ /*! Get channel by name (locks channel) */
struct ast_channel *ast_get_channel_by_name_locked(const char *chan); struct ast_channel *ast_get_channel_by_name_locked(const char *chan);
/*! Get channel by name prefix (locks channel) */
struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen);
/*! Waits for a digit */ /*! Waits for a digit */
/*! /*!
* \param c channel to wait for a digit on * \param c channel to wait for a digit on

@ -306,11 +306,13 @@ struct { \
used to link entries of this list together. used to link entries of this list together.
Removes the head entry from the list, and returns a pointer to it. The Removes the head entry from the list, and returns a pointer to it. The
forward-link pointer in the returned entry is \b not cleared. forward-link pointer in the returned entry is \b not cleared. This macro
is safe to call on an empty list.
*/ */
#define AST_LIST_REMOVE_HEAD(head, field) ({ \ #define AST_LIST_REMOVE_HEAD(head, field) ({ \
typeof((head)->first) cur = (head)->first; \ typeof((head)->first) cur = (head)->first; \
(head)->first = (head)->first->field.next; \ if (cur) \
(head)->first = cur->field.next; \
cur; \ cur; \
}) })

@ -51,8 +51,6 @@ struct ast_sw;
typedef int (*ast_state_cb_type)(char *context, char* id, int state, void *data); typedef int (*ast_state_cb_type)(char *context, char* id, int state, void *data);
typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data);
/*! Data structure associated with a custom function */ /*! Data structure associated with a custom function */
struct ast_custom_function { struct ast_custom_function {
char *name; char *name;
@ -253,16 +251,6 @@ int ast_unregister_application(const char *app);
*/ */
int ast_extension_state(struct ast_channel *c, char *context, char *exten); int ast_extension_state(struct ast_channel *c, char *context, char *exten);
/*! Tells Asterisk the State for Device is changed */
/*!
* \param fmt devicename like a dialstring with format parameters
* Asterisk polls the new extensionstates and calls the registered
* callbacks for the changed extensions
* Returns 0 on success, -1 on failure
*/
int ast_device_state_changed(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
/*! Registers a state change callback */ /*! Registers a state change callback */
/*! /*!
* \param context which context to look in * \param context which context to look in
@ -275,15 +263,6 @@ int ast_device_state_changed(const char *fmt, ...)
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 callback, void *data);
/*! Registers a device state change callback */
/*!
* \param data to pass to callback
* The callback is called if the state for extension is changed
* Return -1 on failure, ID on success
*/
int ast_devstate_add(ast_devstate_cb_type callback, void *data);
void ast_devstate_del(ast_devstate_cb_type callback, void *data);
/*! Deletes a registered state change callback by ID */ /*! Deletes a registered state change callback by ID */
/*! /*!
* \param id of the callback to delete * \param id of the callback to delete
@ -629,6 +608,8 @@ char *ast_func_read(struct ast_channel *chan, const char *in, char *workspace, s
*/ */
void ast_func_write(struct ast_channel *chan, const char *in, const char *value); void ast_func_write(struct ast_channel *chan, const char *in, const char *value);
void ast_hint_state_changed(const char *device);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} }
#endif #endif

134
pbx.c

@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/causes.h" #include "asterisk/causes.h"
#include "asterisk/musiconhold.h" #include "asterisk/musiconhold.h"
#include "asterisk/app.h" #include "asterisk/app.h"
#include "asterisk/devicestate.h"
/* /*
* I M P O R T A N T : * I M P O R T A N T :
@ -160,15 +161,6 @@ struct ast_state_cb {
struct ast_state_cb *next; struct ast_state_cb *next;
}; };
/* ast_devstate_cb: An extension state notify */
struct ast_devstate_cb {
void *data;
ast_devstate_cb_type callback;
struct ast_devstate_cb *next;
};
static struct ast_devstate_cb *devcbs;
/* Hints are pointers from an extension in the dialplan to one or more devices (tech/name) */ /* Hints are pointers from an extension in the dialplan to one or more devices (tech/name) */
struct ast_hint { struct ast_hint {
struct ast_exten *exten; /* Extension */ struct ast_exten *exten; /* Extension */
@ -1805,127 +1797,45 @@ int ast_extension_state(struct ast_channel *c, char *context, char *exten)
return ast_extension_state2(e); /* Check all devices in the hint */ return ast_extension_state2(e); /* Check all devices in the hint */
} }
/*--- ast_device_state_changed: If device state in cblist is changed - then notify callback function */ void ast_hint_state_changed(const char *device)
int ast_device_state_changed(const char *fmt, ...)
{ {
struct ast_hint *list; struct ast_hint *hint;
struct ast_state_cb *cblist; struct ast_state_cb *cblist;
struct ast_devstate_cb *devcb; char buf[AST_MAX_EXTENSION];
char hint[AST_MAX_EXTENSION] = ""; char *parse;
char device[AST_MAX_EXTENSION]; char *cur;
char *cur, *rest;
int state; int state;
va_list ap;
va_start(ap, fmt);
vsnprintf(device, sizeof(device), fmt, ap);
va_end(ap);
rest = strchr(device, '-');
if (rest) {
*rest = 0;
}
state = ast_device_state(device);
if (option_debug > 2)
ast_log(LOG_DEBUG, "Changing state for %s - state %d\n", device, state);
ast_mutex_lock(&hintlock); ast_mutex_lock(&hintlock);
/* First check device callbacks */ for (hint = hints; hint; hint = hint->next) {
devcb = devcbs; ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
while(devcb) { parse = buf;
if (devcb->callback) for (cur = strsep(&parse, "&"); cur; cur = strsep(&parse, "&")) {
devcb->callback(device, state, devcb->data); if (strcmp(cur, device))
devcb = devcb->next; continue;
}
/* Then check callbacks in hints */
list = hints;
while (list) {
ast_copy_string(hint, ast_get_extension_app(list->exten), sizeof(hint));
cur = hint;
do {
rest = strchr(cur, '&');
if (rest) {
*rest = 0;
rest++;
}
if (!strcmp(cur, device)) { /* Is this device referred to in this hint? */
/* Get device state for this hint */ /* Get device state for this hint */
state = ast_extension_state2(list->exten); state = ast_extension_state2(hint->exten);
if ((state != -1) && (state != list->laststate)) { if ((state == -1) || (state == hint->laststate))
/* Device state changed since last check - notify the watcher */ continue;
/* Device state changed since last check - notify the watchers */
/* For general callbacks */ /* For general callbacks */
cblist = statecbs; for (cblist = statecbs; cblist; cblist = cblist->next)
while (cblist) { cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
cblist = cblist->next;
}
/* For extension callbacks */ /* For extension callbacks */
cblist = list->callbacks; for (cblist = hint->callbacks; cblist; cblist = cblist->next)
while (cblist) { cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
cblist = cblist->next;
}
list->laststate = state; hint->laststate = state;
}
break; break;
} }
cur = rest;
} while (cur);
list = list->next;
}
ast_mutex_unlock(&hintlock);
return 1;
} }
/*--- ast_devstate_add: Add device state watcher */
int ast_devstate_add(ast_devstate_cb_type callback, void *data)
{
struct ast_devstate_cb *devcb;
devcb = malloc(sizeof(struct ast_devstate_cb));
if (devcb) {
memset(devcb, 0, sizeof(struct ast_devstate_cb));
ast_mutex_lock(&hintlock);
devcb->data = data;
devcb->callback = callback;
devcb->next = devcbs;
devcbs = devcb;
ast_mutex_unlock(&hintlock);
}
return 0;
}
/*--- ast_devstate_del: Remove device state watcher */
void ast_devstate_del(ast_devstate_cb_type callback, void *data)
{
struct ast_devstate_cb *devcb, *prev = NULL, *next;
ast_mutex_lock(&hintlock);
devcb = devcbs;
while(devcb) {
next = devcb->next;
if ((devcb->data == data) && (devcb->callback == callback)) {
if (prev)
prev->next = next;
else
devcbs = next;
free(devcb);
} else
prev = devcb;
devcb = next;
}
ast_mutex_unlock(&hintlock); ast_mutex_unlock(&hintlock);
} }

Loading…
Cancel
Save