Asterisk - The Open Source Telephony Project  21.4.1
bridge_roles.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Jonathan Rose <jrose@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 Channel Bridging Roles API
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  *
25  * \ingroup bridges
26  */
27 
28 /*** MODULEINFO
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include <signal.h>
35 
36 #include "asterisk/logger.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/datastore.h"
39 #include "asterisk/linkedlists.h"
40 #include "asterisk/bridge.h"
41 #include "asterisk/bridge_roles.h"
42 #include "asterisk/stringfields.h"
43 
47  AST_STRING_FIELD(option);
48  AST_STRING_FIELD(value);
49  );
50 };
51 
52 struct bridge_role {
55  char role[AST_ROLE_LEN];
56 };
57 
59  AST_LIST_HEAD_NOLOCK(, bridge_role) role_list;
60 };
61 
62 /*!
63  * \internal
64  * \brief Destructor function for a bridge role
65  * \since 12.0.0
66  *
67  * \param role bridge_role being destroyed
68  */
69 static void bridge_role_destroy(struct bridge_role *role)
70 {
71  struct bridge_role_option *role_option;
72  while ((role_option = AST_LIST_REMOVE_HEAD(&role->options, list))) {
73  ast_string_field_free_memory(role_option);
74  ast_free(role_option);
75  }
76  ast_free(role);
77 }
78 
79 /*!
80  * \internal
81  * \brief Destructor function for bridge role datastores
82  * \since 12.0.0
83  *
84  * \param data Pointer to the datastore being destroyed
85  */
86 static void bridge_role_datastore_destroy(void *data)
87 {
88  struct bridge_roles_datastore *roles_datastore = data;
89  struct bridge_role *role;
90 
91  while ((role = AST_LIST_REMOVE_HEAD(&roles_datastore->role_list, list))) {
92  bridge_role_destroy(role);
93  }
94 
95  ast_free(roles_datastore);
96 }
97 
98 static const struct ast_datastore_info bridge_role_info = {
99  .type = "bridge roles",
100  .destroy = bridge_role_datastore_destroy,
101 };
102 
103 /*!
104  * \internal
105  * \brief Setup a bridge role datastore on a channel
106  * \since 12.0.0
107  *
108  * \param chan Chan the datastore is being setup on
109  *
110  * \retval NULL if failed
111  * \return pointer to the newly created datastore
112  */
113 static struct bridge_roles_datastore *setup_bridge_roles_datastore(struct ast_channel *chan)
114 {
115  struct ast_datastore *datastore = NULL;
116  struct bridge_roles_datastore *roles_datastore = NULL;
117 
118  if (!(datastore = ast_datastore_alloc(&bridge_role_info, NULL))) {
119  return NULL;
120  }
121 
122  if (!(roles_datastore = ast_calloc(1, sizeof(*roles_datastore)))) {
123  ast_datastore_free(datastore);
124  return NULL;
125  }
126 
127  AST_LIST_HEAD_INIT_NOLOCK(&roles_datastore->role_list);
128 
129  datastore->data = roles_datastore;
130  ast_channel_datastore_add(chan, datastore);
131  return roles_datastore;
132 }
133 
134 /*!
135  * \internal
136  * \brief Get the bridge_roles_datastore from a channel if it exists. Don't create one if it doesn't.
137  * \since 12.0.0
138  *
139  * \param chan Channel we want the bridge_roles_datastore from
140  *
141  * \retval NULL if we can't find the datastore
142  * \return pointer to the bridge_roles_datastore
143  */
144 static struct bridge_roles_datastore *fetch_bridge_roles_datastore(struct ast_channel *chan)
145 {
146  struct ast_datastore *datastore = NULL;
147 
148  ast_channel_lock(chan);
149  if (!(datastore = ast_channel_datastore_find(chan, &bridge_role_info, NULL))) {
150  ast_channel_unlock(chan);
151  return NULL;
152  }
153  ast_channel_unlock(chan);
154 
155  return datastore->data;
156 }
157 
158 /*!
159  * \internal
160  * \brief Get the bridge_roles_datastore from a channel if it exists. If not, create one.
161  * \since 12.0.0
162  *
163  * \param chan Channel we want the bridge_roles_datastore from
164  *
165  * \retval NULL If we can't find and can't create the datastore
166  * \return pointer to the bridge_roles_datastore
167  */
168 static struct bridge_roles_datastore *fetch_or_create_bridge_roles_datastore(struct ast_channel *chan)
169 {
170  struct bridge_roles_datastore *roles_datastore;
171 
172  ast_channel_lock(chan);
173  roles_datastore = fetch_bridge_roles_datastore(chan);
174  if (!roles_datastore) {
175  roles_datastore = setup_bridge_roles_datastore(chan);
176  }
177  ast_channel_unlock(chan);
178 
179  return roles_datastore;
180 }
181 
182 /*!
183  * \internal
184  * \brief Obtain a role from a bridge_roles_datastore if the datastore has it
185  * \since 12.0.0
186  *
187  * \param roles_datastore The bridge_roles_datastore we are looking for the role of
188  * \param role_name Name of the role being sought
189  *
190  * \retval NULL if the datastore does not have the requested role
191  * \return pointer to the requested role
192  */
193 static struct bridge_role *get_role_from_datastore(struct bridge_roles_datastore *roles_datastore, const char *role_name)
194 {
195  struct bridge_role *role;
196 
197  AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
198  if (!strcmp(role->role, role_name)) {
199  return role;
200  }
201  }
202 
203  return NULL;
204 }
205 
206 /*!
207  * \internal
208  * \brief Obtain a role from a channel structure if the channel's datastore has it
209  * \since 12.0.0
210  *
211  * \param channel The channel we are checking the role of
212  * \param role_name Name of the role sought
213  *
214  * \retval NULL if the channel's datastore does not have the requested role
215  * \return pointer to the requested role
216  */
217 static struct bridge_role *get_role_from_channel(struct ast_channel *channel, const char *role_name)
218 {
219  struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(channel);
220  return roles_datastore ? get_role_from_datastore(roles_datastore, role_name) : NULL;
221 }
222 
223 /*!
224  * \internal
225  * \brief Obtain a role option from a bridge role if it exists in the bridge role's option list
226  * \since 12.0.0
227  *
228  * \param role a pointer to the bridge role wea re searching for the option of
229  * \param option Name of the option sought
230  *
231  * \retval NULL if the bridge role doesn't have the requested option
232  * \return pointer to the requested option
233  */
234 static struct bridge_role_option *get_role_option(struct bridge_role *role, const char *option)
235 {
236  struct bridge_role_option *role_option = NULL;
237  AST_LIST_TRAVERSE(&role->options, role_option, list) {
238  if (!strcmp(role_option->option, option)) {
239  return role_option;
240  }
241  }
242  return NULL;
243 }
244 
245 /*!
246  * \internal
247  * \brief Setup a bridge role on an existing bridge role datastore
248  * \since 12.0.0
249  *
250  * \param roles_datastore bridge_roles_datastore receiving the new role
251  * \param role_name Name of the role being received
252  *
253  * \retval 0 on success
254  * \retval -1 on failure
255  */
256 static int setup_bridge_role(struct bridge_roles_datastore *roles_datastore, const char *role_name)
257 {
258  struct bridge_role *role;
259  role = ast_calloc(1, sizeof(*role));
260 
261  if (!role) {
262  return -1;
263  }
264 
265  AST_LIST_HEAD_INIT_NOLOCK(&role->options);
266 
267  ast_copy_string(role->role, role_name, sizeof(role->role));
268 
269  AST_LIST_INSERT_TAIL(&roles_datastore->role_list, role, list);
270  ast_debug(3, "Set role '%s'\n", role_name);
271 
272  return 0;
273 }
274 
275 /*!
276  * \internal
277  * \brief Setup a bridge role option on an existing bridge role
278  * \since 12.0.0
279  *
280  * \param role The role receiving the option
281  * \param option Name of the option
282  * \param value the option's value
283  *
284  * \retval 0 on success
285  * \retval -1 on failure
286  */
287 static int setup_bridge_role_option(struct bridge_role *role, const char *option, const char *value)
288 {
289  struct bridge_role_option *role_option;
290 
291  if (!value) {
292  value = "";
293  }
294 
295  role_option = ast_calloc(1, sizeof(*role_option));
296  if (!role_option) {
297  return -1;
298  }
299 
300  if (ast_string_field_init(role_option, 32)) {
301  ast_free(role_option);
302  return -1;
303  }
304 
305  ast_string_field_set(role_option, option, option);
306  ast_string_field_set(role_option, value, value);
307 
308  AST_LIST_INSERT_TAIL(&role->options, role_option, list);
309 
310  return 0;
311 }
312 
313 int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
314 {
315  struct bridge_roles_datastore *roles_datastore = fetch_or_create_bridge_roles_datastore(chan);
316 
317  if (!roles_datastore) {
318  ast_log(LOG_WARNING, "Unable to set up bridge role datastore on channel %s\n", ast_channel_name(chan));
319  return -1;
320  }
321 
322  /* Check to make sure we aren't adding a redundant role */
323  if (get_role_from_datastore(roles_datastore, role_name)) {
324  ast_debug(2, "Bridge role %s is already applied to the channel %s\n", role_name, ast_channel_name(chan));
325  return 0;
326  }
327 
328  /* It wasn't already there, so we can just finish setting it up now. */
329  return setup_bridge_role(roles_datastore, role_name);
330 }
331 
332 void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name)
333 {
334  struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
335  struct bridge_role *role;
336 
337  if (!roles_datastore) {
338  /* The roles datastore didn't already exist, so there is no need to remove a role */
339  ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
340  return;
341  }
342 
343  AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
344  if (!strcmp(role->role, role_name)) {
345  ast_debug(2, "Removing bridge role %s from channel %s\n", role_name, ast_channel_name(chan));
347  bridge_role_destroy(role);
348  return;
349  }
350  }
352 
353  ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
354 }
355 
357 {
358  struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
359  struct bridge_role *role;
360 
361  if (!roles_datastore) {
362  /* The roles datastore didn't already exist, so there is no need to remove any roles */
363  ast_debug(2, "Roles did not exist on channel %s\n", ast_channel_name(chan));
364  return;
365  }
366 
367  AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
368  ast_debug(2, "Removing bridge role %s from channel %s\n", role->role, ast_channel_name(chan));
370  bridge_role_destroy(role);
371  }
373 }
374 
375 int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
376 {
377  struct bridge_role *role = get_role_from_channel(channel, role_name);
378  struct bridge_role_option *role_option;
379 
380  if (!role) {
381  return -1;
382  }
383 
384  role_option = get_role_option(role, option);
385 
386  if (role_option) {
387  ast_string_field_set(role_option, value, value);
388  return 0;
389  }
390 
391  return setup_bridge_role_option(role, option, value);
392 }
393 
394 int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
395 {
396  return get_role_from_channel(channel, role_name) ? 1 : 0;
397 }
398 
399 const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
400 {
401  struct bridge_role *role;
402  struct bridge_role_option *role_option;
403 
404  role = get_role_from_channel(channel, role_name);
405  if (!role) {
406  return NULL;
407  }
408 
409  role_option = get_role_option(role, option);
410 
411  return role_option ? role_option->value : NULL;
412 }
413 
414 int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
415 {
416  if (!bridge_channel->bridge_roles) {
417  return 0;
418  }
419 
420  return get_role_from_datastore(bridge_channel->bridge_roles, role_name) ? 1 : 0;
421 }
422 
423 const char *ast_bridge_channel_get_role_option(struct ast_bridge_channel *bridge_channel, const char *role_name, const char *option)
424 {
425  struct bridge_role *role;
426  struct bridge_role_option *role_option = NULL;
427 
428  if (!bridge_channel->bridge_roles) {
429  return NULL;
430  }
431 
432  role = get_role_from_datastore(bridge_channel->bridge_roles, role_name);
433 
434  if (!role) {
435  return NULL;
436  }
437 
438  role_option = get_role_option(role, option);
439 
440  return role_option ? role_option->value : NULL;
441 }
442 
444 {
445  struct bridge_roles_datastore *roles_datastore;
446  struct bridge_role *role = NULL;
447  struct bridge_role_option *role_option;
448 
449  if (!bridge_channel->chan) {
450  ast_debug(2, "Attempted to set roles on a bridge channel that has no associated channel. That's a bad idea.\n");
451  return -1;
452  }
453 
454  if (bridge_channel->bridge_roles) {
455  ast_debug(2, "Attempted to reset roles while roles were already established. Purge existing roles first.\n");
456  return -1;
457  }
458 
459  roles_datastore = fetch_bridge_roles_datastore(bridge_channel->chan);
460  if (!roles_datastore) {
461  /* No roles to establish. */
462  return 0;
463  }
464 
465  if (!(bridge_channel->bridge_roles = ast_calloc(1, sizeof(*bridge_channel->bridge_roles)))) {
466  return -1;
467  }
468 
469  AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
470  struct bridge_role *this_role_copy;
471 
472  if (setup_bridge_role(bridge_channel->bridge_roles, role->role)) {
473  /* We need to abandon the copy because we couldn't setup a role */
474  ast_bridge_channel_clear_roles(bridge_channel);
475  return -1;
476  }
477  this_role_copy = AST_LIST_LAST(&bridge_channel->bridge_roles->role_list);
478 
479  AST_LIST_TRAVERSE(&role->options, role_option, list) {
480  if (setup_bridge_role_option(this_role_copy, role_option->option, role_option->value)) {
481  /* We need to abandon the copy because we couldn't setup a role option */
482  ast_bridge_channel_clear_roles(bridge_channel);
483  return -1;
484  }
485  }
486  }
487 
488  return 0;
489 }
490 
492 {
493  if (bridge_channel->bridge_roles) {
494  bridge_role_datastore_destroy(bridge_channel->bridge_roles);
495  bridge_channel->bridge_roles = NULL;
496  }
497 }
const char * type
Definition: datastore.h:32
void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel)
Clear all roles from a bridge_channel's role list.
Definition: bridge_roles.c:491
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
const char * ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
Retrieve the value of a requested role option from a channel.
Definition: bridge_roles.c:399
int ast_bridge_channel_establish_roles(struct ast_bridge_channel *bridge_channel)
Clone the roles from a bridge_channel's attached ast_channel onto the bridge_channel's role list...
Definition: bridge_roles.c:443
void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name)
Removes a bridge role from a channel.
Definition: bridge_roles.c:332
int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
Adds a bridge role to a channel.
Definition: bridge_roles.c:313
const char * ast_bridge_channel_get_role_option(struct ast_bridge_channel *bridge_channel, const char *role_name, const char *option)
Retrieve the value of a requested role option from a bridge channel.
Definition: bridge_roles.c:423
Structure for a data store type.
Definition: datastore.h:31
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
Structure for a data store object.
Definition: datastore.h:64
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
Check if a role exists on a channel.
Definition: bridge_roles.c:394
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
Asterisk datastore objects.
Channel Bridging Roles API.
General Asterisk PBX channel definitions.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
A set of macros to manage forward-linked lists.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
struct bridge_roles_datastore * bridge_roles
#define AST_LIST_LAST(head)
Returns the last entry contained in a list.
Definition: linkedlists.h:429
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Support for logging to various files, console and syslog Configuration in file logger.conf.
void ast_channel_clear_bridge_roles(struct ast_channel *chan)
Removes all bridge roles currently on a channel.
Definition: bridge_roles.c:356
void * data
Definition: datastore.h:66
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
struct ast_channel * chan
Structure that contains information regarding a channel in a bridge.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
Bridging API.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
Set a role option on a channel.
Definition: bridge_roles.c:375
int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
Check to see if a bridge channel inherited a specific role from its channel.
Definition: bridge_roles.c:414
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521