aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_queue.c32
-rw-r--r--include/asterisk.h1
-rw-r--r--include/asterisk/devicestate.h40
-rw-r--r--include/asterisk/event_defs.h26
-rw-r--r--include/asterisk/pbx.h2
-rw-r--r--main/asterisk.c2
-rw-r--r--main/devicestate.c66
-rw-r--r--main/pbx.c95
8 files changed, 174 insertions, 90 deletions
diff --git a/apps/app_queue.c b/apps/app_queue.c
index dad3ac33b..7d06d2df2 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -92,6 +92,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
+#include "asterisk/event.h"
enum {
QUEUE_STRATEGY_RINGALL = 0,
@@ -257,6 +258,9 @@ static int autofill_default = 0;
/*! \brief queues.conf [general] option */
static int montype_default = 0;
+/*! \brief Subscription to device state change events */
+static struct ast_event_sub *device_state_sub;
+
enum queue_result {
QUEUE_UNKNOWN = 0,
QUEUE_TIMEOUT = 1,
@@ -656,10 +660,8 @@ static void *device_state_thread(void *data)
return NULL;
}
-static int statechange_queue(const char *dev, enum ast_device_state state, void *data)
+static int statechange_queue(const char *dev, enum ast_device_state state)
{
- /* Avoid potential for deadlocks by spawning a new thread to handle
- the event */
struct statechange *sc;
if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
@@ -676,6 +678,22 @@ static int statechange_queue(const char *dev, enum ast_device_state state, void
return 0;
}
+static void device_state_cb(const struct ast_event *event, void *unused)
+{
+ enum ast_device_state state;
+ const char *device;
+
+ state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+ device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
+
+ if (ast_strlen_zero(device)) {
+ ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
+ return;
+ }
+
+ statechange_queue(device, state);
+}
+
static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
{
struct member *cur;
@@ -4747,7 +4765,9 @@ static int unload_module(void)
res |= ast_custom_function_unregister(&queuemembercount_function);
res |= ast_custom_function_unregister(&queuememberlist_function);
res |= ast_custom_function_unregister(&queuewaitingcount_function);
- ast_devstate_del(statechange_queue, NULL);
+
+ if (device_state_sub)
+ ast_event_unsubscribe(device_state_sub);
ast_module_user_hangup_all();
@@ -4788,7 +4808,9 @@ static int load_module(void)
res |= ast_custom_function_register(&queuemembercount_function);
res |= ast_custom_function_register(&queuememberlist_function);
res |= ast_custom_function_register(&queuewaitingcount_function);
- res |= ast_devstate_add(statechange_queue, NULL);
+
+ if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END)))
+ res = -1;
return res;
}
diff --git a/include/asterisk.h b/include/asterisk.h
index bdd59402e..6e20c16a9 100644
--- a/include/asterisk.h
+++ b/include/asterisk.h
@@ -87,6 +87,7 @@ void dnsmgr_start_refresh(void); /*!< Provided by dnsmgr.c */
int dnsmgr_reload(void); /*!< Provided by dnsmgr.c */
void threadstorage_init(void); /*!< Provided by threadstorage.c */
void ast_event_init(void); /*!< Provided by event.c */
+int ast_device_state_engine_init(void); /*!< Provided by devicestate.c */
/* Many headers need 'ast_channel' to be defined */
struct ast_channel;
diff --git a/include/asterisk/devicestate.h b/include/asterisk/devicestate.h
index dd7b939be..b13547a16 100644
--- a/include/asterisk/devicestate.h
+++ b/include/asterisk/devicestate.h
@@ -19,6 +19,18 @@
/*! \file
* \brief Device state management
*
+ * To subscribe to device state changes, use the generic ast_event_subscribe
+ * method. For an example, see apps/app_queue.c.
+ *
+ * \todo Currently, when the state of a device changes, the device state provider
+ * calls one of the functions defined here to queue an object to say that the
+ * state of a device has changed. However, this does not include the new state.
+ * Another thread processes these device state change objects and calls the
+ * device state provider's callback to figure out what the new state is. It
+ * would make a lot more sense for the new state to be included in the original
+ * function call that says the state of a device has changed. However, it
+ * will take a lot of work to change this.
+ *
* \arg See \ref AstExtState
*/
@@ -29,7 +41,11 @@
extern "C" {
#endif
-/*! Device States */
+/*! Device States
+ * \note The order of these states may not change because they are included
+ * in Asterisk events which may be transmitted across the network to
+ * other servers.
+ */
enum ast_device_state {
AST_DEVICE_UNKNOWN, /*!< Device is valid but channel didn't know state */
AST_DEVICE_NOT_INUSE, /*!< Device is not used */
@@ -42,9 +58,6 @@ enum ast_device_state {
AST_DEVICE_ONHOLD, /*!< Device is on hold */
};
-/*! \brief Devicestate watcher call back */
-typedef int (*ast_devstate_cb_type)(const char *dev, enum ast_device_state state, void *data);
-
/*! \brief Devicestate provider call back */
typedef enum ast_device_state (*ast_devstate_prov_cb_type)(const char *data);
@@ -93,7 +106,6 @@ enum ast_device_state ast_device_state(const char *device);
int ast_device_state_changed(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
-
/*! \brief Tells Asterisk the State for Device is changed
* \param device devicename like a dialstring
* Asterisk polls the new extensionstates and calls the registered
@@ -102,22 +114,6 @@ int ast_device_state_changed(const char *fmt, ...)
*/
int ast_device_state_changed_literal(const char *device);
-/*! \brief Registers a device state change callback
- * \param callback 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);
-
-/*! \brief Unregisters a device state change callback
- * \param callback 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
- */
-void ast_devstate_del(ast_devstate_cb_type callback, void *data);
-
/*! \brief Add device state provider
* \param label to use in hint, like label:object
* \param callback Callback
@@ -132,8 +128,6 @@ int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
*/
int ast_devstate_prov_del(const char *label);
-int ast_device_state_engine_init(void);
-
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index b1e27c981..2627bae8d 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -37,13 +37,15 @@ enum ast_event_type {
unique to the event itself, not necessarily across all events. */
AST_EVENT_CUSTOM = 0x01,
/*! Voicemail message waiting indication */
- AST_EVENT_MWI = 0x02,
+ AST_EVENT_MWI = 0x02,
/*! Someone has subscribed to events */
- AST_EVENT_SUB = 0x03,
+ AST_EVENT_SUB = 0x03,
/*! Someone has unsubscribed from events */
- AST_EVENT_UNSUB = 0x04,
+ AST_EVENT_UNSUB = 0x04,
+ /*! The state of a device has changed */
+ AST_EVENT_DEVICE_STATE = 0x05,
/*! Number of event types. This should be the last event type + 1 */
- AST_EVENT_TOTAL = 0x05,
+ AST_EVENT_TOTAL = 0x06,
};
/*! \brief Event Information Element types */
@@ -82,11 +84,25 @@ enum ast_event_ie_type {
*/
AST_EVENT_IE_EVENTTYPE = 0x05,
/*!
- * \brief Hint that someone cares than an IE exists
+ * \brief Hint that someone cares that an IE exists
* Used by: AST_EVENT_SUB
* Payload type: UINT (ast_event_ie_type)
*/
AST_EVENT_IE_EXISTS = 0x06,
+ /*!
+ * \brief Device Name
+ * Used by AST_EVENT_DEVICE_STATE
+ * Payload type: STR
+ */
+ AST_EVENT_IE_DEVICE = 0x07,
+ /*!
+ * \brief Generic State IE
+ * Used by AST_EVENT_DEVICE_STATE
+ * Payload type: UINT
+ * The actual state values depend on the event which
+ * this IE is a part of.
+ */
+ AST_EVENT_IE_STATE = 0x08,
};
/*!
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index 2759b36d1..e7468c6ef 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -877,8 +877,6 @@ int ast_func_read(struct ast_channel *chan, const char *function, char *workspac
*/
int ast_func_write(struct ast_channel *chan, const char *function, const char *value);
-void ast_hint_state_changed(const char *device);
-
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/main/asterisk.c b/main/asterisk.c
index a2f9efb33..efb6403d9 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -2930,7 +2930,7 @@ int main(int argc, char *argv[])
threadstorage_init();
- if (load_modules(1)) { /* Load modules */
+ if (load_modules(1)) { /* Load modules, pre-load only */
printf(term_quit());
exit(1);
}
diff --git a/main/devicestate.c b/main/devicestate.c
index 567a52a36..a1b4c4ca3 100644
--- a/main/devicestate.c
+++ b/main/devicestate.c
@@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Copyright (C) 1999 - 2007, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@@ -126,6 +126,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/options.h"
+#include "asterisk/event.h"
/*! \brief Device state strings for printing */
static const char *devstatestring[] = {
@@ -150,16 +151,6 @@ struct devstate_prov {
/*! \brief A list of providers */
static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
-/*! \brief A device state watcher (callback) */
-struct devstate_cb {
- void *data;
- ast_devstate_cb_type callback; /*!< Where to report when state changes */
- AST_RWLIST_ENTRY(devstate_cb) list;
-};
-
-/*! \brief A device state watcher list */
-static AST_RWLIST_HEAD_STATIC(devstate_cbs, devstate_cb);
-
struct state_change {
AST_LIST_ENTRY(state_change) list;
char device[1];
@@ -380,59 +371,28 @@ static int getproviderstate(const char *provider, const char *address)
return res;
}
-/*! \brief Add device state watcher */
-int ast_devstate_add(ast_devstate_cb_type callback, void *data)
-{
- struct devstate_cb *devcb;
-
- if (!callback || !(devcb = ast_calloc(1, sizeof(*devcb))))
- return -1;
-
- devcb->data = data;
- devcb->callback = callback;
-
- AST_RWLIST_WRLOCK(&devstate_cbs);
- AST_RWLIST_INSERT_HEAD(&devstate_cbs, devcb, list);
- AST_RWLIST_UNLOCK(&devstate_cbs);
-
- return 0;
-}
-
-/*! \brief Remove device state watcher */
-void ast_devstate_del(ast_devstate_cb_type callback, void *data)
-{
- struct devstate_cb *devcb;
-
- AST_RWLIST_WRLOCK(&devstate_cbs);
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
- if ((devcb->callback == callback) && (devcb->data == data)) {
- AST_RWLIST_REMOVE_CURRENT(&devstate_cbs, list);
- free(devcb);
- break;
- }
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
- AST_RWLIST_UNLOCK(&devstate_cbs);
-}
-
/*! \brief Notify callback watchers of change, and notify PBX core for hint updates
Normally executed within a separate thread
*/
static void do_state_change(const char *device)
{
- int state;
- struct devstate_cb *devcb;
+ enum ast_device_state state;
+ struct ast_event *event;
state = ast_device_state(device);
if (option_debug > 2)
ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
- AST_RWLIST_RDLOCK(&devstate_cbs);
- AST_RWLIST_TRAVERSE(&devstate_cbs, devcb, list)
- devcb->callback(device, state, devcb->data);
- AST_RWLIST_UNLOCK(&devstate_cbs);
+ if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+ AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
+ AST_EVENT_IE_END))) {
+ return;
+ }
- ast_hint_state_changed(device);
+ ast_event_queue_and_cache(event,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
+ AST_EVENT_IE_END);
}
static int __ast_device_state_changed_literal(char *buf)
diff --git a/main/pbx.c b/main/pbx.c
index cf27f905b..49767206c 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -63,6 +63,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
+#include "asterisk/event.h"
/*!
* \note I M P O R T A N T :
@@ -220,6 +221,29 @@ static const struct cfextension_states {
{ AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
};
+struct statechange {
+ AST_LIST_ENTRY(statechange) entry;
+ char dev[0];
+};
+
+/*!
+ * \brief Data used by the device state thread
+ */
+static struct {
+ /*! Set to 1 to stop the thread */
+ unsigned int stop:1;
+ /*! The device state monitoring thread */
+ pthread_t thread;
+ /*! Lock for the state change queue */
+ ast_mutex_t lock;
+ /*! Condition for the state change queue */
+ ast_cond_t cond;
+ /*! Queue of state changes */
+ AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
+} device_state = {
+ .thread = AST_PTHREADT_NULL,
+};
+
static int pbx_builtin_answer(struct ast_channel *, void *);
static int pbx_builtin_goto(struct ast_channel *, void *);
static int pbx_builtin_hangup(struct ast_channel *, void *);
@@ -248,6 +272,9 @@ static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
static int autofallthrough = 1;
+/*! \brief Subscription for device state change events */
+static struct ast_event_sub *device_state_sub;
+
AST_MUTEX_DEFINE_STATIC(maxcalllock);
static int countcalls;
@@ -1917,7 +1944,7 @@ int ast_extension_state(struct ast_channel *c, const char *context, const char *
return ast_extension_state2(e); /* Check all devices in the hint */
}
-void ast_hint_state_changed(const char *device)
+static void handle_statechange(const char *device)
{
struct ast_hint *hint;
@@ -1960,6 +1987,49 @@ void ast_hint_state_changed(const char *device)
AST_RWLIST_UNLOCK(&hints);
}
+static int statechange_queue(const char *dev)
+{
+ /* Avoid potential for deadlocks by spawning a new thread to handle
+ the event */
+ struct statechange *sc;
+
+ if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
+ return 0;
+
+ strcpy(sc->dev, dev);
+
+ ast_mutex_lock(&device_state.lock);
+ AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
+ ast_cond_signal(&device_state.cond);
+ ast_mutex_unlock(&device_state.lock);
+
+ return 0;
+}
+
+static void *device_state_thread(void *data)
+{
+ struct statechange *sc;
+
+ while (!device_state.stop) {
+ ast_mutex_lock(&device_state.lock);
+ while (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
+ ast_cond_wait(&device_state.cond, &device_state.lock);
+ /* Check to see if we were woken up to see the request to stop */
+ if (device_state.stop) {
+ ast_mutex_unlock(&device_state.lock);
+ return NULL;
+ }
+ }
+ ast_mutex_unlock(&device_state.lock);
+
+ handle_statechange(sc->dev);
+
+ free(sc);
+ }
+
+ return NULL;
+}
+
/*! \brief 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)
@@ -6032,6 +6102,19 @@ static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
return res;
}
+static void device_state_cb(const struct ast_event *event, void *unused)
+{
+ const char *device;
+
+ device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
+ if (ast_strlen_zero(device)) {
+ ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
+ return;
+ }
+
+ statechange_queue(device);
+}
+
int load_pbx(void)
{
int x;
@@ -6055,6 +6138,16 @@ int load_pbx(void)
/* Register manager application */
ast_manager_register2("ShowDialPlan", EVENT_FLAG_CONFIG, manager_show_dialplan, "List dialplan", mandescr_show_dialplan);
+
+ ast_mutex_init(&device_state.lock);
+ ast_cond_init(&device_state.cond, NULL);
+ ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
+
+ if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL,
+ AST_EVENT_IE_END))) {
+ return -1;
+ }
+
return 0;
}