summaryrefslogtreecommitdiff
path: root/res/stasis/control.c
diff options
context:
space:
mode:
Diffstat (limited to 'res/stasis/control.c')
-rw-r--r--res/stasis/control.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/res/stasis/control.c b/res/stasis/control.c
new file mode 100644
index 000000000..e32781b5f
--- /dev/null
+++ b/res/stasis/control.c
@@ -0,0 +1,199 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Stasis application control support.
+ *
+ * \author David M. Lee, II <dlee@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/stasis_channels.h"
+
+#include "command.h"
+#include "control.h"
+
+struct stasis_app_control {
+ /*! Queue of commands to dispatch on the channel */
+ struct ao2_container *command_queue;
+ /*!
+ * When set, /c app_stasis should exit and continue in the dialplan.
+ */
+ int is_done:1;
+ /*!
+ * The associated channel.
+ * Be very careful with the threading associated w/ manipulating
+ * the channel.
+ */
+ struct ast_channel *channel;
+};
+
+struct stasis_app_control *control_create(struct ast_channel *channel)
+{
+ struct stasis_app_control *control;
+
+ control = ao2_alloc(sizeof(*control), NULL);
+ if (!control) {
+ return NULL;
+ }
+
+ control->command_queue = ao2_container_alloc_list(0, 0, NULL, NULL);
+
+ control->channel = channel;
+
+ return control;
+}
+
+static struct stasis_app_command *exec_command(
+ struct stasis_app_control *control, stasis_app_command_cb command_fn,
+ void *data)
+{
+ RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
+
+ command = command_create(command_fn, data);
+
+ if (!command) {
+ return NULL;
+ }
+
+ ao2_lock(control);
+ ao2_ref(command, +1);
+ ao2_link(control->command_queue, command);
+ ao2_unlock(control);
+
+ ao2_ref(command, +1);
+ return command;
+}
+
+int control_is_done(struct stasis_app_control *control)
+{
+ /* Called from stasis_app_exec thread; no lock needed */
+ return control->is_done;
+}
+
+static void *app_control_continue(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ /* Called from stasis_app_exec thread; no lock needed */
+ control->is_done = 1;
+ return NULL;
+}
+
+void stasis_app_control_continue(struct stasis_app_control *control)
+{
+ stasis_app_send_command_async(control, app_control_continue, NULL);
+}
+
+struct ast_channel_snapshot *stasis_app_control_get_snapshot(
+ const struct stasis_app_control *control)
+{
+ RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+ struct stasis_caching_topic *caching_topic;
+ struct ast_channel_snapshot *snapshot;
+
+ caching_topic = ast_channel_topic_all_cached();
+ ast_assert(caching_topic != NULL);
+
+ msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
+ stasis_app_control_get_channel_id(control));
+ if (!msg) {
+ return NULL;
+ }
+
+ snapshot = stasis_message_data(msg);
+ ast_assert(snapshot != NULL);
+
+ ao2_ref(snapshot, +1);
+ return snapshot;
+}
+
+void *stasis_app_send_command(struct stasis_app_control *control,
+ stasis_app_command_cb command_fn, void *data)
+{
+ RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
+
+ if (control == NULL) {
+ return NULL;
+ }
+
+ command = exec_command(control, command_fn, data);
+ if (!command) {
+ return NULL;
+ }
+
+ return command_join(command);
+}
+
+int stasis_app_send_command_async(struct stasis_app_control *control,
+ stasis_app_command_cb command_fn, void *data)
+{
+ RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
+
+ if (control == NULL) {
+ return -1;
+ }
+
+ command = exec_command(control, command_fn, data);
+ if (!command) {
+ return -1;
+ }
+
+ return 0;
+}
+
+const char *stasis_app_control_get_channel_id(
+ const struct stasis_app_control *control)
+{
+ return ast_channel_uniqueid(control->channel);
+}
+
+void stasis_app_control_publish(
+ struct stasis_app_control *control, struct stasis_message *message)
+{
+ if (!control || !control->channel || !message) {
+ return;
+ }
+ stasis_publish(ast_channel_topic(control->channel), message);
+}
+
+int control_dispatch_all(struct stasis_app_control *control,
+ struct ast_channel *chan)
+{
+ int count = 0;
+ struct ao2_iterator i;
+ void *obj;
+
+ SCOPED_AO2LOCK(lock, control);
+
+ ast_assert(control->channel == chan);
+
+ i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
+
+ while ((obj = ao2_iterator_next(&i))) {
+ RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
+ command_invoke(command, control, chan);
+ ++count;
+ }
+
+ ao2_iterator_destroy(&i);
+ return count;
+}