Merge "chan_dahdi: Fix build with clang/llvm"
[asterisk/asterisk.git] / res / res_pjsip / pjsip_transport_events.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2017, Digium Inc.
5  *
6  * Richard Mudgett <rmudgett@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 /*!
20  * \file
21  * \brief Manages the global transport event notification callbacks.
22  *
23  * \author Richard Mudgett <rmudgett@digium.com>
24  *      See Also:
25  *
26  * \arg \ref AstCREDITS
27  */
28
29
30 #include "asterisk.h"
31
32 #include "asterisk/res_pjsip.h"
33 #include "include/res_pjsip_private.h"
34 #include "asterisk/linkedlists.h"
35 #include "asterisk/vector.h"
36
37 /* ------------------------------------------------------------------- */
38
39 /*! \brief Number of buckets for monitored active transports */
40 #define ACTIVE_TRANSPORTS_BUCKETS 127
41
42 /*! Who to notify when transport shuts down. */
43 struct transport_monitor_notifier {
44         /*! Who to call when transport shuts down. */
45         ast_transport_monitor_shutdown_cb cb;
46         /*! ao2 data object to pass to callback. */
47         void *data;
48 };
49
50 /*! \brief Structure for transport to be monitored */
51 struct transport_monitor {
52         /*! \brief The underlying PJSIP transport */
53         pjsip_transport *transport;
54         /*! Who is interested in when this transport shuts down. */
55         AST_VECTOR(, struct transport_monitor_notifier) monitors;
56 };
57
58 /*! \brief Global container of active reliable transports */
59 static AO2_GLOBAL_OBJ_STATIC(active_transports);
60
61 /*! \brief Existing transport events callback that we need to invoke */
62 static pjsip_tp_state_callback tpmgr_state_callback;
63
64 /*! List of registered transport state callbacks. */
65 static AST_RWLIST_HEAD(, ast_sip_tpmgr_state_callback) transport_state_list;
66
67
68 /*! \brief Hashing function for struct transport_monitor */
69 AO2_STRING_FIELD_HASH_FN(transport_monitor, transport->obj_name);
70
71 /*! \brief Comparison function for struct transport_monitor */
72 AO2_STRING_FIELD_CMP_FN(transport_monitor, transport->obj_name);
73
74 static const char *transport_state2str(pjsip_transport_state state)
75 {
76         const char *name;
77
78         switch (state) {
79         case PJSIP_TP_STATE_CONNECTED:
80                 name = "CONNECTED";
81                 break;
82         case PJSIP_TP_STATE_DISCONNECTED:
83                 name = "DISCONNECTED";
84                 break;
85         case PJSIP_TP_STATE_SHUTDOWN:
86                 name = "SHUTDOWN";
87                 break;
88         case PJSIP_TP_STATE_DESTROY:
89                 name = "DESTROY";
90                 break;
91         default:
92                 /*
93                  * We have to have a default case because the enum is
94                  * defined by a third-party library.
95                  */
96                 ast_assert(0);
97                 name = "<unknown>";
98                 break;
99         }
100         return name;
101 }
102
103 static void transport_monitor_dtor(void *vdoomed)
104 {
105         struct transport_monitor *monitored = vdoomed;
106         int idx;
107
108         for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
109                 struct transport_monitor_notifier *notifier;
110
111                 notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
112                 ao2_cleanup(notifier->data);
113         }
114         AST_VECTOR_FREE(&monitored->monitors);
115 }
116
117 /*!
118  * \internal
119  * \brief Do registered callbacks for the transport.
120  * \since 13.21.0
121  *
122  * \param transports Active transports container
123  * \param transport Which transport to do callbacks for.
124  *
125  * \return Nothing
126  */
127 static void transport_state_do_reg_callbacks(struct ao2_container *transports, pjsip_transport *transport)
128 {
129         struct transport_monitor *monitored;
130
131         monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_UNLINK);
132         if (monitored) {
133                 int idx;
134
135                 for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
136                         struct transport_monitor_notifier *notifier;
137
138                         notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
139                         ast_debug(3, "running callback %p(%p) for transport %s\n",
140                                 notifier->cb, notifier->data, transport->obj_name);
141                         notifier->cb(notifier->data);
142                 }
143                 ao2_ref(monitored, -1);
144         }
145 }
146
147 /*! \brief Callback invoked when transport state changes occur */
148 static void transport_state_callback(pjsip_transport *transport,
149         pjsip_transport_state state, const pjsip_transport_state_info *info)
150 {
151         struct ao2_container *transports;
152
153         /* We only care about monitoring reliable transports */
154         if (PJSIP_TRANSPORT_IS_RELIABLE(transport)
155                 && (transports = ao2_global_obj_ref(active_transports))) {
156                 struct transport_monitor *monitored;
157
158                 ast_debug(3, "Reliable transport '%s' state:%s\n",
159                         transport->obj_name, transport_state2str(state));
160                 switch (state) {
161                 case PJSIP_TP_STATE_CONNECTED:
162                         monitored = ao2_alloc_options(sizeof(*monitored),
163                                 transport_monitor_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
164                         if (!monitored) {
165                                 break;
166                         }
167                         monitored->transport = transport;
168                         if (AST_VECTOR_INIT(&monitored->monitors, 5)) {
169                                 ao2_ref(monitored, -1);
170                                 break;
171                         }
172
173                         ao2_link(transports, monitored);
174                         ao2_ref(monitored, -1);
175                         break;
176                 case PJSIP_TP_STATE_DISCONNECTED:
177                         if (!transport->is_shutdown) {
178                                 pjsip_transport_shutdown(transport);
179                         }
180                         transport_state_do_reg_callbacks(transports, transport);
181                         break;
182                 case PJSIP_TP_STATE_SHUTDOWN:
183                         /*
184                          * Set shutdown flag early so we can force a new transport to be
185                          * created if a monitor callback needs to reestablish a link.
186                          * PJPROJECT sets the flag after this routine returns even though
187                          * it has already called the transport's shutdown routine.
188                          */
189                         transport->is_shutdown = PJ_TRUE;
190
191                         transport_state_do_reg_callbacks(transports, transport);
192                         break;
193                 case PJSIP_TP_STATE_DESTROY:
194                         transport_state_do_reg_callbacks(transports, transport);
195                         break;
196                 default:
197                         /*
198                          * We have to have a default case because the enum is
199                          * defined by a third-party library.
200                          */
201                         ast_assert(0);
202                         break;
203                 }
204
205                 ao2_ref(transports, -1);
206         }
207
208         /* Loop over other transport state callbacks registered with us. */
209         if (!AST_LIST_EMPTY(&transport_state_list)) {
210                 struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
211
212                 AST_RWLIST_RDLOCK(&transport_state_list);
213                 AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
214                         tpmgr_notifier->cb(transport, state, info);
215                 }
216                 AST_RWLIST_UNLOCK(&transport_state_list);
217         }
218
219         /* Forward to the old state callback if present */
220         if (tpmgr_state_callback) {
221                 tpmgr_state_callback(transport, state, info);
222         }
223 }
224
225 struct callback_data {
226         ast_transport_monitor_shutdown_cb cb;
227         void *data;
228         ast_transport_monitor_data_matcher matches;
229 };
230
231 static int transport_monitor_unregister_cb(void *obj, void *arg, int flags)
232 {
233         struct transport_monitor *monitored = obj;
234         struct callback_data *cb_data = arg;
235         int idx;
236
237         for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
238                 struct transport_monitor_notifier *notifier;
239
240                 notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
241                 if (notifier->cb == cb_data->cb && (!cb_data->data
242                         || cb_data->matches(cb_data->data, notifier->data))) {
243                         ao2_cleanup(notifier->data);
244                         AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
245                         ast_debug(3, "Unregistered monitor %p(%p) from transport %s\n",
246                                 notifier->cb, notifier->data, monitored->transport->obj_name);
247                 }
248         }
249         return 0;
250 }
251
252 static int ptr_matcher(void *a, void *b)
253 {
254         return a == b;
255 }
256
257 void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb,
258         void *data, ast_transport_monitor_data_matcher matches)
259 {
260         struct ao2_container *transports;
261         struct callback_data cb_data = {
262                 .cb = cb,
263                 .data = data,
264                 .matches = matches ?: ptr_matcher,
265         };
266
267         ast_assert(cb != NULL);
268
269         transports = ao2_global_obj_ref(active_transports);
270         if (!transports) {
271                 return;
272         }
273         ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_cb, &cb_data);
274         ao2_ref(transports, -1);
275 }
276
277 void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
278         ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
279 {
280         struct ao2_container *transports;
281         struct transport_monitor *monitored;
282
283         ast_assert(transport != NULL && cb != NULL);
284
285         transports = ao2_global_obj_ref(active_transports);
286         if (!transports) {
287                 return;
288         }
289
290         ao2_lock(transports);
291         monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
292         if (monitored) {
293                 struct callback_data cb_data = {
294                         .cb = cb,
295                         .data = data,
296                         .matches = matches ?: ptr_matcher,
297                 };
298
299                 transport_monitor_unregister_cb(monitored, &cb_data, 0);
300                 ao2_ref(monitored, -1);
301         }
302         ao2_unlock(transports);
303         ao2_ref(transports, -1);
304 }
305
306 enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
307         ast_transport_monitor_shutdown_cb cb, void *ao2_data)
308 {
309         return ast_sip_transport_monitor_register_replace(transport, cb, ao2_data, NULL);
310 }
311
312 enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace(pjsip_transport *transport,
313         ast_transport_monitor_shutdown_cb cb, void *ao2_data, ast_transport_monitor_data_matcher matches)
314 {
315         struct ao2_container *transports;
316         struct transport_monitor *monitored;
317         enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;
318
319         ast_assert(transport != NULL && cb != NULL);
320
321         transports = ao2_global_obj_ref(active_transports);
322         if (!transports) {
323                 return res;
324         }
325
326         ao2_lock(transports);
327         monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
328         if (monitored) {
329                 struct transport_monitor_notifier new_monitor;
330                 struct callback_data cb_data = {
331                         .cb = cb,
332                         .data = ao2_data,
333                         .matches = matches ?: ptr_matcher,
334                 };
335
336                 transport_monitor_unregister_cb(monitored, &cb_data, 0);
337
338                 /* Add new monitor to vector */
339                 new_monitor.cb = cb;
340                 new_monitor.data = ao2_bump(ao2_data);
341                 if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {
342                         ao2_cleanup(ao2_data);
343                         res = AST_TRANSPORT_MONITOR_REG_FAILED;
344                         ast_debug(3, "Register monitor %p(%p) to transport %s FAILED\n",
345                                 cb, ao2_data, transport->obj_name);
346                 } else {
347                         res = AST_TRANSPORT_MONITOR_REG_SUCCESS;
348                         ast_debug(3, "Registered monitor %p(%p) to transport %s\n",
349                                 cb, ao2_data, transport->obj_name);
350                 }
351
352                 ao2_ref(monitored, -1);
353         }
354         ao2_unlock(transports);
355         ao2_ref(transports, -1);
356         return res;
357 }
358
359 void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element)
360 {
361         AST_RWLIST_WRLOCK(&transport_state_list);
362         AST_LIST_REMOVE(&transport_state_list, element, node);
363         AST_RWLIST_UNLOCK(&transport_state_list);
364 }
365
366 void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *element)
367 {
368         struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
369
370         AST_RWLIST_WRLOCK(&transport_state_list);
371         AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
372                 if (element == tpmgr_notifier) {
373                         /* Already registered. */
374                         AST_RWLIST_UNLOCK(&transport_state_list);
375                         return;
376                 }
377         }
378         AST_LIST_INSERT_HEAD(&transport_state_list, element, node);
379         AST_RWLIST_UNLOCK(&transport_state_list);
380 }
381
382 void ast_sip_destroy_transport_events(void)
383 {
384         pjsip_tpmgr *tpmgr;
385
386         tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
387         if (tpmgr) {
388                 pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
389         }
390
391         ao2_global_obj_release(active_transports);
392 }
393
394 int ast_sip_initialize_transport_events(void)
395 {
396         pjsip_tpmgr *tpmgr;
397         struct ao2_container *transports;
398
399         tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
400         if (!tpmgr) {
401                 return -1;
402         }
403
404         transports = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
405                 ACTIVE_TRANSPORTS_BUCKETS, transport_monitor_hash_fn, NULL,
406                 transport_monitor_cmp_fn);
407         if (!transports) {
408                 return -1;
409         }
410         ao2_global_obj_replace_unref(active_transports, transports);
411         ao2_ref(transports, -1);
412
413         tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
414         pjsip_tpmgr_set_state_cb(tpmgr, &transport_state_callback);
415
416         return 0;
417 }