diff --git a/Makefile b/Makefile index 86b17ee8d525ddb5a2dc1a0ff0ed19e71b30ee07..5fe54e86602f8d06f9dd22d0f44a7cfb6cd55899 100755 --- a/Makefile +++ b/Makefile @@ -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 \ 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 \ - 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) OBJS+=poll.o dlfcn.o ASTLINK=-Wl,-dynamic diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c index 1fe374108cbfc4999afcd7c8ddb0389b4b8cedef..3039f12752aa838c82610d106fcedeee57a1c485 100755 --- a/apps/app_chanisavail.c +++ b/apps/app_chanisavail.c @@ -31,6 +31,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/module.h" #include "asterisk/app.h" +#include "asterisk/devicestate.h" static char *tdesc = "Check if channel is available"; diff --git a/apps/app_queue.c b/apps/app_queue.c index 4b99f4d6e1babe13ba34be8463734f831e4fcc0f..cae7e5ef636bc98b0ee41ce48adf73062b135143 100755 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -71,6 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/utils.h" #include "asterisk/causes.h" #include "asterisk/astdb.h" +#include "asterisk/devicestate.h" #define QUEUE_STRATEGY_RINGALL 0 #define QUEUE_STRATEGY_ROUNDROBIN 1 diff --git a/asterisk.c b/asterisk.c index 249931ed3a38378b1bc8aef2f1c5f9936b273071..b2ce56ea10fe34c126ab1b2b31eb7187b884c098 100755 --- a/asterisk.c +++ b/asterisk.c @@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/version.h" #include "asterisk/build.h" #include "asterisk/linkedlists.h" +#include "asterisk/devicestate.h" #include "defaults.h" @@ -2095,6 +2096,10 @@ int main(int argc, char *argv[]) printf(term_quit()); exit(1); } + if (ast_device_state_engine_init()) { + printf(term_quit()); + exit(1); + } ast_rtp_init(); if (ast_image_init()) { printf(term_quit()); diff --git a/channel.c b/channel.c index 04b77af27e206817ee652a12e37882cc6d4cddc6..6ec2ff044f1262187d560e2605e2b96564f20ae9 100755 --- a/channel.c +++ b/channel.c @@ -57,6 +57,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/app.h" #include "asterisk/transcap.h" +#include "asterisk/devicestate.h" /* uncomment if you have problems with 'monitoring' synchronized files */ #if 0 @@ -295,6 +296,58 @@ int ast_channel_register(const struct ast_channel_tech *tech) 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 */ 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. */ 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"; int retries, done; @@ -615,8 +668,14 @@ static struct ast_channel *channel_find_locked(const struct ast_channel *prev, ast_mutex_lock(&chlock); for (c = channels; c; c = c->next) { if (prev == NULL) { - /* want either head of list or match by name */ - if (name == NULL || !strcasecmp(name, c->name)) + /* want head of list */ + 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; } else if (c == prev) { /* found, return 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 */ 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 */ 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 */ @@ -964,37 +1029,6 @@ int ast_hangup(struct ast_channel *chan) 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 res = 0; @@ -2167,70 +2201,6 @@ struct ast_channel *ast_request(const char *type, int format, void *data, int *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) { /* Place an outgoing call, but don't wait any longer than timeout ms before returning. diff --git a/channels/chan_agent.c b/channels/chan_agent.c index 464ef64ea338506517c6719919cd3646c8bcf937..cdc98a321bc0086e94c22ce66958171f78c13430 100755 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/utils.h" #include "asterisk/causes.h" #include "asterisk/astdb.h" +#include "asterisk/devicestate.h" static const char desc[] = "Agent Proxy Channel"; static const char channeltype[] = "Agent"; diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index e1ceedaa9fb442ae0ec19e982f9253673d6080f0..7b4f556e981e8ca3251717671d7b9676126f223b 100755 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -42,6 +42,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/localtime.h" #include "asterisk/aes.h" #include "asterisk/dnsmgr.h" +#include "asterisk/devicestate.h" + #include <sys/mman.h> #include <arpa/inet.h> #include <dirent.h> @@ -73,7 +75,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "iax2.h" #include "iax2-parser.h" #include "iax2-provision.h" -#include "asterisk.h" /* Define NEWJB to use the new channel independent jitterbuffer, * otherwise, use the old jitterbuffer */ diff --git a/channels/chan_sip.c b/channels/chan_sip.c index e26ff1902ca0d5a4159914d8986c3c5c20281bf3..5ed890f2b234fe5fc737d8ae24f5c934fd4686c9 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/file.h" #include "asterisk/astobj.h" #include "asterisk/dnsmgr.h" +#include "asterisk/devicestate.h" #ifdef OSP_SUPPORT #include "asterisk/astosp.h" #endif diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 817d3cc97671b4e1888938c45ce0bb2d98cf9e7c..9380d7884d99c6c5aa709ba88134eb1ed92583cf 100755 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -444,21 +444,6 @@ struct outgoing_helper { /*! Do not transmit voice data */ #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 */ /*! Returns NULL on failure to allocate. New channels are 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); -/*! 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 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); +/*! 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 */ /*! * \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) */ 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 */ /*! * \param c channel to wait for a digit on diff --git a/include/asterisk/linkedlists.h b/include/asterisk/linkedlists.h index 9f5f216fd2bb5f97e7598cd9b7594253a91e6cd0..b4085d7b9cb127845d77e997045032e9444bcbcd 100755 --- a/include/asterisk/linkedlists.h +++ b/include/asterisk/linkedlists.h @@ -306,11 +306,13 @@ struct { \ used to link entries of this list together. 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) ({ \ typeof((head)->first) cur = (head)->first; \ - (head)->first = (head)->first->field.next; \ + if (cur) \ + (head)->first = cur->field.next; \ cur; \ }) diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index b0f92b60a597040e19234cd538674eada21e7aa3..187372dcbf7309935ea00a4a6814d36fe2edabe2 100755 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -51,8 +51,6 @@ struct ast_sw; 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 */ struct ast_custom_function { 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); -/*! 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 */ /*! * \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, 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 */ /*! * \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_hint_state_changed(const char *device); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/pbx.c b/pbx.c index 767023c85fb436f6007a1beefae78612c60654d0..f4deb884dcc5ac8476544a9ed86ef0c39b8f13d3 100755 --- a/pbx.c +++ b/pbx.c @@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/causes.h" #include "asterisk/musiconhold.h" #include "asterisk/app.h" +#include "asterisk/devicestate.h" /* * I M P O R T A N T : @@ -160,15 +161,6 @@ struct ast_state_cb { 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) */ struct ast_hint { struct ast_exten *exten; /* Extension */ @@ -1805,130 +1797,48 @@ int ast_extension_state(struct ast_channel *c, char *context, char *exten) 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 */ -int ast_device_state_changed(const char *fmt, ...) +void ast_hint_state_changed(const char *device) { - struct ast_hint *list; + struct ast_hint *hint; struct ast_state_cb *cblist; - struct ast_devstate_cb *devcb; - char hint[AST_MAX_EXTENSION] = ""; - char device[AST_MAX_EXTENSION]; - - char *cur, *rest; + char buf[AST_MAX_EXTENSION]; + char *parse; + char *cur; 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); - /* First check device callbacks */ - devcb = devcbs; - while(devcb) { - if (devcb->callback) - devcb->callback(device, state, devcb->data); - devcb = devcb->next; - } - - /* Then check callbacks in hints */ - list = hints; - - while (list) { + for (hint = hints; hint; hint = hint->next) { + ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf)); + parse = buf; + for (cur = strsep(&parse, "&"); cur; cur = strsep(&parse, "&")) { + if (strcmp(cur, device)) + continue; - ast_copy_string(hint, ast_get_extension_app(list->exten), sizeof(hint)); - cur = hint; - do { - rest = strchr(cur, '&'); - if (rest) { - *rest = 0; - rest++; - } + /* Get device state for this hint */ + state = ast_extension_state2(hint->exten); - if (!strcmp(cur, device)) { /* Is this device referred to in this hint? */ - - /* Get device state for this hint */ - state = ast_extension_state2(list->exten); - - if ((state != -1) && (state != list->laststate)) { - /* Device state changed since last check - notify the watcher */ - - /* For general callbacks */ - cblist = statecbs; - while (cblist) { - cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data); - cblist = cblist->next; - } + if ((state == -1) || (state == hint->laststate)) + continue; - /* For extension callbacks */ - cblist = list->callbacks; - while (cblist) { - cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data); - cblist = cblist->next; - } + /* Device state changed since last check - notify the watchers */ - list->laststate = state; - } - break; - } - cur = rest; - } while (cur); - list = list->next; - } - ast_mutex_unlock(&hintlock); - return 1; -} + /* For general callbacks */ + for (cblist = statecbs; cblist; cblist = cblist->next) + cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); -/*--- 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); + /* For extension callbacks */ + for (cblist = hint->callbacks; cblist; cblist = cblist->next) + cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data); + + hint->laststate = state; + break; + } } - 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_extension_state_add: Add watcher for extension states */ int ast_extension_state_add(const char *context, const char *exten, ast_state_cb_type callback, void *data)