diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/channel.c | 53 | ||||
-rw-r--r-- | main/channel_internal_api.c | 23 |
2 files changed, 71 insertions, 5 deletions
diff --git a/main/channel.c b/main/channel.c index c6cb925b8..278104cc9 100644 --- a/main/channel.c +++ b/main/channel.c @@ -767,6 +767,27 @@ static const struct ast_channel_tech null_tech = { static void ast_channel_destructor(void *obj); static void ast_dummy_channel_destructor(void *obj); +static int ast_channel_by_uniqueid_cb(void *obj, void *arg, void *data, int flags); + +static int does_id_conflict(const char *uniqueid) +{ + struct ast_channel *conflict; + int length = 0; + + if (ast_strlen_zero(uniqueid)) { + return 0; + } + + conflict = ast_channel_callback(ast_channel_by_uniqueid_cb, (char *) uniqueid, &length, OBJ_NOLOCK); + if (conflict) { + ast_log(LOG_ERROR, "Channel Unique ID '%s' already in use by channel %s(%p)\n", + uniqueid, ast_channel_name(conflict), conflict); + ast_channel_unref(conflict); + return 1; + } + + return 0; +} /*! \brief Create a new channel structure */ static struct ast_channel * attribute_malloc __attribute__((format(printf, 15, 0))) @@ -940,16 +961,33 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char ast_channel_tech_set(tmp, &null_tech); } - ast_channel_internal_finalize(tmp); - - ast_atomic_fetchadd_int(&chancount, +1); - /* You might scream "locking inversion" at seeing this but it is actually perfectly fine. * Since the channel was just created nothing can know about it yet or even acquire it. */ ast_channel_lock(tmp); - ao2_link(channels, tmp); + ao2_lock(channels); + + if (assignedids && (does_id_conflict(assignedids->uniqueid) || does_id_conflict(assignedids->uniqueid2))) { + ast_channel_internal_errno_set(AST_CHANNEL_ERROR_ID_EXISTS); + ao2_unlock(channels); + /* This is a bit unorthodox, but we can't just call ast_channel_stage_snapshot_done() + * because that will result in attempting to publish the channel snapshot. That causes + * badness in some places, such as CDRs. So we need to manually clear the flag on the + * channel that says that a snapshot is being cleared. + */ + ast_clear_flag(ast_channel_flags(tmp), AST_FLAG_SNAPSHOT_STAGE); + ast_channel_unlock(tmp); + return ast_channel_unref(tmp); + } + + ast_channel_internal_finalize(tmp); + + ast_atomic_fetchadd_int(&chancount, +1); + + ao2_link_flags(channels, tmp, OBJ_NOLOCK); + + ao2_unlock(channels); if (endpoint) { ast_endpoint_add_channel(endpoint, tmp); @@ -10842,3 +10880,8 @@ int ast_channel_feature_hooks_replace(struct ast_channel *chan, struct ast_bridg { return channel_feature_hooks_set_full(chan, features, 1); } + +enum ast_channel_error ast_channel_errno(void) +{ + return ast_channel_internal_errno(); +} diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index 51d49c29f..90f59d64a 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -1484,6 +1484,7 @@ static int pvt_cause_cmp_fn(void *obj, void *vstr, int flags) struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *file, int line, const char *function) { struct ast_channel *tmp; + #if defined(REF_DEBUG) tmp = __ao2_alloc_debug(sizeof(*tmp), destructor, AO2_ALLOC_OPT_LOCK_MUTEX, "", file, line, function, 1); @@ -1675,3 +1676,25 @@ int ast_channel_internal_setup_topics(struct ast_channel *chan) return 0; } + +AST_THREADSTORAGE(channel_errno); + +void ast_channel_internal_errno_set(enum ast_channel_error error) +{ + enum ast_channel_error *error_code = ast_threadstorage_get(&channel_errno, sizeof(*error_code)); + if (!error_code) { + return; + } + + *error_code = error; +} + +enum ast_channel_error ast_channel_internal_errno(void) +{ + enum ast_channel_error *error_code = ast_threadstorage_get(&channel_errno, sizeof(*error_code)); + if (!error_code) { + return AST_CHANNEL_ERROR_UNKNOWN; + } + + return *error_code; +} |