diff options
35 files changed, 1131 insertions, 274 deletions
@@ -12,6 +12,14 @@ --- Functionality changes from Asterisk 13.6.0 to Asterisk 13.7.0 ------------ ------------------------------------------------------------------------------ +bridge_t38 +------------------ + * A new module for the bridging framework, this bridge technology acts in the + same fashion as bridge_simple, save that it helps to maintain the state of + T.38 for two channels passing a fax through Asterisk. This helps to resolve + several esoteric issues that can occur when channels are removed from a + bridge after completing a T.38 fax. + ConfBridge ------------------ * A new "timeout" user profile option has been added. This configures the number @@ -36,16 +44,33 @@ Dialplan Functions res_pjsip_outbound_registration ------------------------------- -* A new 'fatal_retry_interval' option has been added to outbound registration. - When set (default is zero), and upon receiving a failure response to an - outbound registration, registration is retried at the given interval up to - 'max_retries'. + * A new 'fatal_retry_interval' option has been added to outbound registration. + When set (default is zero), and upon receiving a failure response to an + outbound registration, registration is retried at the given interval up to + 'max_retries'. + + * If res_statsd is loaded and a StatsD server is configured, basic statistics + regarding the state of outbound registrations will now be emitted. This + includes: + - A GAUGE statistic for the overall number of outbound registrations, i.e.: + PJSIP.registrations.count + - A GAUGE statistic for the overall number of outbound registrations in a + particular state, e.g.: + PJSIP.registrations.state.Registered res_pjsip ------------------ * The ability to use "like" has been added to the pjsip list and show CLI commands. For instance: CLI> pjsip list endpoints like abc + * If res_statsd is loaded and a StatsD server is configured, basic statistics + regarding the state of PJSIP contacts will now be emitted. This includes: + - A GAUGE statistic for the overall number of contacts in a particular + state, e.g.: + PJSIP.contacts.states.Reachable + - A TIMER statistic for the RTT time for each qualified contact, e.g.: + PJSIP.contacts.alice@@127.0.0.1:5061.rtt + func_callerid ------------------- * CALLERID(pres) is now documented as a valid alternative to setting both @@ -56,6 +81,18 @@ func_callerid The same applies to CONNECTEDLINE(pres), REDIRECTING(orig-pres), REDIRECTING(to-pres) and REDIRECTING(from-pres). +res_endpoint_stats +------------------- + * A new module that emits StatsD statistics regarding Asterisk endpoints. + This includes a total count of the number of endpoints, the count of the + number of endpoints in the technology agnostic state of the endpoint - + online or offline - as well as the number of channels associated with each + endpoint. These are recorded as three different GAUGE statistics: + - endpoints.count + - endpoints.state.{unknown|offline|online} + - endpoints.{tech}.{resource}.channels + + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 13.5.0 to Asterisk 13.6.0 ------------ ------------------------------------------------------------------------------ diff --git a/bridges/bridge_t38.c b/bridges/bridge_t38.c new file mode 100644 index 000000000..706ed227f --- /dev/null +++ b/bridges/bridge_t38.c @@ -0,0 +1,261 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2015, Digium, Inc. + * + * Matt Jordan <mjordan@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 Bridging module for maintaining T.38 state for faxing channels + * + * \author Matt Jordan <mjordan@digium.com> + * + * \ingroup bridges + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/channel.h" +#include "asterisk/bridge.h" +#include "asterisk/bridge_technology.h" +#include "asterisk/frame.h" + +/*! \brief The current state of the T.38 fax for the channels in our bridge */ +struct t38_bridge_state { + /* \brief First channel in the bridge */ + struct ast_bridge_channel *bc0; + /*! \brief Second channel in the bridge */ + struct ast_bridge_channel *bc1; + /*! \brief T.38 state of \c bc0 */ + enum ast_t38_state c0_state; + /*! \brief T.38 state of \c bc1 */ + enum ast_t38_state c1_state; +}; + +static void t38_bridge_destroy(struct ast_bridge *bridge) +{ + struct t38_bridge_state *state = bridge->tech_pvt; + + ast_free(state); + bridge->tech_pvt = NULL; +} + +static int t38_bridge_create(struct ast_bridge *bridge) +{ + struct t38_bridge_state *state; + + state = ast_calloc(1, sizeof(*state)); + if (!state) { + return -1; + } + + bridge->tech_pvt = state; + + return 0; +} + +static int t38_bridge_start(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels); + struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels); + struct t38_bridge_state *state = bridge->tech_pvt; + + state->bc0 = bc0; + state->bc1 = bc1; + state->c0_state = ast_channel_get_t38_state(state->bc0->chan); + state->c1_state = ast_channel_get_t38_state(state->bc1->chan); + + return 0; +} + +static void send_termination_update(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, enum ast_t38_state chan_state) +{ + /* Inform the other side that T.38 faxing is done */ + struct ast_control_t38_parameters parameters = { .request_response = 0, }; + + if (!bridge_channel) { + return; + } + + ast_debug(5, "Bridge %s T.38: Current state of %s is %d\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan), chan_state); + if (chan_state == T38_STATE_NEGOTIATING) { + parameters.request_response = AST_T38_REFUSED; + } else if (chan_state == T38_STATE_NEGOTIATED) { + parameters.request_response = AST_T38_TERMINATED; + } + + if (parameters.request_response) { + struct ast_frame f = { + .frametype = AST_FRAME_CONTROL, + .subclass.integer = AST_CONTROL_T38_PARAMETERS, + .data.ptr = ¶meters, + .datalen = sizeof(parameters), + }; + + /* When sending a termination update to a channel, the bridge is highly + * likely to be getting torn down. Queueing a frame through the bridging + * framework won't work, as the frame will likely just get tossed as the + * bridge collapses. Hence, we write directly to the channel to ensure that + * they know they aren't in a T.38 fax any longer. + */ + ast_debug(3, "Bridge %s T.38: Informing %s to switch to %d\n", + bridge->uniqueid, ast_channel_name(bridge_channel->chan), parameters.request_response); + ast_write(bridge_channel->chan, &f); + } +} + +static void t38_bridge_stop(struct ast_bridge *bridge) +{ + struct t38_bridge_state *state = bridge->tech_pvt; + + send_termination_update(bridge, state->bc0, state->c0_state); + send_termination_update(bridge, state->bc1, state->c1_state); +} + +static void t38_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +{ + struct t38_bridge_state *state = bridge->tech_pvt; + + if (bridge_channel == state->bc0) { + send_termination_update(bridge, state->bc0, state->c0_state); + state->bc0 = NULL; + state->c0_state = T38_STATE_UNKNOWN; + } else { + send_termination_update(bridge, state->bc1, state->c1_state); + state->bc1 = NULL; + state->c1_state = T38_STATE_UNKNOWN; + } +} + +static int t38_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + struct t38_bridge_state *state = bridge->tech_pvt; + enum ast_t38_state *c_state; + enum ast_t38_state *other_state; + + if (!bridge_channel) { + return -1; + } + + c_state = bridge_channel == state->bc0 ? &(state->c0_state) : &(state->c1_state); + other_state = bridge_channel == state->bc0 ? &(state->c1_state) : &(state->c0_state); + + switch (frame->frametype) { + case AST_FRAME_CONTROL: + switch (frame->subclass.integer) { + case AST_CONTROL_T38_PARAMETERS: + { + struct ast_control_t38_parameters *parameters = frame->data.ptr; + + switch (parameters->request_response) { + case AST_T38_REQUEST_NEGOTIATE: + *c_state = T38_STATE_NEGOTIATING; + *other_state = T38_STATE_NEGOTIATING; + break; + case AST_T38_NEGOTIATED: + *c_state = T38_STATE_NEGOTIATED; + break; + case AST_T38_TERMINATED: + case AST_T38_REQUEST_TERMINATE: + case AST_T38_REFUSED: + *c_state = T38_STATE_REJECTED; + break; + case AST_T38_REQUEST_PARMS: + default: + /* No state change */ + break; + } + ast_debug(3, "Bridge %s T.38 state: %s: %d; %s: %d\n", + bridge->uniqueid, ast_channel_name(state->bc0->chan), state->c0_state, + ast_channel_name(state->bc1->chan), state->c1_state); + break; + } + default: + break; + } + break; + default: + break; + } + + return ast_bridge_queue_everyone_else(bridge, bridge_channel, frame); +} + +static int t38_bridge_compatible(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels); + struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels); + enum ast_t38_state c0_state; + enum ast_t38_state c1_state; + + /* We must have two, and only two, channels in a T.38 bridge */ + if (bridge->num_channels != 2) { + ast_debug(1, "Bridge '%s' can not use T.38 bridge as two channels are required\n", + bridge->uniqueid); + return 0; + } + + /* We can be the bridge tech so long as one side is in the process + * of negotiating T.38 + */ + c0_state = ast_channel_get_t38_state(bc0->chan); + c1_state = ast_channel_get_t38_state(bc1->chan); + if (c0_state != T38_STATE_NEGOTIATING && c0_state != T38_STATE_NEGOTIATED + && c1_state != T38_STATE_NEGOTIATING && c1_state != T38_STATE_NEGOTIATED) { + ast_debug(1, "Bridge '%s' can not use T.38 bridge: channel %s has T.38 state %d; channel %s has T.38 state %d\n", + bridge->uniqueid, ast_channel_name(bc0->chan), c0_state, ast_channel_name(bc1->chan), c1_state); + return 0; + } + + return 1; +} + +static struct ast_bridge_technology t38_bridge = { + .name = "t38_bridge", + .capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX, + .preference = AST_BRIDGE_PREFERENCE_BASE_1TO1MIX + 1, + .create = t38_bridge_create, + .destroy = t38_bridge_destroy, + .start = t38_bridge_start, + .stop = t38_bridge_stop, + .leave = t38_bridge_leave, + .write = t38_bridge_write, + .compatible = t38_bridge_compatible, +}; + +static int unload_module(void) +{ + ast_bridge_technology_unregister(&t38_bridge); + + return 0; +} + +static int load_module(void) +{ + if (ast_bridge_technology_register(&t38_bridge)) { + return AST_MODULE_LOAD_DECLINE; + } + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Two channel bridging module that maintains T.38 state"); diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 42a538fd4..8d5018d2c 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -2249,6 +2249,14 @@ static struct chan_iax2_pvt *new_iax(struct ast_sockaddr *addr, const char *host return NULL; } + tmp->pingid = -1; + tmp->lagid = -1; + tmp->autoid = -1; + tmp->authid = -1; + tmp->initid = -1; + tmp->keyrotateid = -1; + tmp->jbid = -1; + if (ast_string_field_init(tmp, 32)) { ao2_ref(tmp, -1); tmp = NULL; @@ -2256,18 +2264,11 @@ static struct chan_iax2_pvt *new_iax(struct ast_sockaddr *addr, const char *host } tmp->prefs = prefs_global; - tmp->pingid = -1; - tmp->lagid = -1; - tmp->autoid = -1; - tmp->authid = -1; - tmp->initid = -1; - tmp->keyrotateid = -1; ast_string_field_set(tmp,exten, "s"); ast_string_field_set(tmp,host, host); tmp->jb = jb_new(); - tmp->jbid = -1; jbconf.max_jitterbuf = maxjitterbuffer; jbconf.resync_threshold = resyncthreshold; jbconf.max_contig_interp = maxjitterinterps; diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 1319094cf..0a8d1bcb5 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -162,11 +162,18 @@ static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan); struct chan_pjsip_pvt *pvt; struct ast_sip_endpoint *endpoint; + struct ast_datastore *datastore; if (!channel || !channel->session || !(pvt = channel->pvt) || !pvt->media[SIP_MEDIA_AUDIO]->rtp) { return AST_RTP_GLUE_RESULT_FORBID; } + datastore = ast_sip_session_get_datastore(channel->session, "t38"); + if (datastore) { + ao2_ref(datastore, -1); + return AST_RTP_GLUE_RESULT_FORBID; + } + endpoint = channel->session->endpoint; *instance = pvt->media[SIP_MEDIA_AUDIO]->rtp; diff --git a/channels/chan_sip.c b/channels/chan_sip.c index c4d26d56b..98df83a0e 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1708,6 +1708,7 @@ static int publish_expire(const void *data) ast_assert(esc != NULL); ao2_unlink(esc->compositor, esc_entry); + esc_entry->sched_id = -1; ao2_ref(esc_entry, -1); return 0; } @@ -1740,6 +1741,11 @@ static struct sip_esc_entry *create_esc_entry(struct event_state_compositor *esc /* Bump refcount for scheduler */ ao2_ref(esc_entry, +1); esc_entry->sched_id = ast_sched_add(sched, expires_ms, publish_expire, esc_entry); + if (esc_entry->sched_id == -1) { + ao2_ref(esc_entry, -1); + ao2_ref(esc_entry, -1); + return NULL; + } /* Note: This links the esc_entry into the ESC properly */ create_new_sip_etag(esc_entry, 0); @@ -9368,6 +9374,9 @@ static int sip_register(const char *value, int lineno) return -1; } + reg->expire = -1; + reg->timeout = -1; + if (ast_string_field_init(reg, 256)) { ao2_t_ref(reg, -1, "failed to string_field_init, drop reg"); return -1; @@ -9441,6 +9450,8 @@ static int sip_subscribe_mwi(const char *value, int lineno) return -1; } + mwi->resub = -1; + if (ast_string_field_init(mwi, 256)) { ao2_t_ref(mwi, -1, "failed to string_field_init, drop mwi"); return -1; @@ -9455,7 +9466,6 @@ static int sip_subscribe_mwi(const char *value, int lineno) } ast_string_field_set(mwi, hostname, hostname); ast_string_field_set(mwi, mailbox, mailbox); - mwi->resub = -1; mwi->portno = portnum; mwi->transport = transport; diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index b6d269d95..5cdfe1b78 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -1364,6 +1364,12 @@ static int gendigittimeout = 8000; /* How long to wait for an extra digit, if there is an ambiguous match */ static int matchdigittimeout = 3000; +/*! + * To apease the stupid compiler option on ast_sched_del() + * since we don't care about the return value. + */ +static int not_used; + #define SUBSTATE_UNSET 0 #define SUBSTATE_OFFHOOK 1 #define SUBSTATE_ONHOOK 2 @@ -2262,10 +2268,10 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s) int instance; int res = -1; - if (s->auth_timeout_sched && ast_sched_del(sched, s->auth_timeout_sched)) { - return 0; + if (-1 < s->auth_timeout_sched) { + not_used = ast_sched_del(sched, s->auth_timeout_sched); + s->auth_timeout_sched = -1; } - s->auth_timeout_sched = 0; AST_LIST_LOCK(&devices); AST_LIST_TRAVERSE(&devices, d, list){ @@ -5548,12 +5554,12 @@ static void setsubstate(struct skinny_subchannel *sub, int state) skinny_locksub(sub); - if (sub->dialer_sched) { + if (-1 < sub->dialer_sched) { skinny_sched_del(sub->dialer_sched, sub); sub->dialer_sched = -1; } - if (state != SUBSTATE_RINGIN && sub->aa_sched) { + if (state != SUBSTATE_RINGIN && -1 < sub->aa_sched) { skinny_sched_del(sub->aa_sched, sub); sub->aa_sched = -1; sub->aa_beep = 0; @@ -5571,6 +5577,7 @@ static void setsubstate(struct skinny_subchannel *sub, int state) sub->cfwd_sched = -1; } else if (state == SUBSTATE_ONHOOK) { skinny_sched_del(sub->cfwd_sched, sub); + sub->cfwd_sched = -1; } } @@ -6170,9 +6177,7 @@ static int handle_ip_port_message(struct skinny_req *req, struct skinnysession * static void handle_keepalive_message(struct skinny_req *req, struct skinnysession *s) { - if (ast_sched_del(sched, s->keepalive_timeout_sched)) { - return; - } + not_used = ast_sched_del(sched, s->keepalive_timeout_sched); #ifdef AST_DEVMODE { @@ -6238,7 +6243,7 @@ static int handle_keypad_button_message(struct skinny_req *req, struct skinnyses } if ((sub->owner && ast_channel_state(sub->owner) < AST_STATE_UP)) { - if (sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) { + if (-1 < sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) { SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %u - Got a digit and not timed out, so try dialing\n", sub->callid); sub->dialer_sched = -1; len = strlen(sub->exten); @@ -7075,7 +7080,7 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse case SOFTKEY_BKSPC: SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_BKSPC from %s, inst %d, callref %d\n", d->name, instance, callreference); - if (sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) { + if (-1 < sub->dialer_sched && !skinny_sched_del(sub->dialer_sched, sub)) { size_t len; sub->dialer_sched = -1; len = strlen(sub->exten); @@ -7415,7 +7420,7 @@ static int skinny_noauth_cb(const void *data) { struct skinnysession *s = (struct skinnysession *)data; ast_log(LOG_WARNING, "Skinny Client failed to authenticate in %d seconds (SCHED %d)\n", auth_timeout, s->auth_timeout_sched); - s->auth_timeout_sched = 0; + s->auth_timeout_sched = -1; end_session(s); return 0; } @@ -7424,7 +7429,7 @@ static int skinny_nokeepalive_cb(const void *data) { struct skinnysession *s = (struct skinnysession *)data; ast_log(LOG_WARNING, "Skinny Client failed to send keepalive in last %d seconds (SCHED %d)\n", keep_alive*3, s->keepalive_timeout_sched); - s->keepalive_timeout_sched = 0; + s->keepalive_timeout_sched = -1; end_session(s); return 0; } @@ -7442,11 +7447,13 @@ static void skinny_session_cleanup(void *data) ast_mutex_unlock(&s->lock); } - if (s->auth_timeout_sched && !ast_sched_del(sched, s->auth_timeout_sched)) { - s->auth_timeout_sched = 0; + if (-1 < s->auth_timeout_sched) { + not_used = ast_sched_del(sched, s->auth_timeout_sched); + s->auth_timeout_sched = -1; } - if (s->keepalive_timeout_sched && !ast_sched_del(sched, s->keepalive_timeout_sched)) { - s->keepalive_timeout_sched = 0; + if (-1 < s->keepalive_timeout_sched) { + not_used = ast_sched_del(sched, s->keepalive_timeout_sched); + s->keepalive_timeout_sched = -1; } if (d) { @@ -7651,6 +7658,8 @@ static void *accept_thread(void *ignore) ast_mutex_init(&s->lock); memcpy(&s->sin, &sin, sizeof(sin)); s->fd = as; + s->auth_timeout_sched = -1; + s->keepalive_timeout_sched = -1; if (ast_pthread_create(&s->t, NULL, skinny_session, s)) { destroy_session(s); diff --git a/channels/sip/config_parser.c b/channels/sip/config_parser.c index d48ff08dd..50495427e 100644 --- a/channels/sip/config_parser.c +++ b/channels/sip/config_parser.c @@ -79,13 +79,17 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const AST_APP_ARG(port); ); - if (!value) { + if (!reg) { return -1; } - if (!reg) { + reg->expire = -1; + reg->timeout = -1; + + if (!value) { return -1; } + ast_copy_string(buf, value, sizeof(buf)); /*! register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] @@ -261,7 +265,6 @@ int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const ast_string_field_set(reg, regdomain, ast_strip_quoted(S_OR(user2.domain, ""), "\"", "\"")); reg->transport = transport; - reg->timeout = reg->expire = -1; reg->portno = portnum; reg->regdomainport = domainport; reg->callid_valid = FALSE; diff --git a/codecs/codec_resample.c b/codecs/codec_resample.c index 26b1f0e08..fb06b82e5 100644 --- a/codecs/codec_resample.c +++ b/codecs/codec_resample.c @@ -38,7 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/translate.h" #include "asterisk/slin.h" -#define OUTBUF_SIZE 8096 +#define OUTBUF_SAMPLES 5760 static struct ast_translator *translators; static int trans_size; @@ -114,7 +114,7 @@ static void resamp_destroy(struct ast_trans_pvt *pvt) static int resamp_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { SpeexResamplerState *resamp_pvt = pvt->pvt; - unsigned int out_samples = (OUTBUF_SIZE / sizeof(int16_t)) - pvt->samples; + unsigned int out_samples = OUTBUF_SAMPLES - pvt->samples; unsigned int in_samples; if (!f->datalen) { @@ -167,8 +167,8 @@ static int load_module(void) translators[idx].destroy = resamp_destroy; translators[idx].framein = resamp_framein; translators[idx].desc_size = 0; - translators[idx].buffer_samples = (OUTBUF_SIZE / sizeof(int16_t)); - translators[idx].buf_size = OUTBUF_SIZE; + translators[idx].buffer_samples = OUTBUF_SAMPLES; + translators[idx].buf_size = (OUTBUF_SAMPLES * sizeof(int16_t)); memcpy(&translators[idx].src_codec, &codec_list[x], sizeof(struct ast_codec)); memcpy(&translators[idx].dst_codec, &codec_list[y], sizeof(struct ast_codec)); snprintf(translators[idx].name, sizeof(translators[idx].name), "slin %ukhz -> %ukhz", diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h index fc3726270..30ac0953c 100644 --- a/include/asterisk/bridge.h +++ b/include/asterisk/bridge.h @@ -746,6 +746,18 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan); */ int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan); +/*! + * \brief Sets BRIDGECHANNEL and BRIDGEPVTCALLID for a channel + * + * \pre chan must be locked before calling + * + * \param name channel name of the bridged peer + * \param pvtid Private CallID of the bridged peer + * + * \return nothing + */ +void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid); + struct ast_unreal_pvt; /*! diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 37b766211..6ca56bdcf 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -170,6 +170,8 @@ struct ast_sip_contact { double qualify_timeout; /*! Endpoint that added the contact, only available in observers */ struct ast_sip_endpoint *endpoint; + /*! The name of the aor this contact belongs to */ + char *aor; }; #define CONTACT_STATUS "contact_status" @@ -201,6 +203,10 @@ struct ast_sip_contact_status { int64_t rtt; /*! Last status for a contact (default - unavailable) */ enum ast_sip_contact_status_type last_status; + /*! The name of the aor this contact_status belongs to */ + char *aor; + /*! The original contact's URI */ + char *uri; }; /*! diff --git a/include/asterisk/statsd.h b/include/asterisk/statsd.h index 8e5e2f987..4cbd213ad 100644 --- a/include/asterisk/statsd.h +++ b/include/asterisk/statsd.h @@ -29,7 +29,12 @@ #include "asterisk/optional_api.h" /*! An instantaneous measurement of a value. */ -#define AST_STATSD_GUAGE "g" +#define AST_STATSD_GAUGE "g" +/*! + * Embarrassingly, gauge was misspelled for quite some time. + * \deprecated You should spell gauge correctly. + */ +#define AST_STATSD_GUAGE AST_STATSD_GAUGE /*! A change in a value. */ #define AST_STATSD_COUNTER "c" /*! Measure of milliseconds. */ @@ -42,8 +47,46 @@ /*! * \brief Send a stat to the configured statsd server. * - * The is the most flexible function for sending a message to the statsd server, - * but also the least easy to use. See ast_statsd_log() or + * This function uses a character argument for value instead of + * an intmax_t argument. This is designed to be simpler to use for + * updating a current value rather than resetting it. + * + * \param metric_name String (UTF-8) name of the metric. + * \param type_str Type of metric to send. + * \param value Value to send. + * \param sample_rate Percentage of samples to send. + * \since 13 + */ +AST_OPTIONAL_API(void, ast_statsd_log_string, (const char *metric_name, + const char *metric_type, const char *value, double sample_rate), {}); + +/*! + * \brief Send a stat to the configured statsd server. + * \since 13.7.0 + * + * This is the most flexible function for sending a message to the statsd + * server. In addition to allowing the string value and sample rate to be specified, + * the metric_name can be formed as a printf style string with variable + * arguments. + * + * \param metric_name Format string (UTF-8) specifying the name of the metric. + * \param metric_type Type of metric to send. + * \param value Value to send. + * \param sample_rate Percentage of samples to send. + * + * Example Usage: + * \code + * ast_statsd_log_string_va(AST_STATSD_GAUGE, "+1", 1.0, "endpoints.states.%s", state_name); + * \endcode + */ +AST_OPTIONAL_API_ATTR(void, format(printf, 1, 5), ast_statsd_log_string_va, + (const char *metric_name, const char *metric_type, const char *value, double sample_rate, ...), {}); + +/*! + * \brief Send a stat to the configured statsd server. + * + * The is nearly the most flexible function for sending a message to the statsd + * server, but also the least easy to use. See ast_statsd_log() or * ast_statsd_log_sample() for a slightly more convenient interface. * * \param metric_name String (UTF-8) name of the metric. @@ -57,6 +100,28 @@ AST_OPTIONAL_API(void, ast_statsd_log_full, (const char *metric_name, /*! * \brief Send a stat to the configured statsd server. + * \since 13.7.0 + * + * This is the most flexible function for sending a message to the statsd + * server. In addition to allowing the value and sample rate to be specified, + * the metric_name can be formed as a printf style string with variable + * arguments. + * + * \param metric_name Format string (UTF-8) specifying the name of the metric. + * \param metric_type Type of metric to send. + * \param value Value to send. + * \param sample_rate Percentage of samples to send. + * + * Example Usage: + * \code + * ast_statsd_log_full_va(AST_STATSD_TIMER, rtt, 1.0, "endpoint.%s.rtt", endpoint_name); + * \endcode + */ +AST_OPTIONAL_API_ATTR(void, format(printf, 1, 5), ast_statsd_log_full_va, + (const char *metric_name, const char *metric_type, intmax_t value, double sample_rate, ...), {}); + +/*! + * \brief Send a stat to the configured statsd server. * \param metric_name String (UTF-8) name of the metric. * \param metric_type Type of metric to send. * \param value Value to send. diff --git a/include/asterisk/term.h b/include/asterisk/term.h index 18d743b76..fac36eb5e 100644 --- a/include/asterisk/term.h +++ b/include/asterisk/term.h @@ -67,8 +67,8 @@ extern "C" { #define COLORIZE_FMT "%s%s%s" #define COLORIZE(fg, bg, str) ast_term_color(fg,bg),str,ast_term_reset() /*! \brief Maximum number of characters needed for a color escape sequence, - * plus a null char */ -#define AST_TERM_MAX_ESCAPE_CHARS 12 + * and another one for a trailing reset, plus a null char */ +#define AST_TERM_MAX_ESCAPE_CHARS 23 #define AST_TERM_MAX_ROTATING_BUFFERS 15 /*! \brief Colorize a specified string by adding terminal color codes diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h index e8e4c02d2..e2a4f300f 100644 --- a/include/asterisk/translate.h +++ b/include/asterisk/translate.h @@ -223,6 +223,14 @@ struct ast_trans_pvt { struct ast_trans_pvt *next; /*!< next in translator chain */ struct timeval nextin; struct timeval nextout; + /*! If a translation path using a format with attributes requires the output + * to be a specific set of attributes, this variable will be set describing + * those attributes to the translator. Otherwise, the translator must choose + * a set of format attributes for the destination that preserves the quality + * of the audio in the best way possible. For example with the Opus Codec, + * explicit_dst contains an attribute which describes whether both parties + * want to do forward-error correction (FEC). */ + struct ast_format *explicit_dst; }; /*! \brief generic frameout function */ diff --git a/main/asterisk.c b/main/asterisk.c index cf0a59537..3f16caf06 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -372,9 +372,14 @@ struct console consoles[AST_MAX_CONNECTS]; char ast_defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE; -static int ast_el_add_history(char *); -static int ast_el_read_history(char *); -static int ast_el_write_history(char *); +static int ast_el_add_history(const char *); +static int ast_el_read_history(const char *); +static int ast_el_write_history(const char *); + +static void ast_el_read_default_histfile(void); +static void ast_el_write_default_histfile(void); + +static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup); struct _cfg_paths { char config_dir[PATH_MAX]; @@ -2064,13 +2069,7 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart) } if (ast_opt_console || (ast_opt_remote && !ast_opt_exec)) { - char filename[80] = ""; - if (getenv("HOME")) { - snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); - } - if (!ast_strlen_zero(filename)) { - ast_el_write_history(filename); - } + ast_el_write_default_histfile(); if (consolethread == AST_PTHREADT_NULL || consolethread == pthread_self()) { /* Only end if we are the consolethread, otherwise there's a race with that thread. */ if (el != NULL) { @@ -2301,7 +2300,7 @@ static void console_verboser(const char *s) } } -static int ast_all_zeros(char *s) +static int ast_all_zeros(const char *s) { while (*s) { if (*s > 32) @@ -2312,7 +2311,7 @@ static int ast_all_zeros(char *s) } /* This is the main console CLI command handler. Run by the main() thread. */ -static void consolehandler(char *s) +static void consolehandler(const char *s) { printf("%s", term_end()); fflush(stdout); @@ -2330,7 +2329,7 @@ static void consolehandler(char *s) ast_cli_command(STDOUT_FILENO, s); } -static int remoteconsolehandler(char *s) +static int remoteconsolehandler(const char *s) { int ret = 0; @@ -3247,7 +3246,7 @@ static int ast_el_initialize(void) #define MAX_HISTORY_COMMAND_LENGTH 256 -static int ast_el_add_history(char *buf) +static int ast_el_add_history(const char *buf) { HistEvent ev; char *stripped_buf; @@ -3269,7 +3268,7 @@ static int ast_el_add_history(char *buf) return history(el_hist, &ev, H_ENTER, stripped_buf); } -static int ast_el_write_history(char *filename) +static int ast_el_write_history(const char *filename) { HistEvent ev; @@ -3279,7 +3278,7 @@ static int ast_el_write_history(char *filename) return (history(el_hist, &ev, H_SAVE, filename)); } -static int ast_el_read_history(char *filename) +static int ast_el_read_history(const char *filename) { HistEvent ev; @@ -3290,11 +3289,32 @@ static int ast_el_read_history(char *filename) return history(el_hist, &ev, H_LOAD, filename); } +static void ast_el_read_default_histfile(void) +{ + char histfile[80] = ""; + const char *home = getenv("HOME"); + + if (!ast_strlen_zero(home)) { + snprintf(histfile, sizeof(histfile), "%s/.asterisk_history", home); + ast_el_read_history(histfile); + } +} + +static void ast_el_write_default_histfile(void) +{ + char histfile[80] = ""; + const char *home = getenv("HOME"); + + if (!ast_strlen_zero(home)) { + snprintf(histfile, sizeof(histfile), "%s/.asterisk_history", home); + ast_el_write_history(histfile); + } +} + static void ast_remotecontrol(char *data) { char buf[256] = ""; int res; - char filename[80] = ""; char *hostname; char *cpid; char *version; @@ -3304,6 +3324,10 @@ static void ast_remotecontrol(char *data) char *ebuf; int num = 0; + ast_term_init(); + printf("%s", term_end()); + fflush(stdout); + memset(&sig_flags, 0, sizeof(sig_flags)); signal(SIGINT, __remote_quit_handler); signal(SIGTERM, __remote_quit_handler); @@ -3402,16 +3426,12 @@ static void ast_remotecontrol(char *data) ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid); remotehostname = hostname; - if (getenv("HOME")) - snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); if (el_hist == NULL || el == NULL) ast_el_initialize(); + ast_el_read_default_histfile(); el_set(el, EL_GETCFN, ast_el_read_char); - if (!ast_strlen_zero(filename)) - ast_el_read_history(filename); - for (;;) { ebuf = (char *)el_gets(el, &num); @@ -3870,18 +3890,11 @@ static void main_atexit(void) int main(int argc, char *argv[]) { int c; - char filename[80] = ""; - char hostname[MAXHOSTNAMELEN] = ""; char * xarg = NULL; int x; - FILE *f; - sigset_t sigs; - int num; int isroot = 1, rundir_exists = 0; - char *buf; const char *runuser = NULL, *rungroup = NULL; char *remotesock = NULL; - int moduleresult; /*!< Result from the module load subsystem */ struct rlimit l; /* Remember original args for restart */ @@ -3900,12 +3913,8 @@ int main(int argc, char *argv[]) if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) { ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE); } - if (gethostname(hostname, sizeof(hostname)-1)) - ast_copy_string(hostname, "<Unknown>", sizeof(hostname)); ast_mainpid = getpid(); - if (getenv("HOME")) - snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); /*! \brief Check for options * * \todo Document these options @@ -4249,6 +4258,10 @@ int main(int argc, char *argv[]) quit_handler(0, SHUTDOWN_FAST, 0); exit(0); } + ast_term_init(); + printf("%s", term_end()); + fflush(stdout); + print_intro_message(runuser, rungroup); printf("%s", term_quit()); ast_remotecontrol(NULL); @@ -4265,6 +4278,19 @@ int main(int argc, char *argv[]) exit(1); } + /* Not a remote console? Start the daemon. */ + asterisk_daemon(isroot, runuser, rungroup); + return 0; +} + +static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup) +{ + FILE *f; + sigset_t sigs; + int num; + char *buf; + int moduleresult; /*!< Result from the module load subsystem */ + /* This needs to remain as high up in the initial start up as possible. * daemon causes a fork to occur, which has all sorts of unintended * consequences for things that interact with threads. This call *must* @@ -4361,9 +4387,7 @@ int main(int argc, char *argv[]) if (ast_opt_console) { if (el_hist == NULL || el == NULL) ast_el_initialize(); - - if (!ast_strlen_zero(filename)) - ast_el_read_history(filename); + ast_el_read_default_histfile(); } ast_json_init(); @@ -4528,7 +4552,7 @@ int main(int argc, char *argv[]) /* initialize the data retrieval API */ if (ast_data_init()) { - printf ("Failed: ast_data_init\n%s", term_quit()); + printf("Failed: ast_data_init\n%s", term_quit()); exit(1); } @@ -4695,6 +4719,11 @@ int main(int argc, char *argv[]) /* Console stuff now... */ /* Register our quit function */ char title[256]; + char hostname[MAXHOSTNAMELEN] = ""; + + if (gethostname(hostname, sizeof(hostname) - 1)) { + ast_copy_string(hostname, "<Unknown>", sizeof(hostname)); + } ast_pthread_create_detached(&mon_sig_flags, NULL, monitor_sig_flags, NULL); @@ -4712,30 +4741,17 @@ int main(int argc, char *argv[]) buf = (char *) el_gets(el, &num); if (!buf && write(1, "", 1) < 0) - goto lostterm; + return; /* quit */ if (buf) { if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; - consolehandler((char *)buf); - } else if (ast_opt_remote && (write(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n", - strlen("\nUse EXIT or QUIT to exit the asterisk console\n")) < 0)) { - /* Whoa, stdout disappeared from under us... Make /dev/null's */ - int fd; - fd = open("/dev/null", O_RDWR); - if (fd > -1) { - dup2(fd, STDOUT_FILENO); - dup2(fd, STDIN_FILENO); - } else - ast_log(LOG_WARNING, "Failed to open /dev/null to recover from dead console. Bad things will happen!\n"); - break; + consolehandler(buf); } } } + /* Stall until a quit signal is given */ monitor_sig_flags(NULL); - -lostterm: - return 0; } diff --git a/main/audiohook.c b/main/audiohook.c index 93d3521f2..5dfbb5d36 100644 --- a/main/audiohook.c +++ b/main/audiohook.c @@ -824,13 +824,20 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l return new_frame; } - if (ast_format_cmp(frame->subclass.format, in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) { + if (!in_translate->format || + ast_format_cmp(frame->subclass.format, in_translate->format) != AST_FORMAT_CMP_EQUAL) { + struct ast_trans_pvt *new_trans; + + new_trans = ast_translator_build_path(slin, frame->subclass.format); + if (!new_trans) { + return NULL; + } + if (in_translate->trans_pvt) { ast_translator_free_path(in_translate->trans_pvt); } - if (!(in_translate->trans_pvt = ast_translator_build_path(slin, frame->subclass.format))) { - return NULL; - } + in_translate->trans_pvt = new_trans; + ao2_replace(in_translate->format, frame->subclass.format); } diff --git a/main/bridge.c b/main/bridge.c index 7644884eb..b5c59514c 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -1213,7 +1213,7 @@ static void check_bridge_play_sounds(struct ast_bridge *bridge) } } -static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid) +void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid) { ast_channel_stage_snapshot(chan); pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name); @@ -1253,12 +1253,12 @@ static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_chann ast_channel_unlock(c1); ast_channel_lock(c0); - update_bridge_vars_set(c0, c1_name, c1_pvtid); + ast_bridge_vars_set(c0, c1_name, c1_pvtid); UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid); ast_channel_unlock(c0); ast_channel_lock(c1); - update_bridge_vars_set(c1, c0_name, c0_pvtid); + ast_bridge_vars_set(c1, c0_name, c0_pvtid); ast_channel_unlock(c1); } @@ -1359,7 +1359,7 @@ static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge) ++idx; ast_channel_lock(bridge_channel->chan); - update_bridge_vars_set(bridge_channel->chan, buf, NULL); + ast_bridge_vars_set(bridge_channel->chan, buf, NULL); ast_channel_unlock(bridge_channel->chan); } } @@ -1381,7 +1381,7 @@ static void set_bridge_peer_vars_holding(struct ast_bridge *bridge) AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { ast_channel_lock(bridge_channel->chan); - update_bridge_vars_set(bridge_channel->chan, NULL, NULL); + ast_bridge_vars_set(bridge_channel->chan, NULL, NULL); ast_channel_unlock(bridge_channel->chan); } } diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 8ce022005..3874e50ff 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -289,6 +289,10 @@ void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_ch channel_set_cause(bridge_channel->chan, cause); + ast_channel_lock(bridge_channel->chan); + ast_bridge_vars_set(bridge_channel->chan, NULL, NULL); + ast_channel_unlock(bridge_channel->chan); + /* Change the state on the bridge channel */ bridge_channel->state = new_state; diff --git a/main/channel.c b/main/channel.c index 67f0da8ba..6507ef979 100644 --- a/main/channel.c +++ b/main/channel.c @@ -872,6 +872,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char ast_channel_hold_state_set(tmp, AST_CONTROL_UNHOLD); ast_channel_streamid_set(tmp, -1); + ast_channel_vstreamid_set(tmp, -1); ast_channel_fin_set(tmp, global_fin); ast_channel_fout_set(tmp, global_fout); diff --git a/main/cli.c b/main/cli.c index 0d66f3e48..7f86eab3a 100644 --- a/main/cli.c +++ b/main/cli.c @@ -1076,10 +1076,12 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar char locbuf[40] = "(None)"; char appdata[40] = "(None)"; - if (!cs->context && !cs->exten) + if (!ast_strlen_zero(cs->context) && !ast_strlen_zero(cs->exten)) { snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", cs->exten, cs->context, cs->priority); - if (cs->appl) + } + if (!ast_strlen_zero(cs->appl)) { snprintf(appdata, sizeof(appdata), "%s(%s)", cs->appl, S_OR(cs->data, "")); + } ast_cli(a->fd, FORMAT_STRING, cs->name, locbuf, ast_state2str(cs->state), appdata); } } diff --git a/main/sched.c b/main/sched.c index e92e3ffd0..f851670af 100644 --- a/main/sched.c +++ b/main/sched.c @@ -315,9 +315,16 @@ static int add_ids(struct ast_sched_context *con) if (!new_id) { break; } - new_id->id = i; + + /* + * According to the API doxygen a sched ID of 0 is valid. + * Unfortunately, 0 was never returned historically and + * several users incorrectly coded usage of the returned + * sched ID assuming that 0 was invalid. + */ + new_id->id = ++con->id_queue_size; + AST_LIST_INSERT_TAIL(&con->id_queue, new_id, list); - ++con->id_queue_size; } return con->id_queue_size - original_size; diff --git a/main/term.c b/main/term.c index 871a7cb3c..6a1a64533 100644 --- a/main/term.c +++ b/main/term.c @@ -175,16 +175,14 @@ end: if (ast_opt_light_background) { snprintf(prepdata, sizeof(prepdata), "%c[%dm", ESC, COLOR_BROWN); snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, COLOR_BLACK); - snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC); } else if (ast_opt_force_black_background) { snprintf(prepdata, sizeof(prepdata), "%c[%d;%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN, COLOR_BLACK + 10); snprintf(enddata, sizeof(enddata), "%c[%d;%d;%dm", ESC, ATTR_RESET, COLOR_WHITE, COLOR_BLACK + 10); - snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC); } else { snprintf(prepdata, sizeof(prepdata), "%c[%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN); - snprintf(enddata, sizeof(enddata), "%c[%d;%dm", ESC, ATTR_RESET, COLOR_WHITE); - snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC); + snprintf(enddata, sizeof(enddata), "%c[%dm", ESC, ATTR_RESET); } + snprintf(quitdata, sizeof(quitdata), "%c[%dm", ESC, ATTR_RESET); } return 0; } @@ -216,9 +214,12 @@ char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int } if (ast_opt_force_black_background) { - snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%c[%d;%dm", ESC, attr, fgcolor, bgcolor + 10, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10); + if (!bgcolor) { + bgcolor = COLOR_BLACK; + } + snprintf(outbuf, maxout, "%c[%d;%d;%dm%s%s", ESC, attr, fgcolor, bgcolor + 10, inbuf, term_end()); } else { - snprintf(outbuf, maxout, "%c[%d;%dm%s%c[0m", ESC, attr, fgcolor, inbuf, ESC); + snprintf(outbuf, maxout, "%c[%d;%dm%s%s", ESC, attr, fgcolor, inbuf, term_end()); } return outbuf; } @@ -242,16 +243,16 @@ static void check_bgcolor(int *bgcolor) } } -static int check_colors_allowed(int fgcolor) +static int check_colors_allowed(void) { - return (!vt100compat || !fgcolor) ? 0 : 1; + return vt100compat; } int ast_term_color_code(struct ast_str **str, int fgcolor, int bgcolor) { int attr = 0; - if (!check_colors_allowed(fgcolor)) { + if (!check_colors_allowed()) { return -1; } @@ -273,7 +274,7 @@ char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout) { int attr = 0; - if (!check_colors_allowed(fgcolor)) { + if (!check_colors_allowed()) { *outbuf = '\0'; return outbuf; } @@ -310,11 +311,7 @@ const char *ast_term_color(int fgcolor, int bgcolor) const char *ast_term_reset(void) { - if (ast_opt_force_black_background) { - return enddata; - } else { - return quitdata; - } + return term_end(); } char *term_strip(char *outbuf, const char *inbuf, int maxout) diff --git a/main/translate.c b/main/translate.c index 6d9277736..44cfa2bb9 100644 --- a/main/translate.c +++ b/main/translate.c @@ -298,6 +298,10 @@ static void destroy(struct ast_trans_pvt *pvt) t->destroy(pvt); } ao2_cleanup(pvt->f.subclass.format); + if (pvt->explicit_dst) { + ao2_ref(pvt->explicit_dst, -1); + pvt->explicit_dst = NULL; + } ast_free(pvt); ast_module_unref(t->module); } @@ -306,7 +310,7 @@ static void destroy(struct ast_trans_pvt *pvt) * \brief Allocate the descriptor, required outbuf space, * and possibly desc. */ -static struct ast_trans_pvt *newpvt(struct ast_translator *t) +static struct ast_trans_pvt *newpvt(struct ast_translator *t, struct ast_format *explicit_dst) { struct ast_trans_pvt *pvt; int len; @@ -332,6 +336,12 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t) if (t->buf_size) {/* finally buffer and header */ pvt->outbuf.c = ofs + AST_FRIENDLY_OFFSET; } + /* + * If the format has an attribute module, explicit_dst includes the (joined) + * result of the SDP negotiation. For example with the Opus Codec, the format + * knows whether both parties want to do forward-error correction (FEC). + */ + pvt->explicit_dst = ao2_bump(explicit_dst); ast_module_ref(t->module); @@ -349,9 +359,16 @@ static struct ast_trans_pvt *newpvt(struct ast_translator *t) pvt->f.src = pvt->t->name; pvt->f.data.ptr = pvt->outbuf.c; - /* if the translator has not provided a format find one in the cache or create one */ + /* + * If the translator has not provided a format + * A) use the joined one, + * B) use the cached one, or + * C) create one. + */ if (!pvt->f.subclass.format) { - if (!ast_strlen_zero(pvt->t->format)) { + pvt->f.subclass.format = ao2_bump(pvt->explicit_dst); + + if (!pvt->f.subclass.format && !ast_strlen_zero(pvt->t->format)) { pvt->f.subclass.format = ast_format_cache_get(pvt->t->format); } @@ -477,6 +494,7 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a while (src_index != dst_index) { struct ast_trans_pvt *cur; + struct ast_format *explicit_dst = NULL; struct ast_translator *t = matrix_get(src_index, dst_index)->step; if (!t) { ast_log(LOG_WARNING, "No translator path from %s to %s\n", @@ -484,7 +502,10 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a AST_RWLIST_UNLOCK(&translators); return NULL; } - if (!(cur = newpvt(t))) { + if ((t->dst_codec.sample_rate == ast_format_get_sample_rate(dst)) && (t->dst_codec.type == ast_format_get_type(dst)) && (!strcmp(t->dst_codec.name, ast_format_get_name(dst)))) { + explicit_dst = dst; + } + if (!(cur = newpvt(t, explicit_dst))) { ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n", ast_format_get_name(src), ast_format_get_name(dst)); if (head) { @@ -638,7 +659,7 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) return; } - pvt = newpvt(t); + pvt = newpvt(t, NULL); if (!pvt) { ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name); t->comp_cost = 999999; diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c index fc9e182b2..04da24788 100644 --- a/pbx/pbx_dundi.c +++ b/pbx/pbx_dundi.c @@ -3264,6 +3264,7 @@ static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, i pack = ast_calloc(1, len); if (pack) { pack->h = (struct dundi_hdr *)(pack->data); + pack->retransid = -1; if (cmdresp != DUNDI_COMMAND_ACK) { pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack); pack->retrans = DUNDI_DEFAULT_RETRANS - 1; diff --git a/res/res_agi.c b/res/res_agi.c index 921eeffce..ff3358062 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -2630,8 +2630,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const } f = ast_read(chan); if (!f) { - ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset); ast_closestream(fs); + ast_agi_send(agi->fd, chan, "200 result=%d (hangup) endpos=%ld\n", -1, sample_offset); if (sildet) ast_dsp_free(sildet); return RESULT_FAILURE; @@ -2645,8 +2645,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const ast_stream_rewind(fs, 200); ast_truncstream(fs); sample_offset = ast_tellstream(fs); - ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset); ast_closestream(fs); + ast_agi_send(agi->fd, chan, "200 result=%d (dtmf) endpos=%ld\n", f->subclass.integer, sample_offset); ast_frfree(f); if (sildet) ast_dsp_free(sildet); @@ -2690,8 +2690,8 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, const ast_truncstream(fs); sample_offset = ast_tellstream(fs); } - ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); ast_closestream(fs); + ast_agi_send(agi->fd, chan, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset); } if (silence > 0) { diff --git a/res/res_endpoint_stats.c b/res/res_endpoint_stats.c new file mode 100644 index 000000000..6b8479433 --- /dev/null +++ b/res/res_endpoint_stats.c @@ -0,0 +1,157 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2015, Digium, Inc. + * + * Matthew Jordan <mjordan@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. + */ + +/*! + * \brief Statsd Endpoint stats. + * + * This module subscribes to Stasis endpoints and send statistics + * based on their state. + * + * \author Matthew Jordan <mjordan@digium.com> + * \since 13.7.0 + */ + +/*** MODULEINFO + <depend>res_statsd</depend> + <defaultenabled>no</defaultenabled> + <support_level>extended</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/stasis_endpoints.h" +#include "asterisk/stasis_message_router.h" +#include "asterisk/statsd.h" + +/*! Stasis message router */ +static struct stasis_message_router *router; + +static void update_endpoint_state(struct ast_endpoint_snapshot *snapshot, const char *delta) +{ + switch (snapshot->state) { + case AST_ENDPOINT_UNKNOWN: + ast_statsd_log_string("endpoints.state.unknown", AST_STATSD_GAUGE, delta, 1.0); + break; + case AST_ENDPOINT_OFFLINE: + ast_statsd_log_string("endpoints.state.offline", AST_STATSD_GAUGE, delta, 1.0); + break; + case AST_ENDPOINT_ONLINE: + ast_statsd_log_string("endpoints.state.online", AST_STATSD_GAUGE, delta, 1.0); + break; + } +} + +static void handle_endpoint_update(struct ast_endpoint_snapshot *old_snapshot, struct ast_endpoint_snapshot *new_snapshot) +{ + if (!old_snapshot && new_snapshot) { + ast_statsd_log_string("endpoints.count", AST_STATSD_GAUGE, "+1", 1.0); + update_endpoint_state(new_snapshot, "+1"); + } else if (old_snapshot && !new_snapshot) { + ast_statsd_log_string("endpoints.count", AST_STATSD_GAUGE, "-1", 1.0); + update_endpoint_state(old_snapshot, "-1"); + } else { + if (old_snapshot->state != new_snapshot->state) { + update_endpoint_state(old_snapshot, "-1"); + update_endpoint_state(new_snapshot, "+1"); + } + ast_statsd_log_full_va("endpoints.%s.%s.channels", AST_STATSD_GAUGE, new_snapshot->num_channels, 1.0, + new_snapshot->tech, new_snapshot->resource); + } +} + +static void cache_update_cb(void *data, struct stasis_subscription *sub, + struct stasis_message *message) +{ + struct stasis_cache_update *update = stasis_message_data(message); + struct ast_endpoint_snapshot *old_snapshot; + struct ast_endpoint_snapshot *new_snapshot; + + if (ast_endpoint_snapshot_type() != update->type) { + return; + } + + old_snapshot = stasis_message_data(update->old_snapshot); + new_snapshot = stasis_message_data(update->new_snapshot); + + handle_endpoint_update(old_snapshot, new_snapshot); +} + +static int dump_cache_load(void *obj, void *arg, int flags) +{ + struct stasis_message *msg = obj; + struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg); + + handle_endpoint_update(NULL, snapshot); + + return 0; +} + +static int dump_cache_unload(void *obj, void *arg, int flags) +{ + struct stasis_message *msg = obj; + struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg); + + handle_endpoint_update(snapshot, NULL); + + return 0; +} + +static int load_module(void) +{ + struct ao2_container *endpoints; + + router = stasis_message_router_create(ast_endpoint_topic_all_cached()); + if (!router) { + return AST_MODULE_LOAD_FAILURE; + } + stasis_message_router_add(router, stasis_cache_update_type(), cache_update_cb, NULL); + + endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type()); + if (endpoints) { + ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_load, NULL); + ao2_ref(endpoints, -1); + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + struct ao2_container *endpoints; + + endpoints = stasis_cache_dump(ast_endpoint_cache(), ast_endpoint_snapshot_type()); + if (endpoints) { + ao2_callback(endpoints, OBJ_MULTIPLE | OBJ_NODATA | OBJ_NOLOCK, dump_cache_unload, NULL); + ao2_ref(endpoints, -1); + } + + stasis_message_router_unsubscribe_and_join(router); + router = NULL; + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Endpoint statistics", + .support_level = AST_MODULE_SUPPORT_EXTENDED, + .load = load_module, + .unload = unload_module, + .nonoptreq = "res_statsd" + ); diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index eef1d43d2..170ada851 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -26,6 +26,7 @@ #include "asterisk/sorcery.h" #include "include/res_pjsip_private.h" #include "asterisk/res_pjsip_cli.h" +#include "asterisk/statsd.h" /*! \brief Destructor for AOR */ static void aor_destroy(void *obj) @@ -91,6 +92,7 @@ static void contact_destroy(void *obj) struct ast_sip_contact *contact = obj; ast_string_field_free_memory(contact); + ast_free(contact->aor); ao2_cleanup(contact->endpoint); } @@ -98,6 +100,9 @@ static void contact_destroy(void *obj) static void *contact_alloc(const char *name) { struct ast_sip_contact *contact = ast_sorcery_generic_alloc(sizeof(*contact), contact_destroy); + char *id = ast_strdupa(name); + char *aor = id; + char *aor_separator = NULL; if (!contact) { return NULL; @@ -108,6 +113,18 @@ static void *contact_alloc(const char *name) return NULL; } + /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ + if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) { + *aor_separator = '\0'; + } + ast_assert(aor_separator != NULL); + + contact->aor = ast_strdup(aor); + if (!contact->aor) { + ao2_cleanup(contact); + return NULL; + } + return contact; } @@ -376,7 +393,8 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab while ((contact_uri = strsep(&contacts, ","))) { struct ast_sip_contact *contact; struct ast_sip_contact_status *status; - char contact_id[strlen(aor_id) + strlen(contact_uri) + 2 + 1]; + char hash[33]; + char contact_id[strlen(aor_id) + sizeof(hash) + 2 + 1]; if (!aor->permanent_contacts) { aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, @@ -386,7 +404,8 @@ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variab } } - snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, contact_uri); + ast_md5_hash(hash, contact_uri); + snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, hash); contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id); if (!contact) { return -1; @@ -762,12 +781,12 @@ static int cli_contact_print_header(void *obj, void *arg, int flags) { struct ast_sip_cli_context *context = arg; int indent = CLI_INDENT_TO_SPACES(context->indent_level); - int filler = CLI_LAST_TABSTOP - indent - 18; + int filler = CLI_LAST_TABSTOP - indent - 23; ast_assert(context->output_buffer != NULL); ast_str_append(&context->output_buffer, 0, - "%*s: <Aor/ContactUri%*.*s> <Status....> <RTT(ms)..>\n", + "%*s: <Aor/ContactUri%*.*s> <Hash....> <Status> <RTT(ms)..>\n", indent, "Contact", filler, filler, CLI_HEADER_FILLER); return 0; @@ -780,22 +799,26 @@ static int cli_contact_print_body(void *obj, void *arg, int flags) struct ast_sip_cli_context *context = arg; int indent; int flexwidth; + const char *contact_id = ast_sorcery_object_get_id(contact); + const char *hash_start = contact_id + strlen(contact->aor) + 2; RAII_VAR(struct ast_sip_contact_status *, status, - ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)), + ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, contact_id), ao2_cleanup); ast_assert(contact->uri != NULL); ast_assert(context->output_buffer != NULL); indent = CLI_INDENT_TO_SPACES(context->indent_level); - flexwidth = CLI_LAST_TABSTOP - indent - 2; + flexwidth = CLI_LAST_TABSTOP - indent - 9 - strlen(contact->aor) + 1; - ast_str_append(&context->output_buffer, 0, "%*s: %-*.*s %-12.12s %11.3f\n", + ast_str_append(&context->output_buffer, 0, "%*s: %s/%-*.*s %-10.10s %-7.7s %11.3f\n", indent, "Contact", + contact->aor, flexwidth, flexwidth, - wrapper->contact_id, + contact->uri, + hash_start, ast_sip_get_contact_short_status_label(status ? status->status : UNKNOWN), (status && (status->status != UNKNOWN) ? ((long long) status->rtt) / 1000.0 : NAN)); @@ -940,6 +963,8 @@ static int contact_apply_handler(const struct ast_sorcery *sorcery, void *object int ast_sip_initialize_sorcery_location(void) { struct ast_sorcery *sorcery = ast_sip_get_sorcery(); + int i; + ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar"); ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor"); @@ -1006,6 +1031,15 @@ int ast_sip_initialize_sorcery_location(void) ast_sip_register_cli_formatter(aor_formatter); ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); + /* + * Reset StatsD gauges in case we didn't shut down cleanly. + * Note that this must done here, as contacts will create the contact_status + * object before PJSIP options handling is initialized. + */ + for (i = 0; i < REMOVED; i++) { + ast_statsd_log_full_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, 0, 1.0, ast_sip_get_contact_status_label(i)); + } + return 0; } diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 7e0c4b087..04002ef94 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -56,47 +56,37 @@ static int persistent_endpoint_cmp(void *obj, void *arg, int flags) return !strcmp(ast_endpoint_get_resource(persistent1->endpoint), id) ? CMP_MATCH | CMP_STOP : 0; } -/*! \brief Structure for communicating contact status to - * persistent_endpoint_update_state from the contact/contact_status - * observers. - */ -struct sip_contact_status { - char *uri; - enum ast_sip_contact_status_type status; - int64_t rtt; -}; - /*! \brief Callback function for changing the state of an endpoint */ -static int persistent_endpoint_update_state(void *obj, void *arg, void *data, int flags) +static int persistent_endpoint_update_state(void *obj, void *arg, int flags) { struct sip_persistent_endpoint *persistent = obj; struct ast_endpoint *endpoint = persistent->endpoint; - char *aor = arg; - struct sip_contact_status *status = data; + struct ast_sip_contact_status *status = arg; struct ao2_container *contacts; struct ast_json *blob; struct ao2_iterator i; struct ast_sip_contact *contact; enum ast_endpoint_state state = AST_ENDPOINT_OFFLINE; - if (!ast_strlen_zero(aor)) { - if (!strstr(persistent->aors, aor)) { + if (status) { + char rtt[32]; + + /* If the status' aor isn't one of the endpoint's, we skip */ + if (!strstr(persistent->aors, status->aor)) { return 0; } - if (status) { - char rtt[32]; - snprintf(rtt, 31, "%" PRId64, status->rtt); - blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}", - "contact_status", ast_sip_get_contact_status_label(status->status), - "aor", aor, - "uri", status->uri, - "roundtrip_usec", rtt, - "endpoint_name", ast_endpoint_get_resource(endpoint)); - ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob); - ast_json_unref(blob); - } + snprintf(rtt, sizeof(rtt), "%" PRId64, status->rtt); + blob = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}", + "contact_status", ast_sip_get_contact_status_label(status->status), + "aor", status->aor, + "uri", status->uri, + "roundtrip_usec", rtt, + "endpoint_name", ast_endpoint_get_resource(endpoint)); + ast_endpoint_blob_publish(endpoint, ast_endpoint_contact_state_type(), blob); + ast_json_unref(blob); } + /* Find all the contacts for this endpoint. If ANY are available, * mark the endpoint as ONLINE. */ @@ -142,57 +132,56 @@ static int persistent_endpoint_update_state(void *obj, void *arg, void *data, in return 0; } -/*! \brief Function called when stuff relating to a contact happens (created/deleted) */ +/*! \brief Function called when a contact is created */ static void persistent_endpoint_contact_created_observer(const void *object) { const struct ast_sip_contact *contact = object; - char *id = ast_strdupa(ast_sorcery_object_get_id(contact)); - char *aor = NULL; - char *contact_uri = NULL; - struct sip_contact_status status; - - aor = id; - /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ - if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) { - *contact_uri = '\0'; - contact_uri += 2; - } else { - contact_uri = id; - } + struct ast_sip_contact_status *contact_status; - status.uri = contact_uri; - status.status = CREATED; - status.rtt = 0; + contact_status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, + ast_sorcery_object_get_id(contact)); + if (!contact_status) { + ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n", + contact->aor, contact->uri); + return; + } + contact_status->uri = ast_strdup(contact->uri); + if (!contact_status->uri) { + ao2_cleanup(contact_status); + return; + } + contact_status->status = CREATED; - ast_verb(1, "Contact %s/%s has been created\n", aor, contact_uri); + ast_verb(1, "Contact %s/%s has been created\n",contact->aor, contact->uri); - ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status); + ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status); + ao2_cleanup(contact_status); } -/*! \brief Function called when stuff relating to a contact happens (created/deleted) */ +/*! \brief Function called when a contact is deleted */ static void persistent_endpoint_contact_deleted_observer(const void *object) { - char *id = ast_strdupa(ast_sorcery_object_get_id(object)); - char *aor = NULL; - char *contact_uri = NULL; - struct sip_contact_status status; + const struct ast_sip_contact *contact = object; + struct ast_sip_contact_status *contact_status; - aor = id; - /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ - if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) { - *contact_uri = '\0'; - contact_uri += 2; - } else { - contact_uri = id; + contact_status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, + ast_sorcery_object_get_id(contact)); + if (!contact_status) { + ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n", + contact->aor, contact->uri); + return; } + contact_status->uri = ast_strdup(contact->uri); + if (!contact_status->uri) { + ao2_cleanup(contact_status); + return; + } + contact_status->status = REMOVED; - ast_verb(1, "Contact %s/%s has been deleted\n", aor, contact_uri); - - status.uri = contact_uri; - status.status = REMOVED; - status.rtt = 0; + ast_verb(1, "Contact %s/%s has been deleted\n", contact->aor, contact->uri); - ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status); + ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status); + ao2_cleanup(contact_status); } /*! \brief Observer for contacts so state can be updated on respective endpoints */ @@ -201,36 +190,23 @@ static const struct ast_sorcery_observer state_contact_observer = { .deleted = persistent_endpoint_contact_deleted_observer, }; -/*! \brief Function called when stuff relating to a contact status happens (updated) */ +/*! \brief Function called when a contact_status is updated */ static void persistent_endpoint_contact_status_observer(const void *object) { - const struct ast_sip_contact_status *contact_status = object; - char *id = ast_strdupa(ast_sorcery_object_get_id(object)); - char *aor = NULL; - char *contact_uri = NULL; - struct sip_contact_status status; + struct ast_sip_contact_status *contact_status = (struct ast_sip_contact_status *)object; /* If rtt_start is set (this is the outgoing OPTIONS), ignore. */ if (contact_status->rtt_start.tv_sec > 0) { return; } - aor = id; - /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ - if ((contact_uri = strstr(id, ";@")) || (contact_uri = strstr(id, "@@"))) { - *contact_uri = '\0'; - contact_uri += 2; - } else { - contact_uri = id; - } - if (contact_status->status == contact_status->last_status) { - ast_debug(3, "Contact %s status didn't change: %s, RTT: %.3f msec\n", - contact_uri, ast_sip_get_contact_status_label(contact_status->status), + ast_debug(3, "Contact %s/%s status didn't change: %s, RTT: %.3f msec\n", + contact_status->aor, contact_status->uri, ast_sip_get_contact_status_label(contact_status->status), contact_status->rtt / 1000.0); return; } else { - ast_verb(1, "Contact %s/%s is now %s. RTT: %.3f msec\n", aor, contact_uri, + ast_verb(1, "Contact %s/%s is now %s. RTT: %.3f msec\n", contact_status->aor, contact_status->uri, ast_sip_get_contact_status_label(contact_status->status), contact_status->rtt / 1000.0); } @@ -241,11 +217,7 @@ static void persistent_endpoint_contact_status_observer(const void *object) ast_sorcery_object_get_id(contact_status), ast_sip_get_contact_status_label(contact_status->status)); - status.uri = contact_uri; - status.status = contact_status->status; - status.rtt = contact_status->rtt; - - ao2_callback_data(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, aor, &status); + ao2_callback(persistent_endpoints, OBJ_NODATA, persistent_endpoint_update_state, contact_status); } /*! \brief Observer for contacts so state can be updated on respective endpoints */ @@ -1085,7 +1057,7 @@ static struct ast_endpoint *persistent_endpoint_find_or_create(const struct ast_ if (ast_strlen_zero(persistent->aors)) { ast_endpoint_set_state(persistent->endpoint, AST_ENDPOINT_UNKNOWN); } else { - persistent_endpoint_update_state(persistent, NULL, NULL, 0); + persistent_endpoint_update_state(persistent, NULL, 0); } ao2_link_flags(persistent_endpoints, persistent, OBJ_NOLOCK); diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index 32ee401ea..e0557871e 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -29,6 +29,7 @@ #include "asterisk/cli.h" #include "asterisk/time.h" #include "asterisk/test.h" +#include "asterisk/statsd.h" #include "include/res_pjsip_private.h" #define DEFAULT_LANGUAGE "en" @@ -41,7 +42,6 @@ static const char *status_map [] = { [UNKNOWN] = "Unknown", [CREATED] = "Created", [REMOVED] = "Removed", - }; static const char *short_status_map [] = { @@ -64,18 +64,43 @@ const char *ast_sip_get_contact_short_status_label(const enum ast_sip_contact_st /*! * \internal + * \brief Destroy a ast_sip_contact_status object. + */ +static void contact_status_destroy(void * obj) +{ + struct ast_sip_contact_status *status = obj; + + ast_free(status->aor); + ast_free(status->uri); +} + +/*! + * \internal * \brief Create a ast_sip_contact_status object. */ static void *contact_status_alloc(const char *name) { - struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL); + struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), contact_status_destroy); + char *id = ast_strdupa(name); + char *aor = id; + char *aor_separator = NULL; if (!status) { ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n"); return NULL; } - status->status = UNKNOWN; + /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ + if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) { + *aor_separator = '\0'; + } + ast_assert(aor_separator != NULL); + + status->aor = ast_strdup(aor); + if (!status->aor) { + ao2_cleanup(status); + return NULL; + } return status; } @@ -97,12 +122,17 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (!status) { - ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s\n", - contact->uri); + ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n", + contact->aor, contact->uri); + return NULL; + } + + status->uri = ast_strdup(contact->uri); + if (!status->uri) { + ao2_cleanup(status); return NULL; } - status->status = UNKNOWN; status->rtt_start = ast_tv(0, 0); status->rtt = 0; @@ -113,6 +143,9 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const return NULL; } + ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, + "+1", 1.0, ast_sip_get_contact_status_label(status->status)); + return status; } @@ -123,8 +156,8 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const static void update_contact_status(const struct ast_sip_contact *contact, enum ast_sip_contact_status_type value) { - struct ast_sip_contact_status *status; - struct ast_sip_contact_status *update; + RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup); + RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup); status = ast_res_pjsip_find_or_create_contact_status(contact); if (!status) { @@ -141,8 +174,19 @@ static void update_contact_status(const struct ast_sip_contact *contact, return; } + update->uri = ast_strdup(contact->uri); + if (!update->uri) { + return; + } + update->last_status = status->status; update->status = value; + if (update->last_status != update->status) { + ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, + "-1", 1.0, ast_sip_get_contact_status_label(update->last_status)); + ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, + "+1", 1.0, ast_sip_get_contact_status_label(update->status)); + } /* if the contact is available calculate the rtt as the diff between the last start time and "now" */ @@ -151,10 +195,12 @@ static void update_contact_status(const struct ast_sip_contact *contact, update->rtt_start = ast_tv(0, 0); + ast_statsd_log_full_va("PJSIP.contacts.%s.rtt", AST_STATSD_TIMER, + update->rtt / 1000, 1.0, ast_sorcery_object_get_id(update)); ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT", "Contact: %s\r\n" - "Status: %s\r\n" - "RTT: %" PRId64, + "Status: %s\r\n" + "RTT: %" PRId64, ast_sorcery_object_get_id(update), ast_sip_get_contact_status_label(update->status), update->rtt); @@ -163,9 +209,6 @@ static void update_contact_status(const struct ast_sip_contact *contact, ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n", contact->uri); } - - ao2_ref(status, -1); - ao2_ref(update, -1); } /*! @@ -175,8 +218,8 @@ static void update_contact_status(const struct ast_sip_contact *contact, */ static void init_start_time(const struct ast_sip_contact *contact) { - struct ast_sip_contact_status *status; - struct ast_sip_contact_status *update; + RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup); + RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup); status = ast_res_pjsip_find_or_create_contact_status(contact); if (!status) { @@ -193,6 +236,11 @@ static void init_start_time(const struct ast_sip_contact *contact) return; } + update->uri = ast_strdup(contact->uri); + if (!update->uri) { + return; + } + update->status = status->status; update->last_status = status->last_status; update->rtt = status->rtt; @@ -202,9 +250,6 @@ static void init_start_time(const struct ast_sip_contact *contact) ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n", contact->uri); } - - ao2_ref(status, -1); - ao2_ref(update, -1); } /*! @@ -981,6 +1026,9 @@ static int rtt_start_to_str(const void *obj, const intptr_t *args, char **buf) return 0; } +static char status_value_unknown[2]; +static char status_value_created[2]; + int ast_sip_initialize_sorcery_qualify(void) { struct ast_sorcery *sorcery = ast_sip_get_sorcery(); @@ -994,10 +1042,12 @@ int ast_sip_initialize_sorcery_qualify(void) return -1; } + snprintf(status_value_unknown, sizeof(status_value_unknown), "%u", UNKNOWN); ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status", - "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status)); + status_value_unknown, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status)); + snprintf(status_value_created, sizeof(status_value_created), "%u", CREATED); ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status", - "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status)); + status_value_created, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status)); ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start", "0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0); ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c index e494daf26..96367cf61 100644 --- a/res/res_pjsip_notify.c +++ b/res/res_pjsip_notify.c @@ -840,7 +840,7 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a e->usage = "Usage: pjsip send notify <type> {endpoint|uri} <peer> [<peer>...]\n" " Send a NOTIFY request to an endpoint\n" - " Message types are defined in sip_notify.conf\n"; + " Message types are defined in pjsip_notify.conf\n"; return NULL; case CLI_GENERATE: if (a->argc > 4 && (!strcasecmp(a->argv[4], "uri"))) { diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c index 1c772b4ad..4ba57481b 100644 --- a/res/res_pjsip_outbound_registration.c +++ b/res/res_pjsip_outbound_registration.c @@ -35,6 +35,7 @@ #include "asterisk/stasis_system.h" #include "asterisk/threadstorage.h" #include "asterisk/threadpool.h" +#include "asterisk/statsd.h" #include "res_pjsip/include/res_pjsip_private.h" /*** DOCUMENTATION @@ -612,6 +613,19 @@ static void schedule_registration(struct sip_outbound_registration_client_state } } +static void update_client_state_status(struct sip_outbound_registration_client_state *client_state, enum sip_outbound_registration_status status) +{ + if (client_state->status == status) { + return; + } + + ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0, + sip_outbound_registration_status_str(client_state->status)); + ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0, + sip_outbound_registration_status_str(status)); + client_state->status = status; +} + /*! \brief Callback function for unregistering (potentially) and destroying state */ static int handle_client_state_destruction(void *data) { @@ -645,7 +659,7 @@ static int handle_client_state_destruction(void *data) (int) info.server_uri.slen, info.server_uri.ptr, (int) info.client_uri.slen, info.client_uri.ptr); - client_state->status = SIP_REGISTRATION_STOPPING; + update_client_state_status(client_state, SIP_REGISTRATION_STOPPING); client_state->destroy = 1; if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS && registration_client_send(client_state, tdata) == PJ_SUCCESS) { @@ -664,7 +678,7 @@ static int handle_client_state_destruction(void *data) client_state->client = NULL; } - client_state->status = SIP_REGISTRATION_STOPPED; + update_client_state_status(client_state, SIP_REGISTRATION_STOPPED); ast_sip_auth_vector_destroy(&client_state->outbound_auths); ao2_ref(client_state, -1); @@ -726,7 +740,7 @@ static int sip_outbound_registration_is_temporal(unsigned int code, static void schedule_retry(struct registration_response *response, unsigned int interval, const char *server_uri, const char *client_uri) { - response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY; + update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); schedule_registration(response->client_state, interval); if (response->rdata) { @@ -796,14 +810,21 @@ static int handle_registration_response(void *data) if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) { /* Check if this is in regards to registering or unregistering */ if (response->expiration) { + int next_registration_round; + /* If the registration went fine simply reschedule registration for the future */ ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri); - response->client_state->status = SIP_REGISTRATION_REGISTERED; + update_client_state_status(response->client_state, SIP_REGISTRATION_REGISTERED); response->client_state->retries = 0; - schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME); + next_registration_round = response->expiration - REREGISTER_BUFFER_TIME; + if (next_registration_round < 0) { + /* Re-register immediately. */ + next_registration_round = 0; + } + schedule_registration(response->client_state, next_registration_round); } else { ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri); - response->client_state->status = SIP_REGISTRATION_UNREGISTERED; + update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED); } } else if (response->client_state->destroy) { /* We need to deal with the pending destruction instead. */ @@ -814,7 +835,7 @@ static int handle_registration_response(void *data) && sip_outbound_registration_is_temporal(response->code, response->client_state)) { if (response->client_state->retries == response->client_state->max_retries) { /* If we received enough temporal responses to exceed our maximum give up permanently */ - response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT; + update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT); ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n", server_uri, client_uri); } else { @@ -827,7 +848,7 @@ static int handle_registration_response(void *data) && response->client_state->forbidden_retry_interval && response->client_state->retries < response->client_state->max_retries) { /* A forbidden response retry interval is configured and there are retries remaining */ - response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY; + update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); response->client_state->retries++; schedule_registration(response->client_state, response->client_state->forbidden_retry_interval); ast_log(LOG_WARNING, "403 Forbidden fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n", @@ -835,14 +856,14 @@ static int handle_registration_response(void *data) } else if (response->client_state->fatal_retry_interval && response->client_state->retries < response->client_state->max_retries) { /* Some kind of fatal failure response received, so retry according to configured interval */ - response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY; + update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_TEMPORARY); response->client_state->retries++; schedule_registration(response->client_state, response->client_state->fatal_retry_interval); ast_log(LOG_WARNING, "'%d' fatal response received from '%s' on registration attempt to '%s', retrying in '%u' seconds\n", response->code, server_uri, client_uri, response->client_state->fatal_retry_interval); } else { /* Finally if there's no hope of registering give up */ - response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT; + update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT); if (response->rdata) { ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n", response->code, server_uri, client_uri); @@ -927,7 +948,6 @@ static void sip_outbound_registration_state_destroy(void *obj) ast_debug(3, "Destroying registration state for registration to server '%s' from client '%s'\n", state->registration->server_uri, state->registration->client_uri); - ao2_cleanup(state->registration); if (!state->client_state) { @@ -946,6 +966,10 @@ static void sip_outbound_registration_client_state_destroy(void *obj) { struct sip_outbound_registration_client_state *client_state = obj; + ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0); + ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0, + sip_outbound_registration_status_str(client_state->status)); + ast_taskprocessor_unreference(client_state->serializer); } @@ -974,6 +998,10 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a state->client_state->timer.user_data = state->client_state; state->client_state->timer.cb = sip_outbound_registration_timer_cb; + ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0); + ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0, + sip_outbound_registration_status_str(state->client_state->status)); + state->registration = ao2_bump(registration); return state; } @@ -2006,6 +2034,12 @@ static int load_module(void) ast_manager_register_xml("PJSIPRegister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_register); ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations); + /* Clear any previous statsd gauges in case we weren't shutdown cleanly */ + ast_statsd_log("PJSIP.registrations.count", AST_STATSD_GAUGE, 0); + ast_statsd_log("PJSIP.registrations.state.Registered", AST_STATSD_GAUGE, 0); + ast_statsd_log("PJSIP.registrations.state.Unregistered", AST_STATSD_GAUGE, 0); + ast_statsd_log("PJSIP.registrations.state.Rejected", AST_STATSD_GAUGE, 0); + /* Load configuration objects */ ast_sorcery_load_object(ast_sip_get_sorcery(), "registration"); diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c index c867f7f21..2c544bb70 100644 --- a/res/res_pjsip_t38.c +++ b/res/res_pjsip_t38.c @@ -161,6 +161,9 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses parameters.max_ifp = ast_udptl_get_far_max_ifp(session_media->udptl); parameters.request_response = AST_T38_REQUEST_NEGOTIATE; ast_udptl_set_tag(session_media->udptl, "%s", ast_channel_name(session->channel)); + + /* Inform the bridge the channel is in that it needs to be reconfigured */ + ast_channel_set_unbridged(session->channel, 1); break; case T38_ENABLED: parameters = state->their_parms; @@ -177,7 +180,8 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses } break; case T38_LOCAL_REINVITE: - /* wait until we get a peer response before responding to local reinvite */ + /* Inform the bridge the channel is in that it needs to be reconfigured */ + ast_channel_set_unbridged(session->channel, 1); break; case T38_MAX_ENUM: /* Well, that shouldn't happen */ @@ -264,6 +268,7 @@ static int t38_initialize_session(struct ast_sip_session *session, struct ast_si ast_udptl_set_error_correction_scheme(session_media->udptl, session->endpoint->media.t38.error_correction); ast_udptl_setnat(session_media->udptl, session->endpoint->media.t38.nat); ast_udptl_set_far_max_datagram(session_media->udptl, session->endpoint->media.t38.maxdatagram); + ast_debug(3, "UDPTL initialized on session for %s\n", ast_channel_name(session->channel)); return 0; } @@ -462,6 +467,11 @@ static void t38_masq(void *data, int framehook_id, ast_framehook_detach(new_chan, framehook_id); } +static int t38_consume(void *data, enum ast_frame_type type) +{ + return 0; +} + static const struct ast_datastore_info t38_framehook_datastore = { .type = "T38 framehook", }; @@ -474,6 +484,7 @@ static void t38_attach_framehook(struct ast_sip_session *session) static struct ast_framehook_interface hook = { .version = AST_FRAMEHOOK_INTERFACE_VERSION, .event_cb = t38_framehook, + .consume_cb = t38_consume, .chan_fixup_cb = t38_masq, .chan_breakdown_cb = t38_masq, }; @@ -559,6 +570,41 @@ static struct ast_sip_session_supplement t38_supplement = { .outgoing_request = t38_outgoing_invite_request, }; +static int t38_incoming_bye_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) +{ + struct ast_datastore *datastore; + struct ast_sip_session_media *session_media; + + if (!session->channel) { + return 0; + } + + datastore = ast_sip_session_get_datastore(session, "t38"); + if (!datastore) { + return 0; + } + + session_media = ao2_find(session->media, "image", OBJ_KEY); + if (!session_media) { + ao2_ref(datastore, -1); + return 0; + } + + t38_change_state(session, session_media, datastore->data, T38_REJECTED); + + ao2_ref(datastore, -1); + ao2_ref(session_media, -1); + + return 0; +} + +/*! \brief Supplement for handling a remote termination of T.38 state */ +static struct ast_sip_session_supplement t38_bye_supplement = { + .method = "BYE", + .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, + .incoming_request = t38_incoming_bye_request, +}; + /*! \brief Parse a T.38 image stream and store the attribute information */ static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_media *stream) @@ -630,10 +676,12 @@ static enum ast_sip_session_sdp_stream_defer defer_incoming_sdp_stream( struct t38_state *state; if (!session->endpoint->media.t38.enabled) { + ast_debug(3, "Not deferring incoming SDP stream: T.38 not enabled on %s\n", ast_channel_name(session->channel)); return AST_SIP_SESSION_SDP_DEFER_NOT_HANDLED; } if (t38_initialize_session(session, session_media)) { + ast_debug(3, "Not deferring incoming SDP stream: Failed to initialize UDPTL on %s\n", ast_channel_name(session->channel)); return AST_SIP_SESSION_SDP_DEFER_ERROR; } @@ -646,6 +694,7 @@ static enum ast_sip_session_sdp_stream_defer defer_incoming_sdp_stream( /* If they are initiating the re-invite we need to defer responding until later */ if (session->t38state == T38_DISABLED) { t38_change_state(session, session_media, state, T38_PEER_REINVITE); + ast_debug(3, "Deferring incoming SDP stream on %s for peer re-invite\n", ast_channel_name(session->channel)); return AST_SIP_SESSION_SDP_DEFER_NEEDED; } @@ -661,6 +710,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); if (!session->endpoint->media.t38.enabled) { + ast_debug(3, "Declining; T.38 not enabled on session\n"); return -1; } @@ -669,6 +719,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct } if ((session->t38state == T38_REJECTED) || (session->t38state == T38_DISABLED)) { + ast_debug(3, "Declining; T.38 state is rejected or declined\n"); t38_change_state(session, session_media, state, T38_DISABLED); return -1; } @@ -678,6 +729,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_INET) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ + ast_debug(3, "Declining; provided host is invalid\n"); return -1; } @@ -685,6 +737,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.t38.ipv6) || (ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.t38.ipv6)) { /* The address does not match configured */ + ast_debug(3, "Declining, provided host does not match configured address family\n"); return -1; } @@ -713,13 +766,16 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as pj_str_t stmp; if (!session->endpoint->media.t38.enabled) { + ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n"); return 1; } else if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) && (session->t38state != T38_ENABLED)) { + ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n"); return 1; } else if (!(state = t38_state_get_or_alloc(session))) { return -1; } else if (t38_initialize_session(session, session_media)) { + ast_debug(3, "Not creating outgoing SDP stream: Failed to initialize T.38 session\n"); return -1; } @@ -738,6 +794,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } if (ast_strlen_zero(hostip)) { + ast_debug(3, "Not creating outgoing SDP stream: no known host IP\n"); return -1; } @@ -805,6 +862,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a struct t38_state *state; if (!session_media->udptl) { + ast_debug(3, "Not applying negotiated SDP stream: no UDTPL session\n"); return 0; } @@ -817,6 +875,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ + ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); return -1; } @@ -875,6 +934,7 @@ static int unload_module(void) { ast_sip_session_unregister_sdp_handler(&image_sdp_handler, "image"); ast_sip_session_unregister_supplement(&t38_supplement); + ast_sip_session_unregister_supplement(&t38_bye_supplement); return 0; } @@ -901,6 +961,11 @@ static int load_module(void) goto end; } + if (ast_sip_session_register_supplement(&t38_bye_supplement)) { + ast_log(LOG_ERROR, "Unable to register T.38 BYE session supplement\n"); + goto end; + } + if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) { ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n"); goto end; diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c index 0ce0e33a1..58aaada41 100644 --- a/res/res_sorcery_memory_cache.c +++ b/res/res_sorcery_memory_cache.c @@ -848,13 +848,16 @@ static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, if (cached->stale_update_sched_id == -1) { struct stale_update_task_data *task_data; - task_data = stale_update_task_data_alloc((struct ast_sorcery *)sorcery, cache, - type, cached->object); + task_data = stale_update_task_data_alloc((struct ast_sorcery *) sorcery, + cache, type, cached->object); if (task_data) { ast_debug(1, "Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n", type, id); - cached->stale_update_sched_id = ast_sched_add(sched, 1, stale_item_update, task_data); - } else { + cached->stale_update_sched_id = ast_sched_add(sched, 1, + stale_item_update, task_data); + } + if (cached->stale_update_sched_id < 0) { + ao2_cleanup(task_data); ast_log(LOG_ERROR, "Unable to update stale cached object type '%s', ID '%s'.\n", type, id); } diff --git a/res/res_sorcery_realtime.c b/res/res_sorcery_realtime.c index fc22170a5..3412b92cc 100644 --- a/res/res_sorcery_realtime.c +++ b/res/res_sorcery_realtime.c @@ -142,7 +142,7 @@ static struct ast_variable *sorcery_realtime_filter_objectset(struct ast_variabl } } - ao2_ref(object_type, -1); + ao2_cleanup(object_type); return objectset; } diff --git a/res/res_statsd.c b/res/res_statsd.c index 2cbd7f09c..4eb526071 100644 --- a/res/res_statsd.c +++ b/res/res_statsd.c @@ -97,11 +97,11 @@ static void conf_server(const struct conf *cfg, struct ast_sockaddr *addr) } } -void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name, - const char *metric_type, intmax_t value, double sample_rate) +void AST_OPTIONAL_API_NAME(ast_statsd_log_string)(const char *metric_name, + const char *metric_type, const char *value, double sample_rate) { - RAII_VAR(struct conf *, cfg, NULL, ao2_cleanup); - RAII_VAR(struct ast_str *, msg, NULL, ast_free); + struct conf *cfg; + struct ast_str *msg; size_t len; struct ast_sockaddr statsd_server; @@ -109,9 +109,6 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name, return; } - cfg = ao2_global_obj_ref(confs); - conf_server(cfg, &statsd_server); - /* Rates <= 0.0 never get logged. * Rates >= 1.0 always get logged. * All others leave it to chance. @@ -122,9 +119,11 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name, } cfg = ao2_global_obj_ref(confs); + conf_server(cfg, &statsd_server); msg = ast_str_create(40); if (!msg) { + ao2_cleanup(cfg); return; } @@ -132,7 +131,7 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name, ast_str_append(&msg, 0, "%s.", cfg->global->prefix); } - ast_str_append(&msg, 0, "%s:%jd|%s", metric_name, value, metric_type); + ast_str_append(&msg, 0, "%s:%s|%s", metric_name, value, metric_type); if (sample_rate < 1.0) { ast_str_append(&msg, 0, "|@%.2f", sample_rate); @@ -144,20 +143,87 @@ void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name, len = ast_str_strlen(msg); - ast_debug(6, "send: %s\n", ast_str_buffer(msg)); + ast_debug(6, "Sending statistic %s to StatsD server\n", ast_str_buffer(msg)); ast_sendto(socket_fd, ast_str_buffer(msg), len, 0, &statsd_server); + + ao2_cleanup(cfg); + ast_free(msg); +} + +void AST_OPTIONAL_API_NAME(ast_statsd_log_full)(const char *metric_name, + const char *metric_type, intmax_t value, double sample_rate) +{ + char char_value[30]; + snprintf(char_value, sizeof(char_value), "%jd", value); + + ast_statsd_log_string(metric_name, metric_type, char_value, sample_rate); + +} + +AST_THREADSTORAGE(statsd_buf); + +void AST_OPTIONAL_API_NAME(ast_statsd_log_string_va)(const char *metric_name, + const char *metric_type, const char *value, double sample_rate, ...) +{ + struct ast_str *buf; + va_list ap; + int res; + + buf = ast_str_thread_get(&statsd_buf, 128); + if (!buf) { + return; + } + + va_start(ap, sample_rate); + res = ast_str_set_va(&buf, 0, metric_name, ap); + va_end(ap); + + if (res == AST_DYNSTR_BUILD_FAILED) { + return; + } + + ast_statsd_log_string(ast_str_buffer(buf), metric_type, value, sample_rate); +} + +void AST_OPTIONAL_API_NAME(ast_statsd_log_full_va)(const char *metric_name, + const char *metric_type, intmax_t value, double sample_rate, ...) +{ + struct ast_str *buf; + va_list ap; + int res; + + buf = ast_str_thread_get(&statsd_buf, 128); + if (!buf) { + return; + } + + va_start(ap, sample_rate); + res = ast_str_set_va(&buf, 0, metric_name, ap); + va_end(ap); + + if (res == AST_DYNSTR_BUILD_FAILED) { + return; + } + + ast_statsd_log_full(ast_str_buffer(buf), metric_type, value, sample_rate); } void AST_OPTIONAL_API_NAME(ast_statsd_log)(const char *metric_name, const char *metric_type, intmax_t value) { - ast_statsd_log_full(metric_name, metric_type, value, 1.0); + char char_value[30]; + snprintf(char_value, sizeof(char_value), "%jd", value); + + ast_statsd_log_string(metric_name, metric_type, char_value, 1.0); } void AST_OPTIONAL_API_NAME(ast_statsd_log_sample)(const char *metric_name, intmax_t value, double sample_rate) { - ast_statsd_log_full(metric_name, AST_STATSD_COUNTER, value, + char char_value[30]; + snprintf(char_value, sizeof(char_value), "%jd", value); + + ast_statsd_log_string(metric_name, AST_STATSD_COUNTER, char_value, sample_rate); } diff --git a/res/res_statsd.exports.in b/res/res_statsd.exports.in index 6f02b25d6..d4a79c18f 100644 --- a/res/res_statsd.exports.in +++ b/res/res_statsd.exports.in @@ -3,6 +3,7 @@ LINKER_SYMBOL_PREFIX*ast_statsd_log; LINKER_SYMBOL_PREFIX*ast_statsd_log_full; LINKER_SYMBOL_PREFIX*ast_statsd_log_sample; + LINKER_SYMBOL_PREFIX*ast_statsd_log_string; local: *; }; |