*/
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 */
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);
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)))
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);
{
return channel_feature_hooks_set_full(chan, features, 1);
}
+
+enum ast_channel_error ast_channel_errno(void)
+{
+ return ast_channel_internal_errno();
+}
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);
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;
+}
}
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);
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:
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:
{
"code": 400,
"reason": "Invalid parameters for originating a channel."
+ },
+ {
+ "code": 409,
+ "reason": "Channel with given unique ID already exists."
}
]
}
{
"code": 400,
"reason": "Invalid parameters for originating a channel."
+ },
+ {
+ "code": 409,
+ "reason": "Channel with given unique ID already exists."
}
]
},