diff --git a/cel/cel_manager.c b/cel/cel_manager.c
index e485aab496..12671bc4a3 100644
--- a/cel/cel_manager.c
+++ b/cel/cel_manager.c
@@ -229,6 +229,7 @@ static void manager_log(struct ast_event *event)
struct ast_cel_event_record record = {
.version = AST_CEL_EVENT_RECORD_VERSION,
};
+ RAII_VAR(char *, tenant_id, NULL, ast_free);
if (!enablecel) {
return;
@@ -252,6 +253,10 @@ static void manager_log(struct ast_event *event)
}
}
+ if (!ast_strlen_zero(record.tenant_id)) {
+ ast_asprintf(&tenant_id, "TenantID: %s\r\n", record.tenant_id);
+ }
+
manager_event(EVENT_FLAG_CALL, "CEL",
"EventName: %s\r\n"
"AccountCode: %s\r\n"
@@ -269,6 +274,7 @@ static void manager_log(struct ast_event *event)
"AMAFlags: %s\r\n"
"UniqueID: %s\r\n"
"LinkedID: %s\r\n"
+ "%s"
"Userfield: %s\r\n"
"Peer: %s\r\n"
"PeerAccount: %s\r\n"
@@ -290,6 +296,7 @@ static void manager_log(struct ast_event *event)
ast_channel_amaflags2string(record.amaflag),
record.unique_id,
record.linked_id,
+ !ast_strlen_zero(tenant_id) ? tenant_id : "",
record.user_field,
record.peer,
record.peer_account,
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index e3d982c690..dc3e66636e 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -552,19 +552,23 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
struct ast_sip_channel_pvt *channel;
struct ast_variable *var;
struct ast_stream_topology *topology;
+ struct ast_channel_initializers initializers = {
+ .version = AST_CHANNEL_INITIALIZERS_VERSION,
+ .tenantid = session->endpoint->tenantid,
+ };
SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));
if (!(pvt = ao2_alloc_options(sizeof(*pvt), chan_pjsip_pvt_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't create pvt\n");
}
- chan = ast_channel_alloc_with_endpoint(1, state,
+ chan = ast_channel_alloc_with_initializers(1, state,
S_COR(session->id.number.valid, session->id.number.str, ""),
S_COR(session->id.name.valid, session->id.name.str, ""),
session->endpoint->accountcode,
exten, session->endpoint->context,
assignedids, requestor, 0,
- session->endpoint->persistent, "PJSIP/%s-%08x",
+ session->endpoint->persistent, &initializers, "PJSIP/%s-%08x",
ast_sorcery_object_get_id(session->endpoint),
(unsigned) ast_atomic_fetchadd_int((int *) &chan_idx, +1));
if (!chan) {
@@ -664,7 +668,7 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
for (var = session->endpoint->channel_vars; var; var = var->next) {
char buf[512];
pbx_builtin_setvar_helper(chan, var->name, ast_get_encoded_str(
- var->value, buf, sizeof(buf)));
+ var->value, buf, sizeof(buf)));
}
ast_channel_stage_snapshot_done(chan);
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 6b8936b2d4..61c8846b8f 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -998,6 +998,13 @@
; AOC updates can be sent using the AOCMessage AMI action or come
; from PRI channels.
; (default: no)
+;
+; tenantid =
+ ; Sets the tenant ID for this endpoint. It can be read in dialplan
+ ; with the CHANNEL function, and it can be changed later via dialplan
+ ; using the same CHANNEL function if needed. Setting tenant ID here
+ ; will cause it to show up on channel creation and the initial
+ ; channel snapshot.
;==========================AUTH SECTION OPTIONS=========================
diff --git a/contrib/ast-db-manage/config/versions/655054a68ad5_add_pjsip_tenantid.py b/contrib/ast-db-manage/config/versions/655054a68ad5_add_pjsip_tenantid.py
new file mode 100644
index 0000000000..f64a8d8089
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/655054a68ad5_add_pjsip_tenantid.py
@@ -0,0 +1,22 @@
+"""add pjsip tenantid
+
+Revision ID: 655054a68ad5
+Revises: bd9c5159c7ea
+Create Date: 2024-06-11 11:18:41.466929
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '655054a68ad5'
+down_revision = '2b7c507d7d12'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ op.add_column('ps_endpoints', sa.Column('tenantid', sa.String(80)))
+
+
+def downgrade():
+ op.drop_column('ps_endpoints', 'tenantid')
diff --git a/funcs/func_channel.c b/funcs/func_channel.c
index 2ee5165751..19c7243080 100644
--- a/funcs/func_channel.c
+++ b/funcs/func_channel.c
@@ -273,6 +273,9 @@
R/O returns the linkedid if available, otherwise returns the uniqueid.
+
+ R/W The channel tenantid.
+
R/W The maximum number of forwards allowed.
@@ -565,6 +568,8 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
}
} else if (!strcasecmp(data, "device_name")) {
ret = ast_channel_get_device_name(chan, buf, len);
+ } else if (!strcasecmp(data, "tenantid")) {
+ locked_copy_string(chan, buf, ast_channel_tenantid(chan), len);
} else if (!ast_channel_tech(chan) || !ast_channel_tech(chan)->func_channel_read || ast_channel_tech(chan)->func_channel_read(chan, function, data, buf, len)) {
ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n", data);
ret = -1;
@@ -737,6 +742,8 @@ static int func_channel_write_real(struct ast_channel *chan, const char *functio
ret = ast_max_forwards_set(chan, max_forwards);
ast_channel_unlock(chan);
}
+ } else if (!strcasecmp(data, "tenantid")) {
+ ast_channel_tenantid_set(chan, value);
} else if (!ast_channel_tech(chan)->func_channel_write
|| ast_channel_tech(chan)->func_channel_write(chan, function, data, value)) {
ast_log(LOG_WARNING, "Unknown or unavailable item requested: '%s'\n",
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index 06307c91f2..788a879720 100644
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -317,6 +317,10 @@ struct ast_cdr {
char uniqueid[AST_MAX_UNIQUEID];
/*! Linked group Identifier */
char linkedid[AST_MAX_UNIQUEID];
+ /*! Channel tenant Identifier */
+ char tenantid[AST_MAX_TENANT_ID];
+ /*! Channel tenant Identifier of the last person we talked to */
+ char peertenantid[AST_MAX_TENANT_ID];
/*! User field */
char userfield[AST_MAX_USER_FIELD];
/*! Sequence field */
diff --git a/include/asterisk/cel.h b/include/asterisk/cel.h
index 81f375be77..7444938ce6 100644
--- a/include/asterisk/cel.h
+++ b/include/asterisk/cel.h
@@ -140,7 +140,7 @@ struct ast_cel_event_record {
* \brief struct ABI version
* \note This \b must be incremented when the struct changes.
*/
- #define AST_CEL_EVENT_RECORD_VERSION 2
+ #define AST_CEL_EVENT_RECORD_VERSION 3
/*!
* \brief struct ABI version
* \note This \b must stay as the first member.
@@ -164,6 +164,7 @@ struct ast_cel_event_record {
const char *peer_account;
const char *unique_id;
const char *linked_id;
+ const char *tenant_id;
uint amaflag;
const char *user_field;
const char *peer;
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 0ee66a6f2f..4747ee2c43 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -146,6 +146,8 @@ extern "C" {
*/
#define AST_MAX_PUBLIC_UNIQUEID 149
+#define AST_MAX_TENANT_ID 64 /*!< Max length of a channel tenant_id */
+
/*!
* The number of buckets to store channels or channel information
*/
@@ -606,6 +608,24 @@ struct ast_assigned_ids {
const char *uniqueid2;
};
+/*!
+ * \brief Helper struct for initializing additional channel information on channel creation.
+ * \since 18.25.0
+ */
+struct ast_channel_initializers {
+ /*!
+ * \brief struct ABI version
+ * \note This \b must be incremented when the struct changes.
+ */
+ #define AST_CHANNEL_INITIALIZERS_VERSION 1
+ /*!
+ * \brief struct ABI version
+ * \note This \b must stay as the first member.
+ */
+ uint32_t version;
+ const char *tenantid;
+};
+
/*!
* \brief Forward declaration
*/
@@ -1244,6 +1264,27 @@ struct ast_channel * __attribute__((format(printf, 15, 16)))
const char *file, int line, const char *function,
const char *name_fmt, ...);
+/*!
+ * \brief Create a channel structure
+ * \since 18.25.0
+ *
+ * \retval NULL failure
+ * \retval non-NULL successfully allocated channel
+ *
+ * \note Absolutely _NO_ channel locks should be held before calling this function.
+ * \note By default, new channels are set to the "s" extension
+ * and "default" context.
+ * \note Same as __ast_channel_alloc but with ast_channel_initializers struct.
+ */
+struct ast_channel * __attribute__((format(printf, 16, 17)))
+ __ast_channel_alloc_with_initializers(int needqueue, int state, const char *cid_num,
+ const char *cid_name, const char *acctcode,
+ const char *exten, const char *context, const struct ast_assigned_ids *assignedids,
+ const struct ast_channel *requestor, enum ama_flags amaflag,
+ struct ast_endpoint *endpoint, struct ast_channel_initializers *initializers,
+ const char *file, int line, const char *function,
+ const char *name_fmt, ...);
+
/*!
* \brief Create a channel structure
*
@@ -1263,6 +1304,11 @@ struct ast_channel * __attribute__((format(printf, 15, 16)))
__ast_channel_alloc((needqueue), (state), (cid_num), (cid_name), (acctcode), (exten), (context), (assignedids), (requestor), (amaflag), (endpoint), \
__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
+#define ast_channel_alloc_with_initializers(needqueue, state, cid_num, cid_name, acctcode, exten, context, assignedids, requestor, amaflag, endpoint, initializers, ...) \
+ __ast_channel_alloc_with_initializers((needqueue), (state), (cid_num), (cid_name), (acctcode), (exten), (context), (assignedids), (requestor), (amaflag), (endpoint), \
+ (initializers), __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
+
+
/*!
* \brief Create a fake channel structure
*
@@ -4151,6 +4197,8 @@ const char *ast_channel_userfield(const struct ast_channel *chan);
const char *ast_channel_call_forward(const struct ast_channel *chan);
const char *ast_channel_uniqueid(const struct ast_channel *chan);
const char *ast_channel_linkedid(const struct ast_channel *chan);
+const char *ast_channel_tenantid(const struct ast_channel *chan);
+void ast_channel_tenantid_set(struct ast_channel *chan, const char *value);
const char *ast_channel_parkinglot(const struct ast_channel *chan);
const char *ast_channel_hangupsource(const struct ast_channel *chan);
const char *ast_channel_dialcontext(const struct ast_channel *chan);
diff --git a/include/asterisk/channel_internal.h b/include/asterisk/channel_internal.h
index 774c9b03c1..1b994fa9b4 100644
--- a/include/asterisk/channel_internal.h
+++ b/include/asterisk/channel_internal.h
@@ -23,6 +23,8 @@
#define ast_channel_internal_alloc(destructor, assignedid, requestor) __ast_channel_internal_alloc(destructor, assignedid, requestor, __FILE__, __LINE__, __PRETTY_FUNCTION__)
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 *__ast_channel_internal_alloc_with_initializers(void (*destructor)(void *obj), const struct ast_assigned_ids *assignedids,
+ const struct ast_channel *requestor, const struct ast_channel_initializers *initializers, const char *file, int line, const char *function);
void ast_channel_internal_finalize(struct ast_channel *chan);
int ast_channel_internal_is_finalized(struct ast_channel *chan);
void ast_channel_internal_cleanup(struct ast_channel *chan);
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index b9b87e25ff..d4e0033f6a 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -311,8 +311,15 @@ enum ast_event_ie_type {
* Payload type: UINT
*/
AST_EVENT_IE_NODE_ID = 0x003e,
+
+ /*!
+ * \brief Channel Event TenantID
+ * Used by: AST_EVENT_CEL
+ * Payload type: STR
+ */
+ AST_EVENT_IE_CEL_TENANTID = 0x003f,
/*! \brief Must be the last IE value +1 */
- AST_EVENT_IE_TOTAL = 0x003f,
+ AST_EVENT_IE_TOTAL = 0x0040,
};
/*!
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index d0e1e59223..dc66a40184 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -987,6 +987,8 @@ struct ast_sip_endpoint {
AST_STRING_FIELD(incoming_mwi_mailbox);
/*! STIR/SHAKEN profile to use */
AST_STRING_FIELD(stir_shaken_profile);
+ /*! Tenant ID for the endpoint */
+ AST_STRING_FIELD(tenantid);
);
/*! Configuration for extensions */
struct ast_sip_endpoint_extensions extensions;
diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h
index 73771864a6..5b58be5204 100644
--- a/include/asterisk/stasis_channels.h
+++ b/include/asterisk/stasis_channels.h
@@ -109,6 +109,7 @@ struct ast_channel_snapshot_base {
AST_STRING_FIELD(userfield); /*!< Userfield for CEL billing */
AST_STRING_FIELD(language); /*!< The default spoken language for the channel */
AST_STRING_FIELD(type); /*!< Type of channel technology */
+ AST_STRING_FIELD(tenantid); /*!< Channel tenant identifier */
);
struct timeval creationtime; /*!< The time of channel creation */
int tech_properties; /*!< Properties of the channel's technology */
diff --git a/main/cdr.c b/main/cdr.c
index 0bf319b8b7..ee261241b0 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -765,7 +765,8 @@ struct cdr_object {
struct ast_flags flags; /*!< Flags on the CDR */
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(linkedid); /*!< Linked ID. Cached here as it may change out from party A, which must be immutable */
- AST_STRING_FIELD(uniqueid); /*!< Unique id of party A. Cached here as it is the master CDR container key */
+ AST_STRING_FIELD(uniqueid); /*!< Unique id of party A. Cached here as it is the master CDR container key */
+ AST_STRING_FIELD(tenantid); /*!< Tenant ID. Cached here because the value can be manipulated through dialplan */
AST_STRING_FIELD(name); /*!< Channel name of party A. Cached here as the party A address may change */
AST_STRING_FIELD(bridge); /*!< The bridge the party A happens to be in. */
AST_STRING_FIELD(appl); /*!< The last accepted application party A was in */
@@ -1094,6 +1095,7 @@ static struct cdr_object *cdr_object_alloc(struct ast_channel_snapshot *chan, co
ast_string_field_set(cdr, uniqueid, chan->base->uniqueid);
ast_string_field_set(cdr, name, chan->base->name);
ast_string_field_set(cdr, linkedid, chan->peer->linkedid);
+ ast_string_field_set(cdr, tenantid, chan->base->tenantid);
cdr->disposition = AST_CDR_NULL;
cdr->sequence = ast_atomic_fetchadd_int(&global_cdr_sequence, +1);
cdr->lastevent = *event_time;
@@ -1362,6 +1364,7 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
ast_copy_string(cdr_copy->lastdata, it_cdr->data, sizeof(cdr_copy->lastdata));
ast_copy_string(cdr_copy->dst, it_cdr->exten, sizeof(cdr_copy->dst));
ast_copy_string(cdr_copy->dcontext, it_cdr->context, sizeof(cdr_copy->dcontext));
+ ast_copy_string(cdr_copy->tenantid, it_cdr->tenantid, sizeof(cdr_copy->tenantid));
/* Party B */
if (party_b) {
@@ -1370,6 +1373,7 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
if (!ast_strlen_zero(it_cdr->party_b.userfield)) {
snprintf(cdr_copy->userfield, sizeof(cdr_copy->userfield), "%s;%s", it_cdr->party_a.userfield, it_cdr->party_b.userfield);
}
+ ast_copy_string(cdr_copy->peertenantid, party_b->base->tenantid, sizeof(cdr_copy->peertenantid));
}
if (ast_strlen_zero(cdr_copy->userfield) && !ast_strlen_zero(it_cdr->party_a.userfield)) {
ast_copy_string(cdr_copy->userfield, it_cdr->party_a.userfield, sizeof(cdr_copy->userfield));
@@ -3166,6 +3170,10 @@ void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char
ast_copy_string(workspace, cdr->uniqueid, workspacelen);
} else if (!strcasecmp(name, "linkedid")) {
ast_copy_string(workspace, cdr->linkedid, workspacelen);
+ } else if (!strcasecmp(name, "tenantid")) {
+ ast_copy_string(workspace, cdr->tenantid, workspacelen);
+ } else if (!strcasecmp(name, "peertenantid")) {
+ ast_copy_string(workspace, cdr->peertenantid, workspacelen);
} else if (!strcasecmp(name, "userfield")) {
ast_copy_string(workspace, cdr->userfield, workspacelen);
} else if (!strcasecmp(name, "sequence")) {
@@ -3232,6 +3240,7 @@ static const char * const cdr_readonly_vars[] = {
"accountcode",
"uniqueid",
"linkedid",
+ "tenantid",
"userfield",
"sequence",
NULL
@@ -3353,6 +3362,14 @@ static int cdr_object_format_property(struct cdr_object *cdr_obj, const char *na
ast_copy_string(value, party_a->base->uniqueid, length);
} else if (!strcasecmp(name, "linkedid")) {
ast_copy_string(value, cdr_obj->linkedid, length);
+ } else if (!strcasecmp(name, "tenantid")) {
+ ast_copy_string(value, party_a->base->tenantid, length);
+ } else if (!strcasecmp(name, "peertenantid")) {
+ if (party_b) {
+ ast_copy_string(value, party_b->base->tenantid, length);
+ } else {
+ ast_copy_string(value, "", length);
+ }
} else if (!strcasecmp(name, "userfield")) {
ast_copy_string(value, cdr_obj->party_a.userfield, length);
} else if (!strcasecmp(name, "sequence")) {
diff --git a/main/cel.c b/main/cel.c
index d41543c27a..98d31b551a 100644
--- a/main/cel.c
+++ b/main/cel.c
@@ -554,6 +554,7 @@ struct ast_event *ast_cel_create_event_with_time(struct ast_channel_snapshot *sn
AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, snapshot->peer->account,
AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, snapshot->base->uniqueid,
AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->peer->linkedid,
+ AST_EVENT_IE_CEL_TENANTID, AST_EVENT_IE_PLTYPE_STR, snapshot->base->tenantid,
AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->base->userfield,
AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra_txt, ""),
AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, S_OR(peer, ""),
@@ -853,6 +854,7 @@ int ast_cel_fill_record(const struct ast_event *e, struct ast_cel_event_record *
r->peer_account = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEERACCT), "");
r->unique_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_UNIQUEID), "");
r->linked_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_LINKEDID), "");
+ r->tenant_id = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_TENANTID), "");
r->amaflag = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_AMAFLAGS);
r->user_field = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USERFIELD), "");
r->peer = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEER), "");
diff --git a/main/channel.c b/main/channel.c
index 57533c82fb..4eae7fee74 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -733,7 +733,7 @@ static struct ast_channel *__attribute__((format(printf, 15, 0)))
__ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char *cid_name,
const char *acctcode, const char *exten, const char *context, const struct ast_assigned_ids *assignedids,
const struct ast_channel *requestor, enum ama_flags amaflag, struct ast_endpoint *endpoint,
- const char *file, int line,
+ struct ast_channel_initializers *initializers, const char *file, int line,
const char *function, const char *name_fmt, va_list ap)
{
struct ast_channel *tmp;
@@ -752,7 +752,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
return NULL;
}
- tmp = __ast_channel_internal_alloc(ast_channel_destructor, assignedids, requestor,
+ tmp = __ast_channel_internal_alloc_with_initializers(ast_channel_destructor, assignedids, requestor, initializers,
file, line, function);
if (!tmp) {
/* Channel structure allocation failure. */
@@ -964,7 +964,26 @@ struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *ci
va_start(ap, name_fmt);
result = __ast_channel_alloc_ap(needqueue, state, cid_num, cid_name, acctcode, exten, context,
- assignedids, requestor, amaflag, endpoint, file, line, function, name_fmt, ap);
+ assignedids, requestor, amaflag, endpoint, NULL, file, line, function, name_fmt, ap);
+ va_end(ap);
+
+ return result;
+}
+
+struct ast_channel *__ast_channel_alloc_with_initializers(int needqueue, int state, const char *cid_num,
+ const char *cid_name, const char *acctcode,
+ const char *exten, const char *context, const struct ast_assigned_ids *assignedids,
+ const struct ast_channel *requestor, enum ama_flags amaflag,
+ struct ast_endpoint *endpoint, struct ast_channel_initializers *initializers,
+ const char *file, int line, const char *function,
+ const char *name_fmt, ...)
+{
+ va_list ap;
+ struct ast_channel *result;
+
+ va_start(ap, name_fmt);
+ result = __ast_channel_alloc_ap(needqueue, state, cid_num, cid_name, acctcode, exten, context,
+ assignedids, requestor, amaflag, endpoint, initializers, file, line, function, name_fmt, ap);
va_end(ap);
return result;
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index bc0fb4c641..8f3227779a 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -57,6 +57,7 @@ struct ast_channel_id {
time_t creation_time; /*!< Creation time */
int creation_unique; /*!< sub-second unique value */
char unique_id[AST_MAX_UNIQUEID]; /*!< Unique Identifier */
+ char tenant_id[AST_MAX_TENANT_ID]; /*!< Multi-tenant identifier */
};
/*!
@@ -316,6 +317,21 @@ const char *ast_channel_linkedid(const struct ast_channel *chan)
return chan->linkedid.unique_id;
}
+const char *ast_channel_tenantid(const struct ast_channel *chan)
+{
+ /* It's ok for tenantid to be empty, so no need to assert */
+ return chan->linkedid.tenant_id;
+}
+
+void ast_channel_tenantid_set(struct ast_channel *chan, const char *value)
+{
+ if (ast_strlen_zero(value)) {
+ return;
+ }
+ ast_copy_string(chan->linkedid.tenant_id, value, sizeof(chan->linkedid.tenant_id));
+ ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_BASE);
+}
+
const char *ast_channel_appl(const struct ast_channel *chan)
{
return chan->appl;
@@ -1350,7 +1366,8 @@ static int pvt_cause_cmp_fn(void *obj, void *vstr, int flags)
#define DIALED_CAUSES_BUCKETS 37
-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 *__ast_channel_internal_alloc_with_initializers(void (*destructor)(void *obj), const struct ast_assigned_ids *assignedids,
+ const struct ast_channel *requestor, const struct ast_channel_initializers *initializers, const char *file, int line, const char *function)
{
struct ast_channel *tmp;
@@ -1371,6 +1388,20 @@ struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj),
return ast_channel_unref(tmp);
}
+ /* Check initializers validity here for early abort. Unfortunately, we can't do much here because
+ * tenant ID is part of linked ID, which would overwrite it further down. */
+ if (initializers) {
+ if (initializers->version == 0) {
+ ast_log(LOG_ERROR, "Channel initializers must have a non-zero version.\n");
+ return ast_channel_unref(tmp);
+ } else if (initializers->version != AST_CHANNEL_INITIALIZERS_VERSION) {
+ ast_log(LOG_ERROR, "ABI mismatch for ast_channel_initializers. "
+ "Please ensure all modules were compiled for "
+ "this version of Asterisk.\n");
+ return ast_channel_unref(tmp);
+ }
+ }
+
/* set the creation time in the uniqueid */
tmp->uniqueid.creation_time = time(NULL);
tmp->uniqueid.creation_unique = ast_atomic_fetchadd_int(&uniqueint, 1);
@@ -1396,6 +1427,12 @@ struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj),
tmp->linkedid = tmp->uniqueid;
}
+ /* Things like tenant ID need to be set here, otherwise they would be overwritten by
+ * things like inheriting linked ID above. */
+ if (initializers) {
+ ast_copy_string(tmp->linkedid.tenant_id, initializers->tenantid, sizeof(tmp->linkedid.tenant_id));
+ }
+
AST_VECTOR_INIT(&tmp->fds, AST_MAX_FDS);
/* Force all channel snapshot segments to be created on first use, so we don't have to check if
@@ -1406,6 +1443,12 @@ struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj),
return tmp;
}
+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)
+{
+ return __ast_channel_internal_alloc_with_initializers(destructor, assignedids, requestor, NULL, file, line, function);
+}
+
struct ast_channel *ast_channel_internal_oldest_linkedid(struct ast_channel *a, struct ast_channel *b)
{
ast_assert(a->linkedid.creation_time != 0);
diff --git a/main/cli.c b/main/cli.c
index 19355ab6db..db9a86e574 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -1659,6 +1659,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_callid callid;
char callid_buf[32];
int stream_num;
+ RAII_VAR(char *, tenant_id, NULL, ast_free);
switch (cmd) {
case CLI_INIT:
@@ -1717,12 +1718,17 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_callid_strnprint(callid_buf, sizeof(callid_buf), callid);
}
+ if (!ast_strlen_zero(ast_channel_tenantid(chan))) {
+ ast_asprintf(&tenant_id, " TenantID: %s\n", ast_channel_tenantid(chan));
+ }
+
ast_str_append(&output, 0,
" -- General --\n"
" Name: %s\n"
" Type: %s\n"
" UniqueID: %s\n"
" LinkedID: %s\n"
+ "%s"
" Caller ID: %s\n"
" Caller ID Name: %s\n"
"Connected Line ID: %s\n"
@@ -1753,6 +1759,7 @@ static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ast_channel_tech(chan)->type,
ast_channel_uniqueid(chan),
ast_channel_linkedid(chan),
+ !ast_strlen_zero(tenant_id) ? tenant_id : "",
S_COR(ast_channel_caller(chan)->id.number.valid,
ast_channel_caller(chan)->id.number.str, "(N/A)"),
S_COR(ast_channel_caller(chan)->id.name.valid,
diff --git a/main/event.c b/main/event.c
index 2b73cd7385..7c8af91475 100644
--- a/main/event.c
+++ b/main/event.c
@@ -189,6 +189,7 @@ static const struct ie_map {
[AST_EVENT_IE_PRESENCE_STATE] = { AST_EVENT_IE_PLTYPE_UINT, "PresenceState" },
[AST_EVENT_IE_PRESENCE_SUBTYPE] = { AST_EVENT_IE_PLTYPE_STR, "PresenceSubtype" },
[AST_EVENT_IE_PRESENCE_MESSAGE] = { AST_EVENT_IE_PLTYPE_STR, "PresenceMessage" },
+ [AST_EVENT_IE_CEL_TENANTID] = { AST_EVENT_IE_PLTYPE_STR, "TenantID" },
};
const char *ast_event_get_type_name(const struct ast_event *event)
diff --git a/main/manager_channels.c b/main/manager_channels.c
index 344a2487a7..1ffd36d1bc 100644
--- a/main/manager_channels.c
+++ b/main/manager_channels.c
@@ -543,6 +543,10 @@ struct ast_str *ast_manager_build_channel_state_string_prefix(
return NULL;
}
+ if (!ast_strlen_zero(snapshot->base->tenantid)) {
+ ast_str_append(&out, 0, "%sTenantid: %s\r\n", prefix, snapshot->base->tenantid);
+ }
+
if (snapshot->manager_vars) {
struct ast_var_t *var;
char *val;
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index a84f18968e..193f8bf438 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -285,6 +285,7 @@ static struct ast_channel_snapshot_base *channel_snapshot_base_create(struct ast
ast_string_field_set(snapshot, userfield, ast_channel_userfield(chan));
ast_string_field_set(snapshot, uniqueid, ast_channel_uniqueid(chan));
ast_string_field_set(snapshot, language, ast_channel_language(chan));
+ ast_string_field_set(snapshot, tenantid, ast_channel_tenantid(chan));
snapshot->creationtime = ast_channel_creationtime(chan);
snapshot->tech_properties = ast_channel_tech(chan)->properties;
@@ -1329,6 +1330,10 @@ struct ast_json *ast_channel_snapshot_to_json(
ast_json_object_set(json_chan, "channelvars", ast_json_channel_vars(snapshot->ari_vars));
}
+ if (!ast_strlen_zero(snapshot->base->tenantid)) {
+ ast_json_object_set(json_chan, "tenantid", ast_json_string_create(snapshot->base->tenantid));
+ }
+
return json_chan;
}
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index a5ba5cc768..e7773d2683 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -1165,6 +1165,15 @@ int ast_ari_validate_channel(struct ast_json *json)
res = 0;
}
} else
+ if (strcmp("tenantid", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Channel field tenantid failed validation\n");
+ res = 0;
+ }
+ } else
{
ast_log(LOG_ERROR,
"ARI Channel has undocumented field %s\n",
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index f350c8de33..39df8b4866 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -1356,6 +1356,7 @@ ari_validator ast_ari_validate_application_fn(void);
* - name: string (required)
* - protocol_id: string (required)
* - state: string (required)
+ * - tenantid: string
* Dialed
* DialplanCEP
* - app_data: string (required)
diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml
index 74024a45e8..2de4620387 100644
--- a/res/res_pjsip/pjsip_config.xml
+++ b/res/res_pjsip/pjsip_config.xml
@@ -683,6 +683,14 @@
dialplan application such as Ringing.
+
+ The tenant ID for this endpoint.
+
+ Sets the tenant ID for this endpoint. When a channel is created,
+ tenantid will be set to this value. It can be changed via dialplan
+ later if needed.
+
+
Minimum session timers expiration period
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 58a50c484a..c83ee33179 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -2303,6 +2303,7 @@ int ast_res_pjsip_initialize_configuration(void)
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_mechanisms", "", security_mechanism_handler, security_mechanism_to_str, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_negotiation", "no", security_negotiation_handler, security_negotiation_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_aoc", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, send_aoc));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tenantid", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, tenantid));
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index 1b49329fcf..2a1d0a0cb2 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -2191,6 +2191,11 @@
"caller_rdnis": {
"type": "string",
"description": "The Caller ID RDNIS"
+ },
+ "tenantid": {
+ "required": false,
+ "type": "string",
+ "description": "The Tenant ID for the channel"
}
}
}