6a7a9ba4d5446d72e8c7f8250f99adcd6270b117
[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/dial.h"
35 #include "asterisk/bridging.h"
36 #include "asterisk/bridging_basic.h"
37 #include "asterisk/bridging_features.h"
38 #include "asterisk/pbx.h"
39
40 struct stasis_app_control {
41         /*! Queue of commands to dispatch on the channel */
42         struct ao2_container *command_queue;
43         /*!
44          * When set, /c app_stasis should exit and continue in the dialplan.
45          */
46         int is_done:1;
47         /*!
48          * The associated channel.
49          * Be very careful with the threading associated w/ manipulating
50          * the channel.
51          */
52         struct ast_channel *channel;
53 };
54
55 struct stasis_app_control *control_create(struct ast_channel *channel)
56 {
57         struct stasis_app_control *control;
58
59         control = ao2_alloc(sizeof(*control), NULL);
60         if (!control) {
61                 return NULL;
62         }
63
64         control->command_queue = ao2_container_alloc_list(
65                 AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
66
67         control->channel = channel;
68
69         return control;
70 }
71
72 static struct stasis_app_command *exec_command(
73         struct stasis_app_control *control, stasis_app_command_cb command_fn,
74         void *data)
75 {
76         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
77
78         command = command_create(command_fn, data);
79
80         if (!command) {
81                 return NULL;
82         }
83
84         /* command_queue is a thread safe list; no lock needed */
85         ao2_link(control->command_queue, command);
86
87         ao2_ref(command, +1);
88         return command;
89 }
90
91 struct stasis_app_control_dial_data {
92         char endpoint[AST_CHANNEL_NAME];
93         int timeout;
94 };
95
96 static void *app_control_dial(struct stasis_app_control *control,
97         struct ast_channel *chan, void *data)
98 {
99         RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy);
100         RAII_VAR(struct stasis_app_control_dial_data *, dial_data, data, ast_free);
101         enum ast_dial_result res;
102         char *tech, *resource;
103
104         struct ast_channel *new_chan;
105         struct ast_bridge *bridge;
106
107         tech = dial_data->endpoint;
108         if (!(resource = strchr(tech, '/'))) {
109                 return NULL;
110         }
111         *resource++ = '\0';
112
113         if (!dial) {
114                 ast_log(LOG_ERROR, "Failed to create dialing structure.\n");
115                 return NULL;
116         }
117
118         if (ast_dial_append(dial, tech, resource) < 0) {
119                 ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource);
120                 return NULL;
121         }
122
123         ast_dial_set_global_timeout(dial, dial_data->timeout);
124
125         res = ast_dial_run(dial, NULL, 0);
126
127         if (res != AST_DIAL_RESULT_ANSWERED || !(new_chan = ast_dial_answered_steal(dial))) {
128                 return NULL;
129         }
130
131         if (!(bridge = ast_bridge_basic_new())) {
132                 ast_log(LOG_ERROR, "Failed to create basic bridge.\n");
133                 return NULL;
134         }
135
136         ast_bridge_impart(bridge, new_chan, NULL, NULL, 1);
137         stasis_app_control_add_channel_to_bridge(control, bridge);
138
139         return NULL;
140 }
141
142 int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, int timeout)
143 {
144         struct stasis_app_control_dial_data *dial_data;
145
146         if (!(dial_data = ast_calloc(1, sizeof(*dial_data)))) {
147                 return -1;
148         }
149
150         ast_copy_string(dial_data->endpoint, endpoint, sizeof(dial_data->endpoint));
151
152         if (timeout > 0) {
153                 dial_data->timeout = timeout * 1000;
154         } else if (timeout == -1) {
155                 dial_data->timeout = -1;
156         } else {
157                 dial_data->timeout = 30000;
158         }
159
160         stasis_app_send_command_async(control, app_control_dial, dial_data);
161
162         return 0;
163 }
164
165 int control_is_done(struct stasis_app_control *control)
166 {
167         /* Called from stasis_app_exec thread; no lock needed */
168         return control->is_done;
169 }
170
171 struct stasis_app_control_continue_data {
172         char context[AST_MAX_CONTEXT];
173         char extension[AST_MAX_EXTENSION];
174         int priority;
175 };
176
177 static void *app_control_continue(struct stasis_app_control *control,
178         struct ast_channel *chan, void *data)
179 {
180         RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free);
181
182         /* Called from stasis_app_exec thread; no lock needed */
183         ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority);
184
185         control->is_done = 1;
186
187         return NULL;
188 }
189
190 int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority)
191 {
192         struct stasis_app_control_continue_data *continue_data;
193
194         if (!(continue_data = ast_calloc(1, sizeof(*continue_data)))) {
195                 return -1;
196         }
197         ast_copy_string(continue_data->context, S_OR(context, ""), sizeof(continue_data->context));
198         ast_copy_string(continue_data->extension, S_OR(extension, ""), sizeof(continue_data->extension));
199         if (priority > 0) {
200                 continue_data->priority = priority;
201         } else {
202                 continue_data->priority = -1;
203         }
204
205         stasis_app_send_command_async(control, app_control_continue, continue_data);
206
207         return 0;
208 }
209
210 char *stasis_app_control_get_channel_var(struct stasis_app_control *control, const char *variable)
211 {
212         SCOPED_CHANNELLOCK(lockvar, control->channel);
213
214         RAII_VAR(struct ast_str *, tmp, ast_str_create(32), ast_free);
215
216         if (!tmp) {
217                 return NULL;
218         }
219
220         if (variable[strlen(variable) - 1] == ')') {
221                 if (ast_func_read2(control->channel, variable, &tmp, 0)) {
222                         return NULL;
223                 }
224         } else {
225                 if (!ast_str_retrieve_variable(&tmp, 0, control->channel, NULL, variable)) {
226                         return NULL;
227                 }
228         }
229
230         return ast_strdup(ast_str_buffer(tmp));
231 }
232
233 int stasis_app_control_set_channel_var(struct stasis_app_control *control, const char *variable, const char *value)
234 {
235         return pbx_builtin_setvar_helper(control->channel, variable, value);
236 }
237
238 static void *app_control_hold(struct stasis_app_control *control,
239         struct ast_channel *chan, void *data)
240 {
241         ast_indicate(control->channel, AST_CONTROL_HOLD);
242
243         return NULL;
244 }
245
246 void stasis_app_control_hold(struct stasis_app_control *control)
247 {
248         stasis_app_send_command_async(control, app_control_hold, NULL);
249 }
250
251 static void *app_control_unhold(struct stasis_app_control *control,
252         struct ast_channel *chan, void *data)
253 {
254         ast_indicate(control->channel, AST_CONTROL_UNHOLD);
255
256         return NULL;
257 }
258
259 void stasis_app_control_unhold(struct stasis_app_control *control)
260 {
261         stasis_app_send_command_async(control, app_control_unhold, NULL);
262 }
263
264 struct ast_channel_snapshot *stasis_app_control_get_snapshot(
265         const struct stasis_app_control *control)
266 {
267         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
268         struct stasis_caching_topic *caching_topic;
269         struct ast_channel_snapshot *snapshot;
270
271         caching_topic = ast_channel_topic_all_cached();
272         ast_assert(caching_topic != NULL);
273
274         msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
275                 stasis_app_control_get_channel_id(control));
276         if (!msg) {
277                 return NULL;
278         }
279
280         snapshot = stasis_message_data(msg);
281         ast_assert(snapshot != NULL);
282
283         ao2_ref(snapshot, +1);
284         return snapshot;
285 }
286
287 void *stasis_app_send_command(struct stasis_app_control *control,
288         stasis_app_command_cb command_fn, void *data)
289 {
290         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
291
292         if (control == NULL) {
293                 return NULL;
294         }
295
296         command = exec_command(control, command_fn, data);
297         if (!command) {
298                 return NULL;
299         }
300
301         return command_join(command);
302 }
303
304 int stasis_app_send_command_async(struct stasis_app_control *control,
305         stasis_app_command_cb command_fn, void *data)
306 {
307         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
308
309         if (control == NULL) {
310                 return -1;
311         }
312
313         command = exec_command(control, command_fn, data);
314         if (!command) {
315                 return -1;
316         }
317
318         return 0;
319 }
320
321 const char *stasis_app_control_get_channel_id(
322         const struct stasis_app_control *control)
323 {
324         return ast_channel_uniqueid(control->channel);
325 }
326
327 void stasis_app_control_publish(
328         struct stasis_app_control *control, struct stasis_message *message)
329 {
330         if (!control || !control->channel || !message) {
331                 return;
332         }
333         stasis_publish(ast_channel_topic(control->channel), message);
334 }
335
336 int stasis_app_control_queue_control(struct stasis_app_control *control,
337         enum ast_control_frame_type frame_type)
338 {
339         return ast_queue_control(control->channel, frame_type);
340 }
341
342 int control_dispatch_all(struct stasis_app_control *control,
343         struct ast_channel *chan)
344 {
345         int count = 0;
346         struct ao2_iterator i;
347         void *obj;
348
349         ast_assert(control->channel == chan);
350
351         i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
352
353         while ((obj = ao2_iterator_next(&i))) {
354                 RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
355                 command_invoke(command, control, chan);
356                 ++count;
357         }
358
359         ao2_iterator_destroy(&i);
360         return count;
361 }
362
363 /* Must be defined here since it must operate on the channel outside of the queue */
364 int stasis_app_control_remove_channel_from_bridge(
365         struct stasis_app_control *control, struct ast_bridge *bridge)
366 {
367         return ast_bridge_remove(bridge, control->channel);
368 }