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."