summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES56
-rw-r--r--addons/ooh323c/src/Makefile.in2
-rw-r--r--apps/app_dial.c3
-rw-r--r--apps/app_queue.c9
-rw-r--r--apps/app_voicemail.c3
-rw-r--r--autoconf/ast_ext_lib.m42
-rw-r--r--autoconf/ast_prog_ld.m42
-rw-r--r--channels/chan_multicast_rtp.c7
-rw-r--r--channels/chan_pjsip.c9
-rw-r--r--channels/chan_rtp.c415
-rw-r--r--channels/chan_sip.c106
-rw-r--r--channels/chan_unistim.c4
-rw-r--r--configs/basic-pbx/modules.conf4
-rw-r--r--configs/samples/hep.conf.sample2
-rw-r--r--configs/samples/res_odbc.conf.sample5
-rwxr-xr-xconfigure174
-rw-r--r--configure.ac7
-rw-r--r--contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py2
-rw-r--r--funcs/func_env.c4
-rw-r--r--include/asterisk/autoconfig.h.in6
-rw-r--r--include/asterisk/compat.h2
-rw-r--r--include/asterisk/config_options.h22
-rw-r--r--include/asterisk/multicast_rtp.h58
-rw-r--r--include/asterisk/poll-compat.h2
-rw-r--r--include/asterisk/res_hep.h8
-rw-r--r--include/asterisk/res_pjsip.h11
-rw-r--r--include/asterisk/res_pjsip_session.h3
-rw-r--r--include/asterisk/sorcery.h14
-rw-r--r--include/asterisk/stasis.h14
-rw-r--r--include/asterisk/stasis_app.h9
-rw-r--r--include/asterisk/stasis_message_router.h14
-rw-r--r--include/asterisk/taskprocessor.h23
-rw-r--r--main/ast_expr2.c10
-rw-r--r--main/ast_expr2.y10
-rw-r--r--main/asterisk.c16
-rw-r--r--main/astfd.c2
-rw-r--r--main/bridge_channel.c3
-rw-r--r--main/cdr.c3
-rw-r--r--main/cel.c111
-rw-r--r--main/channel.c3
-rw-r--r--main/codec.c70
-rw-r--r--main/codec_builtin.c11
-rw-r--r--main/config_options.c5
-rw-r--r--main/dial.c14
-rw-r--r--main/editline/Makefile.in2
-rw-r--r--main/http.c2
-rw-r--r--main/manager.c3
-rw-r--r--main/say.c20
-rw-r--r--main/sorcery.c21
-rw-r--r--main/stasis.c12
-rw-r--r--main/stasis_message_router.c12
-rw-r--r--main/stdtime/localtime.c11
-rw-r--r--main/taskprocessor.c172
-rw-r--r--res/ael/pval.c70
-rw-r--r--res/ari/resource_bridges.c9
-rw-r--r--res/ari/resource_channels.c47
-rw-r--r--res/ari/resource_channels.h4
-rw-r--r--res/res_ari_channels.c14
-rw-r--r--res/res_fax.c7
-rw-r--r--res/res_format_attr_siren14.c94
-rw-r--r--res/res_format_attr_siren7.c94
-rw-r--r--res/res_hep.c12
-rw-r--r--res/res_hep.exports.in1
-rw-r--r--res/res_hep_pjsip.c5
-rw-r--r--res/res_hep_rtcp.c4
-rw-r--r--res/res_odbc.c232
-rw-r--r--res/res_phoneprov.c5
-rw-r--r--res/res_pjproject.c7
-rw-r--r--res/res_pjsip.c14
-rw-r--r--res/res_pjsip/location.c5
-rw-r--r--res/res_pjsip/pjsip_distributor.c207
-rw-r--r--res/res_pjsip/pjsip_options.c3
-rw-r--r--res/res_pjsip_messaging.c18
-rw-r--r--res/res_pjsip_pubsub.c522
-rw-r--r--res/res_pjsip_registrar.c339
-rw-r--r--res/res_pjsip_sdp_rtp.c2
-rw-r--r--res/res_pjsip_session.c109
-rw-r--r--res/res_pjsip_transport_management.c100
-rw-r--r--res/res_rtp_asterisk.c17
-rw-r--r--res/res_rtp_multicast.c189
-rw-r--r--res/res_rtp_multicast.exports.in6
-rw-r--r--res/res_srtp.c8
-rw-r--r--res/res_stasis.c16
-rw-r--r--rest-api/api-docs/channels.json17
-rw-r--r--tests/test_cel.c2
-rw-r--r--tests/test_netsock2.c6
-rw-r--r--tests/test_res_pjsip_scheduler.c1
-rw-r--r--third-party/pjproject/configure.m41
-rw-r--r--third-party/pjproject/patches/0001-evsub-Add-APIs-to-add-decrement-an-event-subscriptio.patch73
-rw-r--r--third-party/pjproject/patches/config_site.h4
90 files changed, 2880 insertions, 899 deletions
diff --git a/CHANGES b/CHANGES
index 890b3afb3..55ec3292a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,41 @@
--- Functionality changes from Asterisk 13.9.0 to Asterisk 13.10.0 -----------
------------------------------------------------------------------------------
+Core
+------------------
+ * A channel variable FORWARDERNAME is now set which indicates which channel
+ was responsible for a forwarding requests received on dial attempt.
+
+chan_multicast_rtp
+------------------
+ * Deprecated in favor of chan_rtp which is basically chan_multicast_rtp
+ renamed to chan_rtp with UnicastRTP channels added and some internal code
+ improvements.
+
+chan_rtp
+------------------
+ * The format for dialing a unicast RTP channel is:
+ UnicastRTP/<destination-addr>[/[<options>]]
+ Where <destination-addr> is something like '127.0.0.1:5060'.
+ Where <options> are in standard Asterisk flag options format:
+ c(<codec>) - Specify which codec/format to use such as 'ulaw'.
+ e(<engine>) - Specify which RTP engine to use such as 'asterisk'.
+
+ * More options are available over what chan_multicast_rtp supports.
+ The format for dialing a multicast RTP channel is:
+ MulticastRTP/<type>/<destination-addr>[/[<control-addr>][/[<options>]]]
+ Where <type> can be either 'basic' or 'linksys'.
+ Where <destination-addr> is something like '224.0.0.3:5060'.
+ Where <control-addr> is something like '127.0.0.1:5060'.
+ Where <options> are in standard Asterisk flag options format:
+ c(<codec>) - Specify which codec/format to use such as 'ulaw'.
+ i(<address>) - Specify the interface address from which multicast RTP
+ is sent.
+ l(<enable>) - Set whether packets are looped back to the sender. The
+ enable value can be 0 to set looping to off and non-zero to set
+ looping on.
+ t(<ttl>) - Set the time-to-live (TTL) value for multicast packets.
+
func_odbc
------------------
* Added new global option "single_db_connection".
@@ -47,6 +82,9 @@ res_pjsip
into the "reg_server" field in the ps_contacts table to facilitate
multi-server setups.
+ * When starting Asterisk, received traffic will now be ignored until Asterisk
+ has loaded all modules and is fully booted.
+
res_hep
------------------
* Added a new option, 'uuid_type', that sets the preferred source of the Homer
@@ -63,6 +101,12 @@ res_pjsip_info_empty
Some SBCs will terminate a call if their empty INFO packets are not responded
to within a predefined time.
+res_odbc
+------------------
+ * A new option has been added, 'max_connections', which sets the maximum number
+ of concurrent connections to the database. This option defaults to 1 which
+ returns the behavior to that of Asterisk 13.7 and prior.
+
app_confbridge
------------------
* Added a bridge profile option called regcontext that allows you to
@@ -71,6 +115,18 @@ app_confbridge
server installations via alternate means (DUNDI for example). By default
this feature is not used.
+Codecs
+------------------
+ * Added the associated format name to 'core show codecs'.
+
+res_ari_channels
+------------------
+ * Added 'formats' to channel create/originate to allow setting the allowed
+ formats for a channel when no originator channel is available. Especially
+ useful for Local channel creation where no other format information is
+ available. 'core show codecs' can now be used to look up suitable format
+ names.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.8.0 to Asterisk 13.9.0 ------------
------------------------------------------------------------------------------
diff --git a/addons/ooh323c/src/Makefile.in b/addons/ooh323c/src/Makefile.in
index d3a96024b..15b14f7df 100644
--- a/addons/ooh323c/src/Makefile.in
+++ b/addons/ooh323c/src/Makefile.in
@@ -104,7 +104,7 @@ CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
LIBRARIES = $(noinst_LIBRARIES)
-libooh323c_a_AR = $(AR) cru
+libooh323c_a_AR = $(AR) cr
libooh323c_a_LIBADD =
am_libooh323c_a_OBJECTS = ooLogChan.$(OBJEXT) ooUtils.$(OBJEXT) \
ooGkClient.$(OBJEXT) context.$(OBJEXT) ooDateTime.$(OBJEXT) \
diff --git a/apps/app_dial.c b/apps/app_dial.c
index bc4f8a574..c05aecaf9 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -834,6 +834,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
struct ast_party_id *forced_clid, struct ast_party_id *stored_clid)
{
char tmpchan[256];
+ char forwarder[AST_CHANNEL_NAME];
struct ast_channel *original = o->chan;
struct ast_channel *c = o->chan; /* the winner */
struct ast_channel *in = num->chan; /* the input channel */
@@ -842,6 +843,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
int cause;
struct ast_party_caller caller;
+ ast_copy_string(forwarder, ast_channel_name(c), sizeof(forwarder));
ast_copy_string(tmpchan, ast_channel_call_forward(c), sizeof(tmpchan));
if ((stuff = strchr(tmpchan, '/'))) {
*stuff++ = '\0';
@@ -893,6 +895,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
ast_channel_lock_both(in, o->chan);
ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(in, o->chan);
+ pbx_builtin_setvar_helper(o->chan, "FORWARDERNAME", forwarder);
ast_max_forwards_decrement(o->chan);
ast_channel_unlock(in);
ast_channel_unlock(o->chan);
diff --git a/apps/app_queue.c b/apps/app_queue.c
index dbd83938d..3d22f9821 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -4827,16 +4827,22 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
continue;
} else if (!ast_strlen_zero(ast_channel_call_forward(o->chan))) {
struct ast_channel *original = o->chan;
+ char forwarder[AST_CHANNEL_NAME];
char tmpchan[256];
char *stuff;
char *tech;
ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
+ ast_copy_string(forwarder, ast_channel_name(o->chan), sizeof(forwarder));
if ((stuff = strchr(tmpchan, '/'))) {
*stuff++ = '\0';
tech = tmpchan;
} else {
- snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), ast_channel_context(o->chan));
+ const char *forward_context;
+ ast_channel_lock(o->chan);
+ forward_context = pbx_builtin_getvar_helper(o->chan, "FORWARD_CONTEXT");
+ snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), forward_context ? forward_context : ast_channel_context(o->chan));
+ ast_channel_unlock(o->chan);
stuff = tmpchan;
tech = "Local";
}
@@ -4868,6 +4874,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
ast_channel_lock_both(o->chan, in);
ast_channel_inherit_variables(in, o->chan);
ast_channel_datastore_inherit(in, o->chan);
+ pbx_builtin_setvar_helper(o->chan, "FORWARDERNAME", forwarder);
ast_max_forwards_decrement(o->chan);
if (o->pending_connected_update) {
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index a561729fb..e88d7069c 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -3263,7 +3263,8 @@ void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
{
struct ast_str *str;
- if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(MAX_OBJECT_FIELD))) {
+
+ if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
return;
}
diff --git a/autoconf/ast_ext_lib.m4 b/autoconf/ast_ext_lib.m4
index 8f35f4bad..2c73b40c5 100644
--- a/autoconf/ast_ext_lib.m4
+++ b/autoconf/ast_ext_lib.m4
@@ -11,7 +11,7 @@ AC_DEFUN([AST_EXT_LIB_SETUP],
$1_DESCRIP="$2"
$1_OPTION="$3"
PBX_$1=0
- AC_ARG_WITH([$3], AC_HELP_STRING([--with-$3=PATH],[use $2 files in PATH$4]),
+ AC_ARG_WITH([$3], AS_HELP_STRING([--with-$3=PATH],[use $2 files in PATH$4]),
[
case ${withval} in
n|no)
diff --git a/autoconf/ast_prog_ld.m4 b/autoconf/ast_prog_ld.m4
index 9177fedb3..b69c2c2ac 100644
--- a/autoconf/ast_prog_ld.m4
+++ b/autoconf/ast_prog_ld.m4
@@ -3,7 +3,7 @@
# find the pathname to the GNU or non-GNU linker
AC_DEFUN([AST_PROG_LD],
[AC_ARG_WITH([gnu-ld],
- [AC_HELP_STRING([--with-gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
[assume the C compiler uses GNU ld @<:@default=no@:>@])],
[test "$withval" = no || with_gnu_ld=yes],
[with_gnu_ld=no])
diff --git a/channels/chan_multicast_rtp.c b/channels/chan_multicast_rtp.c
index 267baabf1..c45dedf7f 100644
--- a/channels/chan_multicast_rtp.c
+++ b/channels/chan_multicast_rtp.c
@@ -28,7 +28,8 @@
*/
/*** MODULEINFO
- <support_level>core</support_level>
+ <support_level>deprecated</support_level>
+ <defaultenabled>no</defaultenabled>
***/
#include "asterisk.h"
@@ -215,8 +216,8 @@ static int unload_module(void)
return 0;
}
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Paging Channel",
- .support_level = AST_MODULE_SUPPORT_CORE,
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Paging Channel (use chan_rtp instead)",
+ .support_level = AST_MODULE_SUPPORT_DEPRECATED,
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_CHANNEL_DRIVER,
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index f081bd899..970fef496 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -269,6 +269,9 @@ static int direct_media_mitigate_glare(struct ast_sip_session *session)
return 0;
}
+/*!
+ * \pre chan is locked
+ */
static int check_for_rtp_changes(struct ast_channel *chan, struct ast_rtp_instance *rtp,
struct ast_sip_session_media *media, int rtcp_fd)
{
@@ -338,6 +341,11 @@ static int send_direct_media_request(void *data)
int changed = 0;
int res = 0;
+ /* The channel needs to be locked when checking for RTP changes.
+ * Otherwise, we could end up destroying an underlying RTCP structure
+ * at the same time that the channel thread is attempting to read RTCP
+ */
+ ast_channel_lock(cdata->chan);
if (pvt->media[SIP_MEDIA_AUDIO]) {
changed |= check_for_rtp_changes(
cdata->chan, cdata->rtp, pvt->media[SIP_MEDIA_AUDIO], 1);
@@ -346,6 +354,7 @@ static int send_direct_media_request(void *data)
changed |= check_for_rtp_changes(
cdata->chan, cdata->vrtp, pvt->media[SIP_MEDIA_VIDEO], 3);
}
+ ast_channel_unlock(cdata->chan);
if (direct_media_mitigate_glare(cdata->session)) {
ast_debug(4, "Disregarding setting RTP on %s: mitigating re-INVITE glare\n", ast_channel_name(cdata->chan));
diff --git a/channels/chan_rtp.c b/channels/chan_rtp.c
new file mode 100644
index 000000000..0fe66bd20
--- /dev/null
+++ b/channels/chan_rtp.c
@@ -0,0 +1,415 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009 - 2014, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ * Andreas 'MacBrody' Brodmann <andreas.brodmann@gmail.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
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Andreas 'MacBrody' Broadmann <andreas.brodmann@gmail.com>
+ *
+ * \brief RTP (Multicast and Unicast) Media Channel
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/acl.h"
+#include "asterisk/app.h"
+#include "asterisk/rtp_engine.h"
+#include "asterisk/causes.h"
+#include "asterisk/format_cache.h"
+#include "asterisk/multicast_rtp.h"
+
+/* Forward declarations */
+static struct ast_channel *multicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
+static struct ast_channel *unicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
+static int rtp_call(struct ast_channel *ast, const char *dest, int timeout);
+static int rtp_hangup(struct ast_channel *ast);
+static struct ast_frame *rtp_read(struct ast_channel *ast);
+static int rtp_write(struct ast_channel *ast, struct ast_frame *f);
+
+/* Multicast channel driver declaration */
+static struct ast_channel_tech multicast_rtp_tech = {
+ .type = "MulticastRTP",
+ .description = "Multicast RTP Paging Channel Driver",
+ .requester = multicast_rtp_request,
+ .call = rtp_call,
+ .hangup = rtp_hangup,
+ .read = rtp_read,
+ .write = rtp_write,
+};
+
+/* Unicast channel driver declaration */
+static struct ast_channel_tech unicast_rtp_tech = {
+ .type = "UnicastRTP",
+ .description = "Unicast RTP Media Channel Driver",
+ .requester = unicast_rtp_request,
+ .call = rtp_call,
+ .hangup = rtp_hangup,
+ .read = rtp_read,
+ .write = rtp_write,
+};
+
+/*! \brief Function called when we should read a frame from the channel */
+static struct ast_frame *rtp_read(struct ast_channel *ast)
+{
+ struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast);
+ int fdno = ast_channel_fdno(ast);
+
+ switch (fdno) {
+ case 0:
+ return ast_rtp_instance_read(instance, 0);
+ default:
+ return &ast_null_frame;
+ }
+}
+
+/*! \brief Function called when we should write a frame to the channel */
+static int rtp_write(struct ast_channel *ast, struct ast_frame *f)
+{
+ struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast);
+
+ return ast_rtp_instance_write(instance, f);
+}
+
+/*! \brief Function called when we should actually call the destination */
+static int rtp_call(struct ast_channel *ast, const char *dest, int timeout)
+{
+ struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast);
+
+ ast_queue_control(ast, AST_CONTROL_ANSWER);
+
+ return ast_rtp_instance_activate(instance);
+}
+
+/*! \brief Function called when we should hang the channel up */
+static int rtp_hangup(struct ast_channel *ast)
+{
+ struct ast_rtp_instance *instance = ast_channel_tech_pvt(ast);
+
+ ast_rtp_instance_destroy(instance);
+
+ ast_channel_tech_pvt_set(ast, NULL);
+
+ return 0;
+}
+
+/*! \brief Function called when we should prepare to call the multicast destination */
+static struct ast_channel *multicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
+{
+ char *parse;
+ struct ast_rtp_instance *instance;
+ struct ast_sockaddr control_address;
+ struct ast_sockaddr destination_address;
+ struct ast_channel *chan;
+ struct ast_format_cap *caps = NULL;
+ struct ast_format *fmt = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(type);
+ AST_APP_ARG(destination);
+ AST_APP_ARG(control);
+ AST_APP_ARG(options);
+ );
+ struct ast_multicast_rtp_options *mcast_options = NULL;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "A multicast type and destination must be given to the 'MulticastRTP' channel\n");
+ goto failure;
+ }
+ parse = ast_strdupa(data);
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (ast_strlen_zero(args.type)) {
+ ast_log(LOG_ERROR, "Type is required for the 'MulticastRTP' channel\n");
+ goto failure;
+ }
+
+ if (ast_strlen_zero(args.destination)) {
+ ast_log(LOG_ERROR, "Destination is required for the 'MulticastRTP' channel\n");
+ goto failure;
+ }
+ if (!ast_sockaddr_parse(&destination_address, args.destination, PARSE_PORT_REQUIRE)) {
+ ast_log(LOG_ERROR, "Destination address '%s' could not be parsed\n",
+ args.destination);
+ goto failure;
+ }
+
+ ast_sockaddr_setnull(&control_address);
+ if (!ast_strlen_zero(args.control)
+ && !ast_sockaddr_parse(&control_address, args.control, PARSE_PORT_REQUIRE)) {
+ ast_log(LOG_ERROR, "Control address '%s' could not be parsed\n", args.control);
+ goto failure;
+ }
+
+ mcast_options = ast_multicast_rtp_create_options(args.type, args.options);
+ if (!mcast_options) {
+ goto failure;
+ }
+
+ fmt = ast_multicast_rtp_options_get_format(mcast_options);
+ if (!fmt) {
+ fmt = ast_format_cap_get_format(cap, 0);
+ }
+ if (!fmt) {
+ ast_log(LOG_ERROR, "No codec available for sending RTP to '%s'\n",
+ args.destination);
+ goto failure;
+ }
+
+ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ if (!caps) {
+ goto failure;
+ }
+
+ instance = ast_rtp_instance_new("multicast", NULL, &control_address, mcast_options);
+ if (!instance) {
+ ast_log(LOG_ERROR,
+ "Could not create '%s' multicast RTP instance for sending media to '%s'\n",
+ args.type, args.destination);
+ goto failure;
+ }
+
+ chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids,
+ requestor, 0, "MulticastRTP/%p", instance);
+ if (!chan) {
+ ast_rtp_instance_destroy(instance);
+ goto failure;
+ }
+ ast_rtp_instance_set_channel_id(instance, ast_channel_uniqueid(chan));
+ ast_rtp_instance_set_remote_address(instance, &destination_address);
+
+ ast_channel_tech_set(chan, &multicast_rtp_tech);
+
+ ast_format_cap_append(caps, fmt, 0);
+ ast_channel_nativeformats_set(chan, caps);
+ ast_channel_set_writeformat(chan, fmt);
+ ast_channel_set_rawwriteformat(chan, fmt);
+ ast_channel_set_readformat(chan, fmt);
+ ast_channel_set_rawreadformat(chan, fmt);
+
+ ast_channel_tech_pvt_set(chan, instance);
+
+ ast_channel_unlock(chan);
+
+ ao2_ref(fmt, -1);
+ ao2_ref(caps, -1);
+ ast_multicast_rtp_free_options(mcast_options);
+
+ return chan;
+
+failure:
+ ao2_cleanup(fmt);
+ ao2_cleanup(caps);
+ ast_multicast_rtp_free_options(mcast_options);
+ *cause = AST_CAUSE_FAILURE;
+ return NULL;
+}
+
+enum {
+ OPT_RTP_CODEC = (1 << 0),
+ OPT_RTP_ENGINE = (1 << 1),
+};
+
+enum {
+ OPT_ARG_RTP_CODEC,
+ OPT_ARG_RTP_ENGINE,
+ /* note: this entry _MUST_ be the last one in the enum */
+ OPT_ARG_ARRAY_SIZE
+};
+
+AST_APP_OPTIONS(unicast_rtp_options, BEGIN_OPTIONS
+ /*! Set the codec to be used for unicast RTP */
+ AST_APP_OPTION_ARG('c', OPT_RTP_CODEC, OPT_ARG_RTP_CODEC),
+ /*! Set the RTP engine to use for unicast RTP */
+ AST_APP_OPTION_ARG('e', OPT_RTP_ENGINE, OPT_ARG_RTP_ENGINE),
+END_OPTIONS );
+
+/*! \brief Function called when we should prepare to call the unicast destination */
+static struct ast_channel *unicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
+{
+ char *parse;
+ struct ast_rtp_instance *instance;
+ struct ast_sockaddr address;
+ struct ast_sockaddr local_address;
+ struct ast_channel *chan;
+ struct ast_format_cap *caps = NULL;
+ struct ast_format *fmt = NULL;
+ const char *engine_name;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(destination);
+ AST_APP_ARG(options);
+ );
+ struct ast_flags opts = { 0, };
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Destination is required for the 'UnicastRTP' channel\n");
+ goto failure;
+ }
+ parse = ast_strdupa(data);
+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');
+
+ if (ast_strlen_zero(args.destination)) {
+ ast_log(LOG_ERROR, "Destination is required for the 'UnicastRTP' channel\n");
+ goto failure;
+ }
+ if (!ast_sockaddr_parse(&address, args.destination, PARSE_PORT_REQUIRE)) {
+ ast_log(LOG_ERROR, "Destination '%s' could not be parsed\n", args.destination);
+ goto failure;
+ }
+
+ if (!ast_strlen_zero(args.options)
+ && ast_app_parse_options(unicast_rtp_options, &opts, opt_args,
+ ast_strdupa(args.options))) {
+ ast_log(LOG_ERROR, "'UnicastRTP' channel options '%s' parse error\n",
+ args.options);
+ goto failure;
+ }
+
+ if (ast_test_flag(&opts, OPT_RTP_CODEC)
+ && !ast_strlen_zero(opt_args[OPT_ARG_RTP_CODEC])) {
+ fmt = ast_format_cache_get(opt_args[OPT_ARG_RTP_CODEC]);
+ if (!fmt) {
+ ast_log(LOG_ERROR, "Codec '%s' not found for sending RTP to '%s'\n",
+ opt_args[OPT_ARG_RTP_CODEC], args.destination);
+ goto failure;
+ }
+ } else {
+ fmt = ast_format_cap_get_format(cap, 0);
+ if (!fmt) {
+ ast_log(LOG_ERROR, "No codec available for sending RTP to '%s'\n",
+ args.destination);
+ goto failure;
+ }
+ }
+
+ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ if (!caps) {
+ goto failure;
+ }
+
+ engine_name = S_COR(ast_test_flag(&opts, OPT_RTP_ENGINE),
+ opt_args[OPT_ARG_RTP_ENGINE], NULL);
+
+ ast_ouraddrfor(&address, &local_address);
+ instance = ast_rtp_instance_new(engine_name, NULL, &local_address, NULL);
+ if (!instance) {
+ ast_log(LOG_ERROR,
+ "Could not create %s RTP instance for sending media to '%s'\n",
+ S_OR(engine_name, "default"), args.destination);
+ goto failure;
+ }
+
+ chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids,
+ requestor, 0, "UnicastRTP/%s-%p", args.destination, instance);
+ if (!chan) {
+ ast_rtp_instance_destroy(instance);
+ goto failure;
+ }
+ ast_rtp_instance_set_channel_id(instance, ast_channel_uniqueid(chan));
+ ast_rtp_instance_set_remote_address(instance, &address);
+ ast_channel_set_fd(chan, 0, ast_rtp_instance_fd(instance, 0));
+
+ ast_channel_tech_set(chan, &unicast_rtp_tech);
+
+ ast_format_cap_append(caps, fmt, 0);
+ ast_channel_nativeformats_set(chan, caps);
+ ast_channel_set_writeformat(chan, fmt);
+ ast_channel_set_rawwriteformat(chan, fmt);
+ ast_channel_set_readformat(chan, fmt);
+ ast_channel_set_rawreadformat(chan, fmt);
+
+ ast_channel_tech_pvt_set(chan, instance);
+
+ pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_ADDRESS",
+ ast_sockaddr_stringify_addr(&local_address));
+ ast_rtp_instance_get_local_address(instance, &local_address);
+ pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_PORT",
+ ast_sockaddr_stringify_port(&local_address));
+
+ ast_channel_unlock(chan);
+
+ ao2_ref(fmt, -1);
+ ao2_ref(caps, -1);
+
+ return chan;
+
+failure:
+ ao2_cleanup(fmt);
+ ao2_cleanup(caps);
+ *cause = AST_CAUSE_FAILURE;
+ return NULL;
+}
+
+/*! \brief Function called when our module is unloaded */
+static int unload_module(void)
+{
+ ast_channel_unregister(&multicast_rtp_tech);
+ ao2_cleanup(multicast_rtp_tech.capabilities);
+ multicast_rtp_tech.capabilities = NULL;
+
+ ast_channel_unregister(&unicast_rtp_tech);
+ ao2_cleanup(unicast_rtp_tech.capabilities);
+ unicast_rtp_tech.capabilities = NULL;
+
+ return 0;
+}
+
+/*! \brief Function called when our module is loaded */
+static int load_module(void)
+{
+ if (!(multicast_rtp_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ ast_format_cap_append_by_type(multicast_rtp_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN);
+ if (ast_channel_register(&multicast_rtp_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'MulticastRTP'\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (!(unicast_rtp_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ ast_format_cap_append_by_type(unicast_rtp_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN);
+ if (ast_channel_register(&unicast_rtp_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'UnicastRTP'\n");
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "RTP Media Channel",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,
+);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index f64845472..a5aa6c3d3 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -11332,25 +11332,7 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_
ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
}
- if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) {
- if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
- if (bit_rate != 32000) {
- ast_log(LOG_WARNING, "Got Siren7 offer at %u bps, but only 32000 bps supported; ignoring.\n", bit_rate);
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- } else {
- found = TRUE;
- }
- }
- } else if (ast_format_cmp(format, ast_format_siren14) == AST_FORMAT_CMP_EQUAL) {
- if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
- if (bit_rate != 48000) {
- ast_log(LOG_WARNING, "Got Siren14 offer at %u bps, but only 48000 bps supported; ignoring.\n", bit_rate);
- ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec);
- } else {
- found = TRUE;
- }
- }
- } else if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
+ if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) {
if (bit_rate != 64000) {
ast_log(LOG_WARNING, "Got G.719 offer at %u bps, but only 64000 bps supported; ignoring.\n", bit_rate);
@@ -13009,12 +12991,6 @@ static void add_codec_to_sdp(const struct sip_pvt *p,
} else if (ast_format_cmp(format, ast_format_g723) == AST_FORMAT_CMP_EQUAL) {
/* Indicate that we don't support VAD (G.723.1 annex A) */
ast_str_append(a_buf, 0, "a=fmtp:%d annexa=no\r\n", rtp_code);
- } else if (ast_format_cmp(format, ast_format_siren7) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we only expect 32Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=32000\r\n", rtp_code);
- } else if (ast_format_cmp(format, ast_format_siren14) == AST_FORMAT_CMP_EQUAL) {
- /* Indicate that we only expect 48Kbps */
- ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=48000\r\n", rtp_code);
} else if (ast_format_cmp(format, ast_format_g719) == AST_FORMAT_CMP_EQUAL) {
/* Indicate that we only expect 64Kbps */
ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code);
@@ -14154,9 +14130,10 @@ static void build_contact(struct sip_pvt *p, struct sip_request *req, int incomi
/*! \brief Initiate new SIP request to peer/user */
static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, const char * const explicit_uri)
{
- struct ast_str *invite = ast_str_alloca(256);
- char from[256];
- char to[256];
+#define SIPHEADER 256
+ struct ast_str *invite = ast_str_create(SIPHEADER);
+ struct ast_str *from = ast_str_create(SIPHEADER);
+ struct ast_str *to = ast_str_create(SIPHEADER);
char tmp_n[SIPBUFSIZE/2]; /* build a local copy of 'n' if needed */
char tmp_l[SIPBUFSIZE/2]; /* build a local copy of 'l' if needed */
const char *l = NULL; /* XXX what is this, exactly ? */
@@ -14258,34 +14235,40 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
if (!sip_standard_port(p->socket.type, ourport)) {
- ret = snprintf(from, sizeof(from), "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
+ ret = ast_str_set(&from, 0, "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
} else {
- ret = snprintf(from, sizeof(from), "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
+ ret = ast_str_set(&from, 0, "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
}
- if (ret < 0 || ret >= sizeof(from)) { /* a return value of size or more means that the output was truncated */
+ if (ret == AST_DYNSTR_BUILD_FAILED) {
/* We don't have an escape path from here... */
ast_log(LOG_ERROR, "The From header was truncated in call '%s'. This call setup will fail.\n", p->callid);
+ /* Make sure that the field contains something non-broken.
+ See https://issues.asterisk.org/jira/browse/ASTERISK-26069
+ */
+ ast_str_set(&from, 3, "<>");
+
}
/* If a caller id name was specified, prefix a display name, if there is enough room. */
if (cid_has_name || !cid_has_num) {
- size_t written = strlen(from);
- ssize_t left = sizeof(from) - written - 4; /* '"" \0' */
- if (left > 0) {
- size_t name_len;
- if (sip_cfg.pedanticsipchecking) {
- ast_escape_quoted(n, tmp_n, MIN(left + 1, sizeof(tmp_n)));
- n = tmp_n;
- }
- name_len = strlen(n);
- if (left < name_len) {
- name_len = left;
- }
- memmove(from + name_len + 3, from, written + 1);
- from[0] = '"';
- memcpy(from + 1, n, name_len);
- from[name_len + 1] = '"';
- from[name_len + 2] = ' ';
+ size_t written = ast_str_strlen(from);
+ size_t name_len;
+ if (sip_cfg.pedanticsipchecking) {
+ ast_escape_quoted(n, tmp_n, sizeof(tmp_n));
+ n = tmp_n;
+ }
+ name_len = strlen(n);
+ ret = ast_str_make_space(&from, name_len + written + 4);
+
+ if (ret == 0) {
+ /* needed again, as ast_str_make_space coud've changed the pointer */
+ char *from_buf = ast_str_buffer(from);
+
+ memmove(from_buf + name_len + 3, from_buf, written + 1);
+ from_buf[0] = '"';
+ memcpy(from_buf + 1, n, name_len);
+ from_buf[name_len + 1] = '"';
+ from_buf[name_len + 2] = ' ';
}
}
@@ -14328,24 +14311,28 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
/*! \todo Need to add back the VXML URL here at some point, possibly use build_string for all this junk */
if (!strchr(p->todnid, '@')) {
/* We have no domain in the dnid */
- ret = snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+ ret = ast_str_set(&to, 0, "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
} else {
- ret = snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
+ ret = ast_str_set(&to, 0, "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
}
} else {
if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
/* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
- ret = snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
+ ret = ast_str_set(&to, 0, "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "sip:" : ""), p->uri, p->theirtag);
} else if (p->options && p->options->vxml_url) {
/* If there is a VXML URL append it to the SIP URL */
- ret = snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
+ ret = ast_str_set(&to, 0, "<%s>;%s", p->uri, p->options->vxml_url);
} else {
- ret = snprintf(to, sizeof(to), "<%s>", p->uri);
+ ret = ast_str_set(&to, 0, "<%s>", p->uri);
}
}
- if (ret < 0 || ret >= sizeof(to)) { /* a return value of size or more means that the output was truncated */
+ if (ret == AST_DYNSTR_BUILD_FAILED) {
/* We don't have an escape path from here... */
ast_log(LOG_ERROR, "The To header was truncated in call '%s'. This call setup will fail.\n", p->callid);
+ /* Make sure that the field contains something non-broken.
+ See https://issues.asterisk.org/jira/browse/ASTERISK-26069
+ */
+ ast_str_set(&to, 3, "<>");
}
init_req(req, sipmethod, p->uri);
@@ -14360,8 +14347,8 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
*/
add_route(req, &p->route, 0);
- add_header(req, "From", from);
- add_header(req, "To", to);
+ add_header(req, "From", ast_str_buffer(from));
+ add_header(req, "To", ast_str_buffer(to));
ast_string_field_set(p, exten, l);
build_contact(p, req, 0);
add_header(req, "Contact", p->our_contact);
@@ -14370,6 +14357,10 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmetho
if (!ast_strlen_zero(global_useragent)) {
add_header(req, "User-Agent", global_useragent);
}
+
+ ast_free(from);
+ ast_free(to);
+ ast_free(invite);
}
/*! \brief Add "Diversion" header to outgoing message
@@ -15790,11 +15781,12 @@ static void start_register_timeout(struct sip_registry *reg)
static const char *sip_sanitized_host(const char *host)
{
- struct ast_sockaddr addr = { { 0, 0, }, };
+ struct ast_sockaddr addr;
/* peer/sip_pvt->tohost and sip_registry->hostname should never have a port
* in them, so we use PARSE_PORT_FORBID here. If this lookup fails, we return
* the original host which is most likely a host name and not an IP. */
+ memset(&addr, 0, sizeof(addr));
if (!ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID)) {
return host;
}
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index db4720d1a..37281bb48 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -567,8 +567,10 @@ static const unsigned char packet_send_stream_based_tone_off[] =
{ 0x16, 0x05, 0x1c, 0x00, 0x00 };
static const unsigned char packet_send_mute[] = { 0x16, 0x05, 0x04, 0x00, 0x00 };
+#ifdef NOT_USED
static const unsigned char packet_send_CloseAudioStreamRX[] = { 0x16, 0x05, 0x31, 0x00, 0xff };
static const unsigned char packet_send_CloseAudioStreamTX[] = { 0x16, 0x05, 0x31, 0xff, 0x00 };
+#endif
static const unsigned char packet_send_stream_based_tone_on[] =
{ 0x16, 0x06, 0x1b, 0x00, 0x00, 0x05 };
static const unsigned char packet_send_stream_based_tone_single_freq[] =
@@ -1021,7 +1023,7 @@ static int get_to_address(int fd, struct sockaddr_in *toAddr)
memcpy(&toAddr->sin_addr, &ip_msg.address, sizeof(struct in_addr));
return err;
#else
- memcpy(&toAddr, &public_ip, sizeof(&toAddr));
+ memcpy(toAddr, &public_ip, sizeof(*toAddr));
return 0;
#endif
}
diff --git a/configs/basic-pbx/modules.conf b/configs/basic-pbx/modules.conf
index 356153713..7b60125b7 100644
--- a/configs/basic-pbx/modules.conf
+++ b/configs/basic-pbx/modules.conf
@@ -60,11 +60,9 @@ load = func_strings.so
; Core/PBX
load = pbx_config.so
-load = pbx_functions.so
; Resources
-load = res_hep_pjsip.so
load = res_musiconhold.so
load = res_pjproject.so
load = res_pjsip_acl.so
@@ -78,7 +76,6 @@ load = res_pjsip_endpoint_identifier_ip.so
load = res_pjsip_endpoint_identifier_user.so
load = res_pjsip_exten_state.so
load = res_pjsip_header_funcs.so
-load = res_pjsip_log_forwarder.so
load = res_pjsip_logger.so
load = res_pjsip_messaging.so
load = res_pjsip_multihomed.so
@@ -91,7 +88,6 @@ load = res_pjsip_outbound_authenticator_digest.so
load = res_pjsip_outbound_publish.so
load = res_pjsip_outbound_registration.so
load = res_pjsip_path.so
-load = res_pjsip_phoneprov_provider.so
load = res_pjsip_pidf_body_generator.so
load = res_pjsip_pidf_digium_body_supplement.so
load = res_pjsip_pidf_eyebeam_body_supplement.so
diff --git a/configs/samples/hep.conf.sample b/configs/samples/hep.conf.sample
index 6e409d151..e1cd52ebb 100644
--- a/configs/samples/hep.conf.sample
+++ b/configs/samples/hep.conf.sample
@@ -4,7 +4,7 @@
; All settings are currently set in the general section.
[general]
-enabled = yes ; Enable/disable forwarding of packets to a
+enabled = no ; Enable/disable forwarding of packets to a
; HEP server. Default is "yes".
capture_address = 192.168.1.1:9061 ; The address of the HEP capture server.
capture_password = foo ; If specified, the authorization passsword
diff --git a/configs/samples/res_odbc.conf.sample b/configs/samples/res_odbc.conf.sample
index 66659ae42..a21e96d07 100644
--- a/configs/samples/res_odbc.conf.sample
+++ b/configs/samples/res_odbc.conf.sample
@@ -51,6 +51,11 @@ pre-connect => yes
; that we should attempt?
;limit => 5
;
+; The maximum number of connections to have open at any given time.
+; This defaults to 1 and it is highly recommended to only set this higher
+; if using a version of UnixODBC greater than 2.3.1.
+;max_connections => 20
+;
; When the channel is destroyed, should any uncommitted open transactions
; automatically be committed?
;forcecommit => no
diff --git a/configure b/configure
index ff34770a1..08093267b 100755
--- a/configure
+++ b/configure
@@ -915,6 +915,10 @@ PBX_POPT
POPT_DIR
POPT_INCLUDE
POPT_LIB
+PBX_PJSIP_EVSUB_GRP_LOCK
+PJSIP_EVSUB_GRP_LOCK_DIR
+PJSIP_EVSUB_GRP_LOCK_INCLUDE
+PJSIP_EVSUB_GRP_LOCK_LIB
PBX_PJSIP_TLS_TRANSPORT_PROTO
PJSIP_TLS_TRANSPORT_PROTO_DIR
PJSIP_TLS_TRANSPORT_PROTO_INCLUDE
@@ -10563,6 +10567,18 @@ PBX_PJSIP_TLS_TRANSPORT_PROTO=0
+PJSIP_EVSUB_GRP_LOCK_DESCRIP="PJSIP EVSUB Group Lock support"
+PJSIP_EVSUB_GRP_LOCK_OPTION=pjsip
+PJSIP_EVSUB_GRP_LOCK_DIR=${PJPROJECT_DIR}
+
+PBX_PJSIP_EVSUB_GRP_LOCK=0
+
+
+
+
+
+
+
POPT_DESCRIP="popt"
POPT_OPTION="popt"
@@ -13612,7 +13628,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13658,7 +13674,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13682,7 +13698,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13727,7 +13743,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -13751,7 +13767,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -15148,46 +15164,6 @@ _ACEOF
rm -f conftest*
-if ${ac_cv_func_setvbuf_reversed+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_cv_func_setvbuf_reversed=no
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
-$as_echo_n "checking return type of signal handlers... " >&6; }
-if ${ac_cv_type_signal+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <sys/types.h>
-#include <signal.h>
-
-int
-main ()
-{
-return *(signal (0, 0)) (0) == 1;
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_type_signal=int
-else
- ac_cv_type_signal=void
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
-$as_echo "$ac_cv_type_signal" >&6; }
-
-cat >>confdefs.h <<_ACEOF
-#define RETSIGTYPE $ac_cv_type_signal
-_ACEOF
-
-
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5
$as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; }
if ${ac_cv_func_lstat_dereferences_slashed_symlink+:} false; then :
@@ -24463,6 +24439,9 @@ rm -f conftest*
$as_echo "#define HAVE_PJSIP_TLS_TRANSPORT_PROTO 1" >>confdefs.h
+$as_echo "#define HAVE_PJSIP_EVSUB_GRP_LOCK 1" >>confdefs.h
+
+
else
if test "x${PBX_PJPROJECT}" != "x1" -a "${USE_PJPROJECT}" != "no"; then
@@ -25178,6 +25157,111 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
LIBS="${saved_libs}"
CPPFLAGS="${saved_cppflags}"
+
+
+if test "x${PBX_PJSIP_EVSUB_GRP_LOCK}" != "x1" -a "${USE_PJSIP_EVSUB_GRP_LOCK}" != "no"; then
+ pbxlibdir=""
+ # if --with-PJSIP_EVSUB_GRP_LOCK=DIR has been specified, use it.
+ if test "x${PJSIP_EVSUB_GRP_LOCK_DIR}" != "x"; then
+ if test -d ${PJSIP_EVSUB_GRP_LOCK_DIR}/lib; then
+ pbxlibdir="-L${PJSIP_EVSUB_GRP_LOCK_DIR}/lib"
+ else
+ pbxlibdir="-L${PJSIP_EVSUB_GRP_LOCK_DIR}"
+ fi
+ fi
+ pbxfuncname="pjsip_evsub_add_ref"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_PJSIP_EVSUB_GRP_LOCK_FOUND=yes
+ else
+ ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS"
+ as_ac_Lib=`$as_echo "ac_cv_lib_pjsip_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpjsip" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lpjsip... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+ AST_PJSIP_EVSUB_GRP_LOCK_FOUND=yes
+else
+ AST_PJSIP_EVSUB_GRP_LOCK_FOUND=no
+fi
+
+ CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+ fi
+
+ # now check for the header.
+ if test "${AST_PJSIP_EVSUB_GRP_LOCK_FOUND}" = "yes"; then
+ PJSIP_EVSUB_GRP_LOCK_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
+ # if --with-PJSIP_EVSUB_GRP_LOCK=DIR has been specified, use it.
+ if test "x${PJSIP_EVSUB_GRP_LOCK_DIR}" != "x"; then
+ PJSIP_EVSUB_GRP_LOCK_INCLUDE="-I${PJSIP_EVSUB_GRP_LOCK_DIR}/include"
+ fi
+ PJSIP_EVSUB_GRP_LOCK_INCLUDE="${PJSIP_EVSUB_GRP_LOCK_INCLUDE} $PJPROJECT_CFLAGS"
+ if test "xpjsip.h" = "x" ; then # no header, assume found
+ PJSIP_EVSUB_GRP_LOCK_HEADER_FOUND="1"
+ else # check for the header
+ ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${PJSIP_EVSUB_GRP_LOCK_INCLUDE}"
+ ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default"
+if test "x$ac_cv_header_pjsip_h" = xyes; then :
+ PJSIP_EVSUB_GRP_LOCK_HEADER_FOUND=1
+else
+ PJSIP_EVSUB_GRP_LOCK_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+ fi
+ if test "x${PJSIP_EVSUB_GRP_LOCK_HEADER_FOUND}" = "x0" ; then
+ PJSIP_EVSUB_GRP_LOCK_LIB=""
+ PJSIP_EVSUB_GRP_LOCK_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ PJSIP_EVSUB_GRP_LOCK_LIB=""
+ fi
+ PBX_PJSIP_EVSUB_GRP_LOCK=1
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PJSIP_EVSUB_GRP_LOCK 1
+_ACEOF
+
+ fi
+ fi
+fi
+
+
fi
fi
diff --git a/configure.ac b/configure.ac
index f2e42ba1f..9544060cd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -487,6 +487,7 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_GET_DEST_INFO], [pjsip_get_dest_info support],
AST_EXT_LIB_SETUP_OPTIONAL([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_from_files2 support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EXTERNAL_RESOLVER], [PJSIP External Resolver Support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TLS_TRANSPORT_PROTO], [PJSIP TLS Transport proto field support], [PJPROJECT], [pjsip])
+AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_GRP_LOCK], [PJSIP EVSUB Group Lock support], [PJPROJECT], [pjsip])
AST_EXT_LIB_SETUP([POPT], [popt], [popt])
AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
@@ -682,8 +683,6 @@ AC_PROG_GCC_TRADITIONAL
AC_FUNC_MEMCMP
AC_FUNC_MMAP
AC_FUNC_SELECT_ARGTYPES
-AC_FUNC_SETVBUF_REVERSED
-AC_TYPE_SIGNAL
AC_FUNC_STAT
AC_FUNC_STRCOLL
AC_FUNC_STRFTIME
@@ -1230,7 +1229,7 @@ AC_SUBST(AST_NATIVE_ARCH)
dnl Check to see if rpath should be set in LDFLAGS
AC_ARG_ENABLE(rpath,
- [AC_HELP_STRING([--disable-rpath],
+ [AS_HELP_STRING([--disable-rpath],
[Disables rpath linker option checking])],
[case "${enableval}" in
y|ye|yes) check_rpath=yes ;;
@@ -2194,6 +2193,8 @@ if test "$USE_PJPROJECT" != "no" ; then
AST_C_COMPILE_CHECK([PJSIP_TLS_TRANSPORT_PROTO], [struct pjsip_tls_setting setting; int proto; proto = setting.proto;], [pjsip.h])
LIBS="${saved_libs}"
CPPFLAGS="${saved_cppflags}"
+
+ AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
fi
fi
diff --git a/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py b/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
index c25fc7233..0919370ba 100644
--- a/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
+++ b/contrib/ast-db-manage/config/versions/81b01a191a46_pjsip_add_contact_reg_server.py
@@ -16,10 +16,8 @@ import sqlalchemy as sa
def upgrade():
op.add_column('ps_contacts', sa.Column('reg_server', sa.String(20)))
- op.drop_constraint(UniqueConstraint('id'), 'ps_contacts', type_='unique')
op.create_unique_constraint('ps_contacts_uq', 'ps_contacts', ['id','reg_server'])
def downgrade():
op.drop_constraint('ps_contacts_uq', 'ps_contacts', type_='unique')
op.drop_column('ps_contacts', 'reg_server')
- op.create_unique_constraint(None, 'ps_contacts', 'id')
diff --git a/funcs/func_env.c b/funcs/func_env.c
index 3c260a2fb..072714f19 100644
--- a/funcs/func_env.c
+++ b/funcs/func_env.c
@@ -624,7 +624,7 @@ static int file_read(struct ast_channel *chan, const char *cmd, char *data, stru
ast_log(LOG_ERROR, "Cannot seek to offset %" PRId64 ": %s\n", i, strerror(errno));
}
end = fread(fbuf, 1, sizeof(fbuf), ff);
- for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos > fbuf - 1; pos--) {
+ for (pos = (end < sizeof(fbuf) ? fbuf + end - 1 : fbuf + sizeof(fbuf) - 1); pos >= fbuf; pos--) {
LINE_COUNTER(pos, format, count);
if (length < 0 && count * -1 == length) {
@@ -1024,7 +1024,7 @@ static int file_write(struct ast_channel *chan, const char *cmd, char *data, con
fclose(ff);
return -1;
}
- for (pos = fbuf + sizeof(fbuf) - 1; pos > fbuf - 1; pos--) {
+ for (pos = fbuf + sizeof(fbuf) - 1; pos >= fbuf; pos--) {
LINE_COUNTER(pos, newline_format, count);
if (length < 0 && count * -1 == length) {
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index a01131cc3..64580205c 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -587,6 +587,9 @@
/* Define if your system has pjsip_dlg_create_uas_and_inc_lock declared. */
#undef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
+/* Define if your system has PJSIP_EVSUB_GRP_LOCK */
+#undef HAVE_PJSIP_EVSUB_GRP_LOCK
+
/* Define if your system has pjsip_endpt_set_ext_resolver declared. */
#undef HAVE_PJSIP_EXTERNAL_RESOLVER
@@ -1249,9 +1252,6 @@
/* Define if your system needs braces around PTHREAD_ONCE_INIT */
#undef PTHREAD_ONCE_INIT_NEEDS_BRACES
-/* Define as the return type of signal handlers (`int' or `void'). */
-#undef RETSIGTYPE
-
/* Define to the type of arg 1 for `select'. */
#undef SELECT_TYPE_ARG1
diff --git a/include/asterisk/compat.h b/include/asterisk/compat.h
index c9c99c150..c7bc5b093 100644
--- a/include/asterisk/compat.h
+++ b/include/asterisk/compat.h
@@ -68,7 +68,7 @@
#endif
#ifndef AST_POLL_COMPAT
-#include <sys/poll.h>
+#include <poll.h>
#else
#include "asterisk/poll-compat.h"
#endif
diff --git a/include/asterisk/config_options.h b/include/asterisk/config_options.h
index 30c042176..30d0c9142 100644
--- a/include/asterisk/config_options.h
+++ b/include/asterisk/config_options.h
@@ -445,6 +445,28 @@ enum aco_option_type {
* {endcode}
*/
OPT_UINT_T,
+
+ /*! \brief Type for default option handler for bools (ast_true/ast_false)
+ * \note aco_option_register flags:
+ * non-zero : process via ast_true
+ * 0 : process via ast_false
+ * aco_option_register varargs:
+ * FLDSET macro with the field of type int. It is important to note that the field
+ * cannot be a bitfield. If bitfields are required, they must be set via a custom handler.
+ *
+ * This is exactly the same as OPT_BOOL_T. The only difference is that when
+ * translated to a string, OPT_BOOL_T becomes "true" or "false"; OPT_YESNO_T becomes
+ * "yes" or "no".
+ *
+ * Example:
+ * {code}
+ * struct test_item {
+ * int enabled;
+ * };
+ * aco_option_register(&cfg_info, "enabled", ACO_EXACT, my_types, "no", OPT_YESNO_T, 1, FLDSET(struct test_item, enabled));
+ * {endcode}
+ */
+ OPT_YESNO_T,
};
/*! \brief A callback function for handling a particular option
diff --git a/include/asterisk/multicast_rtp.h b/include/asterisk/multicast_rtp.h
new file mode 100644
index 000000000..c286c1f96
--- /dev/null
+++ b/include/asterisk/multicast_rtp.h
@@ -0,0 +1,58 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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.
+ */
+
+#ifndef MULTICAST_RTP_H_
+#define MULTICAST_RTP_H_
+struct ast_multicast_rtp_options;
+
+/*!
+ * \brief Create multicast RTP options.
+ *
+ * These are passed to the multicast RTP engine on its creation.
+ *
+ * \param type The type of multicast RTP, either "basic" or "linksys"
+ * \param options Miscellaneous options
+ * \retval NULL Failure
+ * \retval non-NULL success
+ */
+struct ast_multicast_rtp_options *ast_multicast_rtp_create_options(const char *type,
+ const char *options);
+
+/*!
+ * \brief Free multicast RTP options
+ *
+ * This function is NULL-tolerant
+ *
+ * \param mcast_options Options to free
+ */
+void ast_multicast_rtp_free_options(struct ast_multicast_rtp_options *mcast_options);
+
+/*!
+ * \brief Get format specified in multicast options
+ *
+ * Multicast options allow for a format to be selected.
+ * This function accesses the selected format and creates
+ * an ast_format structure for it.
+ *
+ * \param mcast_options The options where a codec was specified
+ * \retval NULL No format specified in the options
+ * \revval non-NULL The format to use for communication
+ */
+struct ast_format *ast_multicast_rtp_options_get_format(struct ast_multicast_rtp_options *mcast_options);
+
+#endif /* MULTICAST_RTP_H_ */
diff --git a/include/asterisk/poll-compat.h b/include/asterisk/poll-compat.h
index cbb610925..72ac2c3e2 100644
--- a/include/asterisk/poll-compat.h
+++ b/include/asterisk/poll-compat.h
@@ -83,7 +83,7 @@
#ifndef AST_POLL_COMPAT
-#include <sys/poll.h>
+#include <poll.h>
#define ast_poll(a, b, c) poll(a, b, c)
diff --git a/include/asterisk/res_hep.h b/include/asterisk/res_hep.h
index bd0129eea..cfd213ad7 100644
--- a/include/asterisk/res_hep.h
+++ b/include/asterisk/res_hep.h
@@ -118,6 +118,14 @@ int hepv3_send_packet(struct hepv3_capture_info *capture_info);
*/
enum hep_uuid_type hepv3_get_uuid_type(void);
+/*!
+ * \brief Return whether or not we're currently loaded and active
+ *
+ * \retval 0 The module is not loaded
+ * \retval 1 The module is loaded
+ */
+int hepv3_is_loaded(void);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 4319dbdb6..5b830ea2e 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -1348,6 +1348,17 @@ struct ast_taskprocessor *ast_sip_create_serializer_group(struct ast_serializer_
struct ast_taskprocessor *ast_sip_create_serializer_group_named(const char *name, struct ast_serializer_shutdown_group *shutdown_group);
/*!
+ * \brief Determine the distributor serializer for the SIP message.
+ * \since 13.10.0
+ *
+ * \param rdata The incoming message.
+ *
+ * \retval Calculated distributor serializer on success.
+ * \retval NULL on error.
+ */
+struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdata);
+
+/*!
* \brief Set a serializer on a SIP dialog so requests and responses are automatically serialized
*
* Passing a NULL serializer is a way to remove a serializer from a dialog.
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 75d37ac3d..e4c54a173 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -406,9 +406,10 @@ struct ast_sip_channel_pvt *ast_sip_channel_pvt_alloc(void *pvt, struct ast_sip_
* \param endpoint The endpoint that this session communicates with
* \param contact The contact associated with this session
* \param inv_session The PJSIP INVITE session data
+ * \param rdata INVITE request received (NULL if for outgoing allocation)
*/
struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
- struct ast_sip_contact *contact, pjsip_inv_session *inv);
+ struct ast_sip_contact *contact, pjsip_inv_session *inv, pjsip_rx_data *rdata);
/*!
* \brief Request and wait for the session serializer to be suspended.
diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h
index 5e947257b..0cb434766 100644
--- a/include/asterisk/sorcery.h
+++ b/include/asterisk/sorcery.h
@@ -692,6 +692,20 @@ int __ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type,
__ast_sorcery_object_register((sorcery), (type), 1, 1, (alloc), (transform), (apply))
/*!
+ * \brief Set the high and low alert water marks of the sorcery object type.
+ * \since 13.10.0
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param low_water New queue low water mark. (-1 to set as 90% of high_water)
+ * \param high_water New queue high water mark.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error (water marks not changed).
+ */
+int ast_sorcery_object_set_congestion_levels(struct ast_sorcery *sorcery, const char *type, long low_water, long high_water);
+
+/*!
* \brief Set the copy handler for an object type
*
* \param sorcery Pointer to a sorcery structure
diff --git a/include/asterisk/stasis.h b/include/asterisk/stasis.h
index 4fc295bc4..de44206b6 100644
--- a/include/asterisk/stasis.h
+++ b/include/asterisk/stasis.h
@@ -601,6 +601,20 @@ struct stasis_subscription *stasis_unsubscribe(
struct stasis_subscription *subscription);
/*!
+ * \brief Set the high and low alert water marks of the stasis subscription.
+ * \since 13.10.0
+ *
+ * \param subscription Pointer to a stasis subscription
+ * \param low_water New queue low water mark. (-1 to set as 90% of high_water)
+ * \param high_water New queue high water mark.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error (water marks not changed).
+ */
+int stasis_subscription_set_congestion_limits(struct stasis_subscription *subscription,
+ long low_water, long high_water);
+
+/*!
* \brief Block until the last message is processed on a subscription.
*
* This function will not return until the \a subscription's callback for the
diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h
index 90ef82ebf..8ceeffba3 100644
--- a/include/asterisk/stasis_app.h
+++ b/include/asterisk/stasis_app.h
@@ -746,6 +746,15 @@ int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge,
struct stasis_app_control *control);
/*!
+ * \brief remove channel from list of ARI playback channels for bridges.
+ *
+ * \param bridge_id The unique ID of the bridge the playback channel is in.
+ * \param control The app control structure for the playback channel
+ */
+void stasis_app_bridge_playback_channel_remove(char *bridge_id,
+ struct stasis_app_control *control);
+
+/*!
* \brief Result codes used when adding/removing channels to/from bridges.
*/
enum stasis_app_control_channel_result {
diff --git a/include/asterisk/stasis_message_router.h b/include/asterisk/stasis_message_router.h
index 89657a5ee..50270a788 100644
--- a/include/asterisk/stasis_message_router.h
+++ b/include/asterisk/stasis_message_router.h
@@ -127,6 +127,20 @@ void stasis_message_router_publish_sync(struct stasis_message_router *router,
struct stasis_message *message);
/*!
+ * \brief Set the high and low alert water marks of the stasis message router.
+ * \since 13.10.0
+ *
+ * \param router Pointer to a stasis message router
+ * \param low_water New queue low water mark. (-1 to set as 90% of high_water)
+ * \param high_water New queue high water mark.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error (water marks not changed).
+ */
+int stasis_message_router_set_congestion_limits(struct stasis_message_router *router,
+ long low_water, long high_water);
+
+/*!
* \brief Add a route to a message router.
*
* A particular \a message_type may have at most one route per \a router. If
diff --git a/include/asterisk/taskprocessor.h b/include/asterisk/taskprocessor.h
index af3ce747f..e51122269 100644
--- a/include/asterisk/taskprocessor.h
+++ b/include/asterisk/taskprocessor.h
@@ -59,6 +59,7 @@ struct ast_taskprocessor;
/*! \brief Suggested maximum taskprocessor name length (less null terminator). */
#define AST_TASKPROCESSOR_MAX_NAME 45
+/*! Default taskprocessor high water level alert trigger */
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL 500
/*!
@@ -297,4 +298,26 @@ const char *ast_taskprocessor_name(struct ast_taskprocessor *tps);
*/
long ast_taskprocessor_size(struct ast_taskprocessor *tps);
+/*!
+ * \brief Get the current taskprocessor high water alert count.
+ * \since 13.10.0
+ *
+ * \retval 0 if no taskprocessors are in high water alert.
+ * \retval non-zero if some task processors are in high water alert.
+ */
+unsigned int ast_taskprocessor_alert_get(void);
+
+/*!
+ * \brief Set the high and low alert water marks of the given taskprocessor queue.
+ * \since 13.10.0
+ *
+ * \param tps Taskprocessor to update queue water marks.
+ * \param low_water New queue low water mark. (-1 to set as 90% of high_water)
+ * \param high_water New queue high water mark.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error (water marks not changed).
+ */
+int ast_taskprocessor_alert_set_levels(struct ast_taskprocessor *tps, long low_water, long high_water);
+
#endif /* __AST_TASKPROCESSOR_H__ */
diff --git a/main/ast_expr2.c b/main/ast_expr2.c
index 798e3d3ce..781abd95a 100644
--- a/main/ast_expr2.c
+++ b/main/ast_expr2.c
@@ -93,6 +93,7 @@
#include "asterisk.h"
+#include <sys/cdefs.h>
#include <sys/types.h>
#include <stdio.h>
@@ -3667,13 +3668,20 @@ op_tildetilde (struct val *a, struct val *b)
/* strip double quotes from both -- */
strip_quotes(a);
strip_quotes(b);
-
+
vs = malloc(strlen(a->u.s)+strlen(b->u.s)+1);
+ if (vs == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ return NULL;
+ }
+
strcpy(vs,a->u.s);
strcat(vs,b->u.s);
v = make_str(vs);
+ free(vs);
+
/* free arguments */
free_value(a);
free_value(b);
diff --git a/main/ast_expr2.y b/main/ast_expr2.y
index 83d3effe3..913bc2662 100644
--- a/main/ast_expr2.y
+++ b/main/ast_expr2.y
@@ -14,6 +14,7 @@
#include "asterisk.h"
+#include <sys/cdefs.h>
#include <sys/types.h>
#include <stdio.h>
@@ -1660,13 +1661,20 @@ op_tildetilde (struct val *a, struct val *b)
/* strip double quotes from both -- */
strip_quotes(a);
strip_quotes(b);
-
+
vs = malloc(strlen(a->u.s)+strlen(b->u.s)+1);
+ if (vs == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ return NULL;
+ }
+
strcpy(vs,a->u.s);
strcat(vs,b->u.s);
v = make_str(vs);
+ free(vs);
+
/* free arguments */
free_value(a);
free_value(b);
diff --git a/main/asterisk.c b/main/asterisk.c
index ea998d492..164e659ba 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -606,6 +606,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
char buf[BUFSIZ];
struct ast_tm tm;
char eid_str[128];
+ struct rlimit limits;
switch (cmd) {
case CLI_INIT:
@@ -627,10 +628,17 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
ast_cli(a->fd, " Maximum calls: %d (Current %d)\n", ast_option_maxcalls, ast_active_channels());
else
ast_cli(a->fd, " Maximum calls: Not set\n");
- if (ast_option_maxfiles)
- ast_cli(a->fd, " Maximum open file handles: %d\n", ast_option_maxfiles);
- else
- ast_cli(a->fd, " Maximum open file handles: Not set\n");
+
+ if (getrlimit(RLIMIT_NOFILE, &limits)) {
+ ast_cli(a->fd, " Maximum open file handles: Error because of %s\n", strerror(errno));
+ } else if (limits.rlim_cur == RLIM_INFINITY) {
+ ast_cli(a->fd, " Maximum open file handles: Unlimited\n");
+ } else if (limits.rlim_cur < ast_option_maxfiles) {
+ ast_cli(a->fd, " Maximum open file handles: %d (is) %d (requested)\n", (int) limits.rlim_cur, ast_option_maxfiles);
+ } else {
+ ast_cli(a->fd, " Maximum open file handles: %d\n", (int) limits.rlim_cur);
+ }
+
ast_cli(a->fd, " Root console verbosity: %d\n", option_verbose);
ast_cli(a->fd, " Current console verbosity: %d\n", ast_verb_console_get());
ast_cli(a->fd, " Debug level: %d\n", option_debug);
diff --git a/main/astfd.c b/main/astfd.c
index d2cb73a6b..a96471d60 100644
--- a/main/astfd.c
+++ b/main/astfd.c
@@ -271,7 +271,7 @@ static char *handle_show_fd(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
case CLI_GENERATE:
return NULL;
}
- getrlimit(RLIMIT_FSIZE, &rl);
+ getrlimit(RLIMIT_NOFILE, &rl);
if (rl.rlim_cur == RLIM_INFINITY || rl.rlim_max == RLIM_INFINITY) {
ast_copy_string(line, "unlimited", sizeof(line));
} else {
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index 4baae3cc5..543988dde 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -2162,9 +2162,10 @@ int bridge_channel_internal_push_full(struct ast_bridge_channel *bridge_channel,
ast_bridge_publish_enter(bridge, bridge_channel->chan, swap ? swap->chan : NULL);
- /* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */
+ /* Clear any BLINDTRANSFER,ATTENDEDTRANSFER and FORWARDERNAME since the transfer has completed. */
pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL);
pbx_builtin_setvar_helper(bridge_channel->chan, "ATTENDEDTRANSFER", NULL);
+ pbx_builtin_setvar_helper(bridge_channel->chan, "FORWARDERNAME", NULL);
/* Wake up the bridge channel thread to reevaluate any interval timers. */
ast_queue_frame(bridge_channel->chan, &ast_null_frame);
diff --git a/main/cdr.c b/main/cdr.c
index 7795a65fd..ab6530ed3 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -71,6 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_message_router.h"
#include "asterisk/astobj2.h"
+#include "asterisk/taskprocessor.h"
/*** DOCUMENTATION
<configInfo name="cdr" language="en_US">
@@ -4184,6 +4185,8 @@ int ast_cdr_engine_init(void)
if (!stasis_router) {
return -1;
}
+ stasis_message_router_set_congestion_limits(stasis_router, -1,
+ 10 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
if (STASIS_MESSAGE_TYPE_INIT(cdr_sync_message_type)) {
return -1;
diff --git a/main/cel.c b/main/cel.c
index d9fcc5f6b..4abaac7c8 100644
--- a/main/cel.c
+++ b/main/cel.c
@@ -59,6 +59,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/parking.h"
#include "asterisk/pickup.h"
#include "asterisk/core_local.h"
+#include "asterisk/taskprocessor.h"
/*** DOCUMENTATION
<configInfo name="cel" language="en_US">
@@ -170,6 +171,13 @@ struct cel_linkedid {
/*! Container of channel references to a linkedid for CEL purposes. */
static AO2_GLOBAL_OBJ_STATIC(cel_linkedids);
+struct cel_dialstatus {
+ /*! Uniqueid of the channel */
+ char uniqueid[AST_MAX_UNIQUEID];
+ /*! The dial status */
+ char dialstatus[0];
+};
+
/*! \brief Destructor for cel_config */
static void cel_general_config_dtor(void *obj)
{
@@ -372,20 +380,10 @@ static int cel_backend_cmp(void *obj, void *arg, int flags)
return CMP_MATCH;
}
-static const char *get_caller_uniqueid(struct ast_multi_channel_blob *blob)
-{
- struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
- if (!caller) {
- return NULL;
- }
-
- return caller->uniqueid;
-}
-
/*! \brief Hashing function for dialstatus container */
static int dialstatus_hash(const void *obj, int flags)
{
- struct ast_multi_channel_blob *blob;
+ const struct cel_dialstatus *dialstatus;
const char *key;
switch (flags & OBJ_SEARCH_MASK) {
@@ -393,8 +391,8 @@ static int dialstatus_hash(const void *obj, int flags)
key = obj;
break;
case OBJ_SEARCH_OBJECT:
- blob = (void *) obj;
- key = get_caller_uniqueid(blob);
+ dialstatus = obj;
+ key = dialstatus->uniqueid;
break;
default:
/* Hash can only work on something with a full key. */
@@ -407,24 +405,24 @@ static int dialstatus_hash(const void *obj, int flags)
/*! \brief Comparator function for dialstatus container */
static int dialstatus_cmp(void *obj, void *arg, int flags)
{
- struct ast_multi_channel_blob *object_left = obj;
- struct ast_multi_channel_blob *object_right = arg;
+ struct cel_dialstatus *object_left = obj;
+ struct cel_dialstatus *object_right = arg;
const char *right_key = arg;
int cmp;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_OBJECT:
- right_key = get_caller_uniqueid(object_right);
+ right_key = object_right->uniqueid;
/* Fall through */
case OBJ_SEARCH_KEY:
- cmp = strcmp(get_caller_uniqueid(object_left), right_key);
+ cmp = strcmp(object_left->uniqueid, right_key);
break;
case OBJ_SEARCH_PARTIAL_KEY:
/*
* We could also use a partial key struct containing a length
* so strlen() does not get called for every comparison instead.
*/
- cmp = strncmp(get_caller_uniqueid(object_left), right_key, strlen(right_key));
+ cmp = strncmp(object_left->uniqueid, right_key, strlen(right_key));
break;
default:
/*
@@ -958,16 +956,16 @@ typedef void (*cel_channel_snapshot_monitor)(
struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot);
-static struct ast_multi_channel_blob *get_dialstatus_blob(const char *uniqueid)
+static struct cel_dialstatus *get_dialstatus(const char *uniqueid)
{
struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
- struct ast_multi_channel_blob *blob = NULL;
+ struct cel_dialstatus *dialstatus = NULL;
if (dial_statuses) {
- blob = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK);
+ dialstatus = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK);
ao2_ref(dial_statuses, -1);
}
- return blob;
+ return dialstatus;
}
static const char *get_blob_variable(struct ast_multi_channel_blob *blob, const char *varname)
@@ -1010,19 +1008,15 @@ static void cel_channel_state_change(
if (!was_hungup && is_hungup) {
struct ast_json *extra;
- struct ast_multi_channel_blob *blob = get_dialstatus_blob(new_snapshot->uniqueid);
- const char *dialstatus = "";
+ struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->uniqueid);
- if (blob && !ast_strlen_zero(get_blob_variable(blob, "dialstatus"))) {
- dialstatus = get_blob_variable(blob, "dialstatus");
- }
extra = ast_json_pack("{s: i, s: s, s: s}",
"hangupcause", new_snapshot->hangupcause,
"hangupsource", new_snapshot->hangupsource,
- "dialstatus", dialstatus);
+ "dialstatus", dialstatus ? dialstatus->dialstatus : "");
cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
ast_json_unref(extra);
- ao2_cleanup(blob);
+ ao2_cleanup(dialstatus);
return;
}
@@ -1254,16 +1248,48 @@ static void cel_parking_cb(
}
}
-static void save_dialstatus(struct ast_multi_channel_blob *blob)
+static void save_dialstatus(struct ast_multi_channel_blob *blob, struct ast_channel_snapshot *snapshot)
{
struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
+ const char *dialstatus_string = get_blob_variable(blob, "dialstatus");
+ struct cel_dialstatus *dialstatus;
+ size_t dialstatus_string_len;
- ast_assert(blob != NULL);
+ if (!dial_statuses || ast_strlen_zero(dialstatus_string)) {
+ ao2_cleanup(dial_statuses);
+ return;
+ }
- if (dial_statuses) {
- ao2_link(dial_statuses, blob);
+ dialstatus = ao2_find(dial_statuses, snapshot->uniqueid, OBJ_SEARCH_KEY);
+ if (dialstatus) {
+ if (!strcasecmp(dialstatus_string, "ANSWER") && strcasecmp(dialstatus->dialstatus, "ANSWER")) {
+ /* In the case of an answer after we already have a dial status we give
+ * priority to the answer since the call was, well, answered. In the case of
+ * failure dial status results we simply let the first failure be the status.
+ */
+ ao2_unlink(dial_statuses, dialstatus);
+ ao2_ref(dialstatus, -1);
+ } else {
+ ao2_ref(dialstatus, -1);
+ ao2_ref(dial_statuses, -1);
+ return;
+ }
+ }
+
+ dialstatus_string_len = strlen(dialstatus_string) + 1;
+ dialstatus = ao2_alloc_options(sizeof(*dialstatus) + dialstatus_string_len, NULL,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!dialstatus) {
ao2_ref(dial_statuses, -1);
+ return;
}
+
+ ast_copy_string(dialstatus->uniqueid, snapshot->uniqueid, sizeof(dialstatus->uniqueid));
+ ast_copy_string(dialstatus->dialstatus, dialstatus_string, dialstatus_string_len);
+
+ ao2_link(dial_statuses, dialstatus);
+ ao2_ref(dialstatus, -1);
+ ao2_ref(dial_statuses, -1);
}
static int is_valid_dialstatus(struct ast_multi_channel_blob *blob)
@@ -1299,32 +1325,25 @@ static void cel_dial_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_multi_channel_blob *blob = stasis_message_data(message);
+ struct ast_channel_snapshot *snapshot;
- if (cel_filter_channel_snapshot(ast_multi_channel_blob_get_channel(blob, "caller"))) {
- return;
- }
-
- if (!get_caller_uniqueid(blob)) {
+ snapshot = ast_multi_channel_blob_get_channel(blob, "caller");
+ if (!snapshot || cel_filter_channel_snapshot(snapshot)) {
return;
}
if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
- struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
struct ast_json *extra;
- if (!caller) {
- return;
- }
-
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
if (extra) {
- cel_report_event(caller, AST_CEL_FORWARD, NULL, extra, NULL);
+ cel_report_event(snapshot, AST_CEL_FORWARD, NULL, extra, NULL);
ast_json_unref(extra);
}
}
if (is_valid_dialstatus(blob)) {
- save_dialstatus(blob);
+ save_dialstatus(blob, snapshot);
}
}
@@ -1575,6 +1594,8 @@ static int create_routes(void)
if (!cel_state_router) {
return -1;
}
+ stasis_message_router_set_congestion_limits(cel_state_router, -1,
+ 6 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
ret |= stasis_message_router_add(cel_state_router,
stasis_cache_update_type(),
diff --git a/main/channel.c b/main/channel.c
index 4ed1f8b8a..327ec64a6 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -5663,6 +5663,7 @@ static void call_forward_inherit(struct ast_channel *new_chan, struct ast_channe
struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_channel *orig, int *timeout, struct ast_format_cap *cap, struct outgoing_helper *oh, int *outstate)
{
char tmpchan[256];
+ char forwarder[AST_CHANNEL_NAME];
struct ast_channel *new_chan = NULL;
char *data, *type;
int cause = 0;
@@ -5670,6 +5671,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
/* gather data and request the new forward channel */
ast_copy_string(tmpchan, ast_channel_call_forward(orig), sizeof(tmpchan));
+ ast_copy_string(forwarder, ast_channel_name(orig), sizeof(forwarder));
if ((data = strchr(tmpchan, '/'))) {
*data++ = '\0';
type = tmpchan;
@@ -5713,6 +5715,7 @@ struct ast_channel *ast_call_forward(struct ast_channel *caller, struct ast_chan
ast_set_flag(ast_channel_flags(new_chan), AST_FLAG_ORIGINATED);
ast_channel_lock_both(orig, new_chan);
+ pbx_builtin_setvar_helper(new_chan, "FORWARDERNAME", forwarder);
ast_party_connected_line_copy(ast_channel_connected(new_chan), ast_channel_connected(orig));
ast_party_redirecting_copy(ast_channel_redirecting(new_chan), ast_channel_redirecting(orig));
ast_channel_req_accountcodes(new_chan, orig, AST_CHANNEL_REQUESTOR_REPLACEMENT);
diff --git a/main/codec.c b/main/codec.c
index 543d4d0bd..c253233bb 100644
--- a/main/codec.c
+++ b/main/codec.c
@@ -49,6 +49,32 @@ static int codec_id = 1;
/*! \brief Registered codecs */
static struct ao2_container *codecs;
+/*!
+ * \internal
+ * \brief Internal codec structure
+ *
+ * External codecs won't know about the format_name field so the public
+ * ast_codec structure has to leave it out. This structure will be used
+ * for the internal codecs.
+ *
+ */
+struct internal_ast_codec {
+ /*! \brief Public codec structure. Must remain first. */
+ struct ast_codec external;
+ /*! \brief A format name for a default sane format using this codec */
+ const char *format_name;
+};
+
+/*!
+ * \internal
+ * \brief Internal function for registration with format name
+ *
+ * This function is only used by codec.c and codec_builtin.c and
+ * will be removed in Asterisk 14
+ */
+int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
+ struct ast_module *mod);
+
static int codec_hash(const void *obj, int flags)
{
const struct ast_codec *codec;
@@ -113,7 +139,7 @@ static int codec_cmp(void *obj, void *arg, int flags)
static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_iterator i;
- struct ast_codec *codec;
+ struct internal_ast_codec *codec;
switch (cmd) {
case CLI_INIT:
@@ -135,8 +161,8 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
"\tIt does not indicate anything about your configuration.\n");
}
- ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION");
- ast_cli(a->fd, "-----------------------------------------------------------------------------------\n");
+ ast_cli(a->fd, "%8s %-5s %-12s %-16s %s\n","ID","TYPE","NAME","FORMAT","DESCRIPTION");
+ ast_cli(a->fd, "------------------------------------------------------------------------------------------------\n");
ao2_rdlock(codecs);
i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK);
@@ -144,19 +170,19 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) {
if (a->argc == 4) {
if (!strcasecmp(a->argv[3], "audio")) {
- if (codec->type != AST_MEDIA_TYPE_AUDIO) {
+ if (codec->external.type != AST_MEDIA_TYPE_AUDIO) {
continue;
}
} else if (!strcasecmp(a->argv[3], "video")) {
- if (codec->type != AST_MEDIA_TYPE_VIDEO) {
+ if (codec->external.type != AST_MEDIA_TYPE_VIDEO) {
continue;
}
} else if (!strcasecmp(a->argv[3], "image")) {
- if (codec->type != AST_MEDIA_TYPE_IMAGE) {
+ if (codec->external.type != AST_MEDIA_TYPE_IMAGE) {
continue;
}
} else if (!strcasecmp(a->argv[3], "text")) {
- if (codec->type != AST_MEDIA_TYPE_TEXT) {
+ if (codec->external.type != AST_MEDIA_TYPE_TEXT) {
continue;
}
} else {
@@ -164,11 +190,12 @@ static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
}
}
- ast_cli(a->fd, "%8u %5s %8s (%s)\n",
- codec->id,
- ast_codec_media_type2str(codec->type),
- codec->name,
- codec->description);
+ ast_cli(a->fd, "%8u %-5s %-12s %-16s (%s)\n",
+ codec->external.id,
+ ast_codec_media_type2str(codec->external.type),
+ codec->external.name,
+ S_OR(codec->format_name, "no cached format"),
+ codec->external.description);
}
ao2_iterator_destroy(&i);
@@ -189,7 +216,7 @@ static int codec_id_cmp(void *obj, void *arg, int flags)
static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int type_punned_codec;
- struct ast_codec *codec;
+ struct internal_ast_codec *codec;
switch (cmd) {
case CLI_INIT:
@@ -216,7 +243,8 @@ static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
return CLI_SUCCESS;
}
- ast_cli(a->fd, "%11u %s\n", (unsigned int) codec->id, codec->description);
+ ast_cli(a->fd, "%11u %s (%s)\n", (unsigned int) codec->external.id, codec->external.description,
+ S_OR(codec->format_name, "no format"));
ao2_ref(codec, -1);
@@ -261,8 +289,13 @@ static void codec_dtor(void *obj)
int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
{
+ return __ast_codec_register_with_format(codec, NULL, mod);
+}
+
+int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name, struct ast_module *mod)
+{
SCOPED_AO2WRLOCK(lock, codecs);
- struct ast_codec *codec_new;
+ struct internal_ast_codec *codec_new;
/* Some types have specific requirements */
if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
@@ -291,8 +324,9 @@ int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
return -1;
}
- *codec_new = *codec;
- codec_new->id = codec_id++;
+ codec_new->external = *codec;
+ codec_new->format_name = format_name;
+ codec_new->external.id = codec_id++;
ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
@@ -300,7 +334,7 @@ int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
ast_module_shutdown_ref(mod);
ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
- ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->id);
+ ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->external.id);
ao2_ref(codec_new, -1);
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 346b47b87..d3f65174c 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -38,6 +38,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/format_cache.h"
#include "asterisk/frame.h"
+int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
+ struct ast_module *mod);
+
enum frame_type {
TYPE_HIGH, /* 0x0 */
TYPE_LOW, /* 0x1 */
@@ -774,7 +777,7 @@ static struct ast_codec t140 = {
int __res_ ## __LINE__ = 0; \
struct ast_format *__fmt_ ## __LINE__; \
struct ast_codec *__codec_ ## __LINE__; \
- res |= __ast_codec_register(&(codec), NULL); \
+ res |= __ast_codec_register_with_format(&(codec), (codec).name, NULL); \
__codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \
__fmt_ ## __LINE__ = __codec_ ## __LINE__ ? ast_format_create(__codec_ ## __LINE__) : NULL; \
res |= ast_format_cache_set(__fmt_ ## __LINE__); \
@@ -783,14 +786,14 @@ static struct ast_codec t140 = {
__res_ ## __LINE__; \
})
-#define CODEC_REGISTER_AND_CACHE_NAMED(format_name, codec) \
+#define CODEC_REGISTER_AND_CACHE_NAMED(fmt_name, codec) \
({ \
int __res_ ## __LINE__ = 0; \
struct ast_format *__fmt_ ## __LINE__; \
struct ast_codec *__codec_ ## __LINE__; \
- res |= __ast_codec_register(&(codec), NULL); \
+ res |= __ast_codec_register_with_format(&(codec), fmt_name, NULL); \
__codec_ ## __LINE__ = ast_codec_get((codec).name, (codec).type, (codec).sample_rate); \
- __fmt_ ## __LINE__ = ast_format_create_named((format_name), __codec_ ## __LINE__); \
+ __fmt_ ## __LINE__ = ast_format_create_named((fmt_name), __codec_ ## __LINE__); \
res |= ast_format_cache_set(__fmt_ ## __LINE__); \
ao2_ref(__fmt_ ## __LINE__, -1); \
ao2_ref(__codec_ ## __LINE__, -1); \
diff --git a/main/config_options.c b/main/config_options.c
index c8988c984..cc8e218f8 100644
--- a/main/config_options.c
+++ b/main/config_options.c
@@ -97,6 +97,7 @@ static char *aco_option_type_string[] = {
"IP Address", /* OPT_SOCKADDR_T, */
"String", /* OPT_STRINGFIELD_T, */
"Unsigned Integer", /* OPT_UINT_T, */
+ "Boolean", /* OPT_YESNO_T, */
};
void *aco_pending_config(struct aco_info *info)
@@ -139,6 +140,10 @@ static aco_option_handler ast_config_option_default_handler(enum aco_option_type
switch(type) {
case OPT_ACL_T: return acl_handler_fn;
case OPT_BOOL_T: return bool_handler_fn;
+ /* Reading from config files, BOOL and YESNO are handled exactly the
+ * same. Their difference is in how they are rendered to users
+ */
+ case OPT_YESNO_T: return bool_handler_fn;
case OPT_BOOLFLAG_T: return boolflag_handler_fn;
case OPT_CHAR_ARRAY_T: return chararray_handler_fn;
case OPT_CODEC_T: return codec_handler_fn;
diff --git a/main/dial.c b/main/dial.c
index fe592033e..ffa440546 100644
--- a/main/dial.c
+++ b/main/dial.c
@@ -411,16 +411,24 @@ int ast_dial_prerun(struct ast_dial *dial, struct ast_channel *chan, struct ast_
}
/*! \brief Helper function that does the beginning dialing per-appended channel */
-static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_channel *chan, int async, const char *predial_string)
+static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_channel *chan, int async, const char *predial_string, struct ast_channel *forwarder_chan)
{
char numsubst[AST_MAX_EXTENSION];
int res = 1;
+ char forwarder[AST_CHANNEL_NAME];
/* If no owner channel exists yet execute pre-run */
if (!channel->owner && begin_dial_prerun(channel, chan, NULL, predial_string)) {
return 0;
}
+ if (forwarder_chan) {
+ ast_copy_string(forwarder, ast_channel_name(forwarder_chan), sizeof(forwarder));
+ ast_channel_lock(channel->owner);
+ pbx_builtin_setvar_helper(channel->owner, "FORWARDERNAME", forwarder);
+ ast_channel_unlock(channel->owner);
+ }
+
/* Copy device string over */
ast_copy_string(numsubst, channel->device, sizeof(numsubst));
@@ -451,7 +459,7 @@ static int begin_dial(struct ast_dial *dial, struct ast_channel *chan, int async
/* Iterate through channel list, requesting and calling each one */
AST_LIST_LOCK(&dial->channels);
AST_LIST_TRAVERSE(&dial->channels, channel, list) {
- success += begin_dial_channel(channel, chan, async, predial_string);
+ success += begin_dial_channel(channel, chan, async, predial_string, NULL);
}
AST_LIST_UNLOCK(&dial->channels);
@@ -507,7 +515,7 @@ static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *c
channel->owner = NULL;
/* Finally give it a go... send it out into the world */
- begin_dial_channel(channel, chan, chan ? 0 : 1, predial_string);
+ begin_dial_channel(channel, chan, chan ? 0 : 1, predial_string, original);
ast_channel_publish_dial_forward(chan, original, channel->owner, NULL, "CANCEL",
ast_channel_call_forward(original));
diff --git a/main/editline/Makefile.in b/main/editline/Makefile.in
index 112b68b64..2be4333d5 100644
--- a/main/editline/Makefile.in
+++ b/main/editline/Makefile.in
@@ -187,7 +187,7 @@ distclean : clean
#
$(LIB_A) : $(BGCSRCS:.c=.o_a) $(CCSRCS:.c=.o_a)
- $(AR) cru $@ $?
+ $(AR) cr $@ $?
$(RANLIB) $@
$(LIB_S) : $(BGCSRCS:.c=.o_s) $(CCSRCS:.c=.o_s)
diff --git a/main/http.c b/main/http.c
index c343cb236..b2b35ff59 100644
--- a/main/http.c
+++ b/main/http.c
@@ -2220,7 +2220,7 @@ static int __ast_http_load(int reload)
* the non-TLS bindaddress here.
*/
if (ast_sockaddr_isnull(&https_desc.local_address) && http_desc.accept_fd != -1) {
- ast_sockaddr_copy(&https_desc.local_address, &https_desc.local_address);
+ ast_sockaddr_copy(&https_desc.local_address, &http_desc.local_address);
/* Of course, we can't use the same port though.
* Since no bind address was specified, we just use the
* default TLS port
diff --git a/main/manager.c b/main/manager.c
index ba261e8e9..bc4804d89 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -100,6 +100,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/rtp_engine.h"
#include "asterisk/format_cache.h"
#include "asterisk/translate.h"
+#include "asterisk/taskprocessor.h"
/*** DOCUMENTATION
<manager name="Ping" language="en_US">
@@ -8650,6 +8651,8 @@ static int manager_subscriptions_init(void)
if (!stasis_router) {
return -1;
}
+ stasis_message_router_set_congestion_limits(stasis_router, -1,
+ 6 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
res |= stasis_message_router_set_default(stasis_router,
manager_default_msg_cb, NULL);
diff --git a/main/say.c b/main/say.c
index ef80dfa7d..f19ed71a1 100644
--- a/main/say.c
+++ b/main/say.c
@@ -5201,13 +5201,14 @@ int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *
case 'I':
case 'l':
/* 12-Hour */
- if (tm.tm_hour == 0)
+ if (tm.tm_hour == 0) {
ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
- else if (tm.tm_hour > 12)
+ } else if (tm.tm_hour > 12) {
snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
- else
+ } else {
snprintf(nextmsg, sizeof(nextmsg), "digits/%d", tm.tm_hour);
- res = wait_file(chan, ints, nextmsg, lang);
+ }
+ res = wait_file(chan, ints, nextmsg, lang);
break;
case 'H':
case 'k':
@@ -5227,11 +5228,12 @@ int ast_say_date_with_format_it(struct ast_channel *chan, time_t t, const char *
case 'P':
case 'p':
/* AM/PM */
- if (tm.tm_hour > 11)
+ if (tm.tm_hour > 11) {
ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
- else
+ } else {
ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
- res = wait_file(chan, ints, nextmsg, lang);
+ }
+ res = wait_file(chan, ints, nextmsg, lang);
break;
case 'Q':
/* Shorthand for "Today", "Yesterday", or ABdY */
@@ -7948,9 +7950,9 @@ int ast_say_date_with_format_ja(struct ast_channel *chan, time_t time, const cha
/* NOTE: if you add more options here, please try to be consistent with strftime(3) */
case '\'':
/* Literal name of a sound file */
- sndoffset=0;
- for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < sizeof(sndfile) - 1) ; sndoffset++) {
sndfile[sndoffset] = format[offset];
+ }
sndfile[sndoffset] = '\0';
res = wait_file(chan,ints,sndfile,lang);
break;
diff --git a/main/sorcery.c b/main/sorcery.c
index 3a29cfa58..bfc768879 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -290,6 +290,12 @@ static int bool_handler_fn(const void *obj, const intptr_t *args, char **buf)
return !(*buf = ast_strdup(*field ? "true" : "false")) ? -1 : 0;
}
+static int yesno_handler_fn(const void *obj, const intptr_t *args, char **buf)
+{
+ unsigned int *field = (unsigned int *)(obj + args[0]);
+ return !(*buf = ast_strdup(*field ? "yes" : "no")) ? -1 : 0;
+}
+
static int sockaddr_handler_fn(const void *obj, const intptr_t *args, char **buf)
{
struct ast_sockaddr *field = (struct ast_sockaddr *)(obj + args[0]);
@@ -313,6 +319,7 @@ static sorcery_field_handler sorcery_field_default_handler(enum aco_option_type
{
switch(type) {
case OPT_BOOL_T: return bool_handler_fn;
+ case OPT_YESNO_T: return yesno_handler_fn;
case OPT_CHAR_ARRAY_T: return chararray_handler_fn;
case OPT_CODEC_T: return codec_handler_fn;
case OPT_DOUBLE_T: return double_handler_fn;
@@ -1161,6 +1168,20 @@ int __ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type,
return 0;
}
+int ast_sorcery_object_set_congestion_levels(struct ast_sorcery *sorcery, const char *type, long low_water, long high_water)
+{
+ struct ast_sorcery_object_type *object_type;
+ int res = -1;
+
+ object_type = ao2_find(sorcery->types, type, OBJ_SEARCH_KEY);
+ if (object_type) {
+ res = ast_taskprocessor_alert_set_levels(object_type->serializer,
+ low_water, high_water);
+ ao2_ref(object_type, -1);
+ }
+ return res;
+}
+
void ast_sorcery_object_set_copy_handler(struct ast_sorcery *sorcery, const char *type, sorcery_copy_handler copy)
{
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
diff --git a/main/stasis.c b/main/stasis.c
index 4fb69033f..bbafb69e1 100644
--- a/main/stasis.c
+++ b/main/stasis.c
@@ -564,6 +564,18 @@ struct stasis_subscription *stasis_unsubscribe(struct stasis_subscription *sub)
return NULL;
}
+int stasis_subscription_set_congestion_limits(struct stasis_subscription *subscription,
+ long low_water, long high_water)
+{
+ int res = -1;
+
+ if (subscription) {
+ res = ast_taskprocessor_alert_set_levels(subscription->mailbox,
+ low_water, high_water);
+ }
+ return res;
+}
+
void stasis_subscription_join(struct stasis_subscription *subscription)
{
if (subscription) {
diff --git a/main/stasis_message_router.c b/main/stasis_message_router.c
index 26df76c53..cf0ac787e 100644
--- a/main/stasis_message_router.c
+++ b/main/stasis_message_router.c
@@ -289,6 +289,18 @@ void stasis_message_router_publish_sync(struct stasis_message_router *router,
ao2_cleanup(router);
}
+int stasis_message_router_set_congestion_limits(struct stasis_message_router *router,
+ long low_water, long high_water)
+{
+ int res = -1;
+
+ if (router) {
+ res = stasis_subscription_set_congestion_limits(router->subscription,
+ low_water, high_water);
+ }
+ return res;
+}
+
int stasis_message_router_add(struct stasis_message_router *router,
struct stasis_message_type *message_type,
stasis_subscription_cb callback, void *data)
diff --git a/main/stdtime/localtime.c b/main/stdtime/localtime.c
index 702edbe85..9cdf614d5 100644
--- a/main/stdtime/localtime.c
+++ b/main/stdtime/localtime.c
@@ -1849,13 +1849,14 @@ void ast_get_dst_info(const time_t * const timep, int *dst_enabled, time_t *dst_
*dst_enabled = 0;
/* Find where I can get gmtoff */
i = 0;
- while (sp->ttis[i].tt_isdst)
+ while (sp->ttis[i].tt_isdst) {
if (++i >= sp->typecnt) {
- i = 0;
- break;
+ i = 0;
+ break;
}
- *gmt_off = sp->ttis[i].tt_gmtoff;
- return;
+ }
+ *gmt_off = sp->ttis[i].tt_gmtoff;
+ return;
}
for (i = 1; i < sp->timecnt; ++i) {
diff --git a/main/taskprocessor.c b/main/taskprocessor.c
index 1ba0c8a2f..7ce3e4f16 100644
--- a/main/taskprocessor.c
+++ b/main/taskprocessor.c
@@ -76,6 +76,10 @@ struct ast_taskprocessor {
void *local_data;
/*! \brief Taskprocessor current queue size */
long tps_queue_size;
+ /*! \brief Taskprocessor low water clear alert level */
+ long tps_queue_low;
+ /*! \brief Taskprocessor high water alert trigger level */
+ long tps_queue_high;
/*! \brief Taskprocessor queue */
AST_LIST_HEAD_NOLOCK(tps_queue, tps_task) tps_queue;
struct ast_taskprocessor_listener *listener;
@@ -85,6 +89,8 @@ struct ast_taskprocessor {
unsigned int executing:1;
/*! Indicates that a high water warning has been issued on this task processor */
unsigned int high_water_warned:1;
+ /*! Indicates that a high water alert is active on this taskprocessor */
+ unsigned int high_water_alert:1;
};
/*!
@@ -121,15 +127,9 @@ static int tps_hash_cb(const void *obj, const int flags);
/*! \brief The astobj2 compare callback for taskprocessors */
static int tps_cmp_cb(void *obj, void *arg, int flags);
-/*! \brief Destroy the taskprocessor when its refcount reaches zero */
-static void tps_taskprocessor_destroy(void *tps);
-
/*! \brief CLI <example>taskprocessor ping &lt;blah&gt;</example> handler function */
static int tps_ping_handler(void *datap);
-/*! \brief Remove the front task off the taskprocessor queue */
-static struct tps_task *tps_taskprocessor_pop(struct ast_taskprocessor *tps);
-
static char *cli_tps_ping(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *cli_tps_report(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
@@ -472,8 +472,8 @@ static char *cli_tps_report(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
struct ao2_container *sorted_tps;
struct ast_taskprocessor *tps;
struct ao2_iterator iter;
-#define FMT_HEADERS "%-45s %10s %10s %10s\n"
-#define FMT_FIELDS "%-45s %10lu %10lu %10lu\n"
+#define FMT_HEADERS "%-45s %10s %10s %10s %10s %10s\n"
+#define FMT_FIELDS "%-45s %10lu %10lu %10lu %10lu %10lu\n"
switch (cmd) {
case CLI_INIT:
@@ -498,7 +498,7 @@ static char *cli_tps_report(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
return CLI_FAILURE;
}
- ast_cli(a->fd, "\n" FMT_HEADERS, "Processor", "Processed", "In Queue", "Max Depth");
+ ast_cli(a->fd, "\n" FMT_HEADERS, "Processor", "Processed", "In Queue", "Max Depth", "Low water", "High water");
tcount = 0;
iter = ao2_iterator_init(sorted_tps, AO2_ITERATOR_UNLINK);
while ((tps = ao2_iterator_next(&iter))) {
@@ -511,7 +511,8 @@ static char *cli_tps_report(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
maxqsize = 0;
processed = 0;
}
- ast_cli(a->fd, FMT_FIELDS, name, processed, qsize, maxqsize);
+ ast_cli(a->fd, FMT_FIELDS, name, processed, qsize, maxqsize,
+ tps->tps_queue_low, tps->tps_queue_high);
ast_taskprocessor_unreference(tps);
++tcount;
}
@@ -539,28 +540,106 @@ static int tps_cmp_cb(void *obj, void *arg, int flags)
return !strcasecmp(lhs->name, rhsname) ? CMP_MATCH | CMP_STOP : 0;
}
+/*! Count of the number of taskprocessors in high water alert. */
+static unsigned int tps_alert_count;
+
+/*! Access protection for tps_alert_count */
+AST_RWLOCK_DEFINE_STATIC(tps_alert_lock);
+
+/*!
+ * \internal
+ * \brief Add a delta to tps_alert_count with protection.
+ * \since 13.10.0
+ *
+ * \param tps Taskprocessor updating queue water mark alert trigger.
+ * \param delta The amount to add to tps_alert_count.
+ *
+ * \return Nothing
+ */
+static void tps_alert_add(struct ast_taskprocessor *tps, int delta)
+{
+ unsigned int old;
+
+ ast_rwlock_wrlock(&tps_alert_lock);
+ old = tps_alert_count;
+ tps_alert_count += delta;
+ if (DEBUG_ATLEAST(3)
+ /* and tps_alert_count becomes zero or non-zero */
+ && !old != !tps_alert_count) {
+ ast_log(LOG_DEBUG, "Taskprocessor '%s' %s the high water alert.\n",
+ tps->name, tps_alert_count ? "triggered" : "cleared");
+ }
+ ast_rwlock_unlock(&tps_alert_lock);
+}
+
+unsigned int ast_taskprocessor_alert_get(void)
+{
+ unsigned int count;
+
+ ast_rwlock_rdlock(&tps_alert_lock);
+ count = tps_alert_count;
+ ast_rwlock_unlock(&tps_alert_lock);
+
+ return count;
+}
+
+int ast_taskprocessor_alert_set_levels(struct ast_taskprocessor *tps, long low_water, long high_water)
+{
+ if (!tps || high_water < 0 || high_water < low_water) {
+ return -1;
+ }
+
+ if (low_water < 0) {
+ /* Set low water level to 90% of high water level */
+ low_water = (high_water * 9) / 10;
+ }
+
+ ao2_lock(tps);
+
+ tps->tps_queue_low = low_water;
+ tps->tps_queue_high = high_water;
+
+ if (tps->high_water_alert) {
+ if (!tps->tps_queue_size || tps->tps_queue_size < low_water) {
+ /* Update water mark alert immediately */
+ tps->high_water_alert = 0;
+ tps_alert_add(tps, -1);
+ }
+ } else {
+ if (high_water <= tps->tps_queue_size) {
+ /* Update water mark alert immediately */
+ tps->high_water_alert = 1;
+ tps_alert_add(tps, +1);
+ }
+ }
+
+ ao2_unlock(tps);
+
+ return 0;
+}
+
/* destroy the taskprocessor */
-static void tps_taskprocessor_destroy(void *tps)
+static void tps_taskprocessor_dtor(void *tps)
{
struct ast_taskprocessor *t = tps;
struct tps_task *task;
- if (!tps) {
- ast_log(LOG_ERROR, "missing taskprocessor\n");
- return;
+ while ((task = AST_LIST_REMOVE_HEAD(&t->tps_queue, list))) {
+ tps_task_free(task);
}
- ast_debug(1, "destroying taskprocessor '%s'\n", t->name);
- /* free it */
+ t->tps_queue_size = 0;
+
+ if (t->high_water_alert) {
+ t->high_water_alert = 0;
+ tps_alert_add(t, -1);
+ }
+
ast_free(t->stats);
t->stats = NULL;
ast_free((char *) t->name);
- if (t->listener) {
- ao2_ref(t->listener, -1);
- t->listener = NULL;
- }
- while ((task = AST_LIST_REMOVE_HEAD(&t->tps_queue, list))) {
- tps_task_free(task);
- }
+ t->name = NULL;
+ ao2_cleanup(t->listener);
+ t->listener = NULL;
}
/* pop the front task and return it */
@@ -569,7 +648,11 @@ static struct tps_task *tps_taskprocessor_pop(struct ast_taskprocessor *tps)
struct tps_task *task;
if ((task = AST_LIST_REMOVE_HEAD(&tps->tps_queue, list))) {
- tps->tps_queue_size--;
+ --tps->tps_queue_size;
+ if (tps->high_water_alert && tps->tps_queue_size <= tps->tps_queue_low) {
+ tps->high_water_alert = 0;
+ tps_alert_add(tps, -1);
+ }
}
return task;
}
@@ -648,19 +731,22 @@ static void *default_listener_pvt_alloc(void)
static struct ast_taskprocessor *__allocate_taskprocessor(const char *name, struct ast_taskprocessor_listener *listener)
{
- RAII_VAR(struct ast_taskprocessor *, p,
- ao2_alloc(sizeof(*p), tps_taskprocessor_destroy), ao2_cleanup);
+ struct ast_taskprocessor *p;
+ p = ao2_alloc(sizeof(*p), tps_taskprocessor_dtor);
if (!p) {
ast_log(LOG_WARNING, "failed to create taskprocessor '%s'\n", name);
return NULL;
}
- if (!(p->stats = ast_calloc(1, sizeof(*p->stats)))) {
- ast_log(LOG_WARNING, "failed to create taskprocessor stats for '%s'\n", name);
- return NULL;
- }
- if (!(p->name = ast_strdup(name))) {
+ /* Set default congestion water level alert triggers. */
+ p->tps_queue_low = (AST_TASKPROCESSOR_HIGH_WATER_LEVEL * 9) / 10;
+ p->tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
+
+ p->stats = ast_calloc(1, sizeof(*p->stats));
+ p->name = ast_strdup(name);
+ if (!p->stats || !p->name) {
+ ao2_ref(p, -1);
return NULL;
}
@@ -675,22 +761,18 @@ static struct ast_taskprocessor *__allocate_taskprocessor(const char *name, stru
if (!(ao2_link(tps_singletons, p))) {
ast_log(LOG_ERROR, "Failed to add taskprocessor '%s' to container\n", p->name);
listener->tps = NULL;
- ao2_ref(p, -1);
+ ao2_ref(p, -2);
return NULL;
}
if (p->listener->callbacks->start(p->listener)) {
- ast_log(LOG_ERROR, "Unable to start taskprocessor listener for taskprocessor %s\n", p->name);
+ ast_log(LOG_ERROR, "Unable to start taskprocessor listener for taskprocessor %s\n",
+ p->name);
ast_taskprocessor_unreference(p);
return NULL;
}
- /* RAII_VAR will decrement the refcount at the end of the function.
- * Since we want to pass back a reference to p, we bump the refcount
- */
- ao2_ref(p, +1);
return p;
-
}
/* Provide a reference to a taskprocessor. Create the taskprocessor if necessary, but don't
@@ -799,10 +881,16 @@ static int taskprocessor_push(struct ast_taskprocessor *tps, struct tps_task *t)
AST_LIST_INSERT_TAIL(&tps->tps_queue, t, list);
previous_size = tps->tps_queue_size++;
- if (previous_size >= AST_TASKPROCESSOR_HIGH_WATER_LEVEL && !tps->high_water_warned) {
- ast_log(LOG_WARNING, "The '%s' task processor queue reached %d scheduled tasks.\n",
- tps->name, previous_size);
- tps->high_water_warned = 1;
+ if (previous_size >= tps->tps_queue_high) {
+ if (!tps->high_water_warned) {
+ tps->high_water_warned = 1;
+ ast_log(LOG_WARNING, "The '%s' task processor queue reached %d scheduled tasks.\n",
+ tps->name, previous_size);
+ }
+ if (!tps->high_water_alert) {
+ tps->high_water_alert = 1;
+ tps_alert_add(tps, +1);
+ }
}
/* The currently executing task counts as still in queue */
diff --git a/res/ael/pval.c b/res/ael/pval.c
index d72ef0d59..07545f659 100644
--- a/res/ael/pval.c
+++ b/res/ael/pval.c
@@ -3355,9 +3355,9 @@ static int gen_prios(struct ael_extension *exten, char *label, pval *statement,
#ifdef OLD_RAND_ACTION
struct ael_priority *rand_test, *rand_end, *rand_skip;
#endif
- char *buf1;
- char *buf2;
- char *new_label;
+ RAII_VAR(char *, buf1, NULL, free);
+ RAII_VAR(char *, buf2, NULL, free);
+ RAII_VAR(char *, new_label, NULL, free);
char *strp, *strp2;
int default_exists;
int local_control_statement_count;
@@ -4191,9 +4191,6 @@ static int gen_prios(struct ael_extension *exten, char *label, pval *statement,
break;
}
}
- free(buf1);
- free(buf2);
- free(new_label);
return 0;
}
@@ -5052,7 +5049,10 @@ int pvalCheckType( pval *p, char *funcname, pvaltype type )
pval *pvalCreateNode( pvaltype type )
{
pval *p = calloc(1,sizeof(pval)); /* why, oh why, don't I use ast_calloc? Way, way, way too messy if I do! */
- p->type = type; /* remember, this can be used externally or internally to asterisk */
+ /* remember, this can be used externally or internally to asterisk */
+ if (p) {
+ p->type = type;
+ }
return p;
}
@@ -5413,14 +5413,30 @@ void pvalIncludesAddInclude( pval *p, const char *include )
void pvalIncludesAddIncludeWithTimeConstraints( pval *p, const char *include, char *hour_range, char *dom_range, char *dow_range, char *month_range )
{
- pval *hr = pvalCreateNode(PV_WORD);
- pval *dom = pvalCreateNode(PV_WORD);
- pval *dow = pvalCreateNode(PV_WORD);
- pval *mon = pvalCreateNode(PV_WORD);
- pval *s = pvalCreateNode(PV_WORD);
-
- if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES))
+ pval *hr;
+ pval *dom;
+ pval *dow;
+ pval *mon;
+ pval *s;
+
+ if (!pvalCheckType(p, "pvalIncludeAddIncludeWithTimeConstraints", PV_INCLUDES)) {
+ return;
+ }
+
+ hr = pvalCreateNode(PV_WORD);
+ dom = pvalCreateNode(PV_WORD);
+ dow = pvalCreateNode(PV_WORD);
+ mon = pvalCreateNode(PV_WORD);
+ s = pvalCreateNode(PV_WORD);
+
+ if (!hr || !dom || !dow || !mon || !s) {
+ destroy_pval(hr);
+ destroy_pval(dom);
+ destroy_pval(dow);
+ destroy_pval(mon);
+ destroy_pval(s);
return;
+ }
s->u1.str = (char *)include;
p->u1.list = linku1(p->u1.list, s);
@@ -5667,12 +5683,28 @@ char* pvalIfGetCondition( pval *p )
void pvalIfTimeSetCondition( pval *p, char *hour_range, char *dow_range, char *dom_range, char *mon_range ) /* time range format: 24-hour format begin-end|dow range|dom range|month range */
{
- pval *hr = pvalCreateNode(PV_WORD);
- pval *dow = pvalCreateNode(PV_WORD);
- pval *dom = pvalCreateNode(PV_WORD);
- pval *mon = pvalCreateNode(PV_WORD);
- if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME))
+ pval *hr;
+ pval *dow;
+ pval *dom;
+ pval *mon;
+
+ if (!pvalCheckType(p, "pvalIfTimeSetCondition", PV_IFTIME)) {
return;
+ }
+
+ hr = pvalCreateNode(PV_WORD);
+ dow = pvalCreateNode(PV_WORD);
+ dom = pvalCreateNode(PV_WORD);
+ mon = pvalCreateNode(PV_WORD);
+
+ if (!hr || !dom || !dow || !mon) {
+ destroy_pval(hr);
+ destroy_pval(dom);
+ destroy_pval(dow);
+ destroy_pval(mon);
+ return;
+ }
+
pvalWordSetString(hr, hour_range);
pvalWordSetString(dow, dow_range);
pvalWordSetString(dom, dom_range);
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index a86f3129c..39709d022 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -278,6 +278,7 @@ struct bridge_channel_control_thread_data {
struct ast_channel *bridge_channel;
struct stasis_app_control *control;
struct stasis_forward *forward;
+ char bridge_id[0];
};
static void *bridge_channel_control_thread(void *data)
@@ -286,6 +287,7 @@ static void *bridge_channel_control_thread(void *data)
struct ast_channel *bridge_channel = thread_data->bridge_channel;
struct stasis_app_control *control = thread_data->control;
struct stasis_forward *forward = thread_data->forward;
+ char *bridge_id = ast_strdupa(thread_data->bridge_id);
RAII_VAR(struct ast_callid *, callid, ast_channel_callid(bridge_channel), ast_callid_cleanup);
@@ -299,6 +301,7 @@ static void *bridge_channel_control_thread(void *data)
stasis_app_control_execute_until_exhausted(bridge_channel, control);
stasis_app_control_flush_queue(control);
+ stasis_app_bridge_playback_channel_remove(bridge_id, control);
stasis_forward_cancel(forward);
ao2_cleanup(control);
ast_hangup(bridge_channel);
@@ -464,8 +467,9 @@ static void ari_bridges_play_new(const char *args_media,
}
/* Give play_channel and control reference to the thread data */
- thread_data = ast_calloc(1, sizeof(*thread_data));
+ thread_data = ast_malloc(sizeof(*thread_data) + strlen(bridge->uniqueid) + 1);
if (!thread_data) {
+ stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
ast_ari_response_alloc_failed(response);
return;
}
@@ -473,8 +477,11 @@ static void ari_bridges_play_new(const char *args_media,
thread_data->bridge_channel = play_channel;
thread_data->control = control;
thread_data->forward = channel_forward;
+ /* Safe */
+ strcpy(thread_data->bridge_id, bridge->uniqueid);
if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) {
+ stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
ast_ari_response_alloc_failed(response);
ast_free(thread_data);
return;
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 9e2db9de6..6baac7a4e 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -912,6 +912,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
const char *args_channel_id,
const char *args_other_channel_id,
const char *args_originator,
+ const char *args_formats,
struct ast_ari_response *response)
{
char *dialtech;
@@ -930,6 +931,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
};
struct ari_origination *origination;
pthread_t thread;
+ struct ast_format_cap *format_cap = NULL;
if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid))
|| (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) {
@@ -944,6 +946,12 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
return;
}
+ if (!ast_strlen_zero(args_originator) && !ast_strlen_zero(args_formats)) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Originator and formats can't both be specified");
+ return;
+ }
+
dialtech = ast_strdupa(args_endpoint);
if ((stuff = strchr(dialtech, '/'))) {
*stuff++ = '\0';
@@ -1066,7 +1074,41 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
}
}
- if (ast_dial_prerun(dial, other, NULL)) {
+ if (!ast_strlen_zero(args_formats)) {
+ char *format_name;
+ char *formats_copy = ast_strdupa(args_formats);
+
+ if (!(format_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+ ast_ari_response_alloc_failed(response);
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ ast_channel_cleanup(other);
+ return;
+ }
+
+ while ((format_name = ast_strip(strsep(&formats_copy, ",")))) {
+ struct ast_format *fmt = ast_format_cache_get(format_name);
+
+ if (!fmt || ast_format_cap_append(format_cap, fmt, 0)) {
+ if (!fmt) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "Provided format (%s) was not found", format_name);
+ } else {
+ ast_ari_response_alloc_failed(response);
+ }
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ ast_channel_cleanup(other);
+ ao2_ref(format_cap, -1);
+ ao2_cleanup(fmt);
+ return;
+ }
+ ao2_ref(fmt, -1);
+ }
+ }
+
+ if (ast_dial_prerun(dial, other, format_cap)) {
ast_ari_response_alloc_failed(response);
ast_dial_destroy(dial);
ast_free(origination);
@@ -1075,6 +1117,7 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
}
ast_channel_cleanup(other);
+ ao2_cleanup(format_cap);
chan = ast_dial_get_channel(dial, 0);
if (!chan) {
@@ -1215,6 +1258,7 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
args->channel_id,
args->other_channel_id,
args->originator,
+ args->formats,
response);
ast_variables_destroy(variables);
}
@@ -1251,6 +1295,7 @@ void ast_ari_channels_originate(struct ast_variable *headers,
args->channel_id,
args->other_channel_id,
args->originator,
+ args->formats,
response);
ast_variables_destroy(variables);
}
diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h
index 4d3ad5f8b..5bb6f7f1e 100644
--- a/res/ari/resource_channels.h
+++ b/res/ari/resource_channels.h
@@ -78,6 +78,8 @@ struct ast_ari_channels_originate_args {
const char *other_channel_id;
/*! The unique id of the channel which is originating this one. */
const char *originator;
+ /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names an be found with "core show codecs". */
+ const char *formats;
};
/*!
* \brief Body parsing function for /channels.
@@ -141,6 +143,8 @@ struct ast_ari_channels_originate_with_id_args {
const char *other_channel_id;
/*! The unique id of the channel which is originating this one. */
const char *originator;
+ /*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names an be found with "core show codecs". */
+ const char *formats;
};
/*!
* \brief Body parsing function for /channels/{channelId}.
diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c
index d1ae80196..a14a9c8cb 100644
--- a/res/res_ari_channels.c
+++ b/res/res_ari_channels.c
@@ -157,6 +157,10 @@ int ast_ari_channels_originate_parse_body(
if (field) {
args->originator = ast_json_string_get(field);
}
+ field = ast_json_object_get(body, "formats");
+ if (field) {
+ args->formats = ast_json_string_get(field);
+ }
return 0;
}
@@ -217,6 +221,9 @@ static void ast_ari_channels_originate_cb(
if (strcmp(i->name, "originator") == 0) {
args.originator = (i->value);
} else
+ if (strcmp(i->name, "formats") == 0) {
+ args.formats = (i->value);
+ } else
{}
}
/* Look for a JSON request entity */
@@ -377,6 +384,10 @@ int ast_ari_channels_originate_with_id_parse_body(
if (field) {
args->originator = ast_json_string_get(field);
}
+ field = ast_json_object_get(body, "formats");
+ if (field) {
+ args->formats = ast_json_string_get(field);
+ }
return 0;
}
@@ -434,6 +445,9 @@ static void ast_ari_channels_originate_with_id_cb(
if (strcmp(i->name, "originator") == 0) {
args.originator = (i->value);
} else
+ if (strcmp(i->name, "formats") == 0) {
+ args.formats = (i->value);
+ } else
{}
}
for (i = path_vars; i; i = i->next) {
diff --git a/res/res_fax.c b/res/res_fax.c
index 6282b13d7..ad6e2386c 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -2846,11 +2846,8 @@ static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan) {
}
v21_details->caps = AST_FAX_TECH_V21_DETECT;
- if (!(v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
- ao2_ref(v21_details, -1);
- return NULL;
- }
-
+ v21_session = fax_session_new(v21_details, chan, NULL, NULL);
+ ao2_ref(v21_details, -1);
return v21_session;
}
diff --git a/res/res_format_attr_siren14.c b/res/res_format_attr_siren14.c
new file mode 100644
index 000000000..dea13aec6
--- /dev/null
+++ b/res/res_format_attr_siren14.c
@@ -0,0 +1,94 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Siren14 format attribute interface
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+
+/* Destroy is a required callback and must exist */
+static void siren14_destroy(struct ast_format *format)
+{
+}
+
+/* Clone is a required callback and must exist */
+static int siren14_clone(const struct ast_format *src, struct ast_format *dst)
+{
+ return 0;
+}
+
+static struct ast_format *siren14_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+ unsigned int val;
+
+ if (sscanf(attributes, "bitrate=%30u", &val) == 1) {
+ if (val != 48000) {
+ ast_log(LOG_WARNING, "Got siren14 offer at %u bps, but only 48000 bps supported; ignoring.\n", val);
+ return NULL;
+ }
+ }
+
+ /* We aren't modifying the format and once passed back it won't be touched, so use what we were given */
+ return ao2_bump((struct ast_format *)format);
+}
+
+static void siren14_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+ ast_str_append(str, 0, "a=fmtp:%u bitrate=48000\r\n", payload);
+}
+
+static struct ast_format_interface siren14_interface = {
+ .format_destroy = siren14_destroy,
+ .format_clone = siren14_clone,
+ .format_parse_sdp_fmtp = siren14_parse_sdp_fmtp,
+ .format_generate_sdp_fmtp = siren14_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+ if (ast_format_interface_register("siren14", &siren14_interface)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Siren14 Format Attribute Module",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_format_attr_siren7.c b/res/res_format_attr_siren7.c
new file mode 100644
index 000000000..840de4886
--- /dev/null
+++ b/res/res_format_attr_siren7.c
@@ -0,0 +1,94 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Siren7 format attribute interface
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+
+/* Destroy is a required callback and must exist */
+static void siren7_destroy(struct ast_format *format)
+{
+}
+
+/* Clone is a required callback and must exist */
+static int siren7_clone(const struct ast_format *src, struct ast_format *dst)
+{
+ return 0;
+}
+
+static struct ast_format *siren7_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+ unsigned int val;
+
+ if (sscanf(attributes, "bitrate=%30u", &val) == 1) {
+ if (val != 32000) {
+ ast_log(LOG_WARNING, "Got Siren7 offer at %u bps, but only 32000 bps supported; ignoring.\n", val);
+ return NULL;
+ }
+ }
+
+ /* We aren't modifying the format and once passed back it won't be touched, so use what we were given */
+ return ao2_bump((struct ast_format *)format);
+}
+
+static void siren7_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+ ast_str_append(str, 0, "a=fmtp:%u bitrate=32000\r\n", payload);
+}
+
+static struct ast_format_interface siren7_interface = {
+ .format_destroy = siren7_destroy,
+ .format_clone = siren7_clone,
+ .format_parse_sdp_fmtp = siren7_parse_sdp_fmtp,
+ .format_generate_sdp_fmtp = siren7_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+ if (ast_format_interface_register("siren7", &siren7_interface)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Siren7 Format Attribute Module",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);
diff --git a/res/res_hep.c b/res/res_hep.c
index 723b27df8..15e779012 100644
--- a/res/res_hep.c
+++ b/res/res_hep.c
@@ -409,9 +409,21 @@ enum hep_uuid_type hepv3_get_uuid_type(void)
{
RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
+ if (!config) {
+ /* Well, that's unfortunate. Return something. */
+ return HEP_UUID_TYPE_CALL_ID;
+ }
+
return config->general->uuid_type;
}
+int hepv3_is_loaded(void)
+{
+ RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
+
+ return (config != NULL) ? 1 : 0;
+}
+
struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t len)
{
struct hepv3_capture_info *info;
diff --git a/res/res_hep.exports.in b/res/res_hep.exports.in
index df0f2b4f7..e318ac97f 100644
--- a/res/res_hep.exports.in
+++ b/res/res_hep.exports.in
@@ -3,6 +3,7 @@
LINKER_SYMBOL_PREFIX*hepv3_send_packet;
LINKER_SYMBOL_PREFIX*hepv3_create_capture_info;
LINKER_SYMBOL_PREFIX*hepv3_get_uuid_type;
+ LINKER_SYMBOL_PREFIX*hepv3_is_loaded;
local:
*;
};
diff --git a/res/res_hep_pjsip.c b/res/res_hep_pjsip.c
index 936db9300..8f5baa2cb 100644
--- a/res/res_hep_pjsip.c
+++ b/res/res_hep_pjsip.c
@@ -210,6 +210,11 @@ static int load_module(void)
{
CHECK_PJSIP_MODULE_LOADED();
+ if (!ast_module_check("res_hep.so") || !hepv3_is_loaded()) {
+ ast_log(AST_LOG_WARNING, "res_hep is not loaded or running; declining module load\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
ast_sip_register_service(&logging_module);
return AST_MODULE_LOAD_SUCCESS;
}
diff --git a/res/res_hep_rtcp.c b/res/res_hep_rtcp.c
index 49a92539f..d77b19c92 100644
--- a/res/res_hep_rtcp.c
+++ b/res/res_hep_rtcp.c
@@ -149,6 +149,10 @@ static void rtp_topic_handler(void *data, struct stasis_subscription *sub, struc
static int load_module(void)
{
+ if (!ast_module_check("res_hep.so") || !hepv3_is_loaded()) {
+ ast_log(AST_LOG_WARNING, "res_hep is not loaded or running; declining module load\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
stasis_rtp_subscription = stasis_subscribe(ast_rtp_topic(),
rtp_topic_handler, NULL);
diff --git a/res/res_odbc.c b/res/res_odbc.c
index 17b7a76c8..b2204ff09 100644
--- a/res/res_odbc.c
+++ b/res/res_odbc.c
@@ -78,10 +78,19 @@ struct odbc_class
unsigned int forcecommit:1; /*!< Should uncommitted transactions be auto-committed on handle release? */
unsigned int isolation; /*!< Flags for how the DB should deal with data in other, uncommitted transactions */
unsigned int conntimeout; /*!< Maximum time the connection process should take */
+ unsigned int maxconnections; /*!< Maximum number of allowed connections */
/*! When a connection fails, cache that failure for how long? */
struct timeval negative_connection_cache;
/*! When a connection fails, when did that last occur? */
struct timeval last_negative_connect;
+ /*! A pool of available connections */
+ AST_LIST_HEAD_NOLOCK(, odbc_obj) connections;
+ /*! Lock to protect the connections */
+ ast_mutex_t lock;
+ /*! Condition to notify any pending connection requesters */
+ ast_cond_t cond;
+ /*! The total number of current connections */
+ size_t connection_cnt;
};
static struct ao2_container *class_container;
@@ -90,7 +99,7 @@ static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
static odbc_status odbc_obj_connect(struct odbc_obj *obj);
static odbc_status odbc_obj_disconnect(struct odbc_obj *obj);
-static int odbc_register_class(struct odbc_class *class, int connect);
+static void odbc_register_class(struct odbc_class *class, int connect);
AST_THREADSTORAGE(errors_buf);
@@ -157,6 +166,8 @@ int ast_odbc_text2isolation(const char *txt)
static void odbc_class_destructor(void *data)
{
struct odbc_class *class = data;
+ struct odbc_obj *obj;
+
/* Due to refcounts, we can safely assume that any objects with a reference
* to us will prevent our destruction, so we don't need to worry about them.
*/
@@ -169,7 +180,14 @@ static void odbc_class_destructor(void *data)
if (class->sanitysql) {
ast_free(class->sanitysql);
}
+
+ while ((obj = AST_LIST_REMOVE_HEAD(&class->connections, list))) {
+ ao2_ref(obj, -1);
+ }
+
SQLFreeHandle(SQL_HANDLE_ENV, class->env);
+ ast_mutex_destroy(&class->lock);
+ ast_cond_destroy(&class->cond);
}
static int null_hash_fn(const void *obj, const int flags)
@@ -180,21 +198,23 @@ static int null_hash_fn(const void *obj, const int flags)
static void odbc_obj_destructor(void *data)
{
struct odbc_obj *obj = data;
- struct odbc_class *class = obj->parent;
- obj->parent = NULL;
+
odbc_obj_disconnect(obj);
- ao2_ref(class, -1);
}
-static void destroy_table_cache(struct odbc_cache_tables *table) {
+static void destroy_table_cache(struct odbc_cache_tables *table)
+{
struct odbc_cache_columns *col;
+
ast_debug(1, "Destroying table cache for %s\n", table->table);
+
AST_RWLIST_WRLOCK(&table->columns);
while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
ast_free(col);
}
AST_RWLIST_UNLOCK(&table->columns);
AST_RWLIST_HEAD_DESTROY(&table->columns);
+
ast_free(table);
}
@@ -370,18 +390,19 @@ SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT (*prepare_c
* We must therefore redo everything when we establish a new
* connection. */
stmt = prepare_cb(obj, data);
+ if (!stmt) {
+ return NULL;
+ }
- if (stmt) {
- res = SQLExecute(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
- if (res == SQL_ERROR) {
- ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
- }
-
- ast_log(LOG_WARNING, "SQL Execute error %d!\n", res);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- stmt = NULL;
+ res = SQLExecute(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
+ if (res == SQL_ERROR) {
+ ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
}
+
+ ast_log(LOG_WARNING, "SQL Execute error %d!\n", res);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ stmt = NULL;
}
return stmt;
@@ -468,7 +489,7 @@ static int load_odbc_config(void)
struct ast_variable *v;
char *cat;
const char *dsn, *username, *password, *sanitysql;
- int enabled, bse, conntimeout, forcecommit, isolation;
+ int enabled, bse, conntimeout, forcecommit, isolation, maxconnections;
struct timeval ncache = { 0, 0 };
int preconnect = 0, res = 0;
struct ast_flags config_flags = { 0 };
@@ -495,6 +516,7 @@ static int load_odbc_config(void)
conntimeout = 10;
forcecommit = 0;
isolation = SQL_TXN_READ_COMMITTED;
+ maxconnections = 1;
for (v = ast_variable_browse(config, cat); v; v = v->next) {
if (!strcasecmp(v->name, "pooling") ||
!strncasecmp(v->name, "share", 5) ||
@@ -538,6 +560,11 @@ static int load_odbc_config(void)
ast_log(LOG_ERROR, "Unrecognized value for 'isolation': '%s' in section '%s'\n", v->value, cat);
isolation = SQL_TXN_READ_COMMITTED;
}
+ } else if (!strcasecmp(v->name, "max_connections")) {
+ if (sscanf(v->value, "%30d", &maxconnections) != 1 || maxconnections < 1) {
+ ast_log(LOG_WARNING, "max_connections must be a positive integer\n");
+ maxconnections = 1;
+ }
}
}
@@ -563,6 +590,7 @@ static int load_odbc_config(void)
new->isolation = isolation;
new->conntimeout = conntimeout;
new->negative_connection_cache = ncache;
+ new->maxconnections = maxconnections;
if (cat)
ast_copy_string(new->name, cat, sizeof(new->name));
@@ -581,6 +609,9 @@ static int load_odbc_config(void)
break;
}
+ ast_mutex_init(&new->lock);
+ ast_cond_init(&new->cond, NULL);
+
odbc_register_class(new, preconnect);
ast_log(LOG_NOTICE, "Registered ODBC class '%s' dsn->[%s]\n", cat, dsn);
ao2_ref(new, -1);
@@ -641,6 +672,7 @@ static char *handle_cli_odbc_show(struct ast_cli_entry *e, int cmd, struct ast_c
ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
ast_cli(a->fd, " Name: %s\n DSN: %s\n", class->name, class->dsn);
ast_cli(a->fd, " Last connection attempt: %s\n", timestr);
+ ast_cli(a->fd, " Number of active connections: %zd (out of %d)\n", class->connection_cnt, class->maxconnections);
ast_cli(a->fd, "\n");
}
ao2_ref(class, -1);
@@ -654,38 +686,47 @@ static struct ast_cli_entry cli_odbc[] = {
AST_CLI_DEFINE(handle_cli_odbc_show, "List ODBC DSN(s)")
};
-static int odbc_register_class(struct odbc_class *class, int preconnect)
+static void odbc_register_class(struct odbc_class *class, int preconnect)
{
struct odbc_obj *obj;
- if (class) {
- ao2_link(class_container, class);
- /* I still have a reference in the caller, so a deref is NOT missing here. */
-
- if (preconnect) {
- /* Request and release builds a connection */
- obj = ast_odbc_request_obj(class->name, 0);
- if (obj) {
- ast_odbc_release_obj(obj);
- }
- }
- return 0;
- } else {
- ast_log(LOG_WARNING, "Attempted to register a NULL class?\n");
- return -1;
+ ao2_link(class_container, class);
+ /* I still have a reference in the caller, so a deref is NOT missing here. */
+
+ if (!preconnect) {
+ return;
}
+
+ /* Request and release builds a connection */
+ obj = ast_odbc_request_obj(class->name, 0);
+ if (obj) {
+ ast_odbc_release_obj(obj);
+ }
+
+ return;
}
void ast_odbc_release_obj(struct odbc_obj *obj)
{
- ast_debug(2, "Releasing ODBC handle %p\n", obj);
+ struct odbc_class *class = obj->parent;
-#ifdef DEBUG_THREADS
- obj->file[0] = '\0';
- obj->function[0] = '\0';
- obj->lineno = 0;
-#endif
- ao2_ref(obj, -1);
+ ast_debug(2, "Releasing ODBC handle %p into pool\n", obj);
+
+ /* The odbc_obj only holds a reference to the class when it is
+ * actively being used. This guarantees no circular reference
+ * between odbc_class and odbc_obj. Since it is being released
+ * we also release our class reference. If a reload occurred before
+ * the class will go away automatically once all odbc_obj are
+ * released back.
+ */
+ obj->parent = NULL;
+
+ ast_mutex_lock(&class->lock);
+ AST_LIST_INSERT_HEAD(&class->connections, obj, list);
+ ast_cond_signal(&class->cond);
+ ast_mutex_unlock(&class->lock);
+
+ ao2_ref(class, -1);
}
int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
@@ -703,6 +744,50 @@ static int aoro2_class_cb(void *obj, void *arg, int flags)
return 0;
}
+/*
+ * \brief Determine if the connection has died.
+ *
+ * \param connection The connection to check
+ * \param class The ODBC class
+ * \retval 1 Yep, it's dead
+ * \retval 0 It's alive and well
+ */
+static int connection_dead(struct odbc_obj *connection, struct odbc_class *class)
+{
+ char *test_sql = "select 1";
+ SQLINTEGER dead;
+ SQLRETURN res;
+ SQLHSTMT stmt;
+
+ res = SQLGetConnectAttr(connection->con, SQL_ATTR_CONNECTION_DEAD, &dead, 0, 0);
+ if (SQL_SUCCEEDED(res)) {
+ return dead == SQL_CD_TRUE ? 1 : 0;
+ }
+
+ /* If the Driver doesn't support SQL_ATTR_CONNECTION_DEAD do a
+ * probing query instead
+ */
+ res = SQLAllocHandle(SQL_HANDLE_STMT, connection->con, &stmt);
+ if (!SQL_SUCCEEDED(res)) {
+ return 1;
+ }
+
+ if (!ast_strlen_zero(class->sanitysql)) {
+ test_sql = class->sanitysql;
+ }
+
+ res = SQLPrepare(stmt, (unsigned char *)test_sql, SQL_NTS);
+ if (!SQL_SUCCEEDED(res)) {
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return 1;
+ }
+
+ res = SQLExecute(stmt);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+ return SQL_SUCCEEDED(res) ? 0 : 1;
+}
+
struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno)
{
struct odbc_obj *obj = NULL;
@@ -713,17 +798,60 @@ struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags
return NULL;
}
- /* XXX ODBC connection objects do not have shared ownership, so there is no reason
- * to use refcounted objects here.
- */
- obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
- /* Inherit reference from the ao2_callback from before */
- obj->parent = class;
- if (odbc_obj_connect(obj) == ODBC_FAIL) {
- ao2_ref(obj, -1);
- return NULL;
+ ast_mutex_lock(&class->lock);
+
+ while (!obj) {
+ obj = AST_LIST_REMOVE_HEAD(&class->connections, list);
+
+ if (!obj) {
+ if (class->connection_cnt < class->maxconnections) {
+ /* If no connection is immediately available establish a new
+ * one if allowed. If we try and fail we give up completely as
+ * we could go into an infinite loop otherwise.
+ */
+ obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
+ if (!obj) {
+ break;
+ }
+
+ obj->parent = ao2_bump(class);
+ if (odbc_obj_connect(obj) == ODBC_FAIL) {
+ ao2_ref(obj->parent, -1);
+ ao2_ref(obj, -1);
+ obj = NULL;
+ break;
+ }
+
+ class->connection_cnt++;
+ ast_debug(2, "Created ODBC handle %p on class '%s', new count is %zd\n", obj,
+ name, class->connection_cnt);
+ } else {
+ /* Otherwise if we're not allowed to create a new one we
+ * wait for another thread to give up the connection they
+ * own.
+ */
+ ast_cond_wait(&class->cond, &class->lock);
+ }
+ } else if (connection_dead(obj, class)) {
+ /* If the connection is dead try to grab another functional one from the
+ * pool instead of trying to resurrect this one.
+ */
+ ao2_ref(obj, -1);
+ obj = NULL;
+ class->connection_cnt--;
+ ast_debug(2, "ODBC handle %p dead - removing from class '%s', new count is %zd\n",
+ obj, name, class->connection_cnt);
+ } else {
+ /* We successfully grabbed a connection from the pool and all is well!
+ */
+ obj->parent = ao2_bump(class);
+ ast_debug(2, "Reusing ODBC handle %p from class '%s'\n", obj, name);
+ }
}
+ ast_mutex_unlock(&class->lock);
+ ao2_ref(class, -1);
+
return obj;
}
@@ -755,14 +883,6 @@ static odbc_status odbc_obj_disconnect(struct odbc_obj *obj)
obj->con = NULL;
res = SQLDisconnect(con);
- if (obj->parent) {
- if (res == SQL_SUCCESS || res == SQL_SUCCESS_WITH_INFO) {
- ast_debug(3, "Disconnected %d from %s [%s](%p)\n", res, obj->parent->name, obj->parent->dsn, obj);
- } else {
- ast_debug(3, "res_odbc: %s [%s](%p) already disconnected\n", obj->parent->name, obj->parent->dsn, obj);
- }
- }
-
if ((res = SQLFreeHandle(SQL_HANDLE_DBC, con)) == SQL_SUCCESS) {
ast_debug(3, "Database handle %p (connection %p) deallocated\n", obj, con);
} else {
diff --git a/res/res_phoneprov.c b/res/res_phoneprov.c
index df93c5bbc..71f875753 100644
--- a/res/res_phoneprov.c
+++ b/res/res_phoneprov.c
@@ -410,10 +410,13 @@ static int load_file(const char *filename, char **ret)
fseek(f, 0, SEEK_END);
len = ftell(f);
fseek(f, 0, SEEK_SET);
- if (!(*ret = ast_malloc(len + 1)))
+ if (!(*ret = ast_malloc(len + 1))) {
+ fclose(f);
return -2;
+ }
if (len != fread(*ret, sizeof(char), len, f)) {
+ fclose(f);
free(*ret);
*ret = NULL;
return -3;
diff --git a/res/res_pjproject.c b/res/res_pjproject.c
index f54c3713e..08699f3ee 100644
--- a/res/res_pjproject.c
+++ b/res/res_pjproject.c
@@ -177,7 +177,6 @@ static void log_forwarder(int level, const char *data, int len)
const char * log_source = "pjproject";
int log_line = 0;
const char *log_func = "<?>";
- int mod_level;
if (pjproject_log_intercept.fd != -1
&& pjproject_log_intercept.thread == pthread_self()) {
@@ -196,10 +195,8 @@ static void log_forwarder(int level, const char *data, int len)
}
if (ast_level == __LOG_DEBUG) {
- /* For levels 3 and up, obey the debug level for res_pjproject */
- mod_level = ast_opt_dbg_module ?
- ast_debug_get_by_module("res_pjproject") : 0;
- if (option_debug < level && mod_level < level) {
+ /* Obey the debug level for res_pjproject */
+ if (!DEBUG_ATLEAST(level)) {
return;
}
}
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 97ab8e09e..4468975c9 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -3416,7 +3416,7 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
ast_debug(2, "%p: Set timer to %d msec\n", req_wrapper, timeout);
pj_timer_entry_init(req_wrapper->timeout_timer, TIMEOUT_TIMER2,
- req_wrapper, &send_request_timer_callback);
+ req_wrapper, send_request_timer_callback);
pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(endpt),
req_wrapper->timeout_timer, TIMER_INACTIVE);
@@ -3425,8 +3425,18 @@ static pj_status_t endpt_send_request(struct ast_sip_endpoint *endpoint,
* timer callback is executed.
*/
ao2_ref(req_wrapper, +1);
- pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
+ ret_val = pj_timer_heap_schedule(pjsip_endpt_get_timer_heap(endpt),
req_wrapper->timeout_timer, &timeout_timer_val);
+ if (ret_val != PJ_SUCCESS) {
+ ao2_unlock(req_wrapper);
+ ast_log(LOG_ERROR,
+ "Failed to set timer. Not sending %.*s request to endpoint %s.\n",
+ (int) pj_strlen(&tdata->msg->line.req.method.name),
+ pj_strbuf(&tdata->msg->line.req.method.name),
+ endpoint ? ast_sorcery_object_get_id(endpoint) : "<unknown>");
+ ao2_t_ref(req_wrapper, -2, "Drop timer and routine ref");
+ return ret_val;
+ }
req_wrapper->timeout_timer->id = TIMEOUT_TIMER2;
} else {
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index 1b7850f5f..9c08ccee6 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -25,6 +25,7 @@
#include "asterisk/astobj2.h"
#include "asterisk/paths.h"
#include "asterisk/sorcery.h"
+#include "asterisk/taskprocessor.h"
#include "include/res_pjsip_private.h"
#include "asterisk/res_pjsip_cli.h"
#include "asterisk/statsd.h"
@@ -1124,6 +1125,8 @@ int ast_sip_initialize_sorcery_location(void)
ast_pjproject_get_buildopt("PJSIP_MAX_URL_SIZE", "%d", &pjsip_max_url_size);
ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar");
+ ast_sorcery_object_set_congestion_levels(sorcery, "contact", -1,
+ 3 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
ast_sorcery_apply_default(sorcery, "aor", "config", "pjsip.conf,criteria=type=aor");
if (ast_sorcery_object_register(sorcery, "contact", contact_alloc, NULL, contact_apply_handler) ||
@@ -1140,7 +1143,7 @@ int ast_sip_initialize_sorcery_location(void)
ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
ast_sorcery_object_field_register(sorcery, "contact", "qualify_timeout", "3.0", OPT_DOUBLE_T, 0, FLDSET(struct ast_sip_contact, qualify_timeout));
- ast_sorcery_object_field_register(sorcery, "contact", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify));
+ ast_sorcery_object_field_register(sorcery, "contact", "authenticate_qualify", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, authenticate_qualify));
ast_sorcery_object_field_register(sorcery, "contact", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, outbound_proxy));
ast_sorcery_object_field_register(sorcery, "contact", "user_agent", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, user_agent));
ast_sorcery_object_field_register(sorcery, "contact", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, endpoint_name));
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c
index 0d3df06f0..e8ed89361 100644
--- a/res/res_pjsip/pjsip_distributor.c
+++ b/res/res_pjsip/pjsip_distributor.c
@@ -59,6 +59,12 @@ struct unidentified_request{
char src_name[];
};
+/*! Number of serializers in pool if one not otherwise known. (Best if prime number) */
+#define DISTRIBUTOR_POOL_SIZE 31
+
+/*! Pool of serializers to use if not supplied. */
+static struct ast_taskprocessor *distributor_pool[DISTRIBUTOR_POOL_SIZE];
+
/*!
* \internal
* \brief Record the task's serializer name on the tdata structure.
@@ -278,6 +284,83 @@ static pjsip_dialog *find_dialog(pjsip_rx_data *rdata)
return dlg;
}
+/*!
+ * \internal
+ * \brief Compute a hash value on a pjlib string
+ * \since 13.10.0
+ *
+ * \param[in] str The pjlib string to add to the hash
+ * \param[in] hash The hash value to add to
+ *
+ * \details
+ * This version of the function is for when you need to compute a
+ * string hash of more than one string.
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * \sa http://www.cse.yorku.ca/~oz/hash.html
+ */
+static int pjstr_hash_add(pj_str_t *str, int hash)
+{
+ size_t len;
+ const char *pos;
+
+ len = pj_strlen(str);
+ pos = pj_strbuf(str);
+ while (len--) {
+ hash = hash * 33 ^ *pos++;
+ }
+
+ return hash;
+}
+
+/*!
+ * \internal
+ * \brief Compute a hash value on a pjlib string
+ * \since 13.10.0
+ *
+ * \param[in] str The pjlib string to hash
+ *
+ * This famous hash algorithm was written by Dan Bernstein and is
+ * commonly used.
+ *
+ * http://www.cse.yorku.ca/~oz/hash.html
+ */
+static int pjstr_hash(pj_str_t *str)
+{
+ return pjstr_hash_add(str, 5381);
+}
+
+struct ast_taskprocessor *ast_sip_get_distributor_serializer(pjsip_rx_data *rdata)
+{
+ int hash;
+ pj_str_t *remote_tag;
+ struct ast_taskprocessor *serializer;
+
+ if (!rdata->msg_info.msg) {
+ return NULL;
+ }
+
+ if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
+ remote_tag = &rdata->msg_info.from->tag;
+ } else {
+ remote_tag = &rdata->msg_info.to->tag;
+ }
+
+ /* Compute the hash from the SIP message call-id and remote-tag */
+ hash = pjstr_hash(&rdata->msg_info.cid->id);
+ hash = pjstr_hash_add(remote_tag, hash);
+ hash = abs(hash);
+
+ serializer = ao2_bump(distributor_pool[hash % ARRAY_LEN(distributor_pool)]);
+ if (serializer) {
+ ast_debug(3, "Calculated serializer %s to use for %s\n",
+ ast_taskprocessor_name(serializer), pjsip_rx_data_get_info(rdata));
+ }
+ return serializer;
+}
+
static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata);
static pjsip_module endpoint_mod = {
@@ -286,24 +369,31 @@ static pjsip_module endpoint_mod = {
.on_rx_request = endpoint_lookup,
};
-#define SIP_MAX_QUEUE (AST_TASKPROCESSOR_HIGH_WATER_LEVEL * 3)
-
static pj_bool_t distributor(pjsip_rx_data *rdata)
{
- pjsip_dialog *dlg = find_dialog(rdata);
+ pjsip_dialog *dlg;
struct distributor_dialog_data *dist = NULL;
struct ast_taskprocessor *serializer = NULL;
pjsip_rx_data *clone;
+ if (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
+ /*
+ * Ignore everything until we are fully booted. Let the
+ * peer retransmit messages until we are ready.
+ */
+ return PJ_TRUE;
+ }
+
+ dlg = find_dialog(rdata);
if (dlg) {
ast_debug(3, "Searching for serializer on dialog %s for %s\n",
- dlg->obj_name, rdata->msg_info.info);
+ dlg->obj_name, pjsip_rx_data_get_info(rdata));
dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id);
if (dist) {
serializer = ao2_bump(dist->serializer);
if (serializer) {
ast_debug(3, "Found serializer %s on dialog %s\n",
- ast_taskprocessor_name(serializer), dlg->obj_name);
+ ast_taskprocessor_name(serializer), dlg->obj_name);
}
}
pjsip_dlg_dec_lock(dlg);
@@ -313,14 +403,47 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
/* We have a serializer so we know where to send the message. */
} else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) {
ast_debug(3, "No dialog serializer for response %s. Using request transaction as basis\n",
- rdata->msg_info.info);
+ pjsip_rx_data_get_info(rdata));
serializer = find_request_serializer(rdata);
+ if (!serializer) {
+ if (ast_taskprocessor_alert_get()) {
+ /* We're overloaded, ignore the unmatched response. */
+ ast_debug(3, "Taskprocessor overload alert: Ignoring unmatched '%s'.\n",
+ pjsip_rx_data_get_info(rdata));
+ return PJ_TRUE;
+ }
+
+ /*
+ * Pick a serializer for the unmatched response. Maybe
+ * the stack can figure out what it is for, or we really
+ * should just toss it regardless.
+ */
+ serializer = ast_sip_get_distributor_serializer(rdata);
+ }
} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)
|| !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) {
/* We have a BYE or CANCEL request without a serializer. */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata,
PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL);
return PJ_TRUE;
+ } else {
+ if (ast_taskprocessor_alert_get()) {
+ /*
+ * When taskprocessors get backed up, there is a good chance that
+ * we are being overloaded and need to defer adding new work to
+ * the system. To defer the work we will ignore the request and
+ * rely on the peer's transport layer to retransmit the message.
+ * We usually work off the overload within a few seconds. The
+ * alternative is to send back a 503 response to these requests
+ * and be done with it.
+ */
+ ast_debug(3, "Taskprocessor overload alert: Ignoring '%s'.\n",
+ pjsip_rx_data_get_info(rdata));
+ return PJ_TRUE;
+ }
+
+ /* Pick a serializer for the out-of-dialog request. */
+ serializer = ast_sip_get_distributor_serializer(rdata);
}
pjsip_rx_data_clone(rdata, 0, &clone);
@@ -329,18 +452,9 @@ static pj_bool_t distributor(pjsip_rx_data *rdata)
clone->endpt_info.mod_data[endpoint_mod.id] = ao2_bump(dist->endpoint);
}
- if (ast_sip_threadpool_queue_size() > SIP_MAX_QUEUE) {
- /* When the threadpool is backed up this much, there is a good chance that we have encountered
- * some sort of terrible condition and don't need to be adding more work to the threadpool.
- * It's in our best interest to send back a 503 response and be done with it.
- */
- if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 503, NULL, NULL, NULL);
- }
+ if (ast_sip_push_task(serializer, distribute, clone)) {
ao2_cleanup(clone->endpt_info.mod_data[endpoint_mod.id]);
pjsip_rx_data_free_cloned(clone);
- } else {
- ast_sip_push_task(serializer, distribute, clone);
}
ast_taskprocessor_unreference(serializer);
@@ -787,6 +901,7 @@ static int cli_unid_print_header(void *obj, void *arg, int flags)
return 0;
}
+
static int cli_unid_print_body(void *obj, void *arg, int flags)
{
struct unidentified_request *unid = obj;
@@ -877,6 +992,47 @@ static struct ast_sorcery_observer global_observer = {
.loaded = global_loaded,
};
+/*!
+ * \internal
+ * \brief Shutdown the serializers in the distributor pool.
+ * \since 13.10.0
+ *
+ * \return Nothing
+ */
+static void distributor_pool_shutdown(void)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_LEN(distributor_pool); ++idx) {
+ ast_taskprocessor_unreference(distributor_pool[idx]);
+ distributor_pool[idx] = NULL;
+ }
+}
+
+/*!
+ * \internal
+ * \brief Setup the serializers in the distributor pool.
+ * \since 13.10.0
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int distributor_pool_setup(void)
+{
+ char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
+ int idx;
+
+ for (idx = 0; idx < ARRAY_LEN(distributor_pool); ++idx) {
+ /* Create name with seq number appended. */
+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/distributor");
+
+ distributor_pool[idx] = ast_sip_create_serializer_named(tps_name);
+ if (!distributor_pool[idx]) {
+ return -1;
+ }
+ }
+ return 0;
+}
int ast_sip_initialize_distributor(void)
{
@@ -886,6 +1042,11 @@ int ast_sip_initialize_distributor(void)
return -1;
}
+ if (distributor_pool_setup()) {
+ ast_sip_destroy_distributor();
+ return -1;
+ }
+
prune_context = ast_sched_context_create();
if (!prune_context) {
ast_sip_destroy_distributor();
@@ -918,8 +1079,10 @@ int ast_sip_initialize_distributor(void)
return -1;
}
- unid_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
+ unid_formatter = ao2_alloc_options(sizeof(struct ast_sip_cli_formatter_entry), NULL,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!unid_formatter) {
+ ast_sip_destroy_distributor();
ast_log(LOG_ERROR, "Unable to allocate memory for unid_formatter\n");
return -1;
}
@@ -931,6 +1094,7 @@ int ast_sip_initialize_distributor(void)
unid_formatter->get_id = cli_unid_get_id;
unid_formatter->retrieve_by_id = cli_unid_retrieve_by_id;
ast_sip_register_cli_formatter(unid_formatter);
+
ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
return 0;
@@ -941,17 +1105,20 @@ void ast_sip_destroy_distributor(void)
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
ast_sip_unregister_cli_formatter(unid_formatter);
- internal_sip_unregister_service(&distributor_mod);
- internal_sip_unregister_service(&endpoint_mod);
internal_sip_unregister_service(&auth_mod);
+ internal_sip_unregister_service(&endpoint_mod);
+ internal_sip_unregister_service(&distributor_mod);
ao2_cleanup(artificial_auth);
ao2_cleanup(artificial_endpoint);
- ao2_cleanup(unidentified_requests);
ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &global_observer);
if (prune_context) {
ast_sched_context_destroy(prune_context);
}
+
+ distributor_pool_shutdown();
+
+ ao2_cleanup(unidentified_requests);
}
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c
index ede0d5eba..e220f90e4 100644
--- a/res/res_pjsip/pjsip_options.c
+++ b/res/res_pjsip/pjsip_options.c
@@ -31,6 +31,7 @@
#include "asterisk/test.h"
#include "asterisk/statsd.h"
#include "include/res_pjsip_private.h"
+#include "asterisk/taskprocessor.h"
#define DEFAULT_LANGUAGE "en"
#define DEFAULT_ENCODING "text/plain"
@@ -1020,6 +1021,8 @@ int ast_sip_initialize_sorcery_qualify(void)
/* initialize sorcery ast_sip_contact_status resource */
ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL);
+ ast_sorcery_object_set_congestion_levels(sorcery, CONTACT_STATUS, -1,
+ 3 * AST_TASKPROCESSOR_HIGH_WATER_LEVEL);
if (ast_sorcery_internal_object_register(sorcery, CONTACT_STATUS,
contact_status_alloc, NULL, NULL)) {
diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c
index 596223293..594c0fdac 100644
--- a/res/res_pjsip_messaging.c
+++ b/res/res_pjsip_messaging.c
@@ -476,6 +476,24 @@ static enum pjsip_status_code rx_data_to_ast_msg(pjsip_rx_data *rdata, struct as
field = pj_sockaddr_print(&rdata->pkt_info.src_addr, buf, sizeof(buf) - 1, 1);
res |= ast_msg_set_var(msg, "PJSIP_RECVADDR", field);
+ switch (rdata->tp_info.transport->key.type) {
+ case PJSIP_TRANSPORT_UDP:
+ case PJSIP_TRANSPORT_UDP6:
+ field = "udp";
+ break;
+ case PJSIP_TRANSPORT_TCP:
+ case PJSIP_TRANSPORT_TCP6:
+ field = "tcp";
+ break;
+ case PJSIP_TRANSPORT_TLS:
+ case PJSIP_TRANSPORT_TLS6:
+ field = "tls";
+ break;
+ default:
+ field = rdata->tp_info.transport->type_name;
+ }
+ ast_msg_set_var(msg, "PJSIP_TRANSPORT", field);
+
if (print_body(rdata, buf, sizeof(buf) - 1) > 0) {
res |= ast_msg_set_body(msg, "%s", buf);
}
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index 7ed804acf..65c92c72f 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -355,7 +355,7 @@ struct ast_sip_publication {
struct subscription_persistence {
/*! Sorcery object details */
SORCERY_OBJECT(details);
- /*! The name of the endpoint involved in the subscrption */
+ /*! The name of the endpoint involved in the subscription */
char *endpoint;
/*! SIP message that creates the subscription */
char packet[PJSIP_MAX_PKT_LEN];
@@ -378,6 +378,20 @@ struct subscription_persistence {
};
/*!
+ * \brief The state of the subscription tree
+ */
+enum sip_subscription_tree_state {
+ /*! Normal operation */
+ SIP_SUB_TREE_NORMAL = 0,
+ /*! A terminate has been requested by Asterisk, the client, or pjproject */
+ SIP_SUB_TREE_TERMINATE_PENDING,
+ /*! The terminate is in progress */
+ SIP_SUB_TREE_TERMINATE_IN_PROGRESS,
+ /*! The terminate process has finished and the subscription tree is no longer valid */
+ SIP_SUB_TREE_TERMINATED,
+};
+
+/*!
* \brief A tree of SIP subscriptions
*
* Because of the ability to subscribe to resource lists, a SIP
@@ -411,8 +425,8 @@ struct sip_subscription_tree {
int is_list;
/*! Next item in the list */
AST_LIST_ENTRY(sip_subscription_tree) next;
- /*! Indicates that a NOTIFY is currently being sent on the SIP subscription */
- int last_notify;
+ /*! Subscription tree state */
+ enum sip_subscription_tree_state state;
};
/*!
@@ -879,15 +893,15 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
"allocation error afterwards\n", resource);
continue;
}
- ast_debug(1, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
+ ast_debug(2, "Subscription to leaf resource %s resulted in success. Adding to parent %s\n",
resource, parent->resource);
AST_VECTOR_APPEND(&parent->children, current);
} else {
- ast_debug(1, "Subscription to leaf resource %s resulted in error response %d\n",
+ ast_debug(2, "Subscription to leaf resource %s resulted in error response %d\n",
resource, resp);
}
} else {
- ast_debug(1, "Resource %s (child of %s) is a list\n", resource, parent->resource);
+ ast_debug(2, "Resource %s (child of %s) is a list\n", resource, parent->resource);
current = tree_node_alloc(resource, visited, child_list->full_state);
if (!current) {
ast_debug(1, "Cannot build children of resource %s due to allocation failure\n", resource);
@@ -898,7 +912,7 @@ static void build_node_children(struct ast_sip_endpoint *endpoint, const struct
ast_debug(1, "List %s had no successful children.\n", resource);
AST_VECTOR_APPEND(&parent->children, current);
} else {
- ast_debug(1, "List %s had successful children. Adding to parent %s\n",
+ ast_debug(2, "List %s had successful children. Adding to parent %s\n",
resource, parent->resource);
tree_node_destroy(current);
}
@@ -970,7 +984,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
struct resources visited;
if (!has_eventlist_support || !(list = retrieve_resource_list(resource, handler->event_name))) {
- ast_debug(1, "Subscription to resource %s is not to a list\n", resource);
+ ast_debug(2, "Subscription to resource %s is not to a list\n", resource);
tree->root = tree_node_alloc(resource, NULL, 0);
if (!tree->root) {
return 500;
@@ -978,7 +992,7 @@ static int build_resource_tree(struct ast_sip_endpoint *endpoint, const struct a
return handler->notifier->new_subscribe(endpoint, resource);
}
- ast_debug(1, "Subscription to resource %s is a list\n", resource);
+ ast_debug(2, "Subscription to resource %s is a list\n", resource);
if (AST_VECTOR_INIT(&visited, AST_VECTOR_SIZE(&list->items))) {
return 500;
}
@@ -1037,7 +1051,7 @@ static void remove_subscription(struct sip_subscription_tree *obj)
if (i == obj) {
AST_RWLIST_REMOVE_CURRENT(next);
if (i->root) {
- ast_debug(1, "Removing subscription to resource %s from list of subscriptions\n",
+ ast_debug(2, "Removing subscription to resource %s from list of subscriptions\n",
ast_sip_subscription_get_resource_name(i->root));
}
break;
@@ -1229,10 +1243,9 @@ static void subscription_setup_dialog(struct sip_subscription_tree *sub_tree, pj
pjsip_dlg_inc_session(dlg, &pubsub_module);
}
-static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_endpoint *endpoint)
+static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata)
{
struct sip_subscription_tree *sub_tree;
- char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
sub_tree = ao2_alloc(sizeof *sub_tree, subscription_tree_destructor);
if (!sub_tree) {
@@ -1241,11 +1254,24 @@ static struct sip_subscription_tree *allocate_subscription_tree(struct ast_sip_e
ast_module_ref(ast_module_info->self);
- /* Create name with seq number appended. */
- ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/pubsub/%s",
- ast_sorcery_object_get_id(endpoint));
+ if (rdata) {
+ /*
+ * We must continue using the serializer that the original
+ * SUBSCRIBE came in on for the dialog. There may be
+ * retransmissions already enqueued in the original
+ * serializer that can result in reentrancy and message
+ * sequencing problems.
+ */
+ sub_tree->serializer = ast_sip_get_distributor_serializer(rdata);
+ } else {
+ char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
+
+ /* Create name with seq number appended. */
+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/pubsub/%s",
+ ast_sorcery_object_get_id(endpoint));
- sub_tree->serializer = ast_sip_create_serializer_named(tps_name);
+ sub_tree->serializer = ast_sip_create_serializer_named(tps_name);
+ }
if (!sub_tree->serializer) {
ao2_ref(sub_tree, -1);
return NULL;
@@ -1286,7 +1312,7 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
pjsip_dialog *dlg;
struct subscription_persistence *persistence;
- sub_tree = allocate_subscription_tree(endpoint);
+ sub_tree = allocate_subscription_tree(endpoint, rdata);
if (!sub_tree) {
*dlg_status = PJ_ENOMEM;
return NULL;
@@ -1317,6 +1343,10 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &sub_tree->evsub);
subscription_setup_dialog(sub_tree, dlg);
+#ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
+ pjsip_evsub_add_ref(sub_tree->evsub);
+#endif
+
ast_sip_mod_data_set(dlg->pool, dlg->mod_data, pubsub_module.id, MOD_DATA_MSG,
pjsip_msg_clone(dlg->pool, rdata->msg_info.msg));
@@ -1335,109 +1365,176 @@ static struct sip_subscription_tree *create_subscription_tree(const struct ast_s
static int initial_notify_task(void *obj);
static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int force_full_state);
-/*! \brief Callback function to perform the actual recreation of a subscription */
-static int subscription_persistence_recreate(void *obj, void *arg, int flags)
+/*! Persistent subscription recreation continuation under distributor serializer data */
+struct persistence_recreate_data {
+ struct subscription_persistence *persistence;
+ pjsip_rx_data *rdata;
+};
+
+/*!
+ * \internal
+ * \brief subscription_persistence_recreate continuation under distributor serializer.
+ * \since 13.10.0
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int sub_persistence_recreate(void *obj)
{
- struct subscription_persistence *persistence = obj;
- pj_pool_t *pool = arg;
- pjsip_rx_data rdata = { { 0, }, };
- RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
+ struct persistence_recreate_data *recreate_data = obj;
+ struct subscription_persistence *persistence = recreate_data->persistence;
+ pjsip_rx_data *rdata = recreate_data->rdata;
+ struct ast_sip_endpoint *endpoint;
struct sip_subscription_tree *sub_tree;
struct ast_sip_pubsub_body_generator *generator;
- int resp;
+ struct ast_sip_subscription_handler *handler;
char *resource;
- size_t resource_size;
pjsip_sip_uri *request_uri;
+ size_t resource_size;
+ int resp;
struct resource_tree tree;
pjsip_expires_hdr *expires_header;
- struct ast_sip_subscription_handler *handler;
- /* If this subscription has already expired remove it */
- if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) {
- ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
- return 0;
- }
+ request_uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
+ resource_size = pj_strlen(&request_uri->user) + 1;
+ resource = ast_alloca(resource_size);
+ ast_copy_pj_str(resource, &request_uri->user, resource_size);
- endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", persistence->endpoint);
- if (!endpoint) {
- ast_log(LOG_WARNING, "A subscription for '%s' could not be recreated as the endpoint was not found\n",
+ handler = subscription_get_handler_from_rdata(rdata);
+ if (!handler || !handler->notifier) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get subscription handler.\n",
persistence->endpoint);
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
return 0;
}
- pj_pool_reset(pool);
- rdata.tp_info.pool = pool;
-
- if (ast_sip_create_rdata(&rdata, persistence->packet, persistence->src_name, persistence->src_port,
- persistence->transport_key, persistence->local_name, persistence->local_port)) {
- ast_log(LOG_WARNING, "A subscription for '%s' could not be recreated as the message could not be parsed\n",
+ generator = subscription_get_generator_from_rdata(rdata, handler);
+ if (!generator) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Body generator not available.\n",
persistence->endpoint);
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
return 0;
}
- if (rdata.msg_info.msg->type != PJSIP_REQUEST_MSG) {
- ast_log(LOG_NOTICE, "Endpoint %s persisted a SIP response instead of a subscribe request. Unable to reload subscription.\n",
- ast_sorcery_object_get_id(endpoint));
+ ast_sip_mod_data_set(rdata->tp_info.pool, rdata->endpt_info.mod_data,
+ pubsub_module.id, MOD_DATA_PERSISTENCE, persistence);
+
+ /* Getting the endpoint may take some time that can affect the expiration. */
+ endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint",
+ persistence->endpoint);
+ if (!endpoint) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The endpoint was not found\n",
+ persistence->endpoint);
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ ao2_ref(endpoint, -1);
return 0;
}
- request_uri = pjsip_uri_get_uri(rdata.msg_info.msg->line.req.uri);
- resource_size = pj_strlen(&request_uri->user) + 1;
- resource = ast_alloca(resource_size);
- ast_copy_pj_str(resource, &request_uri->user, resource_size);
-
/* Update the expiration header with the new expiration */
- expires_header = pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_EXPIRES, rdata.msg_info.msg->hdr.next);
+ expires_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
+ rdata->msg_info.msg->hdr.next);
if (!expires_header) {
- expires_header = pjsip_expires_hdr_create(pool, 0);
+ expires_header = pjsip_expires_hdr_create(rdata->tp_info.pool, 0);
if (!expires_header) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not update expires header.\n",
+ persistence->endpoint);
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ ao2_ref(endpoint, -1);
return 0;
}
- pjsip_msg_add_hdr(rdata.msg_info.msg, (pjsip_hdr*)expires_header);
+ pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr *) expires_header);
}
expires_header->ivalue = (ast_tvdiff_ms(persistence->expires, ast_tvnow()) / 1000);
-
- handler = subscription_get_handler_from_rdata(&rdata);
- if (!handler || !handler->notifier) {
- ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
- return 0;
- }
-
- generator = subscription_get_generator_from_rdata(&rdata, handler);
- if (!generator) {
+ if (expires_header->ivalue <= 0) {
+ /* The subscription expired since we started recreating the subscription. */
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ ao2_ref(endpoint, -1);
return 0;
}
- ast_sip_mod_data_set(rdata.tp_info.pool, rdata.endpt_info.mod_data,
- pubsub_module.id, MOD_DATA_PERSISTENCE, persistence);
-
memset(&tree, 0, sizeof(tree));
resp = build_resource_tree(endpoint, handler, resource, &tree,
- ast_sip_pubsub_has_eventlist_support(&rdata));
+ ast_sip_pubsub_has_eventlist_support(rdata));
if (PJSIP_IS_STATUS_IN_CLASS(resp, 200)) {
pj_status_t dlg_status;
- sub_tree = create_subscription_tree(handler, endpoint, &rdata, resource, generator, &tree, &dlg_status);
+ sub_tree = create_subscription_tree(handler, endpoint, rdata, resource, generator,
+ &tree, &dlg_status);
if (!sub_tree) {
- ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
- ast_log(LOG_WARNING, "Failed to re-create subscription for %s\n", persistence->endpoint);
- return 0;
- }
- sub_tree->persistence = ao2_bump(persistence);
- subscription_persistence_update(sub_tree, &rdata);
- if (ast_sip_push_task(sub_tree->serializer, initial_notify_task, ao2_bump(sub_tree))) {
- pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
- ao2_ref(sub_tree, -1);
+ if (dlg_status != PJ_EEXISTS) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not create subscription tree.\n",
+ persistence->endpoint);
+ ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ }
+ } else {
+ sub_tree->persistence = ao2_bump(persistence);
+ subscription_persistence_update(sub_tree, rdata);
+ if (ast_sip_push_task(sub_tree->serializer, initial_notify_task,
+ ao2_bump(sub_tree))) {
+ /* Could not send initial subscribe NOTIFY */
+ pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
+ ao2_ref(sub_tree, -1);
+ }
}
} else {
ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
}
resource_tree_destroy(&tree);
+ ao2_ref(endpoint, -1);
+
+ return 0;
+}
+
+/*! \brief Callback function to perform the actual recreation of a subscription */
+static int subscription_persistence_recreate(void *obj, void *arg, int flags)
+{
+ struct subscription_persistence *persistence = obj;
+ pj_pool_t *pool = arg;
+ struct ast_taskprocessor *serializer;
+ pjsip_rx_data rdata;
+ struct persistence_recreate_data recreate_data;
+
+ /* If this subscription has already expired remove it */
+ if (ast_tvdiff_ms(persistence->expires, ast_tvnow()) <= 0) {
+ ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ return 0;
+ }
+
+ memset(&rdata, 0, sizeof(rdata));
+ pj_pool_reset(pool);
+ rdata.tp_info.pool = pool;
+
+ if (ast_sip_create_rdata(&rdata, persistence->packet, persistence->src_name, persistence->src_port,
+ persistence->transport_key, persistence->local_name, persistence->local_port)) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: The message could not be parsed\n",
+ persistence->endpoint);
+ ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ return 0;
+ }
+
+ if (rdata.msg_info.msg->type != PJSIP_REQUEST_MSG) {
+ ast_log(LOG_NOTICE, "Failed recreating '%s' subscription: Stored a SIP response instead of a request.\n",
+ persistence->endpoint);
+ ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ return 0;
+ }
+
+ /* Continue the remainder in the distributor serializer */
+ serializer = ast_sip_get_distributor_serializer(&rdata);
+ if (!serializer) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not get distributor serializer.\n",
+ persistence->endpoint);
+ ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ return 0;
+ }
+ recreate_data.persistence = persistence;
+ recreate_data.rdata = &rdata;
+ if (ast_sip_push_task_synchronous(serializer, sub_persistence_recreate, &recreate_data)) {
+ ast_log(LOG_WARNING, "Failed recreating '%s' subscription: Could not continue under distributor serializer.\n",
+ persistence->endpoint);
+ ast_sorcery_delete(ast_sip_get_sorcery(), persistence);
+ }
+ ast_taskprocessor_unreference(serializer);
return 0;
}
@@ -1593,7 +1690,7 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
pjsip_evsub *evsub;
struct sip_subscription_tree *sub_tree = NULL;
- sub_tree = allocate_subscription_tree(endpoint);
+ sub_tree = allocate_subscription_tree(endpoint, NULL);
if (!sub_tree) {
return NULL;
}
@@ -2173,10 +2270,8 @@ static int send_notify(struct sip_subscription_tree *sub_tree, unsigned int forc
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
}
- if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
- sub_tree->last_notify = 1;
- }
if (sip_subscription_send_request(sub_tree, tdata)) {
+ pjsip_tx_data_dec_ref(tdata);
return -1;
}
@@ -2191,21 +2286,32 @@ static int serialized_send_notify(void *userdata)
pjsip_dialog *dlg = sub_tree->dlg;
pjsip_dlg_inc_lock(dlg);
+
/* It's possible that between when the notification was scheduled
- * and now, that a new SUBSCRIBE arrived, requiring full state to be
- * sent out in an immediate NOTIFY. If that has happened, we need to
+ * and now a new SUBSCRIBE arrived requiring full state to be
+ * sent out in an immediate NOTIFY. It's also possible that we're
+ * already processing a terminate. If that has happened, we need to
* bail out here instead of sending the batched NOTIFY.
*/
- if (!sub_tree->send_scheduled_notify) {
+
+ if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS
+ || !sub_tree->send_scheduled_notify) {
pjsip_dlg_dec_lock(dlg);
ao2_cleanup(sub_tree);
return 0;
}
+ if (sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED) {
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
+ }
+
send_notify(sub_tree, 0);
- ast_test_suite_event_notify("SUBSCRIPTION_STATE_CHANGED",
- "Resource: %s",
- sub_tree->root->resource);
+
+ ast_test_suite_event_notify(
+ sub_tree->state == SIP_SUB_TREE_TERMINATED
+ ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
+ "Resource: %s", sub_tree->root->resource);
+
sub_tree->notify_sched_id = -1;
pjsip_dlg_dec_lock(dlg);
ao2_cleanup(sub_tree);
@@ -2217,7 +2323,10 @@ static int sched_cb(const void *data)
struct sip_subscription_tree *sub_tree = (struct sip_subscription_tree *) data;
/* We don't need to bump the refcount of sub_tree since we bumped it when scheduling this task */
- ast_sip_push_task(sub_tree->serializer, serialized_send_notify, sub_tree);
+ if (ast_sip_push_task(sub_tree->serializer, serialized_send_notify, sub_tree)) {
+ ao2_cleanup(sub_tree);
+ }
+
return 0;
}
@@ -2228,12 +2337,13 @@ static int schedule_notification(struct sip_subscription_tree *sub_tree)
return 0;
}
+ sub_tree->send_scheduled_notify = 1;
sub_tree->notify_sched_id = ast_sched_add(sched, sub_tree->notification_batch_interval, sched_cb, ao2_bump(sub_tree));
if (sub_tree->notify_sched_id < 0) {
+ ao2_cleanup(sub_tree);
return -1;
}
- sub_tree->send_scheduled_notify = 1;
return 0;
}
@@ -2245,7 +2355,7 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
pjsip_dlg_inc_lock(dlg);
- if (!sub->tree->evsub) {
+ if (sub->tree->state != SIP_SUB_TREE_NORMAL) {
pjsip_dlg_dec_lock(dlg);
return 0;
}
@@ -2259,6 +2369,7 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
sub->body_changed = 1;
if (terminate) {
sub->subscription_state = PJSIP_EVSUB_STATE_TERMINATED;
+ sub->tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
}
if (sub->tree->notification_batch_interval) {
@@ -2266,6 +2377,9 @@ int ast_sip_subscription_notify(struct ast_sip_subscription *sub, struct ast_sip
} else {
/* See the note in pubsub_on_rx_refresh() for why sub->tree is refbumped here */
ao2_ref(sub->tree, +1);
+ if (terminate) {
+ sub->tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
+ }
res = send_notify(sub->tree, 0);
ast_test_suite_event_notify(terminate ? "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_STATE_CHANGED",
"Resource: %s",
@@ -3249,71 +3363,72 @@ static void set_state_terminated(struct ast_sip_subscription *sub)
}
}
-/* XXX This function and serialized_pubsub_on_rx_refresh are nearly identical */
-static int serialized_pubsub_on_server_timeout(void *userdata)
-{
- struct sip_subscription_tree *sub_tree = userdata;
- pjsip_dialog *dlg = sub_tree->dlg;
-
- pjsip_dlg_inc_lock(dlg);
- if (!sub_tree->evsub) {
- pjsip_dlg_dec_lock(dlg);
- return 0;
- }
- set_state_terminated(sub_tree->root);
- send_notify(sub_tree, 1);
- ast_test_suite_event_notify("SUBSCRIPTION_TERMINATED",
- "Resource: %s",
- sub_tree->root->resource);
-
- pjsip_dlg_dec_lock(dlg);
- ao2_cleanup(sub_tree);
- return 0;
-}
-
/*!
- * \brief PJSIP callback when underlying SIP subscription changes state
+ * \brief Callback sequence for subscription terminate:
*
- * This callback is a bit of a mess, because it's not always called when
- * you might expect it to be, and it can be called multiple times for the
- * same state.
+ * * Client initiated:
+ * pjproject receives SUBSCRIBE on the subscription's serializer thread
+ * calls pubsub_on_rx_refresh with dialog locked
+ * pubsub_on_rx_refresh sets TERMINATE_PENDING
+ * pushes serialized_pubsub_on_refresh_timeout
+ * returns to pjproject
+ * pjproject calls pubsub_on_evsub_state
+ * pubsub_evsub_set_state checks state == TERMINATE_IN_PROGRESS (no)
+ * ignore and return
+ * pjproject unlocks dialog
+ * serialized_pubsub_on_refresh_timeout starts (1)
+ * locks dialog
+ * checks state == TERMINATE_PENDING
+ * sets TERMINATE_IN_PROGRESS
+ * calls send_notify (2)
+ * send_notify ultimately calls pjsip_evsub_send_request
+ * pjsip_evsub_send_request calls evsub's set_state
+ * set_state calls pubsub_evsub_set_state
+ * pubsub_evsub_set_state checks state == TERMINATE_IN_PROGRESS
+ * removes the subscriptions
+ * cleans up references to evsub
+ * sets state = TERMINATED
+ * serialized_pubsub_on_refresh_timeout unlocks dialog
*
- * For instance, this function is not called at all when an incoming SUBSCRIBE
- * arrives to refresh a subscription. That makes sense in a way, since the
- * subscription state has not made a change; it was active and remains active.
+ * * Subscription timer expires:
+ * pjproject timer expires
+ * locks dialog
+ * calls pubsub_on_server_timeout
+ * pubsub_on_server_timeout checks state == NORMAL
+ * sets TERMINATE_PENDING
+ * pushes serialized_pubsub_on_refresh_timeout
+ * returns to pjproject
+ * pjproject unlocks dialog
+ * serialized_pubsub_on_refresh_timeout starts
+ * See (1) Above
*
- * However, if an incoming SUBSCRIBE arrives to end a subscription, then this
- * will be called into once upon receiving the SUBSCRIBE (after the call to
- * pubsub_on_rx_refresh) and again when sending a NOTIFY to end the subscription.
- * In both cases, the apparent state of the subscription is "terminated".
*
- * However, the double-terminated state changes don't happen in all cases. For
- * instance, if a subscription expires, then the only time this callback is
- * called is when we send the NOTIFY to end the subscription.
+ * * ast_sip_subscription_notify is called
+ * checks state == NORMAL
+ * if not batched...
+ * sets TERMINATE_IN_PROGRESS (if terminate is requested)
+ * calls send_notify
+ * See (2) Above
+ * if batched...
+ * sets TERMINATE_PENDING
+ * schedules task
+ * scheduler runs sched_task
+ * sched_task pushes serialized_send_notify
+ * serialized_send_notify starts
+ * checks state <= TERMINATE_PENDING
+ * if state == TERMINATE_PENDING set state = TERMINATE_IN_PROGRESS
+ * call send_notify
+ * See (2) Above
*
- * As far as state changes are concerned, we only ever care about transitions
- * to the "terminated" state. The action we take here is dependent on the
- * conditions behind why the state change to "terminated" occurred. If the
- * state change has occurred because we are sending a NOTIFY to end the
- * subscription, we consider this to be the final hurrah of the subscription
- * and take measures to start shutting things down. If the state change to
- * terminated occurs for a different reason (e.g. transaction timeout,
- * incoming SUBSCRIBE to end the subscription), then we push a task to
- * send out a NOTIFY. When that NOTIFY is sent, this callback will be
- * called again and we will actually shut down the subscription. The
- * subscription tree's last_notify field let's us know if this is being
- * called as a result of a terminating NOTIFY or not.
- *
- * There is no guarantee that this function will be called from a serializer
- * thread since it can be called due to a transaction timeout. Therefore
- * synchronization primitives are necessary to ensure that no operations
- * step on each others' toes. The dialog lock is always held when this
- * callback is called, so we ensure that relevant structures that may
- * be touched in this function are always protected by the dialog lock
- * elsewhere as well. The dialog lock in particular protects
+ */
+
+/*!
+ * \brief PJSIP callback when underlying SIP subscription changes state
*
- * \li The subscription tree's last_notify field
- * \li The subscription tree's evsub pointer
+ * Although this function is called for every state change, we only care
+ * about the TERMINATED state, and only when we're actually processing the final
+ * notify (SIP_SUB_TREE_TERMINATE_IN_PROGRESS). In this case, we do all
+ * the subscription tree cleanup tasks and decrement the evsub reference.
*/
static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
{
@@ -3326,51 +3441,55 @@ static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event)
}
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
- if (!sub_tree) {
+ if (!sub_tree || sub_tree->state != SIP_SUB_TREE_TERMINATE_IN_PROGRESS) {
+ ast_debug(1, "Possible terminate race prevented %p\n", sub_tree);
return;
}
- if (!sub_tree->last_notify) {
- if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_server_timeout, ao2_bump(sub_tree))) {
- ast_log(LOG_ERROR, "Failed to push task to send final NOTIFY.\n");
- ao2_ref(sub_tree, -1);
- } else {
- return;
- }
- }
-
remove_subscription(sub_tree);
+
pjsip_evsub_set_mod_data(evsub, pubsub_module.id, NULL);
+
+#ifdef HAVE_PJSIP_EVSUB_GRP_LOCK
+ pjsip_evsub_dec_ref(sub_tree->evsub);
+#endif
+
sub_tree->evsub = NULL;
+
ast_sip_dialog_set_serializer(sub_tree->dlg, NULL);
ast_sip_dialog_set_endpoint(sub_tree->dlg, NULL);
+
subscription_persistence_remove(sub_tree);
shutdown_subscriptions(sub_tree->root);
+ sub_tree->state = SIP_SUB_TREE_TERMINATED;
/* Remove evsub's reference to the sub_tree */
ao2_ref(sub_tree, -1);
}
-static int serialized_pubsub_on_rx_refresh(void *userdata)
+static int serialized_pubsub_on_refresh_timeout(void *userdata)
{
struct sip_subscription_tree *sub_tree = userdata;
pjsip_dialog *dlg = sub_tree->dlg;
pjsip_dlg_inc_lock(dlg);
- if (!sub_tree->evsub) {
+ if (sub_tree->state >= SIP_SUB_TREE_TERMINATE_IN_PROGRESS) {
+ ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree->evsub, sub_tree->state);
pjsip_dlg_dec_lock(dlg);
+ ao2_cleanup(sub_tree);
return 0;
}
- if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ if (sub_tree->state == SIP_SUB_TREE_TERMINATE_PENDING) {
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_IN_PROGRESS;
set_state_terminated(sub_tree->root);
}
send_notify(sub_tree, 1);
ast_test_suite_event_notify(sub_tree->root->subscription_state == PJSIP_EVSUB_STATE_TERMINATED ?
- "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
- "Resource: %s", sub_tree->root->resource);
+ "SUBSCRIPTION_TERMINATED" : "SUBSCRIPTION_REFRESHED",
+ "Resource: %s", sub_tree->root->resource);
pjsip_dlg_dec_lock(dlg);
ao2_cleanup(sub_tree);
@@ -3383,10 +3502,8 @@ static int serialized_pubsub_on_rx_refresh(void *userdata)
* This includes both SUBSCRIBE requests that actually refresh the subscription
* as well as SUBSCRIBE requests that end the subscription.
*
- * In the case where the SUBSCRIBE is actually refreshing the subscription we
- * push a task to send an appropriate NOTIFY request. In the case where the
- * SUBSCRIBE is ending the subscription, we let the pubsub_on_evsub_state
- * callback take care of sending the terminal NOTIFY request instead.
+ * In either case we push serialized_pubsub_on_refresh_timeout to send an
+ * appropriate NOTIFY request.
*/
static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
@@ -3394,18 +3511,24 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
struct sip_subscription_tree *sub_tree;
sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
- if (!sub_tree) {
+ if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
+ ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree, sub_tree ? sub_tree->state : -1 );
return;
}
/* PJSIP will set the evsub's state to terminated before calling into this function
* if the Expires value of the incoming SUBSCRIBE is 0.
*/
- if (pjsip_evsub_get_state(sub_tree->evsub) != PJSIP_EVSUB_STATE_TERMINATED) {
- if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_rx_refresh, ao2_bump(sub_tree))) {
- /* If we can't push the NOTIFY refreshing task...we'll just go with it. */
- ao2_ref(sub_tree, -1);
- }
+
+ if (pjsip_evsub_get_state(sub_tree->evsub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
+ }
+
+ if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_refresh_timeout, ao2_bump(sub_tree))) {
+ /* If we can't push the NOTIFY refreshing task...we'll just go with it. */
+ ast_log(LOG_ERROR, "Failed to push task to send NOTIFY.\n");
+ sub_tree->state = SIP_SUB_TREE_NORMAL;
+ ao2_ref(sub_tree, -1);
}
if (sub_tree->is_list) {
@@ -3416,9 +3539,9 @@ static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata,
static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code,
pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body)
{
- struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
+ struct ast_sip_subscription *sub;
- if (!sub) {
+ if (!(sub = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
return;
}
@@ -3431,45 +3554,62 @@ static int serialized_pubsub_on_client_refresh(void *userdata)
struct sip_subscription_tree *sub_tree = userdata;
pjsip_tx_data *tdata;
+ if (!sub_tree->evsub) {
+ ao2_cleanup(sub_tree);
+ return 0;
+ }
+
if (pjsip_evsub_initiate(sub_tree->evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
pjsip_evsub_send_request(sub_tree->evsub, tdata);
} else {
pjsip_evsub_terminate(sub_tree->evsub, PJ_TRUE);
}
+
ao2_cleanup(sub_tree);
return 0;
}
static void pubsub_on_client_refresh(pjsip_evsub *evsub)
{
- struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
+ struct sip_subscription_tree *sub_tree;
- ao2_ref(sub_tree, +1);
- ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, sub_tree);
+ if (!(sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id))) {
+ return;
+ }
+
+ if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_client_refresh, ao2_bump(sub_tree))) {
+ ao2_cleanup(sub_tree);
+ }
}
static void pubsub_on_server_timeout(pjsip_evsub *evsub)
{
+ struct sip_subscription_tree *sub_tree;
- struct sip_subscription_tree *sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
- if (!sub_tree) {
- /* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
- * with Expires: 0 arrives to end a subscription, nor does it terminate
- * this timer when we send a NOTIFY request in response to receiving such
- * a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
- * NOTIFY transaction has finished (either through receiving a response
- * or through a transaction timeout).
- *
- * Therefore, it is possible that we can be told that a server timeout
- * occurred after we already thought that the subscription had been
- * terminated. In such a case, we will have already removed the sub_tree
- * from the evsub's mod_data array.
- */
+ /* PJSIP does not terminate the server timeout timer when a SUBSCRIBE
+ * with Expires: 0 arrives to end a subscription, nor does it terminate
+ * this timer when we send a NOTIFY request in response to receiving such
+ * a SUBSCRIBE. PJSIP does not stop the server timeout timer until the
+ * NOTIFY transaction has finished (either through receiving a response
+ * or through a transaction timeout).
+ *
+ * Therefore, it is possible that we can be told that a server timeout
+ * occurred after we already thought that the subscription had been
+ * terminated. In such a case, we will have already removed the sub_tree
+ * from the evsub's mod_data array.
+ */
+
+ sub_tree = pjsip_evsub_get_mod_data(evsub, pubsub_module.id);
+ if (!sub_tree || sub_tree->state != SIP_SUB_TREE_NORMAL) {
+ ast_debug(1, "Possible terminate race prevented %p %d\n", sub_tree, sub_tree ? sub_tree->state : -1 );
return;
}
- ao2_ref(sub_tree, +1);
- ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_server_timeout, sub_tree);
+ sub_tree->state = SIP_SUB_TREE_TERMINATE_PENDING;
+ if (ast_sip_push_task(sub_tree->serializer, serialized_pubsub_on_refresh_timeout, ao2_bump(sub_tree))) {
+ sub_tree->state = SIP_SUB_TREE_NORMAL;
+ ao2_cleanup(sub_tree);
+ }
}
static int ami_subscription_detail(struct sip_subscription_tree *sub_tree,
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index 838f4616d..aef0e164b 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -231,155 +231,11 @@ static void registrar_add_date_header(pjsip_tx_data *tdata)
ast_sip_add_header(tdata, "Date", date);
}
-#define SERIALIZER_BUCKETS 59
-
-static struct ao2_container *serializers;
-
-/*! \brief Serializer with associated aor key */
-struct serializer {
- /* Serializer to distribute tasks to */
- struct ast_taskprocessor *serializer;
- /* The name of the aor to associate with the serializer */
- char aor_name[0];
-};
-
-static void serializer_destroy(void *obj)
-{
- struct serializer *ser = obj;
-
- ast_taskprocessor_unreference(ser->serializer);
-}
-
-static struct serializer *serializer_create(const char *aor_name)
-{
- char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
- size_t size = strlen(aor_name) + 1;
- struct serializer *ser = ao2_alloc(
- sizeof(*ser) + size, serializer_destroy);
-
- if (!ser) {
- return NULL;
- }
-
- /* Create name with seq number appended. */
- ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/aor/%s",
- aor_name);
-
- if (!(ser->serializer = ast_sip_create_serializer_named(tps_name))) {
- ao2_ref(ser, -1);
- return NULL;
- }
-
- strcpy(ser->aor_name, aor_name);
- return ser;
-}
-
-static struct serializer *serializer_find_or_create(const char *aor_name)
-{
- struct serializer *ser = ao2_find(serializers, aor_name, OBJ_SEARCH_KEY);
-
- if (ser) {
- return ser;
- }
-
- if (!(ser = serializer_create(aor_name))) {
- return NULL;
- }
-
- ao2_link(serializers, ser);
- return ser;
-}
-
-static int serializer_hash(const void *obj, const int flags)
-{
- const struct serializer *object;
- const char *key;
-
- switch (flags & OBJ_SEARCH_MASK) {
- case OBJ_SEARCH_KEY:
- key = obj;
- return ast_str_hash(key);
- case OBJ_SEARCH_OBJECT:
- object = obj;
- return ast_str_hash(object->aor_name);
- default:
- /* Hash can only work on something with a full key. */
- ast_assert(0);
- return 0;
- }
-}
-
-static int serializer_cmp(void *obj_left, void *obj_right, int flags)
-{
- const struct serializer *object_left = obj_left;
- const struct serializer *object_right = obj_right;
- const char *right_key = obj_right;
- int cmp;
-
- switch (flags & OBJ_SEARCH_MASK) {
- case OBJ_SEARCH_OBJECT:
- right_key = object_right->aor_name;
- /* Fall through */
- case OBJ_SEARCH_KEY:
- cmp = strcmp(object_left->aor_name, right_key);
- break;
- case OBJ_SEARCH_PARTIAL_KEY:
- /*
- * We could also use a partial key struct containing a length
- * so strlen() does not get called for every comparison instead.
- */
- cmp = strncmp(object_left->aor_name, right_key, strlen(right_key));
- break;
- default:
- cmp = 0;
- break;
- }
-
- return cmp ? 0 : CMP_MATCH;
-}
-
-struct rx_task_data {
- pjsip_rx_data *rdata;
- struct ast_sip_endpoint *endpoint;
- struct ast_sip_aor *aor;
-};
-
-static void rx_task_data_destroy(void *obj)
-{
- struct rx_task_data *task_data = obj;
-
- pjsip_rx_data_free_cloned(task_data->rdata);
- ao2_cleanup(task_data->endpoint);
- ao2_cleanup(task_data->aor);
-}
-
-static struct rx_task_data *rx_task_data_create(pjsip_rx_data *rdata,
- struct ast_sip_endpoint *endpoint,
- struct ast_sip_aor *aor)
-{
- struct rx_task_data *task_data = ao2_alloc(
- sizeof(*task_data), rx_task_data_destroy);
-
- if (!task_data) {
- return NULL;
- }
-
- pjsip_rx_data_clone(rdata, 0, &task_data->rdata);
-
- task_data->endpoint = endpoint;
- ao2_ref(task_data->endpoint, +1);
-
- task_data->aor = aor;
- ao2_ref(task_data->aor, +1);
-
- return task_data;
-}
-
static const pj_str_t path_hdr_name = { "Path", 4 };
-static int build_path_data(struct rx_task_data *task_data, struct ast_str **path_str)
+static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
{
- pjsip_generic_string_hdr *path_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &path_hdr_name, NULL);
+ pjsip_generic_string_hdr *path_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, NULL);
if (!path_hdr) {
return 0;
@@ -392,24 +248,24 @@ static int build_path_data(struct rx_task_data *task_data, struct ast_str **path
ast_str_set(path_str, 0, "%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
- while ((path_hdr = (pjsip_generic_string_hdr *) pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &path_hdr_name, path_hdr->next))) {
+ while ((path_hdr = (pjsip_generic_string_hdr *) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, path_hdr->next))) {
ast_str_append(path_str, 0, ",%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
}
return 0;
}
-static int registrar_validate_path(struct rx_task_data *task_data, struct ast_str **path_str)
+static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor, struct ast_str **path_str)
{
const pj_str_t path_supported_name = { "path", 4 };
pjsip_supported_hdr *supported_hdr;
int i;
- if (!task_data->aor->support_path) {
+ if (!aor->support_path) {
return 0;
}
- if (build_path_data(task_data, path_str)) {
+ if (build_path_data(rdata, path_str)) {
return -1;
}
@@ -417,7 +273,7 @@ static int registrar_validate_path(struct rx_task_data *task_data, struct ast_st
return 0;
}
- supported_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_SUPPORTED, NULL);
+ supported_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_SUPPORTED, NULL);
if (!supported_hdr) {
return -1;
}
@@ -433,8 +289,11 @@ static int registrar_validate_path(struct rx_task_data *task_data, struct ast_st
return -1;
}
-static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *contacts,
- const char *aor_name)
+static int register_aor_core(pjsip_rx_data *rdata,
+ struct ast_sip_endpoint *endpoint,
+ struct ast_sip_aor *aor,
+ const char *aor_name,
+ struct ao2_container *contacts)
{
static const pj_str_t USER_AGENT = { "User-Agent", 10 };
@@ -458,38 +317,38 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
/* So we don't count static contacts against max_contacts we prune them out from the container */
ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);
- if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) {
+ if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
/* The provided Contact headers do not conform to the specification */
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL);
- ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided");
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
+ ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
- ast_sorcery_object_get_id(task_data->endpoint));
+ ast_sorcery_object_get_id(endpoint));
return PJ_TRUE;
}
- if (registrar_validate_path(task_data, &path_str)) {
+ if (registrar_validate_path(rdata, aor, &path_str)) {
/* Ensure that intervening proxies did not make invalid modifications to the request */
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 420, NULL, NULL, NULL);
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 420, NULL, NULL, NULL);
ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
- ast_sorcery_object_get_id(task_data->endpoint));
+ ast_sorcery_object_get_id(endpoint));
return PJ_TRUE;
}
- if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) {
+ if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
/* Enforce the maximum number of contacts */
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL);
- ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
+ ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n",
- ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts);
+ ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts);
return PJ_TRUE;
}
if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL);
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
return PJ_TRUE;
}
- user_agent_hdr = pjsip_msg_find_hdr_by_name(task_data->rdata->msg_info.msg, &USER_AGENT, NULL);
+ user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL);
if (user_agent_hdr) {
alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1;
user_agent = ast_alloca(alloc_size);
@@ -497,10 +356,10 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
}
/* Find the first Via header */
- via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_VIA, NULL);
+ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL);
if (via_hdr) {
/* Find the last Via header */
- while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(task_data->rdata->msg_info.msg,
+ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg,
PJSIP_H_VIA, via_hdr->next)) != NULL) {
via_hdr_last = via_hdr;
}
@@ -510,7 +369,7 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
via_port=via_hdr_last->sent_by.port;
}
- call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL);
+ call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL);
if (call_id_hdr) {
alloc_size = pj_strlen(&call_id_hdr->id) + 1;
call_id = ast_alloca(alloc_size);
@@ -518,7 +377,7 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
}
/* Iterate each provided Contact header and add, update, or delete */
- while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
+ while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
int expiration;
char contact_uri[pjsip_max_url_size];
RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
@@ -534,7 +393,7 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
continue;
}
- expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata);
+ expiration = registrar_get_expiration(aor, contact_hdr, rdata);
details.uri = pjsip_uri_get_uri(contact_hdr->uri);
pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));
@@ -546,9 +405,9 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
continue;
}
- if (ast_sip_location_add_contact_nolock(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(),
+ if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(),
ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
- user_agent, via_addr, via_port, call_id, task_data->endpoint)) {
+ user_agent, via_addr, via_port, call_id, endpoint)) {
ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
contact_uri, aor_name);
continue;
@@ -576,8 +435,8 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
}
contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
- contact_update->qualify_frequency = task_data->aor->qualify_frequency;
- contact_update->authenticate_qualify = task_data->aor->authenticate_qualify;
+ contact_update->qualify_frequency = aor->qualify_frequency;
+ contact_update->authenticate_qualify = aor->authenticate_qualify;
if (path_str) {
ast_string_field_set(contact_update, path, ast_str_buffer(path_str));
}
@@ -625,16 +484,16 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
* do so
*/
- if (task_data->aor->remove_existing) {
+ if (aor->remove_existing) {
ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
}
/* Re-retrieve contacts. Caller will clean up the original container. */
- contacts = ast_sip_location_retrieve_aor_contacts_nolock(task_data->aor);
+ contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
response_contact = ao2_callback(contacts, 0, NULL, NULL);
/* Send a response containing all of the contacts (including static) that are present on this AOR */
- if (ast_sip_create_response(task_data->rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
+ if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
ao2_cleanup(response_contact);
ao2_cleanup(contacts);
return PJ_TRUE;
@@ -647,44 +506,42 @@ static int rx_task_core(struct rx_task_data *task_data, struct ao2_container *co
ao2_callback(contacts, 0, registrar_add_contact, tdata);
ao2_cleanup(contacts);
- if ((expires_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
- expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(task_data->aor, NULL, task_data->rdata));
+ if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
+ expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata));
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
}
- ast_sip_send_stateful_response(task_data->rdata, tdata, task_data->endpoint);
+ ast_sip_send_stateful_response(rdata, tdata, endpoint);
return PJ_TRUE;
}
-static int rx_task(void *data)
+static int register_aor(pjsip_rx_data *rdata,
+ struct ast_sip_endpoint *endpoint,
+ struct ast_sip_aor *aor,
+ const char *aor_name)
{
int res;
- struct rx_task_data *task_data = data;
struct ao2_container *contacts = NULL;
struct ast_named_lock *lock;
- const char *aor_name = ast_sorcery_object_get_id(task_data->aor);
lock = ast_named_lock_get(AST_NAMED_LOCK_TYPE_RWLOCK, "aor", aor_name);
if (!lock) {
- ao2_cleanup(task_data);
return PJ_TRUE;
}
ao2_wrlock(lock);
- contacts = ast_sip_location_retrieve_aor_contacts_nolock(task_data->aor);
+ contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
if (!contacts) {
ao2_unlock(lock);
ast_named_lock_put(lock);
- ao2_cleanup(task_data);
return PJ_TRUE;
}
- res = rx_task_core(task_data, contacts, aor_name);
+ res = register_aor_core(rdata, endpoint, aor, aor_name, contacts);
ao2_cleanup(contacts);
ao2_unlock(lock);
ast_named_lock_put(lock);
- ao2_cleanup(task_data);
return res;
}
@@ -748,44 +605,20 @@ static char *find_aor_name(const char *username, const char *domain, const char
return NULL;
}
-static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
+static struct ast_sip_aor *find_registrar_aor(struct pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint)
{
- RAII_VAR(struct serializer *, ser, NULL, ao2_cleanup);
- struct rx_task_data *task_data;
-
- RAII_VAR(struct ast_sip_endpoint *, endpoint,
- ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
- RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
- char *domain_name = NULL;
+ struct ast_sip_aor *aor = NULL;
+ char *aor_name = NULL;
+ char *domain_name;
char *username = NULL;
- RAII_VAR(char *, aor_name, NULL, ast_free);
int i;
- if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
- return PJ_FALSE;
- }
-
- if (ast_strlen_zero(endpoint->aors)) {
- /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
- ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
- ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint));
- return PJ_TRUE;
- }
-
- if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
- ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
- ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint));
- return PJ_TRUE;
- }
-
- for (i = 0; i < AST_VECTOR_SIZE(&endpoint->ident_method_order); i++) {
+ for (i = 0; i < AST_VECTOR_SIZE(&endpoint->ident_method_order); ++i) {
pjsip_sip_uri *uri;
pjsip_authorization_hdr *header = NULL;
switch (AST_VECTOR_GET(&endpoint->ident_method_order, i)) {
- case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME :
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME:
uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
domain_name = ast_alloca(uri->host.slen + 1);
@@ -798,7 +631,7 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
ast_debug(3, "Matched aor '%s' by To username\n", aor_name);
}
break;
- case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME :
+ case AST_SIP_ENDPOINT_IDENTIFY_BY_AUTH_USERNAME:
while ((header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION,
header ? header->next : NULL))) {
if (header && !pj_stricmp2(&header->scheme, "digest")) {
@@ -828,42 +661,57 @@ static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
/* The provided AOR name was not found (be it within the configuration or sorcery itself) */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL);
ast_sip_report_req_no_support(endpoint, rdata, "registrar_requested_aor_not_found");
- ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n", username, ast_sorcery_object_get_id(endpoint));
- return PJ_TRUE;
+ ast_log(LOG_WARNING, "AOR '%s' not found for endpoint '%s'\n",
+ username ?: "", ast_sorcery_object_get_id(endpoint));
}
+ ast_free(aor_name);
+ return aor;
+}
- if (!aor->max_contacts) {
- /* Registration is not permitted for this AOR */
+static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata)
+{
+ RAII_VAR(struct ast_sip_endpoint *, endpoint,
+ ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup);
+ struct ast_sip_aor *aor;
+ const char *aor_name;
+
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) {
+ return PJ_FALSE;
+ }
+
+ if (ast_strlen_zero(endpoint->aors)) {
+ /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
- ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted");
- ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n",
- ast_sorcery_object_get_id(aor), ast_sorcery_object_get_id(endpoint));
+ ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_without_configured_aors");
+ ast_log(LOG_WARNING, "Endpoint '%s' has no configured AORs\n", ast_sorcery_object_get_id(endpoint));
return PJ_TRUE;
}
- if (!(ser = serializer_find_or_create(aor_name))) {
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
- ast_sip_report_mem_limit(endpoint, rdata);
- ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not get serializer\n",
- ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
+ if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
+ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL);
+ ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_uri_in_to_received");
+ ast_log(LOG_WARNING, "Endpoint '%s' attempted to register to an AOR with a non-SIP URI\n", ast_sorcery_object_get_id(endpoint));
return PJ_TRUE;
}
- if (!(task_data = rx_task_data_create(rdata, endpoint, aor))) {
- pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
- ast_sip_report_mem_limit(endpoint, rdata);
- ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not create rx_task_data\n",
- ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
+ aor = find_registrar_aor(rdata, endpoint);
+ if (!aor) {
+ /* We've already responded about not finding an AOR. */
return PJ_TRUE;
}
- if (ast_sip_push_task(ser->serializer, rx_task, task_data)) {
+ aor_name = ast_sorcery_object_get_id(aor);
+
+ if (!aor->max_contacts) {
+ /* Registration is not permitted for this AOR */
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
- ast_sip_report_mem_limit(endpoint, rdata);
- ast_log(LOG_WARNING, "Endpoint '%s' unable to register on AOR '%s' - could not serialize task\n",
- ast_sorcery_object_get_id(endpoint), ast_sorcery_object_get_id(aor));
- ao2_ref(task_data, -1);
+ ast_sip_report_req_no_support(endpoint, rdata, "registrar_attempt_without_registration_permitted");
+ ast_log(LOG_WARNING, "AOR '%s' has no configured max_contacts. Endpoint '%s' unable to register\n",
+ aor_name, ast_sorcery_object_get_id(endpoint));
+ } else {
+ register_aor(rdata, endpoint, aor, aor_name);
}
+ ao2_ref(aor, -1);
return PJ_TRUE;
}
@@ -952,11 +800,6 @@ static int load_module(void)
CHECK_PJSIP_MODULE_LOADED();
- if (!(serializers = ao2_container_alloc(
- SERIALIZER_BUCKETS, serializer_hash, serializer_cmp))) {
- return AST_MODULE_LOAD_DECLINE;
- }
-
if (ast_sip_register_service(&registrar_module)) {
return AST_MODULE_LOAD_DECLINE;
}
@@ -976,8 +819,6 @@ static int unload_module(void)
{
ast_manager_unregister(AMI_SHOW_REGISTRATIONS);
ast_sip_unregister_service(&registrar_module);
-
- ao2_cleanup(serializers);
return 0;
}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 18a7f3f6a..029eb5d27 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -420,7 +420,7 @@ static pjmedia_sdp_attr* generate_fmtp_attr(pj_pool_t *pool, struct ast_format *
*++tmp = '\0';
/* ast...generate gives us everything, just need value */
tmp = strchr(ast_str_buffer(fmtp0), ':');
- if (tmp && tmp + 1) {
+ if (tmp && tmp[1] != '\0') {
fmtp1 = pj_str(tmp + 1);
} else {
fmtp1 = pj_str(ast_str_buffer(fmtp0));
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index d66a819d7..23d2f2f2a 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1403,12 +1403,11 @@ struct ast_sip_channel_pvt *ast_sip_channel_pvt_alloc(void *pvt, struct ast_sip_
}
struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
- struct ast_sip_contact *contact, pjsip_inv_session *inv_session)
+ struct ast_sip_contact *contact, pjsip_inv_session *inv_session, pjsip_rx_data *rdata)
{
RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
struct ast_sip_session_supplement *iter;
int dsp_features = 0;
- char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
session = ao2_alloc(sizeof(*session), session_destructor);
if (!session) {
@@ -1429,11 +1428,24 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
/* fill session->media with available types */
ao2_callback(sdp_handlers, OBJ_NODATA, add_session_media, session);
- /* Create name with seq number appended. */
- ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/session/%s",
- ast_sorcery_object_get_id(endpoint));
+ if (rdata) {
+ /*
+ * We must continue using the serializer that the original
+ * INVITE came in on for the dialog. There may be
+ * retransmissions already enqueued in the original
+ * serializer that can result in reentrancy and message
+ * sequencing problems.
+ */
+ session->serializer = ast_sip_get_distributor_serializer(rdata);
+ } else {
+ char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
+
+ /* Create name with seq number appended. */
+ ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "pjsip/outsess/%s",
+ ast_sorcery_object_get_id(endpoint));
- session->serializer = ast_sip_create_serializer_named(tps_name);
+ session->serializer = ast_sip_create_serializer_named(tps_name);
+ }
if (!session->serializer) {
return NULL;
}
@@ -1731,7 +1743,9 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
timer.sess_expires = endpoint->extensions.timer.sess_expires;
pjsip_timer_init_session(inv_session, &timer);
- if (!(session = ast_sip_session_alloc(endpoint, found_contact ? found_contact : contact, inv_session))) {
+ session = ast_sip_session_alloc(endpoint, found_contact ? found_contact : contact,
+ inv_session, NULL);
+ if (!session) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
return NULL;
}
@@ -1780,29 +1794,38 @@ void ast_sip_session_terminate(struct ast_sip_session *session, int response)
response = 603;
}
- if ((session->inv_session->state == PJSIP_INV_STATE_CONFIRMED) && session->inv_session->invite_tsx) {
- ast_debug(3, "Delay sending BYE to %s because of outstanding transaction...\n",
- ast_sorcery_object_get_id(session->endpoint));
- /* If this is delayed the only thing that will happen is a BYE request so we don't
- * actually need to store the response code for when it happens.
- */
- delay_request(session, NULL, NULL, NULL, 0, DELAYED_METHOD_BYE);
- } else if (session->inv_session->state == PJSIP_INV_STATE_NULL) {
+ switch (session->inv_session->state) {
+ case PJSIP_INV_STATE_NULL:
pjsip_inv_terminate(session->inv_session, response, PJ_TRUE);
- } else if (((status = pjsip_inv_end_session(session->inv_session, response, NULL, &packet)) == PJ_SUCCESS)
- && packet) {
- struct ast_sip_session_delayed_request *delay;
-
- /* Flush any delayed requests so they cannot overlap this transaction. */
- while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {
- ast_free(delay);
+ break;
+ case PJSIP_INV_STATE_CONFIRMED:
+ if (session->inv_session->invite_tsx) {
+ ast_debug(3, "Delay sending BYE to %s because of outstanding transaction...\n",
+ ast_sorcery_object_get_id(session->endpoint));
+ /* If this is delayed the only thing that will happen is a BYE request so we don't
+ * actually need to store the response code for when it happens.
+ */
+ delay_request(session, NULL, NULL, NULL, 0, DELAYED_METHOD_BYE);
+ break;
}
+ /* Fall through */
+ default:
+ status = pjsip_inv_end_session(session->inv_session, response, NULL, &packet);
+ if (status == PJ_SUCCESS && packet) {
+ struct ast_sip_session_delayed_request *delay;
- if (packet->msg->type == PJSIP_RESPONSE_MSG) {
- ast_sip_session_send_response(session, packet);
- } else {
- ast_sip_session_send_request(session, packet);
+ /* Flush any delayed requests so they cannot overlap this transaction. */
+ while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {
+ ast_free(delay);
+ }
+
+ if (packet->msg->type == PJSIP_RESPONSE_MSG) {
+ ast_sip_session_send_response(session, packet);
+ } else {
+ ast_sip_session_send_request(session, packet);
+ }
}
+ break;
}
}
@@ -2142,7 +2165,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
return;
}
- session = ast_sip_session_alloc(endpoint, NULL, inv_session);
+ session = ast_sip_session_alloc(endpoint, NULL, inv_session, rdata);
if (!session) {
if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) {
pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
@@ -2292,7 +2315,8 @@ static void reschedule_reinvite(struct ast_sip_session *session, ast_sip_session
static void __print_debug_details(const char *function, pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
{
- struct ast_sip_session *session;
+ int id = session_module.id;
+ struct ast_sip_session *session = NULL;
if (!DEBUG_ATLEAST(5)) {
/* Debug not spamy enough */
@@ -2307,7 +2331,9 @@ static void __print_debug_details(const char *function, pjsip_inv_session *inv,
pjsip_tsx_state_str(tsx->state));
return;
}
- session = inv->mod_data[session_module.id];
+ if (id > -1) {
+ session = inv->mod_data[session_module.id];
+ }
if (!session) {
ast_log(LOG_DEBUG, "inv_session %p has no ast session\n", inv);
} else {
@@ -2529,9 +2555,22 @@ static void session_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
{
ast_sip_session_response_cb cb;
- struct ast_sip_session *session = inv->mod_data[session_module.id];
+ int id = session_module.id;
+ struct ast_sip_session *session;
pjsip_tx_data *tdata;
+ /*
+ * A race condition exists at shutdown where the res_pjsip_session can be
+ * unloaded but this callback may still get called afterwards. In this case
+ * the id may end up being -1 which is useless to us. To work around this
+ * we store the current value and check/use it.
+ */
+ if (id < 0) {
+ return;
+ }
+
+ session = inv->mod_data[id];
+
print_debug_details(inv, tsx, e);
if (!session) {
/* The session has ended. Ignore the transaction change. */
@@ -2545,10 +2584,10 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* we transfer the data into the transaction. This way, when we receive a response, we
* can dig this data out again
*/
- tsx->mod_data[session_module.id] = e->body.tsx_state.src.tdata->mod_data[session_module.id];
+ tsx->mod_data[id] = e->body.tsx_state.src.tdata->mod_data[id];
break;
case PJSIP_EVENT_RX_MSG:
- cb = ast_sip_mod_data_get(tsx->mod_data, session_module.id, MOD_DATA_ON_RESPONSE);
+ cb = ast_sip_mod_data_get(tsx->mod_data, id, MOD_DATA_ON_RESPONSE);
/* As the PJSIP invite session implementation responds with a 200 OK before we have a
* chance to be invoked session supplements for BYE requests actually end up executing
* in the invite session state callback as well. To prevent session supplements from
@@ -2627,7 +2666,7 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* Clear the module data now to block session_inv_on_state_changed()
* from calling session_end() if it hasn't already done so.
*/
- inv->mod_data[session_module.id] = NULL;
+ inv->mod_data[id] = NULL;
if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
session_end(session);
@@ -2650,8 +2689,8 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
* the dialog locked to get the session by other threads.
*/
pjsip_dlg_inc_lock(inv->dlg);
- session = inv->mod_data[session_module.id];
- inv->mod_data[session_module.id] = NULL;
+ session = inv->mod_data[id];
+ inv->mod_data[id] = NULL;
pjsip_dlg_dec_lock(inv->dlg);
/*
diff --git a/res/res_pjsip_transport_management.c b/res/res_pjsip_transport_management.c
index afd94eb1f..1cf8e5046 100644
--- a/res/res_pjsip_transport_management.c
+++ b/res/res_pjsip_transport_management.c
@@ -42,7 +42,7 @@
static const pj_str_t keepalive_packet = { "\r\n\r\n", 4 };
/*! \brief Global container of active transports */
-static struct ao2_container *transports;
+static AO2_GLOBAL_OBJ_STATIC(monitored_transports);
/*! \brief Scheduler context for timing out connections with no data received */
static struct ast_sched_context *sched;
@@ -84,6 +84,7 @@ static int keepalive_transport_cb(void *obj, void *arg, int flags)
/*! \brief Thread which sends keepalives to all active connection-oriented transports */
static void *keepalive_transport_thread(void *data)
{
+ struct ao2_container *transports;
pj_thread_desc desc;
pj_thread_t *thread;
@@ -92,6 +93,11 @@ static void *keepalive_transport_thread(void *data)
return NULL;
}
+ transports = ao2_global_obj_ref(monitored_transports);
+ if (!transports) {
+ return NULL;
+ }
+
/* Once loaded this module just keeps on going as it is unsafe to stop and change the underlying
* callback for the transport manager.
*/
@@ -100,6 +106,7 @@ static void *keepalive_transport_thread(void *data)
ao2_callback(transports, OBJ_NODATA, keepalive_transport_cb, NULL);
}
+ ao2_ref(transports, -1);
return NULL;
}
@@ -108,7 +115,6 @@ AST_THREADSTORAGE(desc_storage);
static int idle_sched_cb(const void *data)
{
struct monitored_transport *keepalive = (struct monitored_transport *) data;
- int sip_received = ast_atomic_fetchadd_int(&keepalive->sip_received, 0);
if (!pj_thread_is_registered()) {
pj_thread_t *thread;
@@ -126,7 +132,7 @@ static int idle_sched_cb(const void *data)
pj_thread_register("Transport Monitor", *desc, &thread);
}
- if (!sip_received) {
+ if (!keepalive->sip_received) {
ast_log(LOG_NOTICE, "Shutting down transport '%s' since no request was received in %d seconds\n",
keepalive->transport->info, IDLE_TIMEOUT);
pjsip_transport_shutdown(keepalive->transport);
@@ -148,23 +154,30 @@ static void monitored_transport_destroy(void *obj)
static void monitored_transport_state_callback(pjsip_transport *transport, pjsip_transport_state state,
const pjsip_transport_state_info *info)
{
+ struct ao2_container *transports;
+
/* We only care about reliable transports */
- if (PJSIP_TRANSPORT_IS_RELIABLE(transport) &&
- (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval)) {
+ if (PJSIP_TRANSPORT_IS_RELIABLE(transport)
+ && (transport->dir == PJSIP_TP_DIR_INCOMING || keepalive_interval)
+ && (transports = ao2_global_obj_ref(monitored_transports))) {
struct monitored_transport *monitored;
switch (state) {
case PJSIP_TP_STATE_CONNECTED:
- monitored = ao2_alloc(sizeof(*monitored), monitored_transport_destroy);
+ monitored = ao2_alloc_options(sizeof(*monitored),
+ monitored_transport_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!monitored) {
break;
}
monitored->transport = transport;
pjsip_transport_add_ref(monitored->transport);
+
ao2_link(transports, monitored);
+
if (transport->dir == PJSIP_TP_DIR_INCOMING) {
/* Let the scheduler inherit the reference from allocation */
if (ast_sched_add_variable(sched, IDLE_TIMEOUT, idle_sched_cb, monitored, 1) < 0) {
+ /* Uh Oh. Could not schedule the idle check. Kill the transport. */
ao2_unlink(transports, monitored);
ao2_ref(monitored, -1);
pjsip_transport_shutdown(transport);
@@ -181,6 +194,8 @@ static void monitored_transport_state_callback(pjsip_transport *transport, pjsip
default:
break;
}
+
+ ao2_ref(transports, -1);
}
/* Forward to the old state callback if present */
@@ -242,7 +257,7 @@ static int monitored_transport_cmp_fn(void *obj, void *arg, int flags)
break;
}
- return !cmp ? CMP_MATCH | CMP_STOP : 0;
+ return !cmp ? CMP_MATCH : 0;
}
static void keepalive_global_loaded(const char *object_type)
@@ -265,8 +280,8 @@ static void keepalive_global_loaded(const char *object_type)
if (ast_pthread_create(&keepalive_thread, NULL, keepalive_transport_thread, NULL)) {
ast_log(LOG_ERROR, "Could not create thread for sending keepalive messages.\n");
- ao2_ref(transports, -1);
- return;
+ keepalive_thread = AST_PTHREADT_NULL;
+ keepalive_interval = 0;
}
}
@@ -283,14 +298,21 @@ static struct ast_sorcery_observer keepalive_global_observer = {
*/
static pj_bool_t idle_monitor_on_rx_request(pjsip_rx_data *rdata)
{
+ struct ao2_container *transports;
struct monitored_transport *idle_trans;
+ transports = ao2_global_obj_ref(monitored_transports);
+ if (!transports) {
+ return PJ_FALSE;
+ }
+
idle_trans = ao2_find(transports, rdata->tp_info.transport->obj_name, OBJ_SEARCH_KEY);
+ ao2_ref(transports, -1);
if (!idle_trans) {
return PJ_FALSE;
}
- ast_atomic_fetchadd_int(&idle_trans->sip_received, +1);
+ idle_trans->sip_received = 1;
ao2_ref(idle_trans, -1);
return PJ_FALSE;
@@ -304,35 +326,38 @@ static pjsip_module idle_monitor_module = {
static int load_module(void)
{
+ struct ao2_container *transports;
pjsip_tpmgr *tpmgr;
CHECK_PJSIP_MODULE_LOADED();
+ tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ if (!tpmgr) {
+ ast_log(LOG_ERROR, "No transport manager to attach keepalive functionality to.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
transports = ao2_container_alloc(TRANSPORTS_BUCKETS, monitored_transport_hash_fn,
monitored_transport_cmp_fn);
if (!transports) {
ast_log(LOG_ERROR, "Could not create container for transports to perform keepalive on.\n");
return AST_MODULE_LOAD_DECLINE;
}
-
- tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
- if (!tpmgr) {
- ast_log(LOG_ERROR, "No transport manager to attach keepalive functionality to.\n");
- ao2_ref(transports, -1);
- return AST_MODULE_LOAD_DECLINE;
- }
+ ao2_global_obj_replace_unref(monitored_transports, transports);
+ ao2_ref(transports, -1);
sched = ast_sched_context_create();
if (!sched) {
ast_log(LOG_ERROR, "Failed to create keepalive scheduler context.\n");
- ao2_ref(transports, -1);
+ ao2_global_obj_release(monitored_transports);
return AST_MODULE_LOAD_DECLINE;
}
if (ast_sched_start_thread(sched)) {
ast_log(LOG_ERROR, "Failed to start keepalive scheduler thread\n");
ast_sched_context_destroy(sched);
- ao2_ref(transports, -1);
+ sched = NULL;
+ ao2_global_obj_release(monitored_transports);
return AST_MODULE_LOAD_DECLINE;
}
@@ -343,25 +368,38 @@ static int load_module(void)
ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
ast_sorcery_reload_object(ast_sip_get_sorcery(), "global");
+
ast_module_shutdown_ref(ast_module_info->self);
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
- pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ pjsip_tpmgr *tpmgr;
if (keepalive_interval) {
keepalive_interval = 0;
- pthread_kill(keepalive_thread, SIGURG);
- pthread_join(keepalive_thread, NULL);
+ if (keepalive_thread != AST_PTHREADT_NULL) {
+ pthread_kill(keepalive_thread, SIGURG);
+ pthread_join(keepalive_thread, NULL);
+ keepalive_thread = AST_PTHREADT_NULL;
+ }
}
- ast_sched_context_destroy(sched);
- ao2_ref(transports, -1);
+ ast_sorcery_observer_remove(ast_sip_get_sorcery(), "global", &keepalive_global_observer);
+
+ tpmgr = pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint());
+ if (tpmgr) {
+ pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
+ }
ast_sip_unregister_service(&idle_monitor_module);
- pjsip_tpmgr_set_state_cb(tpmgr, tpmgr_state_callback);
+
+ ast_sched_context_destroy(sched);
+ sched = NULL;
+
+ ao2_global_obj_release(monitored_transports);
+
return 0;
}
@@ -372,9 +410,9 @@ static int reload_module(void)
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Reliable Transport Management",
- .support_level = AST_MODULE_SUPPORT_CORE,
- .load = load_module,
- .reload = reload_module,
- .unload = unload_module,
- .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
- );
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .reload = reload_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND - 4,
+);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index b2ecf5962..feab1ca80 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -540,8 +540,8 @@ static int ice_candidate_cmp(void *obj, void *arg, int flags)
if (strcmp(candidate1->foundation, candidate2->foundation) ||
candidate1->id != candidate2->id ||
- ast_sockaddr_cmp(&candidate1->address, &candidate2->address) ||
- candidate1->type != candidate1->type) {
+ candidate1->type != candidate2->type ||
+ ast_sockaddr_cmp(&candidate1->address, &candidate2->address)) {
return 0;
}
@@ -1357,7 +1357,12 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
return 0;
}
- if (!(rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method()))) {
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+ rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method());
+#else
+ rtp->ssl_ctx = SSL_CTX_new(DTLS_method());
+#endif
+ if (!rtp->ssl_ctx) {
return -1;
}
@@ -1393,7 +1398,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
if (!ast_strlen_zero(dtls_cfg->certfile)) {
char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile;
BIO *certbio;
- X509 *cert;
+ X509 *cert = NULL;
const EVP_MD *type;
unsigned int size, i;
unsigned char fingerprint[EVP_MAX_MD_SIZE];
@@ -1435,6 +1440,9 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n",
dtls_cfg->certfile, instance);
BIO_free_all(certbio);
+ if (cert) {
+ X509_free(cert);
+ }
return -1;
}
@@ -1446,6 +1454,7 @@ static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, con
*(local_fingerprint-1) = 0;
BIO_free_all(certbio);
+ X509_free(cert);
}
if (!ast_strlen_zero(dtls_cfg->cipher)) {
diff --git a/res/res_rtp_multicast.c b/res/res_rtp_multicast.c
index 8327cf20a..53bdf14a4 100644
--- a/res/res_rtp_multicast.c
+++ b/res/res_rtp_multicast.c
@@ -54,6 +54,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/format_cache.h"
+#include "asterisk/multicast_rtp.h"
+#include "asterisk/app.h"
/*! Command value used for Linksys paging to indicate we are starting */
#define LINKSYS_MCAST_STARTCMD 6
@@ -63,8 +65,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
/*! \brief Type of paging to do */
enum multicast_type {
+ /*! Type has not been set yet */
+ MULTICAST_TYPE_UNSPECIFIED = 0,
/*! Simple multicast enabled client/receiver paging like Snom and Barix uses */
- MULTICAST_TYPE_BASIC = 0,
+ MULTICAST_TYPE_BASIC,
/*! More advanced Linksys type paging which requires a start and stop packet */
MULTICAST_TYPE_LINKSYS,
};
@@ -95,6 +99,91 @@ struct multicast_rtp {
struct timeval txcore;
};
+enum {
+ OPT_CODEC = (1 << 0),
+ OPT_LOOP = (1 << 1),
+ OPT_TTL = (1 << 2),
+ OPT_IF = (1 << 3),
+};
+
+enum {
+ OPT_ARG_CODEC = 0,
+ OPT_ARG_LOOP,
+ OPT_ARG_TTL,
+ OPT_ARG_IF,
+ OPT_ARG_ARRAY_SIZE,
+};
+
+AST_APP_OPTIONS(multicast_rtp_options, BEGIN_OPTIONS
+ /*! Set the codec to be used for multicast RTP */
+ AST_APP_OPTION_ARG('c', OPT_CODEC, OPT_ARG_CODEC),
+ /*! Set whether multicast RTP is looped back to the sender */
+ AST_APP_OPTION_ARG('l', OPT_LOOP, OPT_ARG_LOOP),
+ /*! Set the hop count for multicast RTP */
+ AST_APP_OPTION_ARG('t', OPT_TTL, OPT_ARG_TTL),
+ /*! Set the interface from which multicast RTP is sent */
+ AST_APP_OPTION_ARG('i', OPT_IF, OPT_ARG_IF),
+END_OPTIONS );
+
+struct ast_multicast_rtp_options {
+ char *type;
+ char *options;
+ struct ast_format *fmt;
+ struct ast_flags opts;
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+ /*! The type and options are stored in this buffer */
+ char buf[0];
+};
+
+struct ast_multicast_rtp_options *ast_multicast_rtp_create_options(const char *type,
+ const char *options)
+{
+ struct ast_multicast_rtp_options *mcast_options;
+ char *pos;
+
+ mcast_options = ast_calloc(1, sizeof(*mcast_options)
+ + strlen(type)
+ + strlen(options) + 2);
+ if (!mcast_options) {
+ return NULL;
+ }
+
+ pos = mcast_options->buf;
+
+ /* Safe */
+ strcpy(pos, type);
+ mcast_options->type = pos;
+ pos += strlen(type) + 1;
+
+ /* Safe */
+ strcpy(pos, options);
+ mcast_options->options = pos;
+
+ if (ast_app_parse_options(multicast_rtp_options, &mcast_options->opts,
+ mcast_options->opt_args, mcast_options->options)) {
+ ast_log(LOG_WARNING, "Error parsing multicast RTP options\n");
+ ast_multicast_rtp_free_options(mcast_options);
+ return NULL;
+ }
+
+ return mcast_options;
+}
+
+void ast_multicast_rtp_free_options(struct ast_multicast_rtp_options *mcast_options)
+{
+ ast_free(mcast_options);
+}
+
+struct ast_format *ast_multicast_rtp_options_get_format(struct ast_multicast_rtp_options *mcast_options)
+{
+ if (ast_test_flag(&mcast_options->opts, OPT_CODEC)
+ && !ast_strlen_zero(mcast_options->opt_args[OPT_ARG_CODEC])) {
+ return ast_format_cache_get(mcast_options->opt_args[OPT_ARG_CODEC]);
+ }
+
+ return NULL;
+}
+
/* Forward Declarations */
static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data);
static int multicast_rtp_activate(struct ast_rtp_instance *instance);
@@ -112,21 +201,93 @@ static struct ast_rtp_engine multicast_rtp_engine = {
.read = multicast_rtp_read,
};
+static int set_type(struct multicast_rtp *multicast, const char *type)
+{
+ if (!strcasecmp(type, "basic")) {
+ multicast->type = MULTICAST_TYPE_BASIC;
+ } else if (!strcasecmp(type, "linksys")) {
+ multicast->type = MULTICAST_TYPE_LINKSYS;
+ } else {
+ ast_log(LOG_WARNING, "Unrecognized multicast type '%s' specified.\n", type);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void set_ttl(int sock, const char *ttl_str)
+{
+ int ttl;
+
+ if (ast_strlen_zero(ttl_str)) {
+ return;
+ }
+
+ ast_debug(3, "Setting multicast TTL to %s\n", ttl_str);
+
+ if (sscanf(ttl_str, "%30d", &ttl) < 1) {
+ ast_log(LOG_WARNING, "Invalid multicast ttl option '%s'\n", ttl_str);
+ return;
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
+ ast_log(LOG_WARNING, "Could not set multicast ttl to '%s': %s\n",
+ ttl_str, strerror(errno));
+ }
+}
+
+static void set_loop(int sock, const char *loop_str)
+{
+ unsigned char loop;
+
+ if (ast_strlen_zero(loop_str)) {
+ return;
+ }
+
+ ast_debug(3, "Setting multicast loop to %s\n", loop_str);
+
+ if (sscanf(loop_str, "%30hhu", &loop) < 1) {
+ ast_log(LOG_WARNING, "Invalid multicast loop option '%s'\n", loop_str);
+ return;
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ ast_log(LOG_WARNING, "Could not set multicast loop to '%s': %s\n",
+ loop_str, strerror(errno));
+ }
+}
+
+static void set_if(int sock, const char *if_str)
+{
+ struct in_addr iface;
+
+ if (ast_strlen_zero(if_str)) {
+ return;
+ }
+
+ ast_debug(3, "Setting multicast if to %s\n", if_str);
+
+ if (!inet_aton(if_str, &iface)) {
+ ast_log(LOG_WARNING, "Cannot parse if option '%s'\n", if_str);
+ }
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iface, sizeof(iface)) < 0) {
+ ast_log(LOG_WARNING, "Could not set multicast if to '%s': %s\n",
+ if_str, strerror(errno));
+ }
+}
+
/*! \brief Function called to create a new multicast instance */
static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data)
{
struct multicast_rtp *multicast;
- const char *type = data;
+ struct ast_multicast_rtp_options *mcast_options = data;
if (!(multicast = ast_calloc(1, sizeof(*multicast)))) {
return -1;
}
- if (!strcasecmp(type, "basic")) {
- multicast->type = MULTICAST_TYPE_BASIC;
- } else if (!strcasecmp(type, "linksys")) {
- multicast->type = MULTICAST_TYPE_LINKSYS;
- } else {
+ if (set_type(multicast, mcast_options->type)) {
ast_free(multicast);
return -1;
}
@@ -136,6 +297,18 @@ static int multicast_rtp_new(struct ast_rtp_instance *instance, struct ast_sched
return -1;
}
+ if (ast_test_flag(&mcast_options->opts, OPT_LOOP)) {
+ set_loop(multicast->socket, mcast_options->opt_args[OPT_ARG_LOOP]);
+ }
+
+ if (ast_test_flag(&mcast_options->opts, OPT_TTL)) {
+ set_ttl(multicast->socket, mcast_options->opt_args[OPT_ARG_TTL]);
+ }
+
+ if (ast_test_flag(&mcast_options->opts, OPT_IF)) {
+ set_if(multicast->socket, mcast_options->opt_args[OPT_ARG_IF]);
+ }
+
multicast->ssrc = ast_random();
ast_rtp_instance_set_data(instance, multicast);
@@ -314,7 +487,7 @@ static int unload_module(void)
return 0;
}
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Multicast RTP Engine",
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
diff --git a/res/res_rtp_multicast.exports.in b/res/res_rtp_multicast.exports.in
new file mode 100644
index 000000000..995a1802e
--- /dev/null
+++ b/res/res_rtp_multicast.exports.in
@@ -0,0 +1,6 @@
+{
+ global:
+ LINKER_SYMBOL_PREFIXast_multicast_rtp*;
+ local:
+ *;
+};
diff --git a/res/res_srtp.c b/res/res_srtp.c
index 8d8daf0b0..97773c125 100644
--- a/res/res_srtp.c
+++ b/res/res_srtp.c
@@ -40,7 +40,11 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <srtp/srtp.h>
+#ifdef HAVE_OPENSSL
+#include <openssl/rand.h>
+#else
#include <srtp/crypto_kernel.h>
+#endif
#include "asterisk/lock.h"
#include "asterisk/sched.h"
@@ -305,7 +309,11 @@ static int ast_srtp_policy_set_master_key(struct ast_srtp_policy *policy, const
static int ast_srtp_get_random(unsigned char *key, size_t len)
{
+#ifdef HAVE_OPENSSL
+ return RAND_bytes(key, len) > 0 ? 0: -1;
+#else
return crypto_get_random(key, len) != err_status_ok ? -1: 0;
+#endif
}
static void ast_srtp_set_cb(struct ast_srtp *srtp, const struct ast_srtp_cb *cb, void *data)
diff --git a/res/res_stasis.c b/res/res_stasis.c
index fae9aa220..11aeb438e 100644
--- a/res/res_stasis.c
+++ b/res/res_stasis.c
@@ -713,6 +713,22 @@ int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge,
return 0;
}
+void stasis_app_bridge_playback_channel_remove(char *bridge_id,
+ struct stasis_app_control *control)
+{
+ struct stasis_app_bridge_channel_wrapper *wrapper;
+
+ wrapper = ao2_find(app_bridges_playback, bridge_id, OBJ_SEARCH_KEY | OBJ_UNLINK);
+ if (wrapper) {
+ /* If wrapper is not found, then that means the after bridge callback has been
+ * called or is in progress. No need to unlink the control here since that has
+ * been done or is about to be done in the after bridge callback
+ */
+ ao2_unlink(app_controls, control);
+ ao2_ref(wrapper, -1);
+ }
+}
+
struct ast_channel *stasis_app_bridge_playback_channel_find(struct ast_bridge *bridge)
{
struct stasis_app_bridge_channel_wrapper *playback_wrapper;
diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json
index cb41fb681..8eaa5eb9b 100644
--- a/rest-api/api-docs/channels.json
+++ b/rest-api/api-docs/channels.json
@@ -128,6 +128,14 @@
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "formats",
+ "description": "The format name capability list to use if originator is not specified. Ex. \"ulaw,slin16\". Format names can be found with \"core show codecs\".",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
}
],
"errorResponses": [
@@ -276,6 +284,14 @@
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "formats",
+ "description": "The format name capability list to use if originator is not specified. Ex. \"ulaw,slin16\". Format names can be found with \"core show codecs\".",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
}
],
"errorResponses": [
@@ -284,7 +300,6 @@
"reason": "Invalid parameters for originating a channel."
}
]
-
},
{
"httpMethod": "DELETE",
diff --git a/tests/test_cel.c b/tests/test_cel.c
index 03e243c78..9a3dc8114 100644
--- a/tests/test_cel.c
+++ b/tests/test_cel.c
@@ -1610,7 +1610,7 @@ AST_TEST_DEFINE(test_cel_dial_pickup)
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
- HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "CANCEL");
+ HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;
diff --git a/tests/test_netsock2.c b/tests/test_netsock2.c
index e182b0a75..fec1ae2d5 100644
--- a/tests/test_netsock2.c
+++ b/tests/test_netsock2.c
@@ -75,7 +75,7 @@ AST_TEST_DEFINE(parsing)
};
size_t x;
- struct ast_sockaddr addr = { { 0, 0, } };
+ struct ast_sockaddr addr;
int parse_result;
switch (cmd) {
@@ -91,15 +91,17 @@ AST_TEST_DEFINE(parsing)
}
for (x = 0; x < ARRAY_LEN(test_vals); x++) {
+ memset(&addr, 0, sizeof(addr));
if ((parse_result = ast_sockaddr_parse(&addr, test_vals[x].address, 0)) != test_vals[x].expected_result) {
ast_test_status_update(test, "On '%s' expected %d but got %d\n", test_vals[x].address, test_vals[x].expected_result, parse_result);
res = AST_TEST_FAIL;
}
if (parse_result) {
- struct ast_sockaddr tmp_addr = { { 0, 0, } };
+ struct ast_sockaddr tmp_addr;
const char *tmp;
tmp = ast_sockaddr_stringify(&addr);
+ memset(&tmp_addr, 0, sizeof(tmp_addr));
ast_sockaddr_parse(&tmp_addr, tmp, 0);
if (ast_sockaddr_cmp_addr(&addr, &tmp_addr)) {
char buf[64];
diff --git a/tests/test_res_pjsip_scheduler.c b/tests/test_res_pjsip_scheduler.c
index f9a1633ac..a5461accb 100644
--- a/tests/test_res_pjsip_scheduler.c
+++ b/tests/test_res_pjsip_scheduler.c
@@ -26,6 +26,7 @@
/*** MODULEINFO
<depend>TEST_FRAMEWORK</depend>
+ <depend>pjproject</depend>
<depend>res_pjsip</depend>
<support_level>core</support_level>
***/
diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4
index 2cc18bfa8..67ac04d4d 100644
--- a/third-party/pjproject/configure.m4
+++ b/third-party/pjproject/configure.m4
@@ -44,4 +44,5 @@ AC_DEFUN([PJPROJECT_CONFIGURE],
PJPROJECT_SYMBOL_CHECK([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_from_files2], [pjlib.h])
PJPROJECT_SYMBOL_CHECK([PJSIP_EXTERNAL_RESOLVER], [pjsip_endpt_set_ext_resolver], [pjsip.h])
AC_DEFINE([HAVE_PJSIP_TLS_TRANSPORT_PROTO], 1, [Define if your system has PJSIP_TLS_TRANSPORT_PROTO])
+ AC_DEFINE([HAVE_PJSIP_EVSUB_GRP_LOCK], 1, [Define if your system has PJSIP_EVSUB_GRP_LOCK])
])
diff --git a/third-party/pjproject/patches/0001-evsub-Add-APIs-to-add-decrement-an-event-subscriptio.patch b/third-party/pjproject/patches/0001-evsub-Add-APIs-to-add-decrement-an-event-subscriptio.patch
new file mode 100644
index 000000000..d2a47c6c5
--- /dev/null
+++ b/third-party/pjproject/patches/0001-evsub-Add-APIs-to-add-decrement-an-event-subscriptio.patch
@@ -0,0 +1,73 @@
+From a5030c9b33b2c936879fbacb1d2ea5edc2979181 Mon Sep 17 00:00:00 2001
+From: George Joseph <gjoseph@digium.com>
+Date: Sat, 18 Jun 2016 10:14:34 -0600
+Subject: [PATCH] evsub: Add APIs to add/decrement an event subscription's
+ group lock
+
+These APIs can be used to ensure that the evsub isn't destroyed before
+an application is finished using it.
+---
+ pjsip/include/pjsip-simple/evsub.h | 20 ++++++++++++++++++++
+ pjsip/src/pjsip-simple/evsub.c | 14 ++++++++++++++
+ 2 files changed, 34 insertions(+)
+
+diff --git a/pjsip/include/pjsip-simple/evsub.h b/pjsip/include/pjsip-simple/evsub.h
+index 2dc4d69..31f85f8 100644
+--- a/pjsip/include/pjsip-simple/evsub.h
++++ b/pjsip/include/pjsip-simple/evsub.h
+@@ -490,6 +490,26 @@ PJ_DECL(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
+ PJ_DECL(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id );
+
+
++/**
++ * Increment the event subscription's group lock.
++ *
++ * @param sub The server subscription instance.
++ *
++ * @return PJ_SUCCESS on success.
++ */
++PJ_DEF(pj_status_t) pjsip_evsub_add_ref(pjsip_evsub *sub);
++
++
++/**
++ * Decrement the event subscription's group lock.
++ *
++ * @param sub The server subscription instance.
++ *
++ * @return PJ_SUCCESS on success.
++ */
++PJ_DEF(pj_status_t) pjsip_evsub_dec_ref(pjsip_evsub *sub);
++
++
+
+ PJ_END_DECL
+
+diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c
+index 7cd8859..68a9564 100644
+--- a/pjsip/src/pjsip-simple/evsub.c
++++ b/pjsip/src/pjsip-simple/evsub.c
+@@ -831,7 +831,21 @@ static pj_status_t evsub_create( pjsip_dialog *dlg,
+ return PJ_SUCCESS;
+ }
+
++/*
++ * Increment the event subscription's group lock.
++ */
++PJ_DEF(pj_status_t) pjsip_evsub_add_ref(pjsip_evsub *sub)
++{
++ return pj_grp_lock_add_ref(sub->grp_lock);
++}
+
++/*
++ * Decrement the event subscription's group lock.
++ */
++PJ_DEF(pj_status_t) pjsip_evsub_dec_ref(pjsip_evsub *sub)
++{
++ return pj_grp_lock_dec_ref(sub->grp_lock);
++}
+
+ /*
+ * Create client subscription session.
+--
+2.5.5
+
diff --git a/third-party/pjproject/patches/config_site.h b/third-party/pjproject/patches/config_site.h
index 8e854b723..eb9f8b15c 100644
--- a/third-party/pjproject/patches/config_site.h
+++ b/third-party/pjproject/patches/config_site.h
@@ -37,3 +37,7 @@
#undef PJ_TODO
#define PJ_TODO(x)
+
+/* Defaults too low for WebRTC */
+#define PJ_ICE_MAX_CAND 32
+#define PJ_ICE_MAX_CHECKS (PJ_ICE_MAX_CAND * 2)