Continue events when ARI WebSocket reconnects
[asterisk/asterisk.git] / res / stasis / app.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 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 "app.h"
31
32 #include "asterisk/stasis_app.h"
33 #include "asterisk/stasis_channels.h"
34
35 /*!
36  * \brief Number of buckets for the channels container for app instances.  Remember
37  * to keep it a prime number!
38  */
39 #define APP_CHANNELS_BUCKETS 7
40
41 /*!
42  * \brief Number of buckets for the bridges container for app instances.  Remember
43  * to keep it a prime number!
44  */
45 #define APP_BRIDGES_BUCKETS 7
46
47 struct app {
48         /*! Callback function for this application. */
49         stasis_app_cb handler;
50         /*! Opaque data to hand to callback function. */
51         void *data;
52         /*! List of channel identifiers this app instance is interested in */
53         struct ao2_container *channels;
54         /*! List of bridge identifiers this app instance owns */
55         struct ao2_container *bridges;
56         /*! Name of the Stasis application */
57         char name[];
58 };
59
60 static void app_dtor(void *obj)
61 {
62         struct app *app = obj;
63
64         ao2_cleanup(app->data);
65         app->data = NULL;
66         ao2_cleanup(app->channels);
67         app->channels = NULL;
68         ao2_cleanup(app->bridges);
69         app->bridges = NULL;
70 }
71
72 struct app *app_create(const char *name, stasis_app_cb handler, void *data)
73 {
74         RAII_VAR(struct app *, app, NULL, ao2_cleanup);
75         size_t size;
76
77         ast_assert(name != NULL);
78         ast_assert(handler != NULL);
79
80         ast_verb(1, "Creating Stasis app '%s'\n", name);
81
82         size = sizeof(*app) + strlen(name) + 1;
83         app = ao2_alloc_options(size, app_dtor, AO2_ALLOC_OPT_LOCK_MUTEX);
84
85         if (!app) {
86                 return NULL;
87         }
88
89         strncpy(app->name, name, size - sizeof(*app));
90         app->handler = handler;
91         ao2_ref(data, +1);
92         app->data = data;
93
94         app->channels = ast_str_container_alloc(APP_CHANNELS_BUCKETS);
95         if (!app->channels) {
96                 return NULL;
97         }
98
99         app->bridges = ast_str_container_alloc(APP_BRIDGES_BUCKETS);
100         if (!app->bridges) {
101                 return NULL;
102         }
103
104         ao2_ref(app, +1);
105         return app;
106 }
107
108 int app_add_channel(struct app *app, const struct ast_channel *chan)
109 {
110         SCOPED_AO2LOCK(lock, app);
111         const char *uniqueid;
112
113         ast_assert(app != NULL);
114         ast_assert(chan != NULL);
115
116         /* Don't accept new channels in an inactive application */
117         if (!app->handler) {
118                 return -1;
119         }
120
121         uniqueid = ast_channel_uniqueid(chan);
122         return ast_str_container_add(app->channels, uniqueid) ? -1 : 0;
123 }
124
125 void app_remove_channel(struct app* app, const struct ast_channel *chan)
126 {
127         SCOPED_AO2LOCK(lock, app);
128
129         ast_assert(app != NULL);
130         ast_assert(chan != NULL);
131
132         ao2_find(app->channels, ast_channel_uniqueid(chan), OBJ_KEY | OBJ_NODATA | OBJ_UNLINK);
133 }
134
135 int app_add_bridge(struct app *app, const char *uniqueid)
136 {
137         SCOPED_AO2LOCK(lock, app);
138
139         ast_assert(app != NULL);
140         ast_assert(uniqueid != NULL);
141
142         /* Don't accept new bridges in an inactive application */
143         if (!app->handler) {
144                 return -1;
145         }
146
147         return ast_str_container_add(app->bridges, uniqueid) ? -1 : 0;
148 }
149
150 void app_remove_bridge(struct app* app, const char *uniqueid)
151 {
152         SCOPED_AO2LOCK(lock, app);
153
154         ast_assert(app != NULL);
155         ast_assert(uniqueid != NULL);
156
157         ao2_find(app->bridges, uniqueid, OBJ_KEY | OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE);
158 }
159
160 /*!
161  * \brief Send a message to the given application.
162  * \param app App to send the message to.
163  * \param message Message to send.
164  */
165 void app_send(struct app *app, struct ast_json *message)
166 {
167         stasis_app_cb handler;
168         RAII_VAR(void *, data, NULL, ao2_cleanup);
169
170         /* Copy off mutable state with lock held */
171         {
172                 SCOPED_AO2LOCK(lock, app);
173                 handler = app->handler;
174                 if (app->data) {
175                         ao2_ref(app->data, +1);
176                         data = app->data;
177                 }
178                 /* Name is immutable; no need to copy */
179         }
180
181         if (!handler) {
182                 ast_verb(3,
183                         "Inactive Stasis app '%s' missed message\n", app->name);
184                 return;
185         }
186
187         handler(data, app->name, message);
188 }
189
190 void app_deactivate(struct app *app)
191 {
192         SCOPED_AO2LOCK(lock, app);
193         ast_verb(1, "Deactivating Stasis app '%s'\n", app->name);
194         app->handler = NULL;
195         ao2_cleanup(app->data);
196         app->data = NULL;
197 }
198
199 int app_is_active(struct app *app)
200 {
201         SCOPED_AO2LOCK(lock, app);
202         return app->handler != NULL;
203 }
204
205 int app_is_finished(struct app *app)
206 {
207         SCOPED_AO2LOCK(lock, app);
208
209         return app->handler == NULL &&
210                 ao2_container_count(app->channels) == 0;
211 }
212
213 void app_update(struct app *app, stasis_app_cb handler, void *data)
214 {
215         SCOPED_AO2LOCK(lock, app);
216
217         if (app->handler) {
218                 RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
219
220                 ast_verb(1, "Replacing Stasis app '%s'\n", app->name);
221
222                 msg = ast_json_pack("{s: s, s: s}",
223                         "type", "ApplicationReplaced",
224                         "application", app_name);
225                 if (msg) {
226                         app_send(app, msg);
227                 }
228         } else {
229                 ast_verb(1, "Activating Stasis app '%s'\n", app->name);
230         }
231
232
233         app->handler = handler;
234         ao2_cleanup(app->data);
235         if (data) {
236                 ao2_ref(data, +1);
237         }
238         app->data = data;
239 }
240
241 const char *app_name(const struct app *app)
242 {
243         return app->name;
244 }
245
246 int app_is_watching_channel(struct app *app, const char *uniqueid)
247 {
248         RAII_VAR(char *, found, NULL, ao2_cleanup);
249         found = ao2_find(app->channels, uniqueid, OBJ_KEY);
250         return found != NULL;
251 }
252
253 int app_is_watching_bridge(struct app *app, const char *uniqueid)
254 {
255         RAII_VAR(char *, found, NULL, ao2_cleanup);
256         found = ao2_find(app->bridges, uniqueid, OBJ_KEY);
257         return found != NULL;
258 }