static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
+AST_MUTEX_DEFINE_STATIC(bridge_init_lock);
+
static unsigned int optimization_id;
/* Initial starting point for the bridge array of channels */
};
if (bridge->dissolved) {
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": already dissolved\n",
+ BRIDGE_PRINTF_VARS(bridge));
return;
}
bridge->dissolved = 1;
}
bridge->cause = cause;
- ast_debug(1, "Bridge %s: dissolving bridge with cause %d(%s)\n",
- bridge->uniqueid, cause, ast_cause2str(cause));
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": dissolving with cause %d(%s)\n",
+ BRIDGE_PRINTF_VARS(bridge), cause, ast_cause2str(cause));
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": kicking channel %s\n",
+ BRIDGE_PRINTF_VARS(bridge),
+ ast_channel_name(bridge_channel->chan));
ast_bridge_channel_leave_bridge(bridge_channel,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, cause);
}
/* Must defer dissolving bridge because it is already locked. */
ast_bridge_queue_action(bridge, &action);
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": DEFERRED_DISSOLVING queued. current refcound: %d\n",
+ BRIDGE_PRINTF_VARS(bridge), ao2_ref(bridge, 0));
+
}
/*!
AST_RWLIST_RDLOCK(&bridge_technologies);
AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
if (current->suspended) {
- ast_debug(1, "Bridge technology %s is suspended. Skipping.\n",
+ ast_debug(2, "Bridge technology %s is suspended. Skipping.\n",
current->name);
continue;
}
if (!(current->capabilities & capabilities)) {
- ast_debug(1, "Bridge technology %s does not have any capabilities we want.\n",
+ ast_debug(2, "Bridge technology %s does not have any capabilities we want.\n",
current->name);
continue;
}
if (best && current->preference <= best->preference) {
- ast_debug(1, "Bridge technology %s has less preference than %s (%u <= %u). Skipping.\n",
+ ast_debug(2, "Bridge technology %s has less preference than %s (%u <= %u). Skipping.\n",
current->name, best->name, current->preference, best->preference);
continue;
}
if (current->compatible && !current->compatible(bridge)) {
- ast_debug(1, "Bridge technology %s is not compatible with properties of existing bridge.\n",
+ ast_debug(2, "Bridge technology %s is not compatible with properties of existing bridge.\n",
current->name);
continue;
}
if (!ast_module_running_ref(current->mod)) {
- ast_debug(1, "Bridge technology %s is not running, skipping.\n", current->name);
+ ast_debug(2, "Bridge technology %s is not running, skipping.\n", current->name);
continue;
}
if (best) {
{
struct ast_bridge *bridge = obj;
- ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n",
- bridge->uniqueid, bridge->v_table->name);
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": actually destroying %s bridge, nobody wants it anymore\n",
+ BRIDGE_PRINTF_VARS(bridge), bridge->v_table->name);
if (bridge->construction_completed) {
bridge_topics_destroy(bridge);
cleanup_video_mode(bridge);
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": destroyed\n",
+ BRIDGE_PRINTF_VARS(bridge));
ast_string_field_free_memory(bridge);
ao2_cleanup(bridge->current_snapshot);
+ bridge->current_snapshot = NULL;
}
struct ast_bridge *bridge_register(struct ast_bridge *bridge)
{
if (bridge) {
+ SCOPED_MUTEX(lock, &bridge_init_lock);
+ /*
+ * Although bridge_base_init() should have already checked for
+ * an existing bridge with the same uniqueid, bridge_base_init()
+ * and bridge_register() are two separate public APIs so we need
+ * to check again here.
+ */
+ struct ast_bridge *existing = ast_bridge_find_by_id(bridge->uniqueid);
+ if (existing) {
+ ast_log(LOG_WARNING, "Bridge " BRIDGE_PRINTF_SPEC ": already registered\n",
+ BRIDGE_PRINTF_VARS(bridge));
+ ao2_ref(existing, -1);
+ ast_bridge_destroy(bridge, 0);
+ return NULL;
+ }
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": registering\n",
+ BRIDGE_PRINTF_VARS(bridge));
bridge->construction_completed = 1;
ast_bridge_lock(bridge);
ast_bridge_publish_state(bridge);
ast_bridge_unlock(bridge);
if (!ao2_link(bridges, bridge)) {
+ ast_log(LOG_WARNING, "Bridge " BRIDGE_PRINTF_SPEC ": failed to link\n",
+ BRIDGE_PRINTF_VARS(bridge));
ast_bridge_destroy(bridge, 0);
bridge = NULL;
}
return NULL;
}
- if (!ast_strlen_zero(id)) {
- ast_string_field_set(self, uniqueid, id);
- } else {
- ast_uuid_generate_str(uuid_hold, AST_UUID_STR_LEN);
- ast_string_field_set(self, uniqueid, uuid_hold);
+ {
+ /*
+ * We need to ensure that another bridge with the same uniqueid
+ * doesn't get created before the previous bridge's destructor
+ * has run and deleted the existing topic.
+ */
+ SCOPED_MUTEX(lock, &bridge_init_lock);
+ if (!ast_strlen_zero(id)) {
+ if (ast_bridge_topic_exists(id)) {
+ ast_log(LOG_WARNING, "Bridge " BRIDGE_PRINTF_SPEC ": already registered\n",
+ BRIDGE_PRINTF_VARS(self));
+ ast_bridge_destroy(self, 0);
+ return NULL;
+ }
+ ast_string_field_set(self, uniqueid, id);
+ } else {
+ ast_uuid_generate_str(uuid_hold, AST_UUID_STR_LEN);
+ ast_string_field_set(self, uniqueid, uuid_hold);
+ }
+ if (!(flags & AST_BRIDGE_FLAG_INVISIBLE)) {
+ if (bridge_topics_init(self) != 0) {
+ ast_log(LOG_WARNING, "Bridge " BRIDGE_PRINTF_SPEC ": Could not initialize topics\n",
+ BRIDGE_PRINTF_VARS(self));
+ ao2_ref(self, -1);
+ return NULL;
+ }
+ }
}
+
ast_string_field_set(self, creator, creator);
if (!ast_strlen_zero(creator)) {
ast_string_field_set(self, name, name);
}
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": base_init\n",
+ BRIDGE_PRINTF_VARS(self));
ast_set_flag(&self->feature_flags, flags);
self->allowed_capabilities = capabilities;
- if (!(flags & AST_BRIDGE_FLAG_INVISIBLE)) {
- if (bridge_topics_init(self) != 0) {
- ast_log(LOG_WARNING, "Bridge %s: Could not initialize topics\n",
- self->uniqueid);
- ao2_ref(self, -1);
- return NULL;
- }
- }
-
/* Use our helper function to find the "best" bridge technology. */
self->technology = find_best_technology(capabilities, self);
if (!self->technology) {
}
self->creationtime = ast_tvnow();
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": base_init complete\n",
+ BRIDGE_PRINTF_VARS(self));
return self;
}
*/
static void bridge_base_destroy(struct ast_bridge *self)
{
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": destroying bridge (noop)\n",
+ BRIDGE_PRINTF_VARS(self));
}
/*!
*/
static void bridge_base_dissolving(struct ast_bridge *self)
{
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": unlinking bridge. Refcount: %d\n",
+ BRIDGE_PRINTF_VARS(self), ao2_ref(self, 0));
ao2_unlink(bridges, self);
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": unlinked bridge. Refcount: %d\n",
+ BRIDGE_PRINTF_VARS(self), ao2_ref(self, 0));
}
/*!
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
{
- ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid);
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": destroying. current refcount: %d\n",
+ BRIDGE_PRINTF_VARS(bridge), ao2_ref(bridge, 0));
ast_bridge_lock(bridge);
bridge_dissolve(bridge, cause);
ast_bridge_unlock(bridge);
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": unreffing. current refcount: %d\n",
+ BRIDGE_PRINTF_VARS(bridge), ao2_ref(bridge, 0));
+
ao2_ref(bridge, -1);
return 0;
static char *handle_bridge_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
-#define FORMAT_HDR "%-36s %5s %-15s %-15s %s\n"
-#define FORMAT_ROW "%-36s %5u %-15s %-15s %s\n"
+#define FORMAT_HDR "%-36s %-36s %5s %-15s %-15s %s\n"
+#define FORMAT_ROW "%-36s %-36s %5u %-15s %-15s %s\n"
struct ao2_iterator iter;
struct ast_bridge *bridge;
return NULL;
}
- ast_cli(a->fd, FORMAT_HDR, "Bridge-ID", "Chans", "Type", "Technology", "Duration");
+ ast_cli(a->fd, FORMAT_HDR, "Bridge-ID", "Name", "Chans", "Type", "Technology", "Duration");
iter = ao2_iterator_init(bridges, 0);
for (; (bridge = ao2_iterator_next(&iter)); ao2_ref(bridge, -1)) {
if (snapshot) {
ast_format_duration_hh_mm_ss(ast_tvnow().tv_sec - snapshot->creationtime.tv_sec, print_time, sizeof(print_time));
ast_cli(a->fd, FORMAT_ROW,
- snapshot->uniqueid,
+ bridge->uniqueid,
+ S_OR(bridge->name, "<unknown>"),
snapshot->num_channels,
S_OR(snapshot->subclass, "<unknown>"),
S_OR(snapshot->technology, "<unknown>"),
struct ast_ari_bridges_create_args *args,
struct ast_ari_response *response)
{
- RAII_VAR(struct ast_bridge *, bridge, stasis_app_bridge_create(args->type, args->name, args->bridge_id), ao2_cleanup);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
+ if (ast_bridge_topic_exists(args->bridge_id)) {
+ ast_ari_response_error(
+ response, 409, "Conflict",
+ "Bridge with id '%s' already exists", args->bridge_id);
+ return;
+ }
+
+ bridge = stasis_app_bridge_create(args->type, args->name, args->bridge_id);
if (!bridge) {
ast_ari_response_error(
response, 500, "Internal Error",
- "Unable to create bridge");
+ "Unable to create bridge. Possible duplicate bridge id '%s'", args->bridge_id);
return;
}
struct ast_ari_bridges_create_with_id_args *args,
struct ast_ari_response *response)
{
- RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
- if (bridge) {
- /* update */
- if (!ast_strlen_zero(args->name)
- && strcmp(args->name, bridge->name)) {
- ast_ari_response_error(
- response, 500, "Internal Error",
- "Changing bridge name is not implemented");
- return;
- }
- if (!ast_strlen_zero(args->type)) {
- ast_ari_response_error(
- response, 500, "Internal Error",
- "Supplying a bridge type when updating a bridge is not allowed.");
- return;
- }
- ast_ari_response_ok(response,
- ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer()));
+ if (ast_bridge_topic_exists(args->bridge_id)) {
+ ast_ari_response_error(
+ response, 409, "Conflict",
+ "Bridge with id '%s' already exists", args->bridge_id);
return;
}
return CMP_MATCH;
}
+/*! AO2 sort function for bridges container */
+static int bridges_sort (const void *left, const void *right, const int flags)
+{
+ const struct ast_bridge *object_left = left;
+ const struct ast_bridge *object_right = right;
+ const char *right_key = right;
+ int cmp;
+
+ switch (flags & OBJ_SEARCH_MASK) {
+ case OBJ_SEARCH_OBJECT:
+ right_key = object_right->uniqueid;
+ /* Fall through */
+ case OBJ_SEARCH_KEY:
+ cmp = strcmp(object_left->uniqueid, right_key);
+ break;
+ case OBJ_SEARCH_PARTIAL_KEY:
+ cmp = strncmp(object_left->uniqueid, right_key, strlen(right_key));
+ break;
+ default:
+ ast_assert(0);
+ cmp = 0;
+ break;
+ }
+ return cmp;
+}
+
/*!
* Used with app_bridges_moh and app_bridge_control, they provide links
* between bridges and channels used for ARI application purposes
enum ast_bridge_video_mode_type video_mode = AST_BRIDGE_VIDEO_MODE_TALKER_SRC;
int send_sdp_label = 0;
+ ast_debug(1, "Creating bridge of type '%s' with name '%s' and id '%s'\n",
+ type, S_OR(name, "<unknown>"), S_OR(id, "<unknown>"));
if (invisible) {
flags |= AST_BRIDGE_FLAG_INVISIBLE;
}
+ if (!ast_strlen_zero(id)) {
+ bridge = stasis_app_bridge_find_by_id(id);
+ if (bridge) {
+ ast_log(LOG_WARNING, "Bridge with id '%s' already exists\n", id);
+ ao2_ref(bridge, -1);
+ return NULL;
+ }
+ }
+
while ((requested_type = strsep(&requested_types, ","))) {
requested_type = ast_strip(requested_type);
if (!bridge) {
return;
}
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": destroying bridge\n",
+ BRIDGE_PRINTF_VARS(bridge));
+
ao2_unlink(app_bridges, bridge);
+ ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": unlinked from app_bridges. current refcount: %d\n",
+ BRIDGE_PRINTF_VARS(bridge), ao2_ref(bridge, 0));
ast_bridge_destroy(bridge, 0);
}
APPS_NUM_BUCKETS, app_hash, NULL, app_compare);
app_controls = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
CONTROLS_NUM_BUCKETS, control_hash, NULL, control_compare);
- app_bridges = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
- BRIDGES_NUM_BUCKETS, bridges_hash, NULL, bridges_compare);
+ app_bridges = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX,
+ AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
+ BRIDGES_NUM_BUCKETS, bridges_hash, bridges_sort, bridges_compare);
app_bridges_moh = ao2_container_alloc_hash(
AO2_ALLOC_OPT_LOCK_MUTEX, 0,
37, bridges_channel_hash_fn, NULL, bridges_channel_compare);