Asterisk - The Open Source Telephony Project  18.5.0
pjsip_transport_events.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2017, Digium Inc.
5  *
6  * Richard Mudgett <[email protected]>
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 <[email protected]>
24  * See Also:
25  *
26  * \arg \ref AstCREDITS
27  */
28 
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/res_pjsip.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. */
44  /*! Who to call when transport shuts down. */
46  /*! ao2 data object to pass to callback. */
47  void *data;
48 };
49 
50 /*! \brief Structure for transport to be monitored */
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. */
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),
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. */
210  struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
211 
213  AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
214  tpmgr_notifier->cb(transport, state, info);
215  }
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 
227  void *data;
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 
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  }
274  ao2_ref(transports, -1);
275 }
276 
277 void ast_sip_transport_monitor_unregister(pjsip_transport *transport,
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 
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 
314 {
315  struct ao2_container *transports;
316  struct transport_monitor *monitored;
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);
344  ast_debug(3, "Register monitor %p(%p) to transport %s FAILED\n",
345  cb, ao2_data, transport->obj_name);
346  } else {
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 
360 {
364 }
365 
367 {
368  struct ast_sip_tpmgr_state_callback *tpmgr_notifier;
369 
371  AST_LIST_TRAVERSE(&transport_state_list, tpmgr_notifier, node) {
372  if (element == tpmgr_notifier) {
373  /* Already registered. */
375  return;
376  }
377  }
380 }
381 
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 
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 
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 }
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
Definition: test_heap.c:38
Asterisk main include file. File version handling, generic pbx functions.
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
static pjsip_tp_state_callback tpmgr_state_callback
Existing transport events callback that we need to invoke.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
ast_transport_monitor_reg
Definition: res_pjsip.h:3459
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
#define ACTIVE_TRANSPORTS_BUCKETS
Number of buckets for monitored active transports.
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
Assume that the ao2_container is already locked.
Definition: astobj2.h:1067
#define AST_VECTOR_REMOVE_UNORDERED(vec, idx)
Remove an element from an unordered vector by index.
Definition: vector.h:438
#define ao2_global_obj_ref(holder)
Definition: astobj2.h:925
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition: astobj2.h:406
#define ast_assert(a)
Definition: utils.h:695
#define ao2_unlock(a)
Definition: astobj2.h:730
void ast_sip_destroy_transport_events(void)
#define NULL
Definition: resample.c:96
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:855
static void transport_state_callback(pjsip_transport *transport, pjsip_transport_state state, const pjsip_transport_state_info *info)
Callback invoked when transport state changes occur.
#define ao2_bump(obj)
Definition: astobj2.h:491
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
Structure for transport to be monitored.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
#define AST_VECTOR_GET_ADDR(vec, idx)
Get an address of element in a vector.
Definition: vector.h:670
static const char * transport_state2str(pjsip_transport_state state)
ast_transport_monitor_data_matcher matches
AO2_STRING_FIELD_HASH_FN(transport_monitor, transport->obj_name)
Hashing function for struct transport_monitor.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_lock(a)
Definition: astobj2.h:718
Successfully registered the transport monitor.
Definition: res_pjsip.h:3461
A set of macros to manage forward-linked lists.
enum ast_transport_monitor_reg ast_sip_transport_monitor_register(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb, void *ao2_data)
Register a reliable transport shutdown monitor callback.
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
static void transport_state_do_reg_callbacks(struct ao2_container *transports, pjsip_transport *transport)
enum ast_transport_monitor_reg ast_sip_transport_monitor_register_replace(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb, void *ao2_data, ast_transport_monitor_data_matcher matches)
Register a reliable transport shutdown monitor callback replacing any duplicate.
Transport not found to monitor.
Definition: res_pjsip.h:3468
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
pjsip_transport * transport
The underlying PJSIP transport.
static int transport_monitor_unregister_cb(void *obj, void *arg, int flags)
#define ao2_global_obj_release(holder)
Definition: astobj2.h:865
def info(msg)
static struct @480 transport_state_list
int ast_sip_initialize_transport_events(void)
int(* ast_transport_monitor_data_matcher)(void *a, void *b)
Transport shutdown monitor data matcher.
Definition: res_pjsip.h:3457
static int ptr_matcher(void *a, void *b)
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
static const char name[]
Definition: cdr_mysql.c:74
static AO2_GLOBAL_OBJ_STATIC(active_transports)
Global container of active reliable transports.
void ast_sip_transport_monitor_unregister_all(ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a transport shutdown monitor from all reliable transports.
pjsip_tp_state_callback cb
Definition: res_pjsip.h:3555
void ast_sip_transport_monitor_unregister(pjsip_transport *transport, ast_transport_monitor_shutdown_cb cb, void *data, ast_transport_monitor_data_matcher matches)
Unregister a reliable transport shutdown monitor.
Vector container support.
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
ast_transport_monitor_shutdown_cb cb
pjsip_endpoint * ast_sip_get_pjsip_endpoint(void)
Get a pointer to the PJSIP endpoint.
Definition: res_pjsip.c:3718
#define ao2_global_obj_replace_unref(holder, obj)
Definition: astobj2.h:908
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
AO2_STRING_FIELD_CMP_FN(transport_monitor, transport->obj_name)
Comparison function for struct transport_monitor.
static struct test_val b
Generic container type.
void ast_sip_transport_state_unregister(struct ast_sip_tpmgr_state_callback *element)
Unregister a transport state notification callback element.
Error while registering transport monitor.
Definition: res_pjsip.h:3470
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:611
#define AST_RWLIST_HEAD(name, type)
Defines a structure to be used to hold a read/write list of specified type.
Definition: linkedlists.h:198
void ast_sip_transport_state_register(struct ast_sip_tpmgr_state_callback *element)
Register a transport state notification callback element.
static void transport_monitor_dtor(void *vdoomed)
ast_transport_monitor_shutdown_cb cb
void(* ast_transport_monitor_shutdown_cb)(void *data)
Transport shutdown monitor callback.
Definition: res_pjsip.h:3445
static struct test_val a
#define ao2_link(container, obj)
Definition: astobj2.h:1549