Merge "ARI: Detect duplicate channel IDs" into 13

changes/72/4172/1
Joshua Colp 9 years ago committed by Gerrit Code Review
commit 578e34b445

@ -4669,4 +4669,16 @@ int ast_channel_feature_hooks_append(struct ast_channel *chan, struct ast_bridge
*/
int ast_channel_feature_hooks_replace(struct ast_channel *chan, struct ast_bridge_features *features);
enum ast_channel_error {
/* Unable to determine what error occurred. */
AST_CHANNEL_ERROR_UNKNOWN,
/* Channel with this ID already exists */
AST_CHANNEL_ERROR_ID_EXISTS,
};
/*!
* \brief Get error code for latest channel operation.
*/
enum ast_channel_error ast_channel_errno(void);
#endif /* _ASTERISK_CHANNEL_H */

@ -25,3 +25,5 @@ int ast_channel_internal_is_finalized(struct ast_channel *chan);
void ast_channel_internal_cleanup(struct ast_channel *chan);
int ast_channel_internal_setup_topics(struct ast_channel *chan);
void ast_channel_internal_errno_set(enum ast_channel_error error);
enum ast_channel_error ast_channel_internal_errno(void);

@ -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();
}

@ -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;
}

@ -1109,7 +1109,12 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
}
if (ast_dial_prerun(dial, other, format_cap)) {
ast_ari_response_alloc_failed(response);
if (ast_channel_errno() == AST_CHANNEL_ERROR_ID_EXISTS) {
ast_ari_response_error(response, 409, "Conflict",
"Channel with given unique ID already exists");
} else {
ast_ari_response_alloc_failed(response);
}
ast_dial_destroy(dial);
ast_free(origination);
ast_channel_cleanup(other);

@ -253,6 +253,7 @@ static void ast_ari_channels_originate_cb(
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
case 400: /* Invalid parameters for originating a channel. */
case 409: /* Channel with given unique ID already exists. */
is_valid = 1;
break;
default:
@ -483,6 +484,7 @@ static void ast_ari_channels_originate_with_id_cb(
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
case 400: /* Invalid parameters for originating a channel. */
case 409: /* Channel with given unique ID already exists. */
is_valid = 1;
break;
default:

@ -142,6 +142,10 @@
{
"code": 400,
"reason": "Invalid parameters for originating a channel."
},
{
"code": 409,
"reason": "Channel with given unique ID already exists."
}
]
}
@ -298,6 +302,10 @@
{
"code": 400,
"reason": "Invalid parameters for originating a channel."
},
{
"code": 409,
"reason": "Channel with given unique ID already exists."
}
]
},

Loading…
Cancel
Save