1663dd8a6dbb7d158c43fe9ee1fb90b7b8b58234
[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
35 struct stasis_app_control {
36         /*! Queue of commands to dispatch on the channel */
37         struct ao2_container *command_queue;
38         /*!
39          * When set, /c app_stasis should exit and continue in the dialplan.
40          */
41         int is_done:1;
42         /*!
43          * The associated channel.
44          * Be very careful with the threading associated w/ manipulating
45          * the channel.
46          */
47         struct ast_channel *channel;
48 };
49
50 struct stasis_app_control *control_create(struct ast_channel *channel)
51 {
52         struct stasis_app_control *control;
53
54         control = ao2_alloc(sizeof(*control), NULL);
55         if (!control) {
56                 return NULL;
57         }
58
59         control->command_queue = ao2_container_alloc_list(
60                 AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
61
62         control->channel = channel;
63
64         return control;
65 }
66
67 static struct stasis_app_command *exec_command(
68         struct stasis_app_control *control, stasis_app_command_cb command_fn,
69         void *data)
70 {
71         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
72
73         command = command_create(command_fn, data);
74
75         if (!command) {
76                 return NULL;
77         }
78
79         /* command_queue is a thread safe list; no lock needed */
80         ao2_link(control->command_queue, command);
81
82         ao2_ref(command, +1);
83         return command;
84 }
85
86 int control_is_done(struct stasis_app_control *control)
87 {
88         /* Called from stasis_app_exec thread; no lock needed */
89         return control->is_done;
90 }
91
92 static void *app_control_continue(struct stasis_app_control *control,
93         struct ast_channel *chan, void *data)
94 {
95         /* Called from stasis_app_exec thread; no lock needed */
96         control->is_done = 1;
97         return NULL;
98 }
99
100 void stasis_app_control_continue(struct stasis_app_control *control)
101 {
102         stasis_app_send_command_async(control, app_control_continue, NULL);
103 }
104
105 struct ast_channel_snapshot *stasis_app_control_get_snapshot(
106         const struct stasis_app_control *control)
107 {
108         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
109         struct stasis_caching_topic *caching_topic;
110         struct ast_channel_snapshot *snapshot;
111
112         caching_topic = ast_channel_topic_all_cached();
113         ast_assert(caching_topic != NULL);
114
115         msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
116                 stasis_app_control_get_channel_id(control));
117         if (!msg) {
118                 return NULL;
119         }
120
121         snapshot = stasis_message_data(msg);
122         ast_assert(snapshot != NULL);
123
124         ao2_ref(snapshot, +1);
125         return snapshot;
126 }
127
128 void *stasis_app_send_command(struct stasis_app_control *control,
129         stasis_app_command_cb command_fn, void *data)
130 {
131         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
132
133         if (control == NULL) {
134                 return NULL;
135         }
136
137         command = exec_command(control, command_fn, data);
138         if (!command) {
139                 return NULL;
140         }
141
142         return command_join(command);
143 }
144
145 int stasis_app_send_command_async(struct stasis_app_control *control,
146         stasis_app_command_cb command_fn, void *data)
147 {
148         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
149
150         if (control == NULL) {
151                 return -1;
152         }
153
154         command = exec_command(control, command_fn, data);
155         if (!command) {
156                 return -1;
157         }
158
159         return 0;
160 }
161
162 const char *stasis_app_control_get_channel_id(
163         const struct stasis_app_control *control)
164 {
165         return ast_channel_uniqueid(control->channel);
166 }
167
168 void stasis_app_control_publish(
169         struct stasis_app_control *control, struct stasis_message *message)
170 {
171         if (!control || !control->channel || !message) {
172                 return;
173         }
174         stasis_publish(ast_channel_topic(control->channel), message);
175 }
176
177 int stasis_app_control_queue_control(struct stasis_app_control *control,
178         enum ast_control_frame_type frame_type)
179 {
180         return ast_queue_control(control->channel, frame_type);
181 }
182
183 int control_dispatch_all(struct stasis_app_control *control,
184         struct ast_channel *chan)
185 {
186         int count = 0;
187         struct ao2_iterator i;
188         void *obj;
189
190         ast_assert(control->channel == chan);
191
192         i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
193
194         while ((obj = ao2_iterator_next(&i))) {
195                 RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
196                 command_invoke(command, control, chan);
197                 ++count;
198         }
199
200         ao2_iterator_destroy(&i);
201         return count;
202 }