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
certified/11.2
Kinsey Moore 13 years ago
parent 499a445af2
commit cb9756daa2

@ -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

@ -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;

@ -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);

@ -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;
}

@ -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) {

@ -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)) {

@ -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);

@ -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");

@ -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 */

@ -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 */
};

@ -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 */

@ -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);
}

@ -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;

Loading…
Cancel
Save