diff options
author | Kinsey Moore <kmoore@digium.com> | 2012-07-20 15:48:55 +0000 |
---|---|---|
committer | Kinsey Moore <kmoore@digium.com> | 2012-07-20 15:48:55 +0000 |
commit | cb9756daa2042d6e5b91290def004b0f0c9eb168 (patch) | |
tree | 9ea585c25213ade435c83c95a055082f1e291f2a | |
parent | 499a445af213b1cbce99db9219f041ef053fbc18 (diff) |
Add hangupcause translation support
The HANGUPCAUSE hash (trunk only) meant to replace SIP_CAUSE has now
been replaced with the HANGUPCAUSE and HANGUPCAUSE_KEYS dialplan
functions to better facilitate access to the AST_CAUSE translations
for technology-specific cause codes. The HangupCauseClear application
has also been added to remove this data from the channel.
(closes issue SWP-4738)
Review: https://reviewboard.asterisk.org/r/2025/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370316 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r-- | UPGRADE.txt | 12 | ||||
-rw-r--r-- | channels/chan_dahdi.c | 3 | ||||
-rw-r--r-- | channels/chan_iax2.c | 3 | ||||
-rw-r--r-- | channels/chan_sip.c | 1 | ||||
-rw-r--r-- | channels/sig_analog.c | 2 | ||||
-rw-r--r-- | channels/sig_pri.c | 9 | ||||
-rw-r--r-- | channels/sig_ss7.c | 9 | ||||
-rw-r--r-- | funcs/func_hangupcause.c | 258 | ||||
-rw-r--r-- | include/asterisk/channel.h | 64 | ||||
-rw-r--r-- | include/asterisk/frame.h | 3 | ||||
-rw-r--r-- | main/channel.c | 12 | ||||
-rw-r--r-- | main/channel_internal_api.c | 77 | ||||
-rw-r--r-- | main/rtp_engine.c | 4 |
13 files changed, 434 insertions, 23 deletions
diff --git a/UPGRADE.txt b/UPGRADE.txt index a707d9a97..3151418ff 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -46,6 +46,10 @@ Dialplan Functions: CONNECTED_LINE_SEND_SUB, CONNECTED_LINE_SEND_SUB_ARGS. - The REDIRECTING function now supports the redirecting original party id and reason. + - The HANGUPCAUSE and HANGUPCAUSE_KEYS functions have been introduced to + provide a replacement for the SIP_CAUSE hash. The HangupCauseClear + application has also been introduced to remove this data from the channel + when necessary. func_enum: @@ -105,10 +109,10 @@ SIP a call is terminated due to RTP stream inactivity or SIP session timer expiration. - SIP_CAUSE is now deprecated. It has been modified to use the same - mechanism as HANGUPCAUSE. Behavior should not change, but performance - should be vastly improved. The HANGUPCAUSE hash should now be used instead - of SIP_CAUSE. Because of this, the storesipcause option in sip.conf is also - deprecated. + mechanism as the HANGUPCAUSE function. Behavior should not change, but + performance should be vastly improved. The HANGUPCAUSE function should now + be used instead of SIP_CAUSE. Because of this, the storesipcause option in + sip.conf is also deprecated. - The sip paramater for Originating Line Information (oli, isup-oli, and ss7-oli) is now parsed out of the From header and copied into the channel's ANI2 information field. This is readable from the CALLERID(ani2) dialplan diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index a394ec89c..28212e5ee 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -4182,6 +4182,7 @@ static void dahdi_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disco snprintf(cause_str, sizeof(cause_str), "R2 DISCONNECT (%s)", openr2_proto_get_disconnect_string(cause)); datalen += strlen(cause_str); cause_code = alloca(datalen); + cause_code->ast_cause = dahdi_r2_cause_to_ast_cause(cause); ast_copy_string(cause_code->chan_name, ast_channel_name(p->owner), AST_CHANNEL_NAME); ast_copy_string(cause_code->code, cause_str, datalen + 1 - sizeof(*cause_code)); ast_queue_control_data(p->owner, AST_CONTROL_PVT_CAUSE_CODE, cause_code, datalen); @@ -7640,7 +7641,7 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch switch (f ? f->frametype : AST_FRAME_CONTROL) { case AST_FRAME_CONTROL: if (f && f->subclass.integer == AST_CONTROL_PVT_CAUSE_CODE) { - ast_channel_hangupcause_hash_set((who == c0) ? c1 : c0, f->data.ptr); + ast_channel_hangupcause_hash_set((who == c0) ? c1 : c0, f->data.ptr, f->datalen); break; } *fo = f; diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 199834ddf..8efad88c8 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -5653,7 +5653,7 @@ static enum ast_bridge_result iax2_bridge(struct ast_channel *c0, struct ast_cha other = (who == c0) ? c1 : c0; /* the 'other' channel */ if ((f->frametype == AST_FRAME_CONTROL)) { if (f->subclass.integer == AST_CONTROL_PVT_CAUSE_CODE) { - ast_channel_hangupcause_hash_set(other, f->data.ptr); + ast_channel_hangupcause_hash_set(other, f->data.ptr, f->datalen); } else if (!(flags & AST_BRIDGE_IGNORE_SIGS) && (f->subclass.integer != AST_CONTROL_SRCUPDATE)) { *fo = f; @@ -10239,6 +10239,7 @@ static int socket_process_helper(struct iax2_thread *thread) cause_code = alloca(data_size); ast_copy_string(cause_code->chan_name, ast_channel_name(iaxs[fr->callno]->owner), AST_CHANNEL_NAME); + cause_code->ast_cause = ies.causecode; snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "IAX2 %s(%d)", subclass, ies.causecode); iax2_queue_control_data(fr->callno, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 9d57ad5bd..9c2d75d3f 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -27004,6 +27004,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct as snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "SIP %s", REQ_OFFSET_TO_STR(req, rlPart2)); + cause_code->ast_cause = hangup_sip2cause(respid); if (global_store_sip_cause) { cause_code->emulate_sip_cause = 1; } diff --git a/channels/sig_analog.c b/channels/sig_analog.c index f40932f27..608f82b27 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -2735,6 +2735,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ subclass = analog_event2str(res); data_size += strlen(subclass); cause_code = alloca(data_size); + cause_code->ast_cause = AST_CAUSE_NORMAL_CLEARING; ast_copy_string(cause_code->chan_name, ast_channel_name(ast), AST_CHANNEL_NAME); snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "ANALOG %s", subclass); break; @@ -2826,6 +2827,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_ case ANALOG_EVENT_ALARM: analog_set_alarm(p, 1); analog_get_and_handle_alarms(p); + cause_code->ast_cause = AST_CAUSE_NETWORK_OUT_OF_ORDER; case ANALOG_EVENT_ONHOOK: ast_queue_control_data(ast, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size); switch (p->sig) { diff --git a/channels/sig_pri.c b/channels/sig_pri.c index f6d7b0867..30d11e755 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -1277,7 +1277,7 @@ static void pri_queue_control(struct sig_pri_span *pri, int chanpos, int subclas * * \return Nothing */ -static void pri_queue_pvt_cause_data(struct sig_pri_span *pri, int chanpos, const char *cause) +static void pri_queue_pvt_cause_data(struct sig_pri_span *pri, int chanpos, const char *cause, int ast_cause) { struct ast_channel *chan; struct ast_control_pvt_cause_code *cause_code; @@ -1287,6 +1287,7 @@ static void pri_queue_pvt_cause_data(struct sig_pri_span *pri, int chanpos, cons if (chan) { int datalen = sizeof(*cause_code) + strlen(cause); cause_code = alloca(datalen); + cause_code->ast_cause = ast_cause; ast_copy_string(cause_code->chan_name, ast_channel_name(chan), AST_CHANNEL_NAME); ast_copy_string(cause_code->code, cause, datalen + 1 - sizeof(*cause_code)); ast_queue_control_data(chan, AST_CONTROL_PVT_CAUSE_CODE, cause_code, datalen); @@ -6638,7 +6639,7 @@ static void *pri_dchannel(void *vpri) if (e->proceeding.cause > -1) { if (pri->pvts[chanpos]->owner) { snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_PROGRESS (%d)", e->proceeding.cause); - pri_queue_pvt_cause_data(pri, chanpos, cause_str); + pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->proceeding.cause); } ast_verb(3, "PROGRESS with cause code %d received\n", e->proceeding.cause); @@ -6944,7 +6945,7 @@ static void *pri_dchannel(void *vpri) int do_hangup = 0; snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP (%d)", e->hangup.cause); - pri_queue_pvt_cause_data(pri, chanpos, cause_str); + pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause); /* Queue a BUSY instead of a hangup if our cause is appropriate */ ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause); @@ -7099,7 +7100,7 @@ static void *pri_dchannel(void *vpri) int do_hangup = 0; snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP_REQ (%d)", e->hangup.cause); - pri_queue_pvt_cause_data(pri, chanpos, cause_str); + pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause); ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause); switch (ast_channel_state(pri->pvts[chanpos]->owner)) { diff --git a/channels/sig_ss7.c b/channels/sig_ss7.c index 6abca5433..810a0d5f2 100644 --- a/channels/sig_ss7.c +++ b/channels/sig_ss7.c @@ -384,12 +384,13 @@ static void sig_ss7_queue_control(struct sig_ss7_linkset *ss7, int chanpos, int * * \return Nothing */ -static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *cause) +static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *cause, int ast_cause) { struct ast_control_pvt_cause_code *cause_code; int datalen = sizeof(*cause_code) + strlen(cause); cause_code = alloca(datalen); + cause_code->ast_cause = ast_cause; ast_copy_string(cause_code->chan_name, ast_channel_name(owner), AST_CHANNEL_NAME); ast_copy_string(cause_code->code, cause, datalen + 1 - sizeof(*cause_code)); ast_queue_control_data(owner, AST_CONTROL_PVT_CAUSE_CODE, cause_code, datalen); @@ -915,7 +916,7 @@ void *ss7_linkset(void *data) sig_ss7_lock_owner(linkset, chanpos); p->ss7call = NULL; if (p->owner) { - ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_RSC"); + ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_RSC", AST_CAUSE_INTERWORKING); ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); ast_channel_unlock(p->owner); } @@ -973,7 +974,7 @@ void *ss7_linkset(void *data) } p->call_level = SIG_SS7_CALL_LEVEL_GLARE; if (p->owner) { - ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_IAM (glare)"); + ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_IAM (glare)", AST_CAUSE_NORMAL_CLEARING); ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING); ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); ast_channel_unlock(p->owner); @@ -1119,7 +1120,7 @@ void *ss7_linkset(void *data) sig_ss7_lock_owner(linkset, chanpos); if (p->owner) { snprintf(cause_str, sizeof(cause_str), "SS7 ISUP_EVENT_REL (%d)", e->rel.cause); - ss7_queue_pvt_cause_data(p->owner, cause_str); + ss7_queue_pvt_cause_data(p->owner, cause_str, e->rel.cause); ast_channel_hangupcause_set(p->owner, e->rel.cause); ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV); diff --git a/funcs/func_hangupcause.c b/funcs/func_hangupcause.c new file mode 100644 index 000000000..c7ba99f45 --- /dev/null +++ b/funcs/func_hangupcause.c @@ -0,0 +1,258 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999-2012, Digium, Inc. + * + * Kinsey Moore <kmoore@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Functions related to retreiving per-channel hangupcause information + * + * \author Kinsey Moore <kmoore@digium.com> + * \ingroup functions + * + * See Also: + * \arg \ref AstCREDITS + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/utils.h" +#include "asterisk/app.h" + +/*** DOCUMENTATION + <function name="HANGUPCAUSE" language="en_US"> + <synopsis> + Gets per-channel hangupcause information from the channel. + </synopsis> + <syntax> + <parameter name="channel" required="true"> + <para>The name of the channel for which to retreive cause information.</para> + </parameter> + <parameter name="type" required="true"> + <para>Parameter describing which type of information is requested. Types are:</para> + <enumlist> + <enum name="tech"><para>Technology-specific cause information</para></enum> + <enum name="ast"><para>Translated Asterisk cause code</para></enum> + </enumlist> + </parameter> + </syntax> + <description> + <para>Gets technology-specific or translated Asterisk cause code information + from the channel for the specified channel that resulted from a dial.</para> + </description> + <see-also> + <ref type="function">HANGUPCAUSE_KEYS</ref> + <ref type="application">HangupCauseClear</ref> + </see-also> + </function> + <function name="HANGUPCAUSE_KEYS" language="en_US"> + <synopsis> + Gets the list of channels for which hangup causes are available. + </synopsis> + <description> + <para>Returns a comma-separated list of channel names to be used with the HANGUPCAUSE function.</para> + </description> + <see-also> + <ref type="function">HANGUPCAUSE</ref> + <ref type="application">HangupCauseClear</ref> + </see-also> + </function> + <application name="HangupCauseClear" language="en_US"> + <synopsis> + Clears hangup cause information from the channel that is available through HANGUPCAUSE. + </synopsis> + <description> + <para>Clears all channel-specific hangup cause information from the channel. + This is never done automatically (i.e. for new Dial()s).</para> + </description> + <see-also> + <ref type="function">HANGUPCAUSE</ref> + <ref type="function">HANGUPCAUSE_KEYS</ref> + </see-also> + </application> + ***/ + +/*! + * \internal + * \brief Read values from the hangupcause ao2 container. + * + * \param chan Asterisk channel to read + * \param cmd Not used + * \param data HANGUPCAUSE function argument string + * \param buf Buffer to fill with read value. + * \param len Length of the buffer + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int hangupcause_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + char *parms; + struct ast_control_pvt_cause_code *cause_code; + int res = 0; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(channel); /*!< Channel name */ + AST_APP_ARG(type); /*!< Type of information requested (ast or tech) */ + ); + + /* Ensure that the buffer is empty */ + *buf = 0; + + if (!chan) { + return -1; + } + + parms = ast_strdupa(data); + AST_STANDARD_APP_ARGS(args, parms); + if (args.argc != 2) { + /* Must have two arguments. */ + ast_log(LOG_WARNING, "The HANGUPCAUSE function must have 2 parameters, not %d\n", args.argc); + return -1; + } + + ast_channel_lock(chan); + cause_code = ast_channel_dialed_causes_find(chan, args.channel); + ast_channel_unlock(chan); + + if (!cause_code) { + ast_log(LOG_WARNING, "Unable to find information for channel %s\n", args.channel); + return -1; + } + + if (!strcmp(args.type, "ast")) { + ast_copy_string(buf, ast_cause2str(cause_code->ast_cause), len); + } else if (!strcmp(args.type, "tech")) { + ast_copy_string(buf, cause_code->code, len); + } else { + ast_log(LOG_WARNING, "Information type not recognized (%s)\n", args.type); + res = -1; + } + + ao2_ref(cause_code, -1); + + return res; +} + +/*! + * \internal + * \brief Read keys from the hangupcause ao2 container. + * + * \param chan Asterisk channel to read + * \param cmd Not used + * \param data HANGUPCAUSE_KEYS function argument string + * \param buf Buffer to fill with read value. + * \param len Length of the buffer + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int hangupcause_keys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + struct ast_str *chanlist; + + /* Ensure that the buffer is empty */ + *buf = 0; + + if (!chan) { + return -1; + } + + ast_channel_lock(chan); + chanlist = ast_channel_dialed_causes_channels(chan); + ast_channel_unlock(chan); + + if (chanlist && ast_str_strlen(chanlist)) { + ast_copy_string(buf, ast_str_buffer(chanlist), len); + } + + ast_free(chanlist); + return 0; +} + +/*! + * \internal + * \brief Remove all keys from the hangupcause ao2 container. + * + * \param chan Asterisk channel to read + * \param data Not used + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int hangupcause_clear_exec(struct ast_channel *chan, const char *data) { + ast_channel_lock(chan); + ast_channel_dialed_causes_clear(chan); + ast_channel_unlock(chan); + return 0; +} + +static struct ast_custom_function hangupcause_function = { + .name = "HANGUPCAUSE", + .read = hangupcause_read, +}; + +static struct ast_custom_function hangupcause_keys_function = { + .name = "HANGUPCAUSE_KEYS", + .read = hangupcause_keys_read, +}; + +static const char app[] = "HangupcauseClear"; + +/*! + * \internal + * \brief Unload the function module + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int unload_module(void) +{ + int res; + + res = ast_custom_function_unregister(&hangupcause_function); + res |= ast_custom_function_unregister(&hangupcause_keys_function); + res |= ast_unregister_application(app); + return res; +} + +/*! + * \internal + * \brief Load and initialize the function module. + * + * \retval AST_MODULE_LOAD_SUCCESS on success. + * \retval AST_MODULE_LOAD_DECLINE on error. + */ +static int load_module(void) +{ + int res; + + res = ast_custom_function_register(&hangupcause_function); + res |= ast_custom_function_register(&hangupcause_keys_function); + res |= ast_register_application_xml(app, hangupcause_clear_exec); + return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; +} + +/* Do not wrap the following line. */ +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "HANGUPCAUSE related functions and applications"); diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 85a527442..13d3f1f29 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -3592,8 +3592,9 @@ void ast_channel_unlink(struct ast_channel *chan); * * \param chan channel on which to set the cause information * \param cause_code ast_control_pvt_cause_code structure containing cause information + * \param datalen total length of the structure since it may vary */ -void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code); +void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code, int datalen); /* ACCESSOR FUNTIONS */ /*! \brief Set the channel name */ @@ -3825,5 +3826,66 @@ void ast_channel_internal_bridge_set(struct ast_channel *chan, struct ast_bridge struct ast_channel *ast_channel_internal_bridged_channel(const struct ast_channel *chan); void ast_channel_internal_bridged_channel_set(struct ast_channel *chan, struct ast_channel *value); +/*! + * \since 11 + * \brief Retreive a comma-separated list of channels for which dialed cause information is available + * + * \details + * This function makes use of datastore operations on the channel, so + * it is important to lock the channel before calling this function. + * + * \param chan The channel from which to retreive information + * \retval NULL on allocation failure + * \retval Pointer to an ast_str object containing the desired information which must be freed + */ +struct ast_str *ast_channel_dialed_causes_channels(const struct ast_channel *chan); + +/*! + * \since 11 + * \brief Retreive a ref-counted cause code information structure + * + * \details + * This function makes use of datastore operations on the channel, so + * it is important to lock the channel before calling this function. + * This function increases the ref count of the returned object, so the + * calling function must decrease the reference count when it is finished + * with the object. + * + * \param chan The channel from which to retreive information + * \param chan_name The name of the channel about which to retreive information + * \retval NULL on search failure + * \retval Pointer to a ref-counted ast_control_pvt_cause_code object containing the desired information + */ +struct ast_control_pvt_cause_code *ast_channel_dialed_causes_find(const struct ast_channel *chan, const char *chan_name); + +/*! + * \since 11 + * \brief Add cause code information to the channel + * + * \details + * This function makes use of datastore operations on the channel, so + * it is important to lock the channel before calling this function. + * The passed in data is copied and so is still owned by the caller. + * + * \param chan The channel on which to add information + * \param cause_code The cause information to be added to the channel + * \param datalen The total length of the structure since its length is variable + * \retval 0 on success + * \retval -1 on error + */ +int ast_channel_dialed_causes_add(const struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code, int datalen); + +/*! + * \since 11 + * \brief Clear all cause information from the channel + * + * \details + * This function makes use of datastore operations on the channel, so + * it is important to lock the channel before calling this function. + * + * \param chan The channel from which to clear information + */ +void ast_channel_dialed_causes_clear(const struct ast_channel *chan); + struct ast_flags *ast_channel_flags(struct ast_channel *chan); #endif /* _ASTERISK_CHANNEL_H */ diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 66ba2eff0..01aa27b61 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -327,7 +327,8 @@ enum ast_control_transfer { struct ast_control_pvt_cause_code { char chan_name[AST_CHANNEL_NAME]; /*!< Name of the channel that originated the cause information */ - unsigned int emulate_sip_cause:1; /*!< Indicates whether this should be used to emulate SIP_CAUSE support */ + unsigned int emulate_sip_cause:1; /*!< Indicates whether this should be used to emulate SIP_CAUSE support */ + int ast_cause; /*!< Asterisk cause code associated with this message */ char code[1]; /*!< Tech-specific cause code information, beginning with the name of the tech */ }; diff --git a/main/channel.c b/main/channel.c index 6b4f8b721..f2660e1fd 100644 --- a/main/channel.c +++ b/main/channel.c @@ -4298,12 +4298,14 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con return 0; } -void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code) +void ast_channel_hangupcause_hash_set(struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code, int datalen) { char causevar[256]; - snprintf(causevar, sizeof(causevar), "HASH(HANGUPCAUSE,%s)", cause_code->chan_name); - ast_func_write(chan, causevar, cause_code->code); + if (ast_channel_dialed_causes_add(chan, cause_code, datalen)) { + ast_log(LOG_WARNING, "Unable to store hangup cause for %s on %s\n", cause_code->chan_name, ast_channel_name(chan)); + } + if (cause_code->emulate_sip_cause) { snprintf(causevar, sizeof(causevar), "HASH(SIP_CAUSE,%s)", cause_code->chan_name); ast_func_write(chan, causevar, cause_code->code); @@ -4455,7 +4457,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, ts = ast_get_indication_tone(ast_channel_zone(chan), "congestion"); break; case AST_CONTROL_PVT_CAUSE_CODE: - ast_channel_hangupcause_hash_set(chan, data); + ast_channel_hangupcause_hash_set(chan, data, datalen); res = 0; break; case AST_CONTROL_PROGRESS: @@ -5602,7 +5604,7 @@ struct ast_channel *__ast_request_and_dial(const char *type, struct ast_format_c break; case AST_CONTROL_PVT_CAUSE_CODE: - ast_channel_hangupcause_hash_set(chan, f->data.ptr); + ast_channel_hangupcause_hash_set(chan, f->data.ptr, f->datalen); break; /* Ignore these */ diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index a9dea31cc..b659fb9b6 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -88,6 +88,7 @@ struct ast_channel { #ifdef HAVE_EPOLL struct ast_epoll_data *epfd_data[AST_MAX_FDS]; #endif + struct ao2_container *dialed_causes; /*!< Contains tech-specific and Asterisk cause data from dialed channels */ AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(name); /*!< ASCII unique channel name */ @@ -1197,6 +1198,72 @@ struct ast_flags *ast_channel_flags(struct ast_channel *chan) return &chan->flags; } +static int collect_names_cb(void *obj, void *arg, int flags) { + struct ast_control_pvt_cause_code *cause_code = obj; + struct ast_str **str = arg; + + ast_str_append(str, 0, "%s%s", (ast_str_strlen(*str) ? "," : ""), cause_code->chan_name); + + return 0; +} + +struct ast_str *ast_channel_dialed_causes_channels(const struct ast_channel *chan) +{ + struct ast_str *chanlist = ast_str_create(128); + + if (!chanlist) { + return NULL; + } + + ao2_callback(chan->dialed_causes, 0, collect_names_cb, &chanlist); + + return chanlist; +} + +struct ast_control_pvt_cause_code *ast_channel_dialed_causes_find(const struct ast_channel *chan, const char *chan_name) +{ + return ao2_find(chan->dialed_causes, chan_name, OBJ_KEY); +} + +int ast_channel_dialed_causes_add(const struct ast_channel *chan, const struct ast_control_pvt_cause_code *cause_code, int datalen) +{ + struct ast_control_pvt_cause_code *ao2_cause_code; + ao2_find(chan->dialed_causes, cause_code->chan_name, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA); + ao2_cause_code = ao2_alloc(datalen, NULL); + + if (ao2_cause_code) { + memcpy(ao2_cause_code, cause_code, datalen); + ao2_link(chan->dialed_causes, ao2_cause_code); + ao2_ref(ao2_cause_code, -1); + return 0; + } else { + return -1; + } +} + +void ast_channel_dialed_causes_clear(const struct ast_channel *chan) +{ + ao2_callback(chan->dialed_causes, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); +} + +/* \brief Hash function for pvt cause code frames */ +static int pvt_cause_hash_fn(const void *vpc, const int flags) +{ + const struct ast_control_pvt_cause_code *pc = vpc; + return ast_str_hash(ast_tech_to_upper(ast_strdupa(pc->chan_name))); +} + +/* \brief Comparison function for pvt cause code frames */ +static int pvt_cause_cmp_fn(void *obj, void *vstr, int flags) +{ + struct ast_control_pvt_cause_code *pc = obj; + char *str = ast_tech_to_upper(ast_strdupa(vstr)); + char *pc_str = ast_tech_to_upper(ast_strdupa(pc->chan_name)); + return !strcmp(pc_str, str) ? CMP_MATCH | CMP_STOP : 0; +} + +#define DIALED_CAUSES_BUCKETS 37 + struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), const char *file, int line, const char *function) { struct ast_channel *tmp; @@ -1214,11 +1281,21 @@ struct ast_channel *__ast_channel_internal_alloc(void (*destructor)(void *obj), return ast_channel_unref(tmp); } + if (!(tmp->dialed_causes = ao2_container_alloc(DIALED_CAUSES_BUCKETS, pvt_cause_hash_fn, pvt_cause_cmp_fn))) { + return ast_channel_unref(tmp); + } + return tmp; } void ast_channel_internal_cleanup(struct ast_channel *chan) { + if (chan->dialed_causes) { + ao2_t_ref(chan->dialed_causes, -1, + "done with dialed causes since the channel is going away"); + chan->dialed_causes = NULL; + } + ast_string_field_free_memory(chan); } diff --git a/main/rtp_engine.c b/main/rtp_engine.c index a8851e887..68cdfd306 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -933,7 +933,7 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a } ast_frfree(fr); } else if (fr->subclass.integer == AST_CONTROL_PVT_CAUSE_CODE) { - ast_channel_hangupcause_hash_set(other, fr->data.ptr); + ast_channel_hangupcause_hash_set(other, fr->data.ptr, fr->datalen); ast_frfree(fr); } else { *fo = fr; @@ -1227,7 +1227,7 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, } ast_frfree(fr); } else if (fr->subclass.integer == AST_CONTROL_PVT_CAUSE_CODE) { - ast_channel_hangupcause_hash_set(other, fr->data.ptr); + ast_channel_hangupcause_hash_set(other, fr->data.ptr, fr->datalen); ast_frfree(fr); } else { *fo = fr; |