diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index a639d57630..5b03f88951 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -2577,16 +2577,23 @@ static int chan_pjsip_hangup(struct ast_channel *ast)
{
struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
int cause;
+ int tech_cause;
+ int original_tech_cause;
struct hangup_data *h_data;
SCOPE_ENTER(1, "%s\n", ast_channel_name(ast));
if (!channel || !channel->session) {
- SCOPE_EXIT_RTN_VALUE(-1, "No channel or session\n");
+ SCOPE_EXIT_RTN_VALUE(-1, "%s: No channel or session\n", ast_channel_name(ast));
}
- cause = hangup_cause2sip(ast_channel_hangupcause(channel->session->channel));
- h_data = hangup_data_alloc(cause, ast);
+ cause = ast_channel_hangupcause(channel->session->channel);
+ tech_cause = hangup_cause2sip(cause);
+ original_tech_cause = ast_channel_tech_hangupcause(channel->session->channel);
+ if (!original_tech_cause) {
+ ast_channel_tech_hangupcause_set(channel->session->channel, tech_cause);
+ }
+ h_data = hangup_data_alloc(tech_cause, ast);
if (!h_data) {
goto failure;
}
@@ -2596,7 +2603,8 @@ static int chan_pjsip_hangup(struct ast_channel *ast)
goto failure;
}
- SCOPE_EXIT_RTN_VALUE(0, "Cause: %d\n", cause);
+ SCOPE_EXIT_RTN_VALUE(0, "%s: Cause: %d Tech Cause: %d\n", ast_channel_name(ast),
+ cause, tech_cause);
failure:
/* Go ahead and do our cleanup of the session and channel even if we're not going
@@ -2606,7 +2614,7 @@ failure:
ao2_cleanup(channel);
ao2_cleanup(h_data);
- SCOPE_EXIT_RTN_VALUE(-1, "Cause: %d\n", cause);
+ SCOPE_EXIT_RTN_VALUE(-1, "%s: Cause: %d\n", ast_channel_name(ast), cause);
}
struct request_data {
@@ -2943,7 +2951,7 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session));
if (!session->channel) {
- SCOPE_EXIT_RTN("No channel\n");
+ SCOPE_EXIT_RTN("%s: No channel\n", ast_sip_session_get_name(session));
}
@@ -2959,6 +2967,24 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
chan_pjsip_remove_hold(ast_channel_uniqueid(session->channel));
ast_set_hangupsource(session->channel, ast_channel_name(session->channel), 0);
+
+ ast_trace(-1, "%s: channel cause: %d\n", ast_sip_session_get_name(session),
+ ast_channel_hangupcause(session->channel));
+
+ if (session->inv_session) {
+ /*
+ * tech_hangupcause should only be set if off-nominal.
+ */
+ if (session->inv_session->cause / 100 > 2) {
+ ast_trace(-1, "%s: inv_session cause: %d\n", ast_sip_session_get_name(session),
+ session->inv_session->cause);
+ ast_channel_tech_hangupcause_set(session->channel, session->inv_session->cause);
+ } else {
+ ast_trace(-1, "%s: inv_session cause: %d suppressed\n", ast_sip_session_get_name(session),
+ session->inv_session->cause);
+ }
+ }
+
if (!ast_channel_hangupcause(session->channel) && session->inv_session) {
int cause = ast_sip_hangup_sip2cause(session->inv_session->cause);
@@ -2967,7 +2993,7 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
ast_queue_hangup(session->channel);
}
- SCOPE_EXIT_RTN();
+ SCOPE_EXIT_RTN("%s\n", ast_sip_session_get_name(session));
}
static void set_sipdomain_variable(struct ast_sip_session *session)
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index eceb9aa9f2..276037b25d 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -4197,6 +4197,8 @@ int ast_channel_fdno(const struct ast_channel *chan);
void ast_channel_fdno_set(struct ast_channel *chan, int value);
int ast_channel_hangupcause(const struct ast_channel *chan);
void ast_channel_hangupcause_set(struct ast_channel *chan, int value);
+int ast_channel_tech_hangupcause(const struct ast_channel *chan);
+void ast_channel_tech_hangupcause_set(struct ast_channel *chan, int value);
int ast_channel_priority(const struct ast_channel *chan);
void ast_channel_priority_set(struct ast_channel *chan, int value);
int ast_channel_rings(const struct ast_channel *chan);
diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h
index 86422540ae..863973683e 100644
--- a/include/asterisk/stasis_channels.h
+++ b/include/asterisk/stasis_channels.h
@@ -132,7 +132,9 @@ struct ast_channel_snapshot_peer {
*/
struct ast_channel_snapshot_hangup {
int cause; /*!< Why is the channel hanged up. See causes.h */
- char source[0]; /*!< Who is responsible for hanging up this channel */
+ char *source; /*!< Who is responsible for hanging up this channel */
+ int tech_cause; /*!< Technology-specific hangup cause */
+ char buffer[0]; /*!< \private Buffer to store source in */
};
/*!
diff --git a/main/channel.c b/main/channel.c
index 2de4bf85d5..987ea40a3d 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1180,9 +1180,13 @@ int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *fin)
/*! \brief Queue a hangup frame for channel */
int ast_queue_hangup(struct ast_channel *chan)
{
- RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+ RAII_VAR(struct ast_json *, blob, ast_json_object_create(), ast_json_unref);
struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
- int res, cause;
+ int res, cause, tech_cause;
+
+ if (!blob) {
+ return -1;
+ }
/* Yeah, let's not change a lock-critical value without locking */
ast_channel_lock(chan);
@@ -1190,8 +1194,11 @@ int ast_queue_hangup(struct ast_channel *chan)
cause = ast_channel_hangupcause(chan);
if (cause) {
- blob = ast_json_pack("{s: i}",
- "cause", cause);
+ ast_json_object_set(blob, "cause", ast_json_integer_create(cause));
+ }
+ tech_cause = ast_channel_tech_hangupcause(chan);
+ if (tech_cause) {
+ ast_json_object_set(blob, "tech_cause", ast_json_integer_create(tech_cause));
}
ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), blob);
@@ -1207,6 +1214,7 @@ int ast_queue_hangup_with_cause(struct ast_channel *chan, int cause)
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
int res;
+ int tech_cause = 0;
if (cause >= 0) {
f.data.uint32 = cause;
@@ -1220,6 +1228,16 @@ int ast_queue_hangup_with_cause(struct ast_channel *chan, int cause)
}
blob = ast_json_pack("{s: i}",
"cause", cause);
+ if (!blob) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ tech_cause = ast_channel_tech_hangupcause(chan);
+ if (tech_cause) {
+ ast_json_object_set(blob, "tech_cause", ast_json_integer_create(tech_cause));
+ }
+
ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), blob);
res = ast_queue_frame(chan, &f);
@@ -2444,12 +2462,19 @@ int ast_softhangup(struct ast_channel *chan, int cause)
{
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
int res;
+ int tech_cause = 0;
ast_channel_lock(chan);
res = ast_softhangup_nolock(chan, cause);
blob = ast_json_pack("{s: i, s: b}",
"cause", cause,
"soft", 1);
+
+ tech_cause = ast_channel_tech_hangupcause(chan);
+ if (tech_cause) {
+ ast_json_object_set(blob, "tech_cause", ast_json_integer_create(tech_cause));
+ }
+
ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), blob);
ast_channel_unlock(chan);
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index ce2f7d5d64..9b41811bd9 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -304,6 +304,15 @@ void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
chan->hangupcause = value;
ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_HANGUP);
}
+int ast_channel_tech_hangupcause(const struct ast_channel *chan)
+{
+ return chan->tech_hangupcause;
+}
+void ast_channel_tech_hangupcause_set(struct ast_channel *chan, int value)
+{
+ chan->tech_hangupcause = value;
+ ast_channel_snapshot_invalidate_segment(chan, AST_CHANNEL_SNAPSHOT_INVALIDATE_HANGUP);
+}
int ast_channel_priority(const struct ast_channel *chan)
{
return chan->priority;
diff --git a/main/channel_private.h b/main/channel_private.h
index 5842f43da7..3ff56128fa 100644
--- a/main/channel_private.h
+++ b/main/channel_private.h
@@ -158,7 +158,10 @@ struct ast_channel {
* the counter is only in the remaining bits */
unsigned int fout; /*!< Frames out counters. The high bit is a debug mask, so
* the counter is only in the remaining bits */
- int hangupcause; /*!< Why is the channel hanged up. See causes.h */
+ int hangupcause; /*!< Why is the channel hanged up. See causes.h */
+ int tech_hangupcause; /*!< Technology-specific off-nominal hangup cause.
+ * Leave set to 0 for nominal call termination.
+ */
unsigned int finalized:1; /*!< Whether or not the channel has been successfully allocated */
struct ast_flags flags; /*!< channel flags of AST_FLAG_ type */
int alertpipe[2];
diff --git a/main/manager_channels.c b/main/manager_channels.c
index 35d82f099e..6e56d0fe63 100644
--- a/main/manager_channels.c
+++ b/main/manager_channels.c
@@ -80,6 +80,11 @@
A description of why the channel was hung up.
+
+ A technology-specific off-nominal numeric cause code
+ for why the channel was hung up. Suppressed for nominally
+ terminated calls.
+
[Newchannel]
@@ -641,12 +646,23 @@ static struct ast_manager_event_blob *channel_state_change(
is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
if (!was_hungup && is_hungup) {
- return ast_manager_event_blob_create(
- EVENT_FLAG_CALL, "Hangup",
- "Cause: %d\r\n"
- "Cause-txt: %s\r\n",
- new_snapshot->hangup->cause,
- ast_cause2str(new_snapshot->hangup->cause));
+ if (new_snapshot->hangup->tech_cause) {
+ return ast_manager_event_blob_create(
+ EVENT_FLAG_CALL, "Hangup",
+ "Cause: %d\r\n"
+ "Cause-txt: %s\r\n"
+ "TechCause: %d\r\n",
+ new_snapshot->hangup->cause,
+ ast_cause2str(new_snapshot->hangup->cause),
+ new_snapshot->hangup->tech_cause);
+ } else {
+ return ast_manager_event_blob_create(
+ EVENT_FLAG_CALL, "Hangup",
+ "Cause: %d\r\n"
+ "Cause-txt: %s\r\n",
+ new_snapshot->hangup->cause,
+ ast_cause2str(new_snapshot->hangup->cause));
+ }
}
if (old_snapshot->state != new_snapshot->state) {
@@ -815,6 +831,7 @@ static void channel_hangup_request_cb(void *data,
struct ast_str *extra;
struct ast_str *channel_event_string;
struct ast_json *cause;
+ struct ast_json *tech_cause;
int is_soft;
char *manager_event = "HangupRequest";
@@ -841,6 +858,13 @@ static void channel_hangup_request_cb(void *data,
ast_json_integer_get(cause));
}
+ tech_cause = ast_json_object_get(obj->blob, "tech_cause");
+ if (tech_cause) {
+ ast_str_append(&extra, 0,
+ "TechCause: %jd\r\n",
+ ast_json_integer_get(tech_cause));
+ }
+
is_soft = ast_json_is_true(ast_json_object_get(obj->blob, "soft"));
if (is_soft) {
manager_event = "SoftHangupRequest";
diff --git a/main/stasis_channels.c b/main/stasis_channels.c
index 4e74733935..3913a246f6 100644
--- a/main/stasis_channels.c
+++ b/main/stasis_channels.c
@@ -469,6 +469,8 @@ static struct ast_channel_snapshot_hangup *channel_snapshot_hangup_create(struct
}
snapshot->cause = ast_channel_hangupcause(chan);
+ snapshot->tech_cause = ast_channel_tech_hangupcause(chan);
+ snapshot->source = snapshot->buffer;
strcpy(snapshot->source, hangupsource); /* Safe */
return snapshot;
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index 5ef3821112..ae38f60ccf 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -4094,6 +4094,15 @@ int ast_ari_validate_channel_destroyed(struct ast_json *json)
res = 0;
}
} else
+ if (strcmp("tech_cause", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_int(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI ChannelDestroyed field tech_cause failed validation\n");
+ res = 0;
+ }
+ } else
{
ast_log(LOG_ERROR,
"ARI ChannelDestroyed has undocumented field %s\n",
@@ -4575,6 +4584,15 @@ int ast_ari_validate_channel_hangup_request(struct ast_json *json)
res = 0;
}
} else
+ if (strcmp("tech_cause", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_int(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI ChannelHangupRequest field tech_cause failed validation\n");
+ res = 0;
+ }
+ } else
{
ast_log(LOG_ERROR,
"ARI ChannelHangupRequest has undocumented field %s\n",
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index 58701c2af1..0b0762b523 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -1747,6 +1747,7 @@ ari_validator ast_ari_validate_application_fn(void);
* - cause: int (required)
* - cause_txt: string (required)
* - channel: Channel (required)
+ * - tech_cause: int
* ChannelDialplan
* - asterisk_id: string
* - type: string (required)
@@ -1778,6 +1779,7 @@ ari_validator ast_ari_validate_application_fn(void);
* - cause: int
* - channel: Channel (required)
* - soft: boolean
+ * - tech_cause: int
* ChannelHold
* - asterisk_id: string
* - type: string (required)
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 3709033897..57131844ed 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -402,17 +402,30 @@ static struct ast_json *channel_destroyed_event(
const struct timeval *tv)
{
struct ast_json *json_channel = ast_channel_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
+ struct ast_json *blob;
if (!json_channel) {
return NULL;
}
- return ast_json_pack("{s: s, s: o, s: i, s: s, s: o}",
+ blob = ast_json_pack("{s: s, s: o, s: i, s: s}",
"type", "ChannelDestroyed",
"timestamp", ast_json_timeval(*tv, NULL),
"cause", snapshot->hangup->cause,
- "cause_txt", ast_cause2str(snapshot->hangup->cause),
- "channel", json_channel);
+ "cause_txt", ast_cause2str(snapshot->hangup->cause));
+
+ if (!blob) {
+ return NULL;
+ }
+
+ if (snapshot->hangup->tech_cause) {
+ ast_json_object_set(blob, "tech_cause",
+ ast_json_integer_create(snapshot->hangup->tech_cause));
+ }
+
+ ast_json_object_set(blob, "channel", json_channel);
+
+ return blob;
}
static struct ast_json *channel_state_change_event(
diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json
index fd300981b6..f08a665ef2 100644
--- a/rest-api/api-docs/events.json
+++ b/rest-api/api-docs/events.json
@@ -579,6 +579,10 @@
"description": "Text representation of the cause of the hangup",
"type": "string"
},
+ "tech_cause": {
+ "type": "int",
+ "description": "Integer representation of the technology-specific off-nominal cause of the hangup."
+ },
"channel": {
"required": true,
"type": "Channel"
@@ -724,6 +728,10 @@
"type": "int",
"description": "Integer representation of the cause of the hangup."
},
+ "tech_cause": {
+ "type": "int",
+ "description": "Integer representation of the technology-specific off-nominal cause of the hangup."
+ },
"soft": {
"type": "boolean",
"description": "Whether the hangup request was a soft hangup request."