summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES45
-rw-r--r--bridges/bridge_t38.c261
-rw-r--r--channels/chan_iax2.c15
-rw-r--r--channels/chan_pjsip.c7
-rw-r--r--channels/chan_sip.c12
-rw-r--r--channels/chan_skinny.c41
-rw-r--r--channels/sip/config_parser.c9
-rw-r--r--codecs/codec_resample.c8
-rw-r--r--include/asterisk/bridge.h12
-rw-r--r--include/asterisk/res_pjsip.h6
-rw-r--r--include/asterisk/statsd.h71
-rw-r--r--include/asterisk/term.h4
-rw-r--r--include/asterisk/translate.h8
-rw-r--r--main/asterisk.c122
-rw-r--r--main/audiohook.c15
-rw-r--r--main/bridge.c10
-rw-r--r--main/bridge_channel.c4
-rw-r--r--main/channel.c1
-rw-r--r--main/cli.c6
-rw-r--r--main/sched.c11
-rw-r--r--main/term.c27
-rw-r--r--main/translate.c31
-rw-r--r--pbx/pbx_dundi.c1
-rw-r--r--res/res_agi.c6
-rw-r--r--res/res_endpoint_stats.c157
-rw-r--r--res/res_pjsip/location.c50
-rw-r--r--res/res_pjsip/pjsip_configuration.c148
-rw-r--r--res/res_pjsip/pjsip_options.c90
-rw-r--r--res/res_pjsip_notify.c2
-rw-r--r--res/res_pjsip_outbound_registration.c56
-rw-r--r--res/res_pjsip_t38.c67
-rw-r--r--res/res_sorcery_memory_cache.c11
-rw-r--r--res/res_sorcery_realtime.c2
-rw-r--r--res/res_statsd.c88
-rw-r--r--res/res_statsd.exports.in1
35 files changed, 1131 insertions, 274 deletions
diff --git a/CHANGES b/CHANGES
index 4a55a06ff..7fbc7a622 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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 = &parameters,
+ .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:
*;
};