Stasis-HTTP: Flesh out bridge-related capabilities
[asterisk/asterisk.git] / res / stasis / control.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Stasis application control support.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/stasis_channels.h"
31
32 #include "command.h"
33 #include "control.h"
34 #include "asterisk/bridging.h"
35 #include "asterisk/bridging_features.h"
36
37 struct stasis_app_control {
38         /*! Queue of commands to dispatch on the channel */
39         struct ao2_container *command_queue;
40         /*!
41          * When set, /c app_stasis should exit and continue in the dialplan.
42          */
43         int is_done:1;
44         /*!
45          * The associated channel.
46          * Be very careful with the threading associated w/ manipulating
47          * the channel.
48          */
49         struct ast_channel *channel;
50 };
51
52 struct stasis_app_control *control_create(struct ast_channel *channel)
53 {
54         struct stasis_app_control *control;
55
56         control = ao2_alloc(sizeof(*control), NULL);
57         if (!control) {
58                 return NULL;
59         }
60
61         control->command_queue = ao2_container_alloc_list(
62                 AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
63
64         control->channel = channel;
65
66         return control;
67 }
68
69 static struct stasis_app_command *exec_command(
70         struct stasis_app_control *control, stasis_app_command_cb command_fn,
71         void *data)
72 {
73         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
74
75         command = command_create(command_fn, data);
76
77         if (!command) {
78                 return NULL;
79         }
80
81         /* command_queue is a thread safe list; no lock needed */
82         ao2_link(control->command_queue, command);
83
84         ao2_ref(command, +1);
85         return command;
86 }
87
88 int control_is_done(struct stasis_app_control *control)
89 {
90         /* Called from stasis_app_exec thread; no lock needed */
91         return control->is_done;
92 }
93
94 static void *app_control_continue(struct stasis_app_control *control,
95         struct ast_channel *chan, void *data)
96 {
97         /* Called from stasis_app_exec thread; no lock needed */
98         control->is_done = 1;
99         return NULL;
100 }
101
102 void stasis_app_control_continue(struct stasis_app_control *control)
103 {
104         stasis_app_send_command_async(control, app_control_continue, NULL);
105 }
106
107 struct ast_channel_snapshot *stasis_app_control_get_snapshot(
108         const struct stasis_app_control *control)
109 {
110         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
111         struct stasis_caching_topic *caching_topic;
112         struct ast_channel_snapshot *snapshot;
113
114         caching_topic = ast_channel_topic_all_cached();
115         ast_assert(caching_topic != NULL);
116
117         msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
118                 stasis_app_control_get_channel_id(control));
119         if (!msg) {
120                 return NULL;
121         }
122
123         snapshot = stasis_message_data(msg);
124         ast_assert(snapshot != NULL);
125
126         ao2_ref(snapshot, +1);
127         return snapshot;
128 }
129
130 void *stasis_app_send_command(struct stasis_app_control *control,
131         stasis_app_command_cb command_fn, void *data)
132 {
133         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
134
135         if (control == NULL) {
136                 return NULL;
137         }
138
139         command = exec_command(control, command_fn, data);
140         if (!command) {
141                 return NULL;
142         }
143
144         return command_join(command);
145 }
146
147 int stasis_app_send_command_async(struct stasis_app_control *control,
148         stasis_app_command_cb command_fn, void *data)
149 {
150         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
151
152         if (control == NULL) {
153                 return -1;
154         }
155
156         command = exec_command(control, command_fn, data);
157         if (!command) {
158                 return -1;
159         }
160
161         return 0;
162 }
163
164 const char *stasis_app_control_get_channel_id(
165         const struct stasis_app_control *control)
166 {
167         return ast_channel_uniqueid(control->channel);
168 }
169
170 void stasis_app_control_publish(
171         struct stasis_app_control *control, struct stasis_message *message)
172 {
173         if (!control || !control->channel || !message) {
174                 return;
175         }
176         stasis_publish(ast_channel_topic(control->channel), message);
177 }
178
179 int stasis_app_control_queue_control(struct stasis_app_control *control,
180         enum ast_control_frame_type frame_type)
181 {
182         return ast_queue_control(control->channel, frame_type);
183 }
184
185 int control_dispatch_all(struct stasis_app_control *control,
186         struct ast_channel *chan)
187 {
188         int count = 0;
189         struct ao2_iterator i;
190         void *obj;
191
192         ast_assert(control->channel == chan);
193
194         i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
195
196         while ((obj = ao2_iterator_next(&i))) {
197                 RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
198                 command_invoke(command, control, chan);
199                 ++count;
200         }
201
202         ao2_iterator_destroy(&i);
203         return count;
204 }
205
206 /* Must be defined here since it must operate on the channel outside of the queue */
207 int stasis_app_control_remove_channel_from_bridge(
208         struct stasis_app_control *control, struct ast_bridge *bridge)
209 {
210         return ast_bridge_remove(bridge, control->channel);
211 }