2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * David M. Lee, II <dlee@digium.com>
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.
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.
21 * \brief Stasis application control support.
23 * \author David M. Lee, II <dlee@digium.com>
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include "asterisk/stasis_channels.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/frame.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/musiconhold.h"
42 struct stasis_app_control {
43 /*! Queue of commands to dispatch on the channel */
44 struct ao2_container *command_queue;
46 * When set, /c app_stasis should exit and continue in the dialplan.
50 * The associated channel.
51 * Be very careful with the threading associated w/ manipulating
54 struct ast_channel *channel;
57 struct stasis_app_control *control_create(struct ast_channel *channel)
59 struct stasis_app_control *control;
61 control = ao2_alloc(sizeof(*control), NULL);
66 control->command_queue = ao2_container_alloc_list(
67 AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
69 if (!control->command_queue) {
74 control->channel = channel;
79 static struct stasis_app_command *exec_command(
80 struct stasis_app_control *control, stasis_app_command_cb command_fn,
83 RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
85 command = command_create(command_fn, data);
91 /* command_queue is a thread safe list; no lock needed */
92 ao2_link(control->command_queue, command);
98 struct stasis_app_control_dial_data {
99 char endpoint[AST_CHANNEL_NAME];
103 static void *app_control_dial(struct stasis_app_control *control,
104 struct ast_channel *chan, void *data)
106 RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy);
107 RAII_VAR(struct stasis_app_control_dial_data *, dial_data, data, ast_free);
108 enum ast_dial_result res;
109 char *tech, *resource;
111 struct ast_channel *new_chan;
112 struct ast_bridge *bridge;
114 tech = dial_data->endpoint;
115 if (!(resource = strchr(tech, '/'))) {
121 ast_log(LOG_ERROR, "Failed to create dialing structure.\n");
125 if (ast_dial_append(dial, tech, resource) < 0) {
126 ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource);
130 ast_dial_set_global_timeout(dial, dial_data->timeout);
132 res = ast_dial_run(dial, NULL, 0);
134 if (res != AST_DIAL_RESULT_ANSWERED || !(new_chan = ast_dial_answered_steal(dial))) {
138 if (!(bridge = ast_bridge_basic_new())) {
139 ast_log(LOG_ERROR, "Failed to create basic bridge.\n");
143 ast_bridge_impart(bridge, new_chan, NULL, NULL, 1);
144 stasis_app_control_add_channel_to_bridge(control, bridge);
149 int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, int timeout)
151 struct stasis_app_control_dial_data *dial_data;
153 if (!(dial_data = ast_calloc(1, sizeof(*dial_data)))) {
157 ast_copy_string(dial_data->endpoint, endpoint, sizeof(dial_data->endpoint));
160 dial_data->timeout = timeout * 1000;
161 } else if (timeout == -1) {
162 dial_data->timeout = -1;
164 dial_data->timeout = 30000;
167 stasis_app_send_command_async(control, app_control_dial, dial_data);
172 int control_is_done(struct stasis_app_control *control)
174 /* Called from stasis_app_exec thread; no lock needed */
175 return control->is_done;
178 struct stasis_app_control_continue_data {
179 char context[AST_MAX_CONTEXT];
180 char extension[AST_MAX_EXTENSION];
184 static void *app_control_continue(struct stasis_app_control *control,
185 struct ast_channel *chan, void *data)
187 RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free);
189 /* Called from stasis_app_exec thread; no lock needed */
190 ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority);
192 control->is_done = 1;
197 int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority)
199 struct stasis_app_control_continue_data *continue_data;
201 if (!(continue_data = ast_calloc(1, sizeof(*continue_data)))) {
204 ast_copy_string(continue_data->context, S_OR(context, ""), sizeof(continue_data->context));
205 ast_copy_string(continue_data->extension, S_OR(extension, ""), sizeof(continue_data->extension));
207 continue_data->priority = priority;
209 continue_data->priority = -1;
212 stasis_app_send_command_async(control, app_control_continue, continue_data);
217 struct stasis_app_control_mute_data {
218 enum ast_frame_type frametype;
219 unsigned int direction;
222 static void *app_control_mute(struct stasis_app_control *control,
223 struct ast_channel *chan, void *data)
225 RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
226 SCOPED_CHANNELLOCK(lockvar, chan);
228 ast_channel_suppress(control->channel, mute_data->direction, mute_data->frametype);
233 int stasis_app_control_mute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
235 struct stasis_app_control_mute_data *mute_data;
237 if (!(mute_data = ast_calloc(1, sizeof(*mute_data)))) {
241 mute_data->direction = direction;
242 mute_data->frametype = frametype;
244 stasis_app_send_command_async(control, app_control_mute, mute_data);
249 static void *app_control_unmute(struct stasis_app_control *control,
250 struct ast_channel *chan, void *data)
252 RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
253 SCOPED_CHANNELLOCK(lockvar, chan);
255 ast_channel_unsuppress(control->channel, mute_data->direction, mute_data->frametype);
260 int stasis_app_control_unmute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
262 struct stasis_app_control_mute_data *mute_data;
264 if (!(mute_data = ast_calloc(1, sizeof(*mute_data)))) {
268 mute_data->direction = direction;
269 mute_data->frametype = frametype;
271 stasis_app_send_command_async(control, app_control_unmute, mute_data);
276 char *stasis_app_control_get_channel_var(struct stasis_app_control *control, const char *variable)
278 RAII_VAR(struct ast_str *, tmp, ast_str_create(32), ast_free);
279 SCOPED_CHANNELLOCK(lockvar, control->channel);
285 if (variable[strlen(variable) - 1] == ')') {
286 if (ast_func_read2(control->channel, variable, &tmp, 0)) {
290 if (!ast_str_retrieve_variable(&tmp, 0, control->channel, NULL, variable)) {
295 return ast_strdup(ast_str_buffer(tmp));
298 int stasis_app_control_set_channel_var(struct stasis_app_control *control, const char *variable, const char *value)
300 return pbx_builtin_setvar_helper(control->channel, variable, value);
303 static void *app_control_hold(struct stasis_app_control *control,
304 struct ast_channel *chan, void *data)
306 ast_indicate(control->channel, AST_CONTROL_HOLD);
311 void stasis_app_control_hold(struct stasis_app_control *control)
313 stasis_app_send_command_async(control, app_control_hold, NULL);
316 static void *app_control_unhold(struct stasis_app_control *control,
317 struct ast_channel *chan, void *data)
319 ast_indicate(control->channel, AST_CONTROL_UNHOLD);
324 void stasis_app_control_unhold(struct stasis_app_control *control)
326 stasis_app_send_command_async(control, app_control_unhold, NULL);
329 static void *app_control_moh_start(struct stasis_app_control *control,
330 struct ast_channel *chan, void *data)
332 char *moh_class = data;
334 ast_moh_start(chan, moh_class, NULL);
340 void stasis_app_control_moh_start(struct stasis_app_control *control, const char *moh_class)
344 if (!ast_strlen_zero(moh_class)) {
345 data = ast_strdup(moh_class);
348 stasis_app_send_command_async(control, app_control_moh_start, data);
351 static void *app_control_moh_stop(struct stasis_app_control *control,
352 struct ast_channel *chan, void *data)
358 void stasis_app_control_moh_stop(struct stasis_app_control *control)
360 stasis_app_send_command_async(control, app_control_moh_stop, NULL);
363 struct ast_channel_snapshot *stasis_app_control_get_snapshot(
364 const struct stasis_app_control *control)
366 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
367 struct stasis_caching_topic *caching_topic;
368 struct ast_channel_snapshot *snapshot;
370 caching_topic = ast_channel_topic_all_cached();
371 ast_assert(caching_topic != NULL);
373 msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
374 stasis_app_control_get_channel_id(control));
379 snapshot = stasis_message_data(msg);
380 ast_assert(snapshot != NULL);
382 ao2_ref(snapshot, +1);
386 void *stasis_app_send_command(struct stasis_app_control *control,
387 stasis_app_command_cb command_fn, void *data)
389 RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
391 if (control == NULL) {
395 command = exec_command(control, command_fn, data);
400 return command_join(command);
403 int stasis_app_send_command_async(struct stasis_app_control *control,
404 stasis_app_command_cb command_fn, void *data)
406 RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
408 if (control == NULL) {
412 command = exec_command(control, command_fn, data);
420 const char *stasis_app_control_get_channel_id(
421 const struct stasis_app_control *control)
423 return ast_channel_uniqueid(control->channel);
426 void stasis_app_control_publish(
427 struct stasis_app_control *control, struct stasis_message *message)
429 if (!control || !control->channel || !message) {
432 stasis_publish(ast_channel_topic(control->channel), message);
435 int stasis_app_control_queue_control(struct stasis_app_control *control,
436 enum ast_control_frame_type frame_type)
438 return ast_queue_control(control->channel, frame_type);
441 int control_dispatch_all(struct stasis_app_control *control,
442 struct ast_channel *chan)
445 struct ao2_iterator i;
448 ast_assert(control->channel == chan);
450 i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
452 while ((obj = ao2_iterator_next(&i))) {
453 RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
454 command_invoke(command, control, chan);
458 ao2_iterator_destroy(&i);
462 /* Must be defined here since it must operate on the channel outside of the queue */
463 int stasis_app_control_remove_channel_from_bridge(
464 struct stasis_app_control *control, struct ast_bridge *bridge)
466 return ast_bridge_remove(bridge, control->channel);