aboutsummaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/asterisk.c2
-rw-r--r--main/devicestate.c66
-rw-r--r--main/pbx.c95
3 files changed, 108 insertions, 55 deletions
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;
}