res_pjsip: PJSIP Transport state monitor refactor.
[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 /*! \brief Callback invoked when transport state changes occur */
118 static void transport_state_callback(pjsip_transport *transport,
119         pjsip_transport_state state, const pjsip_transport_state_info *info)
120 {
121         struct ao2_container *transports;
122
123         /* We only care about monitoring reliable transports */
124         if (PJSIP_TRANSPORT_IS_RELIABLE(transport)
125                 && (transports = ao2_global_obj_ref(active_transports))) {
126                 struct transport_monitor *monitored;
127
128                 ast_debug(3, "Reliable transport '%s' state:%s\n",
129                         transport->obj_name, transport_state2str(state));
130                 switch (state) {
131                 case PJSIP_TP_STATE_CONNECTED:
132                         monitored = ao2_alloc_options(sizeof(*monitored),
133                                 transport_monitor_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
134                         if (!monitored) {
135                                 break;
136                         }
137                         monitored->transport = transport;
138                         if (AST_VECTOR_INIT(&monitored->monitors, 2)) {
139                                 ao2_ref(monitored, -1);
140                                 break;
141                         }
142
143                         ao2_link(transports, monitored);
144                         ao2_ref(monitored, -1);
145                         break;
146                 case PJSIP_TP_STATE_DISCONNECTED:
147                         if (!transport->is_shutdown) {
148                                 pjsip_transport_shutdown(transport);
149                         }
150                         break;
151                 case PJSIP_TP_STATE_SHUTDOWN:
152                         /*
153                          * Set shutdown flag early so we can force a new transport to be
154                          * created if a monitor callback needs to reestablish a link.
155                          * PJPROJECT sets the flag after this routine returns even though
156                          * it has already called the transport's shutdown routine.
157                          */
158                         transport->is_shutdown = PJ_TRUE;
159
160                         monitored = ao2_find(transports, transport->obj_name,
161                                 OBJ_SEARCH_KEY | OBJ_UNLINK);
162                         if (monitored) {
163                                 int idx;
164
165                                 for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
166                                         struct transport_monitor_notifier *notifier;
167
168                                         notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
169                                         notifier->cb(notifier->data);
170                                 }
171                                 ao2_ref(monitored, -1);
172                         }
173                         break;
174                 default:
175                         break;
176                 }
177
178                 ao2_ref(transports, -1);
179         }
180
181         /* Loop over other transport state callbacks registered with us. */
182         if (!AST_LIST_EMPTY(&transport_state_list)) {
183                 struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
184
185                 AST_RWLIST_RDLOCK(&transport_state_list);
186                 AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
187                         tpmgr_notifier->cb(transport, state, info);
188                 }
189                 AST_RWLIST_UNLOCK(&transport_state_list);
190         }
191
192         /* Forward to the old state callback if present */
193         if (tpmgr_state_callback) {
194                 tpmgr_state_callback(transport, state, info);
195         }
196 }
197
198 static int transport_monitor_unregister_all(void *obj, void *arg, int flags)
199 {
200         struct transport_monitor *monitored = obj;
201         ast_transport_monitor_shutdown_cb cb = arg;
202         int idx;
203
204         for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
205                 struct transport_monitor_notifier *notifier;
206
207                 notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
208                 if (notifier->cb == cb) {
209                         ao2_cleanup(notifier->data);
210                         AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
211                         break;
212                 }
213         }
214         return 0;
215 }
216
217 void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb)
218 {
219         struct ao2_container *transports;
220
221         transports = ao2_global_obj_ref(active_transports);
222         if (!transports) {
223                 return;
224         }
225         ao2_callback(transports, OBJ_MULTIPLE | OBJ_NODATA, transport_monitor_unregister_all,
226                 cb);
227         ao2_ref(transports, -1);
228 }
229
230 void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb)
231 {
232         struct ao2_container *transports;
233         struct transport_monitor *monitored;
234
235         transports = ao2_global_obj_ref(active_transports);
236         if (!transports) {
237                 return;
238         }
239
240         ao2_lock(transports);
241         monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
242         if (monitored) {
243                 int idx;
244
245                 for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
246                         struct transport_monitor_notifier *notifier;
247
248                         notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
249                         if (notifier->cb == cb) {
250                                 ao2_cleanup(notifier->data);
251                                 AST_VECTOR_REMOVE_UNORDERED(&monitored->monitors, idx);
252                                 break;
253                         }
254                 }
255                 ao2_ref(monitored, -1);
256         }
257         ao2_unlock(transports);
258         ao2_ref(transports, -1);
259 }
260
261 enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport,
262         ast_transport_monitor_shutdown_cb cb, void *ao2_data)
263 {
264         struct ao2_container *transports;
265         struct transport_monitor *monitored;
266         enum ast_transport_monitor_reg res = AST_TRANSPORT_MONITOR_REG_NOT_FOUND;
267
268         transports = ao2_global_obj_ref(active_transports);
269         if (!transports) {
270                 return res;
271         }
272
273         ao2_lock(transports);
274         monitored = ao2_find(transports, transport->obj_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
275         if (monitored) {
276                 int idx;
277                 struct transport_monitor_notifier new_monitor;
278
279                 /* Check if the callback monitor already exists */
280                 for (idx = AST_VECTOR_SIZE(&monitored->monitors); idx--;) {
281                         struct transport_monitor_notifier *notifier;
282
283                         notifier = AST_VECTOR_GET_ADDR(&monitored->monitors, idx);
284                         if (notifier->cb == cb) {
285                                 /* The monitor is already in the vector replace with new ao2_data. */
286                                 ao2_replace(notifier->data, ao2_data);
287                                 res = AST_TRANSPORT_MONITOR_REG_REPLACED;
288                                 goto register_done;
289                         }
290                 }
291
292                 /* Add new monitor to vector */
293                 new_monitor.cb = cb;
294                 new_monitor.data = ao2_bump(ao2_data);
295                 if (AST_VECTOR_APPEND(&monitored->monitors, new_monitor)) {
296                         ao2_cleanup(ao2_data);
297                         res = AST_TRANSPORT_MONITOR_REG_FAILED;
298                 }
299
300 register_done:
301                 ao2_ref(monitored, -1);
302         }
303         ao2_unlock(transports);
304         ao2_ref(transports, -1);
305         return res;
306 }
307
308 void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element)
309 {
310         AST_RWLIST_WRLOCK(&transport_state_list);
311         AST_LIST_REMOVE(&transport_state_list, element, node);
312         AST_RWLIST_UNLOCK(&transport_state_list);
313 }
314
315 void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *element)
316 {
317         struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
318
319         AST_RWLIST_WRLOCK(&transport_state_list);
320         AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
321                 if (element == tpmgr_notifier) {
322                         /* Already registered. */
323                         AST_RWLIST_UNLOCK(&transport_state_list);
324                         return;
325                 }
326         }
327         AST_LIST_INSERT_HEAD(&transport_state_list, element, node);
328         AST_RWLIST_UNLOCK(&transport_state_list);
329 }
330
331 void ast_sip_destroy_transport_events(void)
332 {
333         pjsip_tpmgr *tpmgr;
334
335         tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
336         if (tpmgr) {
337                 pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
338         }
339
340         ao2_global_obj_release(active_transports);
341 }
342
343 int ast_sip_initialize_transport_events(void)
344 {
345         pjsip_tpmgr *tpmgr;
346         struct ao2_container *transports;
347
348         tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
349         if (!tpmgr) {
350                 return -1;
351         }
352
353         transports = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
354                 ACTIVE_TRANSPORTS_BUCKETS, transport_monitor_hash_fn, NULL,
355                 transport_monitor_cmp_fn);
356         if (!transports) {
357                 return -1;
358         }
359         ao2_global_obj_replace_unref(active_transports, transports);
360         ao2_ref(transports, -1);
361
362         tpmgr_state_callback = pjsip_tpmgr_get_state_cb(tpmgr);
363         pjsip_tpmgr_set_state_cb(tpmgr, &transport_state_callback);
364
365         return 0;
366 }