summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES12
-rw-r--r--Makefile6
-rw-r--r--addons/cdr_mysql.c8
-rw-r--r--apps/app_agent_pool.c1
-rw-r--r--apps/app_confbridge.c8
-rw-r--r--apps/app_originate.c12
-rw-r--r--apps/confbridge/conf_config_parser.c77
-rw-r--r--apps/confbridge/include/confbridge.h4
-rw-r--r--autoconf/ast_ext_tool_check.m42
-rw-r--r--autoconf/ast_pkgconfig.m42
-rw-r--r--bridges/bridge_softmix.c242
-rw-r--r--bridges/bridge_softmix/include/bridge_softmix_internal.h14
-rw-r--r--channels/chan_pjsip.c16
-rw-r--r--channels/chan_sip.c18
-rw-r--r--channels/pjsip/dialplan_functions.c8
-rw-r--r--configs/samples/confbridge.conf.sample8
-rwxr-xr-xconfigure1203
-rw-r--r--configure.ac95
-rwxr-xr-xcontrib/scripts/install_prereq67
-rw-r--r--include/asterisk/_private.h5
-rw-r--r--include/asterisk/autoconfig.h.in12
-rw-r--r--include/asterisk/bridge.h51
-rw-r--r--include/asterisk/paths.h4
-rw-r--r--include/asterisk/res_pjsip.h115
-rw-r--r--include/asterisk/rtp_engine.h8
-rw-r--r--include/asterisk/utils.h13
-rw-r--r--main/Makefile9
-rw-r--r--main/asterisk.c424
-rw-r--r--main/bridge.c26
-rw-r--r--main/options.c475
-rw-r--r--makeopts.in18
-rwxr-xr-xmenuselect/configure4
-rw-r--r--res/res_musiconhold.c1
-rw-r--r--res/res_pjsip.c123
-rw-r--r--res/res_pjsip/config_system.c2
-rw-r--r--res/res_pjsip/config_transport.c2
-rw-r--r--res/res_pjsip/pjsip_scheduler.c311
-rw-r--r--res/res_pjsip_header_funcs.c22
-rw-r--r--res/res_pjsip_history.c2
-rw-r--r--res/res_pjsip_notify.c230
-rw-r--r--res/res_pjsip_outbound_publish.c9
-rw-r--r--res/res_pjsip_outbound_registration.c12
-rw-r--r--res/res_pjsip_pubsub.c6
-rw-r--r--res/res_pjsip_refer.c18
-rw-r--r--res/res_pjsip_sdp_rtp.c3
-rw-r--r--res/res_pjsip_session.c10
-rw-r--r--res/res_pjsip_t38.c6
-rw-r--r--res/res_pjsip_transport_websocket.c6
-rw-r--r--res/res_rtp_asterisk.c305
-rw-r--r--third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch114
-rw-r--r--third-party/pjproject/patches/0080-timer-Clean-up-usage-of-timer-heap.patch434
-rw-r--r--third-party/pjproject/patches/0090-sip_transaction-In-tsx_timer_callback-check-if-tsx-i.patch31
-rw-r--r--utils/Makefile3
53 files changed, 2890 insertions, 1727 deletions
diff --git a/CHANGES b/CHANGES
index 09a7659d4..3a81ca7ba 100644
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,13 @@ app_fax
* The app_fax module is now deprecated, users should migrate to the
replacement module res_fax.
+app_originate
+------------------
+ * An 'a' option has been added to the Originate dialplan application which
+ will execute the originate in an asynchronous fashion. If set then the
+ application will return immediately without waiting for the originated
+ channel to answer.
+
Build System
------------------
* MALLOC_DEBUG no longer has an effect on Asterisk's ABI. Asterisk built
@@ -115,6 +122,11 @@ res_pjproject
MALLOC_DEBUG. The cache gets in the way of determining if the pool contents
are used after free and who freed it.
+res_pjsip_notify
+------------------
+ * Extend the PJSIPNotify AMI command to send an in-dialog notify on a
+ channel.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 15.2.0 to Asterisk 15.3.0 ------------
------------------------------------------------------------------------------
diff --git a/Makefile b/Makefile
index 54368f7c6..2c10dc497 100644
--- a/Makefile
+++ b/Makefile
@@ -951,6 +951,10 @@ config:
fi ; \
elif [ -f /etc/arch-release -o -f /etc/arch-release ] ; then \
./build_tools/install_subst contrib/init.d/rc.archlinux.asterisk "$(DESTDIR)/etc/init.d/asterisk"; \
+ elif [ -f /etc/slackware-version ]; then \
+ ./build_tools/install_subst contrib/init.d/rc.slackware.asterisk "$(DESTDIR)/etc/rc.d/rc.asterisk"; \
+ elif [ -f /etc/os-release ] && [ "slackware" = "$(shell . /etc/os-release && echo $$ID)" ] ; then \
+ ./build_tools/install_subst contrib/init.d/rc.slackware.asterisk "$(DESTDIR)/etc/rc.d/rc.asterisk"; \
elif [ -d "$(DESTDIR)/Library/LaunchDaemons" ]; then \
if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist" ]; then \
./build_tools/install_subst contrib/init.d/org.asterisk.asterisk.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.asterisk.plist"; \
@@ -958,8 +962,6 @@ config:
if [ ! -f "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist" ]; then \
./build_tools/install_subst contrib/init.d/org.asterisk.muted.plist "$(DESTDIR)/Library/LaunchDaemons/org.asterisk.muted.plist"; \
fi; \
- elif [ -f /etc/slackware-version ]; then \
- echo "Slackware is not currently supported, although an init script does exist for it."; \
else \
echo "We could not install init scripts for your distribution." ; \
fi
diff --git a/addons/cdr_mysql.c b/addons/cdr_mysql.c
index 2fefe4ed1..97ebdf26f 100644
--- a/addons/cdr_mysql.c
+++ b/addons/cdr_mysql.c
@@ -58,6 +58,14 @@
#define DATE_FORMAT "%Y-%m-%d %T"
+#ifndef MYSQL_PORT
+# ifdef MARIADB_PORT
+# define MYSQL_PORT MARIADB_PORT
+# else
+# define MYSQL_PORT 3306
+# endif
+#endif
+
AST_THREADSTORAGE(sql1_buf);
AST_THREADSTORAGE(sql2_buf);
AST_THREADSTORAGE(escape_buf);
diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c
index 3c2ea3870..805c403f5 100644
--- a/apps/app_agent_pool.c
+++ b/apps/app_agent_pool.c
@@ -438,6 +438,7 @@ static void *agent_cfg_alloc(const char *name)
cfg = ao2_alloc_options(sizeof(*cfg), agent_cfg_destructor,
AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!cfg || ast_string_field_init(cfg, 64)) {
+ ao2_cleanup(cfg);
return NULL;
}
ast_string_field_set(cfg, username, name);
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index 4f1108e6b..25cf2758f 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -1543,6 +1543,14 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
} else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
ast_bridge_set_sfu_video_mode(conference->bridge);
ast_bridge_set_video_update_discard(conference->bridge, conference->b_profile.video_update_discard);
+ ast_bridge_set_remb_send_interval(conference->bridge, conference->b_profile.remb_send_interval);
+ if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_LOWEST);
+ } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
+ ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST);
+ }
}
/* Link it into the conference bridges container */
diff --git a/apps/app_originate.c b/apps/app_originate.c
index 30fa565be..107be846d 100644
--- a/apps/app_originate.c
+++ b/apps/app_originate.c
@@ -74,6 +74,10 @@ static const char app_originate[] = "Originate";
</parameter>
<parameter name="options" required="false">
<optionlist>
+ <option name="a">
+ <para>Originate asynchronously. In other words, continue in the dialplan
+ without waiting for the originated channel to answer.</para>
+ </option>
<option name="b" argsep="^">
<para>Before originating the outgoing call, Gosub to the specified
location using the newly created channel.</para>
@@ -123,6 +127,7 @@ static const char app_originate[] = "Originate";
enum {
OPT_PREDIAL_CALLEE = (1 << 0),
OPT_PREDIAL_CALLER = (1 << 1),
+ OPT_ASYNC = (1 << 2),
};
enum {
@@ -133,6 +138,7 @@ enum {
};
AST_APP_OPTIONS(originate_exec_options, BEGIN_OPTIONS
+ AST_APP_OPTION('a', OPT_ASYNC),
AST_APP_OPTION_ARG('b', OPT_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLEE),
AST_APP_OPTION_ARG('B', OPT_PREDIAL_CALLER, OPT_ARG_PREDIAL_CALLER),
END_OPTIONS );
@@ -250,7 +256,8 @@ static int originate_exec(struct ast_channel *chan, const char *data)
res = ast_pbx_outgoing_exten_predial(chantech, cap_slin, chandata,
timeout * 1000, args.arg1, exten, priority, &outgoing_status,
- AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, 0, NULL,
+ ast_test_flag64(&opts, OPT_ASYNC) ? AST_OUTGOING_NO_WAIT : AST_OUTGOING_WAIT,
+ NULL, NULL, NULL, NULL, NULL, 0, NULL,
predial_callee);
} else {
ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
@@ -258,7 +265,8 @@ static int originate_exec(struct ast_channel *chan, const char *data)
res = ast_pbx_outgoing_app_predial(chantech, cap_slin, chandata,
timeout * 1000, args.arg1, args.arg2, &outgoing_status,
- AST_OUTGOING_WAIT, NULL, NULL, NULL, NULL, NULL, NULL,
+ ast_test_flag64(&opts, OPT_ASYNC) ? AST_OUTGOING_NO_WAIT : AST_OUTGOING_WAIT,
+ NULL, NULL, NULL, NULL, NULL, NULL,
predial_callee);
}
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 71da80206..c143e39e2 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -458,6 +458,39 @@
video update requests from clients.
</para></description>
</configOption>
+ <configOption name="remb_send_interval" default="0">
+ <synopsis>Sets the interval in milliseconds that a combined REMB frame will be sent to video sources</synopsis>
+ <description><para>
+ Sets the interval in milliseconds that a combined REMB frame will be sent
+ to video sources. This is done by taking all REMB frames that have been
+ received since the last REMB frame was sent, making a combined value,
+ and sending it to the source. A REMB frame contains receiver estimated
+ maximum bitrate information. By creating a combined REMB frame the
+ sender of video can be influenced on the bitrate they choose, allowing
+ better quality for all receivers.
+ </para></description>
+ </configOption>
+ <configOption name="remb_behavior" default="average">
+ <synopsis>Sets how REMB reports are generated from multiple sources</synopsis>
+ <description><para>
+ Sets how REMB reports are combined from multiple sources to form one. A REMB report
+ consists of information about the receiver estimated maximum bitrate. As a source
+ stream may be forwarded to multiple receivers the reports must be combined into
+ a single one which is sent to the sender.</para>
+ <enumlist>
+ <enum name="average">
+ <para>The average of all estimated maximum bitrates is taken and sent
+ to the sender.</para>
+ </enum>
+ <enum name="lowest">
+ <para>The lowest estimated maximum bitrate is forwarded to the sender.</para>
+ </enum>
+ <enum name="highest">
+ <para>The highest estimated maximum bitrate is forwarded to the sender.</para>
+ </enum>
+ </enumlist>
+ </description>
+ </configOption>
<configOption name="template">
<synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
</configOption>
@@ -1661,6 +1694,24 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
}
ast_cli(a->fd,"Video Update Discard: %u\n", b_profile.video_update_discard);
+ ast_cli(a->fd,"REMB Send Interval: %u\n", b_profile.remb_send_interval);
+
+ switch (b_profile.flags
+ & (BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE | BRIDGE_OPT_REMB_BEHAVIOR_LOWEST
+ | BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST)) {
+ case BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE:
+ ast_cli(a->fd, "REMB Behavior: average\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_LOWEST:
+ ast_cli(a->fd, "REMB Behavior: lowest\n");
+ break;
+ case BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST:
+ ast_cli(a->fd, "REMB Behavior: highest\n");
+ break;
+ default:
+ ast_assert(0);
+ break;
+ }
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
ast_cli(a->fd,"sound_only_one: %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
@@ -1998,6 +2049,30 @@ static int video_mode_handler(const struct aco_option *opt, struct ast_variable
return 0;
}
+static int remb_behavior_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ struct bridge_profile *b_profile = obj;
+
+ if (strcasecmp(var->name, "remb_behavior")) {
+ return -1;
+ }
+
+ ast_clear_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE |
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST |
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
+
+ if (!strcasecmp(var->value, "average")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE);
+ } else if (!strcasecmp(var->value, "lowest")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_LOWEST);
+ } else if (!strcasecmp(var->value, "highest")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST);
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
static int user_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct user_profile *u_profile = obj;
@@ -2231,6 +2306,8 @@ int conf_load_config(void)
aco_option_register(&cfg_info, "language", ACO_EXACT, bridge_types, "en", OPT_CHAR_ARRAY_T, 0, CHARFLDSET(struct bridge_profile, language));
aco_option_register_custom(&cfg_info, "sound_", ACO_PREFIX, bridge_types, NULL, sound_option_handler, 0);
aco_option_register(&cfg_info, "video_update_discard", ACO_EXACT, bridge_types, "2000", OPT_UINT_T, 0, FLDSET(struct bridge_profile, video_update_discard));
+ aco_option_register(&cfg_info, "remb_send_interval", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, remb_send_interval));
+ aco_option_register_custom(&cfg_info, "remb_behavior", ACO_EXACT, bridge_types, "average", remb_behavior_handler, 0);
/* This option should only be used with the CONFBRIDGE dialplan function */
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index 044ab4003..0a0a5713f 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -76,6 +76,9 @@ enum bridge_profile_flags {
BRIDGE_OPT_RECORD_FILE_TIMESTAMP = (1 << 5), /*!< Set if the record file should have a timestamp appended */
BRIDGE_OPT_BINAURAL_ACTIVE = (1 << 6), /*!< Set if binaural convolution is activated */
BRIDGE_OPT_VIDEO_SRC_SFU = (1 << 7), /*!< Selective forwarding unit */
+ BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE = (1 << 8), /*!< The average of all REMB reports is sent to the sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_LOWEST = (1 << 9), /*!< The lowest estimated maximum bitrate is sent to the sender */
+ BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST = (1 << 10), /*!< The highest estimated maximum bitrate is sent to the sender */
};
enum conf_menu_action_id {
@@ -222,6 +225,7 @@ struct bridge_profile {
struct bridge_profile_sounds *sounds;
char regcontext[AST_MAX_CONTEXT];
unsigned int video_update_discard; /*!< Amount of time after sending a video update request that subsequent requests should be discarded */
+ unsigned int remb_send_interval; /*!< Interval at which a combined REMB frame is sent to video sources */
};
/*! \brief The structure that represents a conference bridge */
diff --git a/autoconf/ast_ext_tool_check.m4 b/autoconf/ast_ext_tool_check.m4
index ef762eb87..cbe109e4a 100644
--- a/autoconf/ast_ext_tool_check.m4
+++ b/autoconf/ast_ext_tool_check.m4
@@ -11,7 +11,7 @@ AC_DEFUN([AST_EXT_TOOL_CHECK],
AC_PATH_TOOL(CONFIG_$1, $2, No, [${$1_DIR}/bin:$PATH])
if test ! "x${CONFIG_$1}" = xNo; then
$1_INCLUDE=$(${CONFIG_$1} m4_default([$3],[--cflags]))
- $1_INCLUDE=$(echo ${$1_INCLUDE} | $SED -e "s|-I|-I${$1_DIR}|g")
+ $1_INCLUDE=$(echo ${$1_INCLUDE} | $SED -e "s|-I|-I${$1_DIR}|g" -e "s|-std=c99||g")
$1_LIB=$(${CONFIG_$1} m4_default([$4],[--libs]))
$1_LIB=$(echo ${$1_LIB} | $SED -e "s|-L|-L${$1_DIR}|g")
diff --git a/autoconf/ast_pkgconfig.m4 b/autoconf/ast_pkgconfig.m4
index ae7bbc086..3415ed547 100644
--- a/autoconf/ast_pkgconfig.m4
+++ b/autoconf/ast_pkgconfig.m4
@@ -5,7 +5,7 @@ AC_DEFUN([AST_PKG_CONFIG_CHECK],
if test "x${PBX_$1}" != "x1" -a "${USE_$1}" != "no"; then
PKG_CHECK_MODULES($1, $2, [
PBX_$1=1
- $1_INCLUDE="$$1_CFLAGS"
+ $1_INCLUDE=$(echo ${$1_CFLAGS} | $SED -e "s|-std=c99||g")
$1_LIB="$$1_LIBS"
AC_DEFINE([HAVE_$1], 1, [Define if your system has the $1 libraries.])
], [
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
index 16e1fb897..f0a3fb42d 100644
--- a/bridges/bridge_softmix.c
+++ b/bridges/bridge_softmix.c
@@ -69,6 +69,15 @@
#define SOFTBRIDGE_VIDEO_DEST_LEN strlen(SOFTBRIDGE_VIDEO_DEST_PREFIX)
#define SOFTBRIDGE_VIDEO_DEST_SEPARATOR '_'
+struct softmix_remb_collector {
+ /*! The frame which will be given to each source stream */
+ struct ast_frame frame;
+ /*! The REMB to send to the source which is collecting REMB reports */
+ struct ast_rtp_rtcp_feedback feedback;
+ /*! The maximum bitrate */
+ unsigned int bitrate;
+};
+
struct softmix_stats {
/*! Each index represents a sample rate used above the internal rate. */
unsigned int sample_rates[16];
@@ -768,6 +777,10 @@ static void softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_ch
ast_stream_topology_free(sc->topology);
+ ao2_cleanup(sc->remb_collector);
+
+ AST_VECTOR_FREE(&sc->video_sources);
+
/* Drop mutex lock */
ast_mutex_destroy(&sc->lock);
@@ -1160,6 +1173,39 @@ static int softmix_bridge_write_control(struct ast_bridge *bridge, struct ast_br
/*!
* \internal
+ * \brief Determine what to do with an RTCP frame.
+ * \since 15.4.0
+ *
+ * \param bridge Which bridge is getting the frame
+ * \param bridge_channel Which channel is writing the frame.
+ * \param frame What is being written.
+ */
+static void softmix_bridge_write_rtcp(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct ast_rtp_rtcp_feedback *feedback = frame->data.ptr;
+ struct softmix_channel *sc = bridge_channel->tech_pvt;
+
+ /* We only care about REMB reports right now. In the future we may be able to use sender or
+ * receiver reports to further tweak things, but not yet.
+ */
+ if (frame->subclass.integer != AST_RTP_RTCP_PSFB || feedback->fmt != AST_RTP_RTCP_FMT_REMB ||
+ bridge->softmix.video_mode.mode != AST_BRIDGE_VIDEO_MODE_SFU ||
+ !bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval) {
+ return;
+ }
+
+ /* REMB is the total estimated maximum bitrate across all streams within the session, so we store
+ * only the latest report and use it everywhere.
+ */
+ ast_mutex_lock(&sc->lock);
+ sc->remb = feedback->remb;
+ ast_mutex_unlock(&sc->lock);
+
+ return;
+}
+
+/*!
+ * \internal
* \brief Determine what to do with a frame written into the bridge.
* \since 12.0.0
*
@@ -1204,6 +1250,9 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
case AST_FRAME_CONTROL:
res = softmix_bridge_write_control(bridge, bridge_channel, frame);
break;
+ case AST_FRAME_RTCP:
+ softmix_bridge_write_rtcp(bridge, bridge_channel, frame);
+ break;
case AST_FRAME_BRIDGE_ACTION:
res = ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
break;
@@ -1219,6 +1268,104 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
return res;
}
+static void remb_collect_report(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,
+ struct softmix_bridge_data *softmix_data, struct softmix_channel *sc)
+{
+ int i;
+ unsigned int bitrate;
+
+ /* If there are no video sources that we are a receiver of then we have noone to
+ * report REMB to.
+ */
+ if (!AST_VECTOR_SIZE(&sc->video_sources)) {
+ return;
+ }
+
+ /* We evenly divide the available maximum bitrate across the video sources
+ * to this receiver so each source gets an equal slice.
+ */
+ bitrate = (sc->remb.br_mantissa << sc->remb.br_exp) / AST_VECTOR_SIZE(&sc->video_sources);
+
+ /* If this receiver has no bitrate yet ignore it */
+ if (!bitrate) {
+ return;
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&sc->video_sources); ++i) {
+ struct softmix_remb_collector *collector;
+
+ /* The collector will always exist if a video source is in our list */
+ collector = AST_VECTOR_GET(&softmix_data->remb_collectors, AST_VECTOR_GET(&sc->video_sources, i));
+
+ if (!collector->bitrate) {
+ collector->bitrate = bitrate;
+ continue;
+ }
+
+ switch (bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior) {
+ case AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE:
+ collector->bitrate = (collector->bitrate + bitrate) / 2;
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_LOWEST:
+ if (bitrate < collector->bitrate) {
+ collector->bitrate = bitrate;
+ }
+ break;
+ case AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST:
+ if (bitrate > collector->bitrate) {
+ collector->bitrate = bitrate;
+ }
+ break;
+ }
+ }
+}
+
+static void remb_send_report(struct ast_bridge_channel *bridge_channel, struct softmix_channel *sc)
+{
+ int i;
+
+ if (!sc->remb_collector) {
+ return;
+ }
+
+ /* If we have a new bitrate then use it for the REMB, if not we use the previous
+ * one until we know otherwise. This way the bitrate doesn't drop to 0 all of a sudden.
+ */
+ if (sc->remb_collector->bitrate) {
+ sc->remb_collector->feedback.remb.br_mantissa = sc->remb_collector->bitrate;
+ sc->remb_collector->feedback.remb.br_exp = 0;
+
+ /* The mantissa only has 18 bits available, so while it exceeds them we bump
+ * up the exp.
+ */
+ while (sc->remb_collector->feedback.remb.br_mantissa > 0x3ffff) {
+ sc->remb_collector->feedback.remb.br_mantissa = sc->remb_collector->feedback.remb.br_mantissa >> 1;
+ sc->remb_collector->feedback.remb.br_exp++;
+ }
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&bridge_channel->stream_map.to_bridge); ++i) {
+ int bridge_num = AST_VECTOR_GET(&bridge_channel->stream_map.to_bridge, i);
+
+ /* If this stream is not being provided to the bridge there can be no receivers of it
+ * so therefore no REMB reports.
+ */
+ if (bridge_num == -1) {
+ continue;
+ }
+
+ /* We need to update the frame with this stream, or else it won't be
+ * properly routed. We don't use the actual channel stream identifier as
+ * the bridging core will do the translation from bridge stream identifier to
+ * channel stream identifier.
+ */
+ sc->remb_collector->frame.stream_num = bridge_num;
+ ast_bridge_channel_queue_frame(bridge_channel, &sc->remb_collector->frame);
+ }
+
+ sc->remb_collector->bitrate = 0;
+}
+
static void gather_softmix_stats(struct softmix_stats *stats,
const struct softmix_bridge_data *softmix_data,
struct ast_bridge_channel *bridge_channel)
@@ -1440,6 +1587,7 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
struct ast_format *cur_slin = ast_format_cache_get_slin_by_rate(softmix_data->internal_rate);
unsigned int softmix_samples = SOFTMIX_SAMPLES(softmix_data->internal_rate, softmix_data->internal_mixing_interval);
unsigned int softmix_datalen = SOFTMIX_DATALEN(softmix_data->internal_rate, softmix_data->internal_mixing_interval);
+ int remb_update = 0;
if (softmix_datalen > MAX_DATALEN) {
/* This should NEVER happen, but if it does we need to know about it. Almost
@@ -1478,6 +1626,14 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
check_binaural_position_change(bridge, softmix_data);
#endif
+ /* If we need to do a REMB update to all video sources then do so */
+ if (bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU &&
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval &&
+ ast_tvdiff_ms(ast_tvnow(), softmix_data->last_remb_update) > bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval) {
+ remb_update = 1;
+ softmix_data->last_remb_update = ast_tvnow();
+ }
+
/* Go through pulling audio from each factory that has it available */
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
struct softmix_channel *sc = bridge_channel->tech_pvt;
@@ -1512,6 +1668,9 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
#endif
mixing_array.used_entries++;
}
+ if (remb_update) {
+ remb_collect_report(bridge, bridge_channel, softmix_data, sc);
+ }
ast_mutex_unlock(&sc->lock);
}
@@ -1562,6 +1721,10 @@ static int softmix_mixing_loop(struct ast_bridge *bridge)
/* A frame is now ready for the channel. */
ast_bridge_channel_queue_frame(bridge_channel, &sc->write_frame);
+
+ if (remb_update) {
+ remb_send_report(bridge_channel, sc);
+ }
}
update_all_rates = 0;
@@ -1688,6 +1851,8 @@ static void softmix_bridge_data_destroy(struct softmix_bridge_data *softmix_data
}
ast_mutex_destroy(&softmix_data->lock);
ast_cond_destroy(&softmix_data->cond);
+ AST_VECTOR_RESET(&softmix_data->remb_collectors, ao2_cleanup);
+ AST_VECTOR_FREE(&softmix_data->remb_collectors);
ast_free(softmix_data);
}
@@ -1718,6 +1883,8 @@ static int softmix_bridge_create(struct ast_bridge *bridge)
softmix_data->internal_mixing_interval);
#endif
+ AST_VECTOR_INIT(&softmix_data->remb_collectors, 0);
+
bridge->tech_pvt = softmix_data;
/* Start the mixing thread. */
@@ -1814,7 +1981,10 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
stream = ast_stream_topology_get_stream(topology, i);
if (is_video_dest(stream, source_channel_name, source_stream_name)) {
+ struct softmix_channel *sc = participant->tech_pvt;
+
AST_VECTOR_REPLACE(&participant->stream_map.to_channel, bridge_stream_position, i);
+ AST_VECTOR_APPEND(&sc->video_sources, bridge_stream_position);
break;
}
}
@@ -1824,6 +1994,58 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
}
/*!
+ * \brief Allocate a REMB collector
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct softmix_remb_collector *remb_collector_alloc(void)
+{
+ struct softmix_remb_collector *collector;
+
+ collector = ao2_alloc_options(sizeof(*collector), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!collector) {
+ return NULL;
+ }
+
+ collector->frame.frametype = AST_FRAME_RTCP;
+ collector->frame.subclass.integer = AST_RTP_RTCP_PSFB;
+ collector->feedback.fmt = AST_RTP_RTCP_FMT_REMB;
+ collector->frame.data.ptr = &collector->feedback;
+ collector->frame.datalen = sizeof(collector->feedback);
+
+ return collector;
+}
+
+/*!
+ * \brief Setup REMB collection for a particular bridge stream and channel.
+ *
+ * \param bridge The bridge
+ * \param bridge_channel Channel that is collecting REMB information
+ * \param bridge_stream_position The slot in the bridge where source video comes from
+ */
+static void remb_enable_collection(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,
+ size_t bridge_stream_position)
+{
+ struct softmix_channel *sc = bridge_channel->tech_pvt;
+ struct softmix_bridge_data *softmix_data = bridge->tech_pvt;
+
+ if (!sc->remb_collector) {
+ sc->remb_collector = remb_collector_alloc();
+ if (!sc->remb_collector) {
+ /* This is not fatal. Things will still continue to work but we won't
+ * produce a REMB report to the sender.
+ */
+ return;
+ }
+ }
+
+ if (AST_VECTOR_REPLACE(&softmix_data->remb_collectors, bridge_stream_position, ao2_bump(sc->remb_collector))) {
+ ao2_ref(sc->remb_collector, -1);
+ }
+}
+
+/*!
* \brief stream_topology_changed callback
*
* For most video modes, nothing beyond the ordinary is required.
@@ -1835,6 +2057,8 @@ static void map_source_to_destinations(const char *source_stream_name, const cha
*/
static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
+ struct softmix_bridge_data *softmix_data = bridge->tech_pvt;
+ struct softmix_channel *sc;
struct ast_bridge_channel *participant;
struct ast_vector_int media_types;
int nths[AST_MEDIA_TYPE_END] = {0};
@@ -1852,11 +2076,22 @@ static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, st
AST_VECTOR_INIT(&media_types, AST_MEDIA_TYPE_END);
+ /* The bridge stream identifiers may change, so reset the mapping for them.
+ * When channels end up getting added back in they'll reuse their existing
+ * collector and won't need to allocate a new one (unless they were just added).
+ */
+ AST_VECTOR_RESET(&softmix_data->remb_collectors, ao2_cleanup);
+
/* First traversal: re-initialize all of the participants' stream maps */
AST_LIST_TRAVERSE(&bridge->channels, participant, entry) {
ast_bridge_channel_lock(participant);
+
AST_VECTOR_RESET(&participant->stream_map.to_channel, AST_VECTOR_ELEM_CLEANUP_NOOP);
AST_VECTOR_RESET(&participant->stream_map.to_bridge, AST_VECTOR_ELEM_CLEANUP_NOOP);
+
+ sc = participant->tech_pvt;
+ AST_VECTOR_RESET(&sc->video_sources, AST_VECTOR_ELEM_CLEANUP_NOOP);
+
ast_bridge_channel_unlock(participant);
}
@@ -1897,7 +2132,12 @@ static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, st
if (is_video_source(stream)) {
AST_VECTOR_APPEND(&media_types, AST_MEDIA_TYPE_VIDEO);
AST_VECTOR_REPLACE(&participant->stream_map.to_bridge, i, AST_VECTOR_SIZE(&media_types) - 1);
- AST_VECTOR_REPLACE(&participant->stream_map.to_channel, AST_VECTOR_SIZE(&media_types) - 1, -1);
+ /*
+ * There are cases where we need to bidirectionally send frames, such as for REMB reports
+ * so we also map back to the channel.
+ */
+ AST_VECTOR_REPLACE(&participant->stream_map.to_channel, AST_VECTOR_SIZE(&media_types) - 1, i);
+ remb_enable_collection(bridge, participant, AST_VECTOR_SIZE(&media_types) - 1);
/*
* Unlock the channel and participant to prevent
* potential deadlock in map_source_to_destinations().
diff --git a/bridges/bridge_softmix/include/bridge_softmix_internal.h b/bridges/bridge_softmix/include/bridge_softmix_internal.h
index f842acb5e..3aa90915d 100644
--- a/bridges/bridge_softmix/include/bridge_softmix_internal.h
+++ b/bridges/bridge_softmix/include/bridge_softmix_internal.h
@@ -50,6 +50,8 @@
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/translate.h"
+#include "asterisk/rtp_engine.h"
+#include "asterisk/vector.h"
#ifdef BINAURAL_RENDERING
#include <fftw3.h>
@@ -124,6 +126,8 @@ struct video_follow_talker_data {
int energy_average;
};
+struct softmix_remb_collector;
+
/*! \brief Structure which contains per-channel mixing information */
struct softmix_channel {
/*! Lock to protect this structure */
@@ -169,6 +173,12 @@ struct softmix_channel {
struct video_follow_talker_data video_talker;
/*! The ideal stream topology for the channel */
struct ast_stream_topology *topology;
+ /*! The latest REMB report from this participant */
+ struct ast_rtp_rtcp_feedback_remb remb;
+ /*! The REMB collector for this channel, collects REMB from all video receivers */
+ struct softmix_remb_collector *remb_collector;
+ /*! The bridge streams which are feeding us video sources */
+ AST_VECTOR(, int) video_sources;
};
struct softmix_bridge_data {
@@ -202,6 +212,10 @@ struct softmix_bridge_data {
unsigned int binaural_init;
/*! The last time a video update was sent into the bridge */
struct timeval last_video_update;
+ /*! The last time a REMB frame was sent to each source of video */
+ struct timeval last_remb_update;
+ /*! Per-bridge stream REMB collectors, which flow back to video source */
+ AST_VECTOR(, struct softmix_remb_collector *) remb_collectors;
};
struct softmix_mixing_array {
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 5cb52a5b2..dde7416c3 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -718,7 +718,7 @@ static int chan_pjsip_answer(struct ast_channel *ast)
can occur between this thread and bridging (specifically when native bridging
attempts to do direct media) */
ast_channel_unlock(ast);
- res = ast_sip_push_task_synchronous(session->serializer, answer, session);
+ res = ast_sip_push_task_wait_serializer(session->serializer, answer, session);
if (res) {
if (res == -1) {
ast_log(LOG_ERROR,"Cannot answer '%s': Unable to push answer task to the threadpool.\n",
@@ -966,6 +966,16 @@ static int chan_pjsip_write_stream(struct ast_channel *ast, int stream_num, stru
case AST_FRAME_CNG:
break;
case AST_FRAME_RTCP:
+ /* We only support writing out feedback */
+ if (frame->subclass.integer != AST_RTP_RTCP_PSFB || !media) {
+ return 0;
+ } else if (media->type != AST_MEDIA_TYPE_VIDEO) {
+ ast_debug(3, "Channel %s stream %d is of type '%s', not video! Unable to write RTCP feedback.\n",
+ ast_channel_name(ast), stream_num, ast_codec_media_type2str(media->type));
+ return 0;
+ } else if (media->write_callback) {
+ res = media->write_callback(session, media, frame);
+ }
break;
default:
ast_log(LOG_WARNING, "Can't send %u type frames with PJSIP\n", frame->frametype);
@@ -2492,10 +2502,10 @@ static struct ast_channel *chan_pjsip_request_with_stream_topology(const char *t
req_data.topology = topology;
req_data.dest = data;
- /* Default failure value in case ast_sip_push_task_synchronous() itself fails. */
+ /* Default failure value in case ast_sip_push_task_wait_servant() itself fails. */
req_data.cause = AST_CAUSE_FAILURE;
- if (ast_sip_push_task_synchronous(NULL, request, &req_data)) {
+ if (ast_sip_push_task_wait_servant(NULL, request, &req_data)) {
*cause = req_data.cause;
return NULL;
}
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 138021e82..46f9ad699 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -25856,7 +25856,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
int *nounlock, struct sip_pvt *replaces_pvt, struct ast_channel *replaces_chan)
{
struct ast_channel *c;
- RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+ struct ast_bridge *bridge;
if (req->ignore) {
return 0;
@@ -25872,6 +25872,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
}
append_history(p, "Xfer", "INVITE/Replace received");
+ /* Get a ref to ensure the channel cannot go away on us. */
c = ast_channel_ref(p->owner);
/* Fake call progress */
@@ -25886,21 +25887,24 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req,
ast_raw_answer(c);
- ast_channel_lock(replaces_chan);
- bridge = ast_channel_get_bridge(replaces_chan);
- ast_channel_unlock(replaces_chan);
-
+ bridge = ast_bridge_transfer_acquire_bridge(replaces_chan);
if (bridge) {
+ /*
+ * We have two refs of the channel. One is held in c and the other
+ * is notionally represented by p->owner. The impart is "stealing"
+ * the p->owner ref on success so the bridging system can have
+ * control of when the channel is hung up.
+ */
if (ast_bridge_impart(bridge, c, replaces_chan, NULL,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
ast_hangup(c);
- ast_channel_unref(c);
}
+ ao2_ref(bridge, -1);
} else {
ast_channel_move(replaces_chan, c);
ast_hangup(c);
- ast_channel_unref(c);
}
+ ast_channel_unref(c);
sip_pvt_lock(p);
return 0;
}
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index aa376f892..ce347dcd9 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -897,7 +897,7 @@ int pjsip_acf_channel_read(struct ast_channel *chan, const char *cmd, char *data
func_args.field = args.field;
func_args.buf = buf;
func_args.len = len;
- if (ast_sip_push_task_synchronous(func_args.session->serializer, read_pjsip, &func_args)) {
+ if (ast_sip_push_task_wait_serializer(func_args.session->serializer, read_pjsip, &func_args)) {
ast_log(LOG_WARNING, "Unable to read properties of channel %s: failed to push task\n", ast_channel_name(chan));
ao2_ref(func_args.session, -1);
return -1;
@@ -1219,7 +1219,7 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
mdata.media_type = AST_MEDIA_TYPE_VIDEO;
}
- return ast_sip_push_task_synchronous(channel->session->serializer, media_offer_write_av, &mdata);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer, media_offer_write_av, &mdata);
}
int pjsip_acf_dtmf_mode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
@@ -1390,7 +1390,7 @@ int pjsip_acf_dtmf_mode_write(struct ast_channel *chan, const char *cmd, char *d
ast_channel_unlock(chan);
- return ast_sip_push_task_synchronous(channel->session->serializer, dtmf_mode_refresh_cb, &rdata);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer, dtmf_mode_refresh_cb, &rdata);
}
static int refresh_write_cb(void *obj)
@@ -1438,5 +1438,5 @@ int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, c
rdata.method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
}
- return ast_sip_push_task_synchronous(channel->session->serializer, refresh_write_cb, &rdata);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer, refresh_write_cb, &rdata);
}
diff --git a/configs/samples/confbridge.conf.sample b/configs/samples/confbridge.conf.sample
index e2d8a620d..8b276cdb8 100644
--- a/configs/samples/confbridge.conf.sample
+++ b/configs/samples/confbridge.conf.sample
@@ -235,6 +235,14 @@ type=bridge
; the video stream. Since a full frame can be large limiting how often they occur can
; reduce bandwidth usage at the cost of increasing how long it may take a newly joined
; channel to receive the video stream.
+;remb_send_interval=1000 ; Interval (in milliseconds) at which a combined REMB frame will be sent to sources of video.
+ ; A REMB frame contains receiver estimated maximum bitrate information. By creating a combined
+ ; frame and sending it to the sources of video the sender can be influenced on what bitrate
+ ; they choose allowing a better experience for the receivers. This defaults to 0, or disabled.
+;remb_behavior=average ; How the combined REMB report for an SFU video bridge is constructed. If set to "average" then
+ ; the estimated maximum bitrate of each receiver is used to construct an average bitrate. If
+ ; set to "lowest" the lowest maximum bitrate is forwarded to the sender. If set to "highest"
+ ; the highest maximum bitrate is forwarded to the sender. This defaults to "average".
; All sounds in the conference are customizable using the bridge profile options below.
; Simply state the option followed by the filename or full path of the filename after
diff --git a/configure b/configure
index 86ffdea88..a7657be94 100755
--- a/configure
+++ b/configure
@@ -672,16 +672,11 @@ CONFIG_NEON
CONFIG_MYSQLCLIENT
PBX_MISDN_FAC_ERROR
PBX_MISDN_FAC_RESULT
-LIBEDIT_LIBS
-LIBEDIT_CFLAGS
ILBC_LIBS
ILBC_CFLAGS
ILBC_INTERNAL
GSM_INTERNAL
PBX_DAHDI_HALF_FULL
-PKG_CONFIG_LIBDIR
-PKG_CONFIG_PATH
-PKG_CONFIG
PBX_DLADDR
PBX_IP_MTU_DISCOVER
PBX_RTLD_NOLOAD
@@ -712,7 +707,11 @@ DISABLE_XMLDOC
CONFIG_LIBXML2
UUID_LIB
UUID_INCLUDE
-EDITLINE_LIB
+LIBEDIT_LIBS
+LIBEDIT_CFLAGS
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
ALLOCA
PBX_ZLIB
ZLIB_DIR
@@ -742,18 +741,10 @@ PBX_TONEZONE
TONEZONE_DIR
TONEZONE_INCLUDE
TONEZONE_LIB
-PBX_TINFO
-TINFO_DIR
-TINFO_INCLUDE
-TINFO_LIB
PBX_TIMERFD
TIMERFD_DIR
TIMERFD_INCLUDE
TIMERFD_LIB
-PBX_TERMCAP
-TERMCAP_DIR
-TERMCAP_INCLUDE
-TERMCAP_LIB
PBX_FREETDS
FREETDS_DIR
FREETDS_INCLUDE
@@ -1026,10 +1017,6 @@ PBX_NEON
NEON_DIR
NEON_INCLUDE
NEON_LIB
-PBX_NCURSES
-NCURSES_DIR
-NCURSES_INCLUDE
-NCURSES_LIB
PBX_NBS
NBS_DIR
NBS_INCLUDE
@@ -1162,10 +1149,6 @@ PBX_CRYPT
CRYPT_DIR
CRYPT_INCLUDE
CRYPT_LIB
-PBX_CURSES
-CURSES_DIR
-CURSES_INCLUDE
-CURSES_LIB
PBX_COROSYNC_CFG_STATE_TRACK
COROSYNC_CFG_STATE_TRACK_DIR
COROSYNC_CFG_STATE_TRACK_INCLUDE
@@ -1378,7 +1361,6 @@ with_bluetooth
with_cap
with_codec2
with_cpg
-with_curses
with_crypt
with_crypto
with_dahdi
@@ -1408,7 +1390,6 @@ with_lua
with_misdn
with_mysqlclient
with_nbs
-with_ncurses
with_neon
with_neon29
with_netsnmp
@@ -1441,9 +1422,7 @@ with_srtp
with_ssl
with_suppserv
with_tds
-with_termcap
with_timerfd
-with_tinfo
with_tonezone
with_unbound
with_unixodbc
@@ -1474,10 +1453,10 @@ PJPROJECT_CONFIGURE_OPTS
PKG_CONFIG
PKG_CONFIG_PATH
PKG_CONFIG_LIBDIR
-ILBC_CFLAGS
-ILBC_LIBS
LIBEDIT_CFLAGS
LIBEDIT_LIBS
+ILBC_CFLAGS
+ILBC_LIBS
PJPROJECT_CFLAGS
PJPROJECT_LIBS
PYTHONDEV_CFLAGS
@@ -2131,7 +2110,6 @@ Optional Packages:
--with-cap=PATH use POSIX 1.e capabilities files in PATH
--with-codec2=PATH use Codec 2 Audio Decoder/Encoder files in PATH
--with-cpg=PATH use Corosync files in PATH
- --with-curses=PATH use curses files in PATH
--with-crypt=PATH use password and data encryption files in PATH
--with-crypto=PATH use OpenSSL Cryptography files in PATH
--with-dahdi=PATH use DAHDI files in PATH
@@ -2164,7 +2142,6 @@ Optional Packages:
--with-misdn=PATH use mISDN user files in PATH
--with-mysqlclient=PATH use MySQL client files in PATH
--with-nbs=PATH use Network Broadcast Sound files in PATH
- --with-ncurses=PATH use ncurses files in PATH
--with-neon=PATH use neon files in PATH
--with-neon29=PATH use neon29 files in PATH
--with-netsnmp=PATH use Net-SNMP files in PATH
@@ -2198,9 +2175,7 @@ Optional Packages:
--with-ssl=PATH use OpenSSL Secure Sockets Layer files in PATH
--with-suppserv=PATH use mISDN Supplemental Services files in PATH
--with-tds=PATH use FreeTDS files in PATH
- --with-termcap=PATH use Termcap files in PATH
--with-timerfd=PATH use timerfd files in PATH
- --with-tinfo=PATH use Term Info files in PATH
--with-tonezone=PATH use tonezone files in PATH
--with-unbound=PATH use unbound files in PATH
--with-unixodbc=PATH use unixODBC files in PATH
@@ -2228,12 +2203,12 @@ Some influential environment variables:
directories to add to pkg-config's search path
PKG_CONFIG_LIBDIR
path overriding pkg-config's built-in search path
- ILBC_CFLAGS C compiler flags for ILBC, overriding pkg-config
- ILBC_LIBS linker flags for ILBC, overriding pkg-config
LIBEDIT_CFLAGS
C compiler flags for LIBEDIT, overriding pkg-config
LIBEDIT_LIBS
linker flags for LIBEDIT, overriding pkg-config
+ ILBC_CFLAGS C compiler flags for ILBC, overriding pkg-config
+ ILBC_LIBS linker flags for ILBC, overriding pkg-config
PJPROJECT_CFLAGS
C compiler flags for PJPROJECT, overriding pkg-config
PJPROJECT_LIBS
@@ -7817,7 +7792,7 @@ if test "${WGET}" != ":" ; then
DOWNLOAD=${WGET}
DOWNLOAD_TO_STDOUT="${WGET} -q -O-"
DOWNLOAD_TIMEOUT='--timeout=$1'
-else if test "${CURL}" != ":" ; then
+elif test "${CURL}" != ":" ; then
DOWNLOAD="${CURL} -O --progress-bar -w \"%{url_effective}\n\""
DOWNLOAD_TO_STDOUT="${CURL} -Ls"
DOWNLOAD_TIMEOUT='--max-time $(or $2,$1)'
@@ -7869,7 +7844,6 @@ fi
DOWNLOAD_TIMEOUT='--timeout=$(or $2,$1)'
fi
fi
-fi
@@ -9565,38 +9539,6 @@ PBX_COROSYNC_CFG_STATE_TRACK=0
- CURSES_DESCRIP="curses"
- CURSES_OPTION="curses"
- PBX_CURSES=0
-
-# Check whether --with-curses was given.
-if test "${with_curses+set}" = set; then :
- withval=$with_curses;
- case ${withval} in
- n|no)
- USE_CURSES=no
- # -1 is a magic value used by menuselect to know that the package
- # was disabled, other than 'not found'
- PBX_CURSES=-1
- ;;
- y|ye|yes)
- ac_mandatory_list="${ac_mandatory_list} CURSES"
- ;;
- *)
- CURSES_DIR="${withval}"
- ac_mandatory_list="${ac_mandatory_list} CURSES"
- ;;
- esac
-
-fi
-
-
-
-
-
-
-
-
CRYPT_DESCRIP="password and data encryption"
CRYPT_OPTION="crypt"
PBX_CRYPT=0
@@ -10928,38 +10870,6 @@ fi
- NCURSES_DESCRIP="ncurses"
- NCURSES_OPTION="ncurses"
- PBX_NCURSES=0
-
-# Check whether --with-ncurses was given.
-if test "${with_ncurses+set}" = set; then :
- withval=$with_ncurses;
- case ${withval} in
- n|no)
- USE_NCURSES=no
- # -1 is a magic value used by menuselect to know that the package
- # was disabled, other than 'not found'
- PBX_NCURSES=-1
- ;;
- y|ye|yes)
- ac_mandatory_list="${ac_mandatory_list} NCURSES"
- ;;
- *)
- NCURSES_DIR="${withval}"
- ac_mandatory_list="${ac_mandatory_list} NCURSES"
- ;;
- esac
-
-fi
-
-
-
-
-
-
-
-
NEON_DESCRIP="neon"
NEON_OPTION="neon"
PBX_NEON=0
@@ -12467,38 +12377,6 @@ fi
- TERMCAP_DESCRIP="Termcap"
- TERMCAP_OPTION="termcap"
- PBX_TERMCAP=0
-
-# Check whether --with-termcap was given.
-if test "${with_termcap+set}" = set; then :
- withval=$with_termcap;
- case ${withval} in
- n|no)
- USE_TERMCAP=no
- # -1 is a magic value used by menuselect to know that the package
- # was disabled, other than 'not found'
- PBX_TERMCAP=-1
- ;;
- y|ye|yes)
- ac_mandatory_list="${ac_mandatory_list} TERMCAP"
- ;;
- *)
- TERMCAP_DIR="${withval}"
- ac_mandatory_list="${ac_mandatory_list} TERMCAP"
- ;;
- esac
-
-fi
-
-
-
-
-
-
-
-
TIMERFD_DESCRIP="timerfd"
TIMERFD_OPTION="timerfd"
PBX_TIMERFD=0
@@ -12531,38 +12409,6 @@ fi
- TINFO_DESCRIP="Term Info"
- TINFO_OPTION="tinfo"
- PBX_TINFO=0
-
-# Check whether --with-tinfo was given.
-if test "${with_tinfo+set}" = set; then :
- withval=$with_tinfo;
- case ${withval} in
- n|no)
- USE_TINFO=no
- # -1 is a magic value used by menuselect to know that the package
- # was disabled, other than 'not found'
- PBX_TINFO=-1
- ;;
- y|ye|yes)
- ac_mandatory_list="${ac_mandatory_list} TINFO"
- ;;
- *)
- TINFO_DIR="${withval}"
- ac_mandatory_list="${ac_mandatory_list} TINFO"
- ;;
- esac
-
-fi
-
-
-
-
-
-
-
-
TONEZONE_DESCRIP="tonezone"
TONEZONE_OPTION="tonezone"
PBX_TONEZONE=0
@@ -13381,200 +13227,237 @@ fi
done
-# Any one of these packages support a mandatory requirement, so we want to check on them as early as possible.
+# Find required NetBSD Editline library (libedit).
-if test "x${PBX_TERMCAP}" != "x1" -a "${USE_TERMCAP}" != "no"; then
- pbxlibdir=""
- # if --with-TERMCAP=DIR has been specified, use it.
- if test "x${TERMCAP_DIR}" != "x"; then
- if test -d ${TERMCAP_DIR}/lib; then
- pbxlibdir="-L${TERMCAP_DIR}/lib"
- else
- pbxlibdir="-L${TERMCAP_DIR}"
- fi
- fi
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermcap" >&5
-$as_echo_n "checking for tgetent in -ltermcap... " >&6; }
-if ${ac_cv_lib_termcap_tgetent+:} false; then :
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
$as_echo_n "(cached) " >&6
else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-ltermcap ${pbxlibdir} $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
+ case $PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
-/* 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 tgetent ();
-int
-main ()
-{
-return tgetent ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_termcap_tgetent=yes
-else
- ac_cv_lib_termcap_tgetent=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+ ;;
+esac
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termcap_tgetent" >&5
-$as_echo "$ac_cv_lib_termcap_tgetent" >&6; }
-if test "x$ac_cv_lib_termcap_tgetent" = xyes; then :
- AST_TERMCAP_FOUND=yes
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
else
- AST_TERMCAP_FOUND=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
fi
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_TERMCAP_FOUND}" = "yes"; then
- TERMCAP_LIB="${pbxlibdir} -ltermcap "
- # if --with-TERMCAP=DIR has been specified, use it.
- if test "x${TERMCAP_DIR}" != "x"; then
- TERMCAP_INCLUDE="-I${TERMCAP_DIR}/include"
- fi
- TERMCAP_INCLUDE="${TERMCAP_INCLUDE} "
-
- # no header, assume found
- TERMCAP_HEADER_FOUND="1"
- if test "x${TERMCAP_HEADER_FOUND}" = "x0" ; then
- TERMCAP_LIB=""
- TERMCAP_INCLUDE=""
- else
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+ ac_pt_PKG_CONFIG=$PKG_CONFIG
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ac_pt_PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
- PBX_TERMCAP=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_TERMCAP 1
-_ACEOF
+ ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
- fi
- fi
+ if test "x$ac_pt_PKG_CONFIG" = x; then
+ PKG_CONFIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ PKG_CONFIG=$ac_pt_PKG_CONFIG
+ fi
+else
+ PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
fi
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=0.9.0
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ PKG_CONFIG=""
+ fi
+fi
+ if test "x${PBX_LIBEDIT}" != "x1" -a "${USE_LIBEDIT}" != "no"; then
-if test "x${PBX_TINFO}" != "x1" -a "${USE_TINFO}" != "no"; then
- pbxlibdir=""
- # if --with-TINFO=DIR has been specified, use it.
- if test "x${TINFO_DIR}" != "x"; then
- if test -d ${TINFO_DIR}/lib; then
- pbxlibdir="-L${TINFO_DIR}/lib"
- else
- pbxlibdir="-L${TINFO_DIR}"
- fi
- fi
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEDIT" >&5
+$as_echo_n "checking for LIBEDIT... " >&6; }
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltinfo" >&5
-$as_echo_n "checking for tgetent in -ltinfo... " >&6; }
-if ${ac_cv_lib_tinfo_tgetent+:} false; then :
- $as_echo_n "(cached) " >&6
+if test -n "$LIBEDIT_CFLAGS"; then
+ pkg_cv_LIBEDIT_CFLAGS="$LIBEDIT_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libedit") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_LIBEDIT_CFLAGS=`$PKG_CONFIG --cflags "libedit" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-ltinfo ${pbxlibdir} $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 tgetent ();
-int
-main ()
-{
-return tgetent ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_tinfo_tgetent=yes
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$LIBEDIT_LIBS"; then
+ pkg_cv_LIBEDIT_LIBS="$LIBEDIT_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libedit") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_LIBEDIT_LIBS=`$PKG_CONFIG --libs "libedit" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
else
- ac_cv_lib_tinfo_tgetent=no
+ pkg_failed=yes
fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+ else
+ pkg_failed=untried
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_tgetent" >&5
-$as_echo "$ac_cv_lib_tinfo_tgetent" >&6; }
-if test "x$ac_cv_lib_tinfo_tgetent" = xyes; then :
- AST_TINFO_FOUND=yes
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
else
- AST_TINFO_FOUND=no
+ _pkg_short_errors_supported=no
fi
+ if test $_pkg_short_errors_supported = yes; then
+ LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libedit" 2>&1`
+ else
+ LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libedit" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$LIBEDIT_PKG_ERRORS" >&5
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+ PBX_LIBEDIT=0
- # now check for the header.
- if test "${AST_TINFO_FOUND}" = "yes"; then
- TINFO_LIB="${pbxlibdir} -ltinfo "
- # if --with-TINFO=DIR has been specified, use it.
- if test "x${TINFO_DIR}" != "x"; then
- TINFO_INCLUDE="-I${TINFO_DIR}/include"
- fi
- TINFO_INCLUDE="${TINFO_INCLUDE} "
- # no header, assume found
- TINFO_HEADER_FOUND="1"
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
- if test "x${TINFO_HEADER_FOUND}" = "x0" ; then
- TINFO_LIB=""
- TINFO_INCLUDE=""
- else
+ PBX_LIBEDIT=0
- PBX_TINFO=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_TINFO 1
-_ACEOF
- fi
- fi
-fi
+else
+ LIBEDIT_CFLAGS=$pkg_cv_LIBEDIT_CFLAGS
+ LIBEDIT_LIBS=$pkg_cv_LIBEDIT_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ PBX_LIBEDIT=1
+ LIBEDIT_INCLUDE=$(echo ${LIBEDIT_CFLAGS} | $SED -e "s|-std=c99||g")
+ LIBEDIT_LIB="$LIBEDIT_LIBS"
+
+$as_echo "#define HAVE_LIBEDIT 1" >>confdefs.h
+fi
+ fi
+
+# some platforms do not list libedit via pkg-config, for example OpenBSD 6.2
-if test "x${PBX_CURSES}" != "x1" -a "${USE_CURSES}" != "no"; then
+if test "x${PBX_LIBEDIT}" != "x1" -a "${USE_LIBEDIT}" != "no"; then
pbxlibdir=""
- # if --with-CURSES=DIR has been specified, use it.
- if test "x${CURSES_DIR}" != "x"; then
- if test -d ${CURSES_DIR}/lib; then
- pbxlibdir="-L${CURSES_DIR}/lib"
+ # if --with-LIBEDIT=DIR has been specified, use it.
+ if test "x${LIBEDIT_DIR}" != "x"; then
+ if test -d ${LIBEDIT_DIR}/lib; then
+ pbxlibdir="-L${LIBEDIT_DIR}/lib"
else
- pbxlibdir="-L${CURSES_DIR}"
+ pbxlibdir="-L${LIBEDIT_DIR}"
fi
fi
ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr in -lcurses" >&5
-$as_echo_n "checking for initscr in -lcurses... " >&6; }
-if ${ac_cv_lib_curses_initscr+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for history_init in -ledit" >&5
+$as_echo_n "checking for history_init in -ledit... " >&6; }
+if ${ac_cv_lib_edit_history_init+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
-LIBS="-lcurses ${pbxlibdir} $LIBS"
+LIBS="-ledit ${pbxlibdir} -ltermcap $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -13584,65 +13467,65 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
#ifdef __cplusplus
extern "C"
#endif
-char initscr ();
+char history_init ();
int
main ()
{
-return initscr ();
+return history_init ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_curses_initscr=yes
+ ac_cv_lib_edit_history_init=yes
else
- ac_cv_lib_curses_initscr=no
+ ac_cv_lib_edit_history_init=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_initscr" >&5
-$as_echo "$ac_cv_lib_curses_initscr" >&6; }
-if test "x$ac_cv_lib_curses_initscr" = xyes; then :
- AST_CURSES_FOUND=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_history_init" >&5
+$as_echo "$ac_cv_lib_edit_history_init" >&6; }
+if test "x$ac_cv_lib_edit_history_init" = xyes; then :
+ AST_LIBEDIT_FOUND=yes
else
- AST_CURSES_FOUND=no
+ AST_LIBEDIT_FOUND=no
fi
CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
# now check for the header.
- if test "${AST_CURSES_FOUND}" = "yes"; then
- CURSES_LIB="${pbxlibdir} -lcurses "
- # if --with-CURSES=DIR has been specified, use it.
- if test "x${CURSES_DIR}" != "x"; then
- CURSES_INCLUDE="-I${CURSES_DIR}/include"
+ if test "${AST_LIBEDIT_FOUND}" = "yes"; then
+ LIBEDIT_LIB="${pbxlibdir} -ledit -ltermcap"
+ # if --with-LIBEDIT=DIR has been specified, use it.
+ if test "x${LIBEDIT_DIR}" != "x"; then
+ LIBEDIT_INCLUDE="-I${LIBEDIT_DIR}/include"
fi
- CURSES_INCLUDE="${CURSES_INCLUDE} "
+ LIBEDIT_INCLUDE="${LIBEDIT_INCLUDE} "
# check for the header
ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${CURSES_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default"
-if test "x$ac_cv_header_curses_h" = xyes; then :
- CURSES_HEADER_FOUND=1
+ CPPFLAGS="${CPPFLAGS} ${LIBEDIT_INCLUDE}"
+ ac_fn_c_check_header_mongrel "$LINENO" "histedit.h" "ac_cv_header_histedit_h" "$ac_includes_default"
+if test "x$ac_cv_header_histedit_h" = xyes; then :
+ LIBEDIT_HEADER_FOUND=1
else
- CURSES_HEADER_FOUND=0
+ LIBEDIT_HEADER_FOUND=0
fi
CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
- if test "x${CURSES_HEADER_FOUND}" = "x0" ; then
- CURSES_LIB=""
- CURSES_INCLUDE=""
+ if test "x${LIBEDIT_HEADER_FOUND}" = "x0" ; then
+ LIBEDIT_LIB=""
+ LIBEDIT_INCLUDE=""
else
- PBX_CURSES=1
+ PBX_LIBEDIT=1
cat >>confdefs.h <<_ACEOF
-#define HAVE_CURSES 1
+#define HAVE_LIBEDIT 1
_ACEOF
fi
@@ -13650,115 +13533,49 @@ _ACEOF
fi
+if test "${PBX_LIBEDIT}" != 1; then
+ as_fn_error $? "*** Please install the 'libedit' development package." "$LINENO" 5
+ exit 1
+fi
-if test "x${PBX_NCURSES}" != "x1" -a "${USE_NCURSES}" != "no"; then
- pbxlibdir=""
- # if --with-NCURSES=DIR has been specified, use it.
- if test "x${NCURSES_DIR}" != "x"; then
- if test -d ${NCURSES_DIR}/lib; then
- pbxlibdir="-L${NCURSES_DIR}/lib"
- else
- pbxlibdir="-L${NCURSES_DIR}"
- fi
- fi
+ if test "x${PBX_LIBEDIT_IS_UNICODE}" != "x1" -a "${USE_LIBEDIT_IS_UNICODE}" != "no"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Testing for libedit unicode support" >&5
+$as_echo_n "checking for Testing for libedit unicode support... " >&6; }
+ saved_cppflags="${CPPFLAGS}"
+ if test "x${LIBEDIT_IS_UNICODE_DIR}" != "x"; then
+ LIBEDIT_IS_UNICODE_INCLUDE="-I${LIBEDIT_IS_UNICODE_DIR}/include"
+ fi
+ CPPFLAGS="${CPPFLAGS} ${LIBEDIT_IS_UNICODE_INCLUDE}"
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for initscr in -lncurses" >&5
-$as_echo_n "checking for initscr in -lncurses... " >&6; }
-if ${ac_cv_lib_ncurses_initscr+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-lncurses ${pbxlibdir} $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ 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 initscr ();
+ #include <histedit.h>
int
main ()
{
-return initscr ();
+ el_rfunc_t *callback;;
+
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_ncurses_initscr=yes
-else
- ac_cv_lib_ncurses_initscr=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_initscr" >&5
-$as_echo "$ac_cv_lib_ncurses_initscr" >&6; }
-if test "x$ac_cv_lib_ncurses_initscr" = xyes; then :
- AST_NCURSES_FOUND=yes
-else
- AST_NCURSES_FOUND=no
-fi
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ PBX_LIBEDIT_IS_UNICODE=1
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+$as_echo "#define HAVE_LIBEDIT_IS_UNICODE 1" >>confdefs.h
- # now check for the header.
- if test "${AST_NCURSES_FOUND}" = "yes"; then
- NCURSES_LIB="${pbxlibdir} -lncurses "
- # if --with-NCURSES=DIR has been specified, use it.
- if test "x${NCURSES_DIR}" != "x"; then
- NCURSES_INCLUDE="-I${NCURSES_DIR}/include"
- fi
- NCURSES_INCLUDE="${NCURSES_INCLUDE} "
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${NCURSES_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default"
-if test "x$ac_cv_header_curses_h" = xyes; then :
- NCURSES_HEADER_FOUND=1
else
- NCURSES_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${NCURSES_HEADER_FOUND}" = "x0" ; then
- NCURSES_LIB=""
- NCURSES_INCLUDE=""
- else
-
- PBX_NCURSES=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_NCURSES 1
-_ACEOF
-
- fi
- fi
-fi
-
-
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
-EDITLINE_LIB=""
-if test "x$TERMCAP_LIB" != "x" ; then
- EDITLINE_LIB="$TERMCAP_LIB"
-elif test "x$TINFO_LIB" != "x" ; then
- EDITLINE_LIB="$TINFO_LIB"
-elif test "x$CURSES_LIB" != "x" ; then
- EDITLINE_LIB="$CURSES_LIB"
-elif test "x$NCURSES_LIB" != "x" ; then
- EDITLINE_LIB="$NCURSES_LIB"
-else
- as_fn_error $? "*** termcap support not found (on modern systems, this typically means the ncurses development package is missing)" "$LINENO" 5
fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CPPFLAGS="${saved_cppflags}"
+ fi
# Find required UUID support.
@@ -14284,7 +14101,7 @@ fi
if test ! "x${CONFIG_LIBXML2}" = xNo; then
LIBXML2_INCLUDE=$(${CONFIG_LIBXML2} --cflags)
- LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g")
+ LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g" -e "s|-std=c99||g")
LIBXML2_LIB=$(${CONFIG_LIBXML2} --libs)
LIBXML2_LIB=$(echo ${LIBXML2_LIB} | $SED -e "s|-L|-L${LIBXML2_DIR}|g")
@@ -19384,15 +19201,15 @@ if test $ac_cv_sizeof_int = $ac_cv_sizeof_fd_set_fds_bits; then
$as_echo "#define TYPEOF_FD_SET_FDS_BITS int" >>confdefs.h
-else if test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
$as_echo "#define TYPEOF_FD_SET_FDS_BITS long" >>confdefs.h
-else if test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
$as_echo "#define TYPEOF_FD_SET_FDS_BITS long long" >>confdefs.h
-fi ; fi ; fi
+fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dladdr in dlfcn.h" >&5
$as_echo_n "checking for dladdr in dlfcn.h... " >&6; }
@@ -20556,15 +20373,13 @@ cat >>confdefs.h <<_ACEOF
_ACEOF
GSM_OK=1
- else
- if test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
+ elif test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
cat >>confdefs.h <<_ACEOF
#define HAVE_GSM_GSM_HEADER 1
_ACEOF
- GSM_OK=1
- fi
+ GSM_OK=1
fi
if test "${GSM_OK}" = "1" ; then
GSM_LIB="-lgsm"
@@ -20676,7 +20491,7 @@ else
$as_echo "yes" >&6; }
PBX_ILBC=1
- ILBC_INCLUDE="$ILBC_CFLAGS"
+ ILBC_INCLUDE=$(echo ${ILBC_CFLAGS} | $SED -e "s|-std=c99||g")
ILBC_LIB="$ILBC_LIBS"
$as_echo "#define HAVE_ILBC 1" >>confdefs.h
@@ -20695,236 +20510,6 @@ fi
fi
- if test "x${PBX_LIBEDIT}" != "x1" -a "${USE_LIBEDIT}" != "no"; then
-
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEDIT" >&5
-$as_echo_n "checking for LIBEDIT... " >&6; }
-
-if test -n "$LIBEDIT_CFLAGS"; then
- pkg_cv_LIBEDIT_CFLAGS="$LIBEDIT_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5
- ($PKG_CONFIG --exists --print-errors "libedit") 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_LIBEDIT_CFLAGS=`$PKG_CONFIG --cflags "libedit" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-if test -n "$LIBEDIT_LIBS"; then
- pkg_cv_LIBEDIT_LIBS="$LIBEDIT_LIBS"
- elif test -n "$PKG_CONFIG"; then
- if test -n "$PKG_CONFIG" && \
- { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5
- ($PKG_CONFIG --exists --print-errors "libedit") 2>&5
- ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then
- pkg_cv_LIBEDIT_LIBS=`$PKG_CONFIG --libs "libedit" 2>/dev/null`
- test "x$?" != "x0" && pkg_failed=yes
-else
- pkg_failed=yes
-fi
- else
- pkg_failed=untried
-fi
-
-
-
-if test $pkg_failed = yes; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
- _pkg_short_errors_supported=yes
-else
- _pkg_short_errors_supported=no
-fi
- if test $_pkg_short_errors_supported = yes; then
- LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libedit" 2>&1`
- else
- LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libedit" 2>&1`
- fi
- # Put the nasty error message in config.log where it belongs
- echo "$LIBEDIT_PKG_ERRORS" >&5
-
-
- PBX_LIBEDIT=0
-
-
-elif test $pkg_failed = untried; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
- PBX_LIBEDIT=0
-
-
-else
- LIBEDIT_CFLAGS=$pkg_cv_LIBEDIT_CFLAGS
- LIBEDIT_LIBS=$pkg_cv_LIBEDIT_LIBS
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
- PBX_LIBEDIT=1
- LIBEDIT_INCLUDE="$LIBEDIT_CFLAGS"
- LIBEDIT_LIB="$LIBEDIT_LIBS"
-
-$as_echo "#define HAVE_LIBEDIT 1" >>confdefs.h
-
-
-fi
- fi
-
-# some platforms do not list libedit via pkg-config, for example OpenBSD 6.2
-
-if test "x${PBX_LIBEDIT}" != "x1" -a "${USE_LIBEDIT}" != "no"; then
- pbxlibdir=""
- # if --with-LIBEDIT=DIR has been specified, use it.
- if test "x${LIBEDIT_DIR}" != "x"; then
- if test -d ${LIBEDIT_DIR}/lib; then
- pbxlibdir="-L${LIBEDIT_DIR}/lib"
- else
- pbxlibdir="-L${LIBEDIT_DIR}"
- fi
- fi
-
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for history_init in -ledit" >&5
-$as_echo_n "checking for history_init in -ledit... " >&6; }
-if ${ac_cv_lib_edit_history_init+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-ledit ${pbxlibdir} -ltermcap $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 history_init ();
-int
-main ()
-{
-return history_init ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_edit_history_init=yes
-else
- ac_cv_lib_edit_history_init=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_history_init" >&5
-$as_echo "$ac_cv_lib_edit_history_init" >&6; }
-if test "x$ac_cv_lib_edit_history_init" = xyes; then :
- AST_LIBEDIT_FOUND=yes
-else
- AST_LIBEDIT_FOUND=no
-fi
-
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_LIBEDIT_FOUND}" = "yes"; then
- LIBEDIT_LIB="${pbxlibdir} -ledit -ltermcap"
- # if --with-LIBEDIT=DIR has been specified, use it.
- if test "x${LIBEDIT_DIR}" != "x"; then
- LIBEDIT_INCLUDE="-I${LIBEDIT_DIR}/include"
- fi
- LIBEDIT_INCLUDE="${LIBEDIT_INCLUDE} "
-
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${LIBEDIT_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "histedit.h" "ac_cv_header_histedit_h" "$ac_includes_default"
-if test "x$ac_cv_header_histedit_h" = xyes; then :
- LIBEDIT_HEADER_FOUND=1
-else
- LIBEDIT_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${LIBEDIT_HEADER_FOUND}" = "x0" ; then
- LIBEDIT_LIB=""
- LIBEDIT_INCLUDE=""
- else
-
- PBX_LIBEDIT=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBEDIT 1
-_ACEOF
-
- fi
- fi
-fi
-
-
-if test "${PBX_LIBEDIT}" != 1; then
- as_fn_error $? "*** Please install the 'libedit' development package." "$LINENO" 5
- exit 1
-fi
-
- if test "x${PBX_LIBEDIT_IS_UNICODE}" != "x1" -a "${USE_LIBEDIT_IS_UNICODE}" != "no"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Testing for libedit unicode support" >&5
-$as_echo_n "checking for Testing for libedit unicode support... " >&6; }
- saved_cppflags="${CPPFLAGS}"
- if test "x${LIBEDIT_IS_UNICODE_DIR}" != "x"; then
- LIBEDIT_IS_UNICODE_INCLUDE="-I${LIBEDIT_IS_UNICODE_DIR}/include"
- fi
- CPPFLAGS="${CPPFLAGS} ${LIBEDIT_IS_UNICODE_INCLUDE}"
-
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
- #include <histedit.h>
-int
-main ()
-{
- el_rfunc_t *callback;;
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
- PBX_LIBEDIT_IS_UNICODE=1
-
-$as_echo "#define HAVE_LIBEDIT_IS_UNICODE 1" >>confdefs.h
-
-
-
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- CPPFLAGS="${saved_cppflags}"
- fi
-
-
-
if test "x${PBX_ICONV}" != "x1" -a "${USE_ICONV}" != "no"; then
pbxlibdir=""
# if --with-ICONV=DIR has been specified, use it.
@@ -23044,7 +22629,7 @@ fi
if test ! "x${CONFIG_MYSQLCLIENT}" = xNo; then
MYSQLCLIENT_INCLUDE=$(${CONFIG_MYSQLCLIENT} --cflags)
- MYSQLCLIENT_INCLUDE=$(echo ${MYSQLCLIENT_INCLUDE} | $SED -e "s|-I|-I${MYSQLCLIENT_DIR}|g")
+ MYSQLCLIENT_INCLUDE=$(echo ${MYSQLCLIENT_INCLUDE} | $SED -e "s|-I|-I${MYSQLCLIENT_DIR}|g" -e "s|-std=c99||g")
MYSQLCLIENT_LIB=$(${CONFIG_MYSQLCLIENT} --libs)
MYSQLCLIENT_LIB=$(echo ${MYSQLCLIENT_LIB} | $SED -e "s|-L|-L${MYSQLCLIENT_DIR}|g")
@@ -23261,7 +22846,7 @@ fi
if test ! "x${CONFIG_NEON}" = xNo; then
NEON_INCLUDE=$(${CONFIG_NEON} --cflags)
- NEON_INCLUDE=$(echo ${NEON_INCLUDE} | $SED -e "s|-I|-I${NEON_DIR}|g")
+ NEON_INCLUDE=$(echo ${NEON_INCLUDE} | $SED -e "s|-I|-I${NEON_DIR}|g" -e "s|-std=c99||g")
NEON_LIB=$(${CONFIG_NEON} --libs)
NEON_LIB=$(echo ${NEON_LIB} | $SED -e "s|-L|-L${NEON_DIR}|g")
@@ -23381,7 +22966,7 @@ fi
if test ! "x${CONFIG_NEON29}" = xNo; then
NEON29_INCLUDE=$(${CONFIG_NEON29} --cflags)
- NEON29_INCLUDE=$(echo ${NEON29_INCLUDE} | $SED -e "s|-I|-I${NEON29_DIR}|g")
+ NEON29_INCLUDE=$(echo ${NEON29_INCLUDE} | $SED -e "s|-I|-I${NEON29_DIR}|g" -e "s|-std=c99||g")
NEON29_LIB=$(${CONFIG_NEON29} --libs)
NEON29_LIB=$(echo ${NEON29_LIB} | $SED -e "s|-L|-L${NEON29_DIR}|g")
@@ -23525,7 +23110,7 @@ fi
if test ! "x${CONFIG_NETSNMP}" = xNo; then
NETSNMP_INCLUDE=$(${CONFIG_NETSNMP} --cflags)
- NETSNMP_INCLUDE=$(echo ${NETSNMP_INCLUDE} | $SED -e "s|-I|-I${NETSNMP_DIR}|g")
+ NETSNMP_INCLUDE=$(echo ${NETSNMP_INCLUDE} | $SED -e "s|-I|-I${NETSNMP_DIR}|g" -e "s|-std=c99||g")
NETSNMP_LIB=$(${CONFIG_NETSNMP} --agent-libs)
NETSNMP_LIB=$(echo ${NETSNMP_LIB} | $SED -e "s|-L|-L${NETSNMP_DIR}|g")
@@ -25011,7 +24596,7 @@ else
$as_echo "yes" >&6; }
PBX_PJPROJECT=1
- PJPROJECT_INCLUDE="$PJPROJECT_CFLAGS"
+ PJPROJECT_INCLUDE=$(echo ${PJPROJECT_CFLAGS} | $SED -e "s|-std=c99||g")
PJPROJECT_LIB="$PJPROJECT_LIBS"
$as_echo "#define HAVE_PJPROJECT 1" >>confdefs.h
@@ -26065,7 +25650,7 @@ else
$as_echo "yes" >&6; }
PBX_PYTHONDEV=1
- PYTHONDEV_INCLUDE="$PYTHONDEV_CFLAGS"
+ PYTHONDEV_INCLUDE=$(echo ${PYTHONDEV_CFLAGS} | $SED -e "s|-std=c99||g")
PYTHONDEV_LIB="$PYTHONDEV_LIBS"
$as_echo "#define HAVE_PYTHONDEV 1" >>confdefs.h
@@ -26251,7 +25836,7 @@ else
$as_echo "yes" >&6; }
PBX_PORTAUDIO=1
- PORTAUDIO_INCLUDE="$PORTAUDIO_CFLAGS"
+ PORTAUDIO_INCLUDE=$(echo ${PORTAUDIO_CFLAGS} | $SED -e "s|-std=c99||g")
PORTAUDIO_LIB="$PORTAUDIO_LIBS"
$as_echo "#define HAVE_PORTAUDIO 1" >>confdefs.h
@@ -29310,6 +28895,7 @@ fi
+for ver in 5.3 5.2 5.1; do
if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
pbxlibdir=""
@@ -29324,13 +28910,14 @@ if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua5.3" >&5
-$as_echo_n "checking for luaL_newstate in -llua5.3... " >&6; }
-if ${ac_cv_lib_lua5_3_luaL_newstate+:} false; then :
+ as_ac_Lib=`$as_echo "ac_cv_lib_lua${ver}''_luaL_newstate" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua${ver}" >&5
+$as_echo_n "checking for luaL_newstate in -llua${ver}... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
-LIBS="-llua5.3 ${pbxlibdir} -lm $LIBS"
+LIBS="-llua${ver} ${pbxlibdir} -lm $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -29350,225 +28937,18 @@ return luaL_newstate ();
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_lua5_3_luaL_newstate=yes
+ eval "$as_ac_Lib=yes"
else
- ac_cv_lib_lua5_3_luaL_newstate=no
+ 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
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lua5_3_luaL_newstate" >&5
-$as_echo "$ac_cv_lib_lua5_3_luaL_newstate" >&6; }
-if test "x$ac_cv_lib_lua5_3_luaL_newstate" = xyes; then :
- AST_LUA_FOUND=yes
-else
- AST_LUA_FOUND=no
-fi
-
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_LUA_FOUND}" = "yes"; then
- LUA_LIB="${pbxlibdir} -llua5.3 -lm"
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- LUA_INCLUDE="-I${LUA_DIR}/include"
- fi
- LUA_INCLUDE="${LUA_INCLUDE} "
-
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "lua5.3/lua.h" "ac_cv_header_lua5_3_lua_h" "$ac_includes_default"
-if test "x$ac_cv_header_lua5_3_lua_h" = xyes; then :
- LUA_HEADER_FOUND=1
-else
- LUA_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${LUA_HEADER_FOUND}" = "x0" ; then
- LUA_LIB=""
- LUA_INCLUDE=""
- else
-
- PBX_LUA=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_LUA 1
-_ACEOF
-
- fi
- fi
-fi
-
-
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.3"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.3"
- fi
-fi
-
-
-if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
- pbxlibdir=""
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- if test -d ${LUA_DIR}/lib; then
- pbxlibdir="-L${LUA_DIR}/lib"
- else
- pbxlibdir="-L${LUA_DIR}"
- fi
- fi
-
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua5.2" >&5
-$as_echo_n "checking for luaL_newstate in -llua5.2... " >&6; }
-if ${ac_cv_lib_lua5_2_luaL_newstate+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-llua5.2 ${pbxlibdir} -lm $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 luaL_newstate ();
-int
-main ()
-{
-return luaL_newstate ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_lua5_2_luaL_newstate=yes
-else
- ac_cv_lib_lua5_2_luaL_newstate=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lua5_2_luaL_newstate" >&5
-$as_echo "$ac_cv_lib_lua5_2_luaL_newstate" >&6; }
-if test "x$ac_cv_lib_lua5_2_luaL_newstate" = xyes; then :
- AST_LUA_FOUND=yes
-else
- AST_LUA_FOUND=no
-fi
-
- CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
-
-
- # now check for the header.
- if test "${AST_LUA_FOUND}" = "yes"; then
- LUA_LIB="${pbxlibdir} -llua5.2 -lm"
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- LUA_INCLUDE="-I${LUA_DIR}/include"
- fi
- LUA_INCLUDE="${LUA_INCLUDE} "
-
- # check for the header
- ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
- CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "lua5.2/lua.h" "ac_cv_header_lua5_2_lua_h" "$ac_includes_default"
-if test "x$ac_cv_header_lua5_2_lua_h" = xyes; then :
- LUA_HEADER_FOUND=1
-else
- LUA_HEADER_FOUND=0
-fi
-
-
- CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
-
- if test "x${LUA_HEADER_FOUND}" = "x0" ; then
- LUA_LIB=""
- LUA_INCLUDE=""
- else
-
- PBX_LUA=1
- cat >>confdefs.h <<_ACEOF
-#define HAVE_LUA 1
-_ACEOF
-
- fi
- fi
-fi
-
-
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.2"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.2"
- fi
-fi
-
-
-if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then
- pbxlibdir=""
- # if --with-LUA=DIR has been specified, use it.
- if test "x${LUA_DIR}" != "x"; then
- if test -d ${LUA_DIR}/lib; then
- pbxlibdir="-L${LUA_DIR}/lib"
- else
- pbxlibdir="-L${LUA_DIR}"
- fi
- fi
-
- ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
- CFLAGS="${CFLAGS} "
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for luaL_newstate in -llua5.1" >&5
-$as_echo_n "checking for luaL_newstate in -llua5.1... " >&6; }
-if ${ac_cv_lib_lua5_1_luaL_newstate+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-llua5.1 ${pbxlibdir} -lm $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 luaL_newstate ();
-int
-main ()
-{
-return luaL_newstate ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_lib_lua5_1_luaL_newstate=yes
-else
- ac_cv_lib_lua5_1_luaL_newstate=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lua5_1_luaL_newstate" >&5
-$as_echo "$ac_cv_lib_lua5_1_luaL_newstate" >&6; }
-if test "x$ac_cv_lib_lua5_1_luaL_newstate" = xyes; then :
+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_LUA_FOUND=yes
else
AST_LUA_FOUND=no
@@ -29579,7 +28959,7 @@ fi
# now check for the header.
if test "${AST_LUA_FOUND}" = "yes"; then
- LUA_LIB="${pbxlibdir} -llua5.1 -lm"
+ LUA_LIB="${pbxlibdir} -llua${ver} -lm"
# if --with-LUA=DIR has been specified, use it.
if test "x${LUA_DIR}" != "x"; then
LUA_INCLUDE="-I${LUA_DIR}/include"
@@ -29589,8 +28969,9 @@ fi
# check for the header
ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}"
- ac_fn_c_check_header_mongrel "$LINENO" "lua5.1/lua.h" "ac_cv_header_lua5_1_lua_h" "$ac_includes_default"
-if test "x$ac_cv_header_lua5_1_lua_h" = xyes; then :
+ as_ac_Header=`$as_echo "ac_cv_header_lua${ver}/lua.h" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "lua${ver}/lua.h" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
LUA_HEADER_FOUND=1
else
LUA_HEADER_FOUND=0
@@ -29614,13 +28995,15 @@ _ACEOF
fi
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.1"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.1"
+ if test "x${PBX_LUA}" = "x1" ; then
+ if test x"${LUA_DIR}" = x; then
+ LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua${ver}"
+ else
+ LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua${ver}"
+ fi
+ break;
fi
-fi
+done
# Some distributions (like openSUSE and NetBSD) remove the 5.x suffix.
@@ -32757,7 +32140,7 @@ else
$as_echo "yes" >&6; }
PBX_GMIME=1
- GMIME_INCLUDE="$GMIME_CFLAGS"
+ GMIME_INCLUDE=$(echo ${GMIME_CFLAGS} | $SED -e "s|-std=c99||g")
GMIME_LIB="$GMIME_LIBS"
$as_echo "#define HAVE_GMIME 1" >>confdefs.h
@@ -33666,7 +33049,7 @@ fi
if test ! "x${CONFIG_SDL}" = xNo; then
SDL_INCLUDE=$(${CONFIG_SDL} --cflags)
- SDL_INCLUDE=$(echo ${SDL_INCLUDE} | $SED -e "s|-I|-I${SDL_DIR}|g")
+ SDL_INCLUDE=$(echo ${SDL_INCLUDE} | $SED -e "s|-I|-I${SDL_DIR}|g" -e "s|-std=c99||g")
SDL_LIB=$(${CONFIG_SDL} --libs)
SDL_LIB=$(echo ${SDL_LIB} | $SED -e "s|-L|-L${SDL_DIR}|g")
@@ -34194,7 +33577,7 @@ else
$as_echo "yes" >&6; }
PBX_GTK2=1
- GTK2_INCLUDE="$GTK2_CFLAGS"
+ GTK2_INCLUDE=$(echo ${GTK2_CFLAGS} | $SED -e "s|-std=c99||g")
GTK2_LIB="$GTK2_LIBS"
$as_echo "#define HAVE_GTK2 1" >>confdefs.h
@@ -34305,7 +33688,7 @@ else
$as_echo "yes" >&6; }
PBX_SYSTEMD=1
- SYSTEMD_INCLUDE="$SYSTEMD_CFLAGS"
+ SYSTEMD_INCLUDE=$(echo ${SYSTEMD_CFLAGS} | $SED -e "s|-std=c99||g")
SYSTEMD_LIB="$SYSTEMD_LIBS"
$as_echo "#define HAVE_SYSTEMD 1" >>confdefs.h
@@ -36184,9 +35567,9 @@ fi
${ac_cv_path_EGREP} 'CURSES|GTK2|OSARCH|NEWT' makeopts > makeopts.acbak2
if test "x${ac_cv_path_CMP}" = "x:"; then
( cd `pwd`/menuselect && ./configure )
-else if ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
+elif ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
( cd `pwd`/menuselect && ./configure )
-fi ; fi
+fi
rm makeopts.acbak makeopts.acbak2
diff --git a/configure.ac b/configure.ac
index e2af23493..128b0a0f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -293,7 +293,7 @@ if test "${WGET}" != ":" ; then
DOWNLOAD=${WGET}
DOWNLOAD_TO_STDOUT="${WGET} -q -O-"
DOWNLOAD_TIMEOUT='--timeout=$1'
-else if test "${CURL}" != ":" ; then
+elif test "${CURL}" != ":" ; then
DOWNLOAD="${CURL} -O --progress-bar -w \"%{url_effective}\n\""
DOWNLOAD_TO_STDOUT="${CURL} -Ls"
DOWNLOAD_TIMEOUT='--max-time $(or $2,$1)'
@@ -305,7 +305,6 @@ else
DOWNLOAD_TIMEOUT='--timeout=$(or $2,$1)'
fi
fi
-fi
AC_SUBST(DOWNLOAD)
AC_SUBST(DOWNLOAD_TO_STDOUT)
AC_SUBST(DOWNLOAD_TIMEOUT)
@@ -458,7 +457,6 @@ AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap])
AST_EXT_LIB_SETUP([CODEC2], [Codec 2 Audio Decoder/Encoder], [codec2])
AST_EXT_LIB_SETUP([COROSYNC], [Corosync], [cpg])
AST_EXT_LIB_SETUP_OPTIONAL([COROSYNC_CFG_STATE_TRACK], [A callback only in corosync 1.x], [COROSYNC], [cfg])
-AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
AST_EXT_LIB_SETUP([CRYPT], [password and data encryption], [crypt])
AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto])
AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_SRTP], [OpenSSL SRTP Extension Support], [CRYPTO], [crypto])
@@ -492,7 +490,6 @@ AST_EXT_LIB_SETUP([LUA], [Lua], [lua])
AST_EXT_LIB_SETUP([MISDN], [mISDN user], [misdn])
AST_EXT_LIB_SETUP([MYSQLCLIENT], [MySQL client], [mysqlclient])
AST_EXT_LIB_SETUP([NBS], [Network Broadcast Sound], [nbs])
-AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses])
AST_EXT_LIB_SETUP([NEON], [neon], [neon])
AST_EXT_LIB_SETUP([NEON29], [neon29], [neon29])
AST_EXT_LIB_SETUP([NETSNMP], [Net-SNMP], [netsnmp])
@@ -571,9 +568,7 @@ AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL Secure Sockets Layer], [ssl])
AST_EXT_LIB_SETUP_OPTIONAL([RT], [Realtime functions], [rt])
AST_EXT_LIB_SETUP([SUPPSERV], [mISDN Supplemental Services], [suppserv])
AST_EXT_LIB_SETUP([FREETDS], [FreeTDS], [tds])
-AST_EXT_LIB_SETUP([TERMCAP], [Termcap], [termcap])
AST_EXT_LIB_SETUP([TIMERFD], [timerfd], [timerfd])
-AST_EXT_LIB_SETUP([TINFO], [Term Info], [tinfo])
AST_EXT_LIB_SETUP([TONEZONE], [tonezone], [tonezone])
AST_EXT_LIB_SETUP([UNBOUND], [unbound], [unbound])
AST_EXT_LIB_SETUP([UNIXODBC], [unixODBC], [unixodbc])
@@ -621,25 +616,15 @@ AC_CHECK_HEADERS([ \
AC_CHECK_HEADERS([arpa/inet.h libintl.h malloc.h netdb.h stddef.h strings.h sys/event.h utime.h])
-# Any one of these packages support a mandatory requirement, so we want to check on them as early as possible.
-AST_EXT_LIB_CHECK([TERMCAP], [termcap], [tgetent], [])
-AST_EXT_LIB_CHECK([TINFO], [tinfo], [tgetent], [])
-AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
-AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h])
-
-EDITLINE_LIB=""
-if test "x$TERMCAP_LIB" != "x" ; then
- EDITLINE_LIB="$TERMCAP_LIB"
-elif test "x$TINFO_LIB" != "x" ; then
- EDITLINE_LIB="$TINFO_LIB"
-elif test "x$CURSES_LIB" != "x" ; then
- EDITLINE_LIB="$CURSES_LIB"
-elif test "x$NCURSES_LIB" != "x" ; then
- EDITLINE_LIB="$NCURSES_LIB"
-else
- AC_MSG_ERROR([*** termcap support not found (on modern systems, this typically means the ncurses development package is missing)])
+# Find required NetBSD Editline library (libedit).
+AST_PKG_CONFIG_CHECK(LIBEDIT, libedit)
+# some platforms do not list libedit via pkg-config, for example OpenBSD 6.2
+AST_EXT_LIB_CHECK([LIBEDIT], [edit], [history_init], [histedit.h], [-ltermcap])
+if test "${PBX_LIBEDIT}" != 1; then
+ AC_MSG_ERROR(*** Please install the 'libedit' development package.)
+ exit 1
fi
-AC_SUBST(EDITLINE_LIB)
+AST_C_COMPILE_CHECK([LIBEDIT_IS_UNICODE], [el_rfunc_t *callback;], [histedit.h], [], [Testing for libedit unicode support])
# Find required UUID support.
# * -luuid on Linux
@@ -1440,11 +1425,11 @@ AC_CHECK_SIZEOF(fd_set.fds_bits)
# correctly if the size is wrong.
if test $ac_cv_sizeof_int = $ac_cv_sizeof_fd_set_fds_bits; then
AC_DEFINE([TYPEOF_FD_SET_FDS_BITS], [int], [Define to a type of the same size as fd_set.fds_bits[[0]]])
-else if test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long = $ac_cv_sizeof_fd_set_fds_bits; then
AC_DEFINE([TYPEOF_FD_SET_FDS_BITS], [long], [Define to a type of the same size as fd_set.fds_bits[[0]]])
-else if test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
+elif test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
AC_DEFINE([TYPEOF_FD_SET_FDS_BITS], [long long], [Define to a type of the same size as fd_set.fds_bits[[0]]])
-fi ; fi ; fi
+fi
AC_MSG_CHECKING(for dladdr in dlfcn.h)
PBX_DLADDR=0
@@ -1556,11 +1541,9 @@ if test "${USE_GSM}" != "no"; then
if test "${GSM_HEADER_FOUND}" = "1" ; then
AC_DEFINE_UNQUOTED([HAVE_GSM_HEADER], 1, [Define to indicate that gsm.h has no prefix for its location])
GSM_OK=1
- else
- if test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
- AC_DEFINE_UNQUOTED([HAVE_GSM_GSM_HEADER], 1, [Define to indicate that gsm.h is in gsm/gsm.h])
- GSM_OK=1
- fi
+ elif test "${GSM_GSM_HEADER_FOUND}" = "1" ; then
+ AC_DEFINE_UNQUOTED([HAVE_GSM_GSM_HEADER], 1, [Define to indicate that gsm.h is in gsm/gsm.h])
+ GSM_OK=1
fi
if test "${GSM_OK}" = "1" ; then
GSM_LIB="-lgsm"
@@ -1599,15 +1582,6 @@ if test "${USE_ILBC}" != "no"; then
fi
fi
-AST_PKG_CONFIG_CHECK(LIBEDIT, libedit)
-# some platforms do not list libedit via pkg-config, for example OpenBSD 6.2
-AST_EXT_LIB_CHECK([LIBEDIT], [edit], [history_init], [histedit.h], [-ltermcap])
-if test "${PBX_LIBEDIT}" != 1; then
- AC_MSG_ERROR(*** Please install the 'libedit' development package.)
- exit 1
-fi
-AST_C_COMPILE_CHECK([LIBEDIT_IS_UNICODE], [el_rfunc_t *callback;], [histedit.h], [], [Testing for libedit unicode support])
-
AST_EXT_LIB_CHECK([ICONV], [iconv], [iconv_open], [iconv.h])
# GNU libiconv #define's iconv_open to libiconv_open, so we need to search for that symbol
AST_EXT_LIB_CHECK([ICONV], [iconv], [libiconv_open], [iconv.h])
@@ -2380,32 +2354,17 @@ if test -z "$__opus_include" -o x"$__opus_include" = x" " ; then
fi
AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include])
-AST_EXT_LIB_CHECK([LUA], [lua5.3], [luaL_newstate], [lua5.3/lua.h], [-lm])
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.3"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.3"
- fi
-fi
-
-AST_EXT_LIB_CHECK([LUA], [lua5.2], [luaL_newstate], [lua5.2/lua.h], [-lm])
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.2"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.2"
- fi
-fi
-
-AST_EXT_LIB_CHECK([LUA], [lua5.1], [luaL_newstate], [lua5.1/lua.h], [-lm])
-if test "x${PBX_LUA}" = "x1" ; then
- if test x"${LUA_DIR}" = x; then
- LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua5.1"
- else
- LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua5.1"
+for ver in 5.3 5.2 5.1; do
+ AST_EXT_LIB_CHECK([LUA], lua${ver}, [luaL_newstate], lua${ver}/lua.h, [-lm])
+ if test "x${PBX_LUA}" = "x1" ; then
+ if test x"${LUA_DIR}" = x; then
+ LUA_INCLUDE="${LUA_INCLUDE} -I/usr/include/lua${ver}"
+ else
+ LUA_INCLUDE="${LUA_INCLUDE} -I${LUA_DIR}/lua${ver}"
+ fi
+ break;
fi
-fi
+done
# Some distributions (like openSUSE and NetBSD) remove the 5.x suffix.
AST_EXT_LIB_CHECK([LUA], [lua], [luaL_newstate], [lua.h], [-lm])
@@ -2756,9 +2715,9 @@ AC_OUTPUT
${ac_cv_path_EGREP} 'CURSES|GTK2|OSARCH|NEWT' makeopts > makeopts.acbak2
if test "x${ac_cv_path_CMP}" = "x:"; then
( cd `pwd`/menuselect && ./configure )
-else if ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
+elif ${ac_cv_path_CMP} -s makeopts.acbak makeopts.acbak2; then : ; else
( cd `pwd`/menuselect && ./configure )
-fi ; fi
+fi
rm makeopts.acbak makeopts.acbak2
diff --git a/contrib/scripts/install_prereq b/contrib/scripts/install_prereq
index 887c5e21d..3ac7a0a1f 100755
--- a/contrib/scripts/install_prereq
+++ b/contrib/scripts/install_prereq
@@ -83,6 +83,22 @@ PACKAGES_ARCH="$PACKAGES_ARCH wget subversion"
PACKAGES_ARCH="$PACKAGES_ARCH bzip2 patch python2"
# Basic build system:
+PACKAGES_GENTOO="sys-devel/make sys-devel/gcc dev-util/pkgconfig"
+# Asterisk: basic requirements:
+PACKAGES_GENTOO="$PACKAGES_GENTOO dev-libs/libedit dev-libs/jansson sys-libs/e2fsprogs-libs dev-libs/libxml2 dev-db/sqlite"
+# Asterisk: for addons:
+PACKAGES_GENTOO="$PACKAGES_GENTOO media-libs/speex media-libs/speexdsp media-libs/libogg media-libs/libvorbis media-libs/alsa-lib media-libs/portaudio net-misc/curl app-text/xmlstarlet sys-devel/bison sys-devel/flex"
+PACKAGES_GENTOO="$PACKAGES_GENTOO dev-db/postgresql dev-db/unixODBC net-libs/neon dev-libs/gmime dev-lang/lua dev-libs/uriparser dev-libs/libxslt dev-libs/openssl"
+PACKAGES_GENTOO="$PACKAGES_GENTOO virtual/libmysqlclient net-wireless/bluez net-dialup/radiusclient-ng dev-db/freetds app-shells/bash"
+PACKAGES_GENTOO="$PACKAGES_GENTOO net-analyzer/net-snmp dev-libs/iksemel sys-cluster/corosync dev-libs/newt dev-libs/popt dev-libs/libical media-libs/spandsp"
+PACKAGES_GENTOO="$PACKAGES_GENTOO net-libs/c-client sys-devel/binutils net-libs/libsrtp media-sound/gsm media-libs/libilbc app-doc/doxygen sys-libs/zlib net-nds/openldap"
+PACKAGES_GENTOO="$PACKAGES_GENTOO sci-libs/fftw media-libs/libsndfile net-dns/unbound"
+# Asterisk: for the unpackaged below:
+PACKAGES_GENTOO="$PACKAGES_GENTOO net-misc/wget dev-vcs/subversion"
+# Asterisk: for ./configure --with-pjproject-bundled:
+PACKAGES_GENTOO="$PACKAGES_GENTOO app-arch/bzip2 sys-devel/patch dev-lang/python:2.7"
+
+# Basic build system:
PACKAGES_NBSD="gmake pkg-config"
# Asterisk: basic requirements:
PACKAGES_NBSD="$PACKAGES_NBSD editline jansson sqlite3 libuuid libxml2"
@@ -179,6 +195,20 @@ check_installed_rpms() {
done
}
+check_installed_equery() {
+ for pack in "$@"
+ do
+ # equery --quiet list $pack
+ # is slower and
+ # would require the optional app-portage/gentoolkit
+ # /var/lib/portage/world would be the non-dep list
+ pack_with_version=${pack/:/-} # replace a possible version with '-'
+ if ! ls -d /var/db/pkg/${pack_with_version}* >/dev/null 2>/dev/null
+ then echo $pack
+ fi
+ done
+}
+
check_installed_pacman() {
for pack in "$@"
do
@@ -233,6 +263,13 @@ handle_rh() {
fi
}
+handle_gentoo() {
+ extra_packs=`check_installed_equery $PACKAGES_GENTOO`
+ if [ x"$extra_packs" != "x" ] ; then
+ $testcmd emerge $extra_packs
+ fi
+}
+
handle_arch() {
extra_packs=`check_installed_pacman $PACKAGES_ARCH`
if [ x"$extra_packs" != "x" ] ; then
@@ -297,7 +334,11 @@ install_unpackaged() {
./configure
make all install
cd ..
- echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local.conf
+ if test -d /etc/ld.so.conf.d; then
+ echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local.conf
+ else # for example: Slackware 14.2
+ echo "/usr/local/lib" > /etc/ld.so.conf
+ fi
/sbin/ldconfig
fi
@@ -310,7 +351,11 @@ install_unpackaged() {
./configure --enable-openssl
make shared_library install
cd ..
- echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local.conf
+ if test -d /etc/ld.so.conf.d; then
+ echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local.conf
+ else # for example: Slackware 14.2
+ echo "/usr/local/lib" > /etc/ld.so.conf
+ fi
/sbin/ldconfig
fi
fi
@@ -340,16 +385,18 @@ if [ "$OS" != 'Linux' -a "$OS" != 'NetBSD' -a "$OS" != 'OpenBSD' -a "$OS" != 'Fr
exit 1
fi
-if [ -f /etc/gentoo-release ]; then
- unsupported_distro='Gentoo'
-fi
-
if [ -f /etc/mandrake-release ]; then
unsupported_distro='Mandriva'
fi
-if [ -f /etc/slackware-version ]; then
- unsupported_distro='Slackware'
+if [ -f /etc/slackware-version ] || ([ -f /etc/os-release ] && . /etc/os-release && [ "$ID" = "slackware" ]); then
+ echo >&2 "$0: Your distribution (Slackware) is currently not supported. Aborting. Try manually:"
+ # libedit requires a newer version than Slackware 14.2, for example Slackware-current
+ # or you build it manually: <http://thrysoee.dk/editline/>
+ echo >&2 "$0: # slackpkg install make gcc pkg-config libedit util-linux sqlite libxml2 patch wget"
+ # required for libjansson
+ echo >&2 "$0: # ./contrib/scripts/install_prereq install-unpackaged"
+ exit 1
fi
if [ "$unsupported_distro" != '' ]; then
@@ -370,6 +417,10 @@ elif [ -r /etc/arch-release ]; then
handle_arch
elif [ -f /etc/os-release ] && . /etc/os-release && [ "$ID_LIKE" = "archlinux" ]; then
handle_arch # $ID=arch
+elif [ -f /etc/gentoo-release ]; then
+ handle_gentoo
+elif [ -f /etc/os-release ] && . /etc/os-release && [ "$ID" = "gentoo" ]; then
+ handle_gentoo
elif [ "$OS" = 'NetBSD' ]; then
handle_nbsd
elif [ "$OS" = 'OpenBSD' ]; then
diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index 431f96108..d19c58967 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -15,6 +15,11 @@
#ifndef _ASTERISK__PRIVATE_H
#define _ASTERISK__PRIVATE_H
+/* Load settings from asterisk.conf, provided by options.c */
+void load_asterisk_conf(void);
+void set_asterisk_conf_path(const char *path);
+void set_socket_path(const char *path);
+
int load_modules(unsigned int); /*!< Provided by loader.c */
int modules_shutdown(void); /*!< Provided by loader.c */
int load_pbx(void); /*!< Provided by pbx.c */
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 5e9ba6011..4d1624ee3 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -185,9 +185,6 @@
/* Define to 1 if you have a functional curl library. */
#undef HAVE_CURL
-/* Define to 1 if you have the curses library. */
-#undef HAVE_CURSES
-
/* Define to 1 if your C compiler provides __atomic operations. */
#undef HAVE_C_ATOMICS
@@ -534,9 +531,6 @@
/* Define to 1 if you have the Network Broadcast Sound library. */
#undef HAVE_NBS
-/* Define to 1 if you have the ncurses library. */
-#undef HAVE_NCURSES
-
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
@@ -1155,9 +1149,6 @@
/* Define to 1 if you have the `tanl' function. */
#undef HAVE_TANL
-/* Define to 1 if you have the Termcap library. */
-#undef HAVE_TERMCAP
-
/* Define to 1 if you have the <termios.h> header file. */
#undef HAVE_TERMIOS_H
@@ -1170,9 +1161,6 @@
/* Define to 1 if you have the <time.h> header file. */
#undef HAVE_TIME_H
-/* Define to 1 if you have the Term Info library. */
-#undef HAVE_TINFO
-
/* Define to 1 if you have the tonezone library. */
#undef HAVE_TONEZONE
diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h
index 8d5c50211..3584085af 100644
--- a/include/asterisk/bridge.h
+++ b/include/asterisk/bridge.h
@@ -126,6 +126,24 @@ struct ast_bridge_video_talker_src_data {
struct ast_channel *chan_old_vsrc;
};
+/*! \brief REMB report behaviors */
+enum ast_bridge_video_sfu_remb_behavior {
+ /*! The average of all reports is sent to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE = 0,
+ /*! The lowest reported bitrate is forwarded to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_LOWEST,
+ /*! The highest reported bitrate is forwarded to the sender */
+ AST_BRIDGE_VIDEO_SFU_REMB_HIGHEST,
+};
+
+/*! \brief This is used for selective forwarding unit configuration */
+struct ast_bridge_video_sfu_data {
+ /*! The interval at which a REMB report is generated and sent */
+ unsigned int remb_send_interval;
+ /*! How the combined REMB report is generated */
+ enum ast_bridge_video_sfu_remb_behavior remb_behavior;
+};
+
/*! \brief Data structure that defines a video source mode */
struct ast_bridge_video_mode {
enum ast_bridge_video_mode_type mode;
@@ -133,7 +151,9 @@ struct ast_bridge_video_mode {
union {
struct ast_bridge_video_single_src_data single_src_data;
struct ast_bridge_video_talker_src_data talker_src_data;
+ struct ast_bridge_video_sfu_data sfu_data;
} mode_data;
+ /*! The minimum interval between video updates */
unsigned int video_update_discard;
};
@@ -912,6 +932,26 @@ void ast_bridge_set_sfu_video_mode(struct ast_bridge *bridge);
void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int video_update_discard);
/*!
+ * \brief Set the interval at which a combined REMB frame will be sent to video sources
+ *
+ * \param bridge Bridge to set the REMB send interval on
+ * \param remb_send_interval The REMB send interval
+ *
+ * \note This can only be called when the bridge has been set to the SFU video mode.
+ */
+void ast_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval);
+
+/*!
+ * \brief Set the REMB report generation behavior on a bridge
+ *
+ * \param bridge Bridge to set the REMB behavior on
+ * \param behavior How REMB reports are generated
+ *
+ * \note This can only be called when the bridge has been set to the SFU video mode.
+ */
+void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior);
+
+/*!
* \brief Update information about talker energy for talker src video mode.
*/
void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyfame);
@@ -945,6 +985,17 @@ void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *
*/
const char *ast_bridge_video_mode_to_string(enum ast_bridge_video_mode_type video_mode);
+/*!
+ * \brief Acquire the channel's bridge for transfer purposes.
+ * \since 13.21.0
+ *
+ * \param chan Channel involved in a transfer.
+ *
+ * \return The bridge the channel is in or NULL if it either isn't
+ * in a bridge or should not be considered to be in a bridge.
+ */
+struct ast_bridge *ast_bridge_transfer_acquire_bridge(struct ast_channel *chan);
+
enum ast_transfer_result {
/*! The transfer completed successfully */
AST_BRIDGE_TRANSFER_SUCCESS,
diff --git a/include/asterisk/paths.h b/include/asterisk/paths.h
index 3e3b8eae0..de28c7575 100644
--- a/include/asterisk/paths.h
+++ b/include/asterisk/paths.h
@@ -37,5 +37,9 @@ extern const char *ast_config_AST_RUN_GROUP;
extern const char *ast_config_AST_RUN_USER;
extern const char *ast_config_AST_SYSTEM_NAME;
extern const char *ast_config_AST_SBIN_DIR;
+extern const char *ast_config_AST_CTL_PERMISSIONS;
+extern const char *ast_config_AST_CTL_OWNER;
+extern const char *ast_config_AST_CTL_GROUP;
+extern const char *ast_config_AST_CTL;
#endif /* _ASTERISK_PATHS_H */
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index c017e62db..092bb8420 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -263,7 +263,7 @@ struct ast_sip_contact {
struct timeval expiration_time;
/*! Frequency to send OPTIONS requests to contact. 0 is disabled. */
unsigned int qualify_frequency;
- /*! If true authenticate the qualify if needed */
+ /*! If true authenticate the qualify challenge response if needed */
int authenticate_qualify;
/*! Qualify timeout. 0 is diabled. */
double qualify_timeout;
@@ -346,7 +346,7 @@ struct ast_sip_aor {
unsigned int default_expiration;
/*! Frequency to send OPTIONS requests to AOR contacts. 0 is disabled. */
unsigned int qualify_frequency;
- /*! If true authenticate the qualify if needed */
+ /*! If true authenticate the qualify challenge response if needed */
int authenticate_qualify;
/*! Maximum number of external contacts, 0 to disable */
unsigned int max_contacts;
@@ -1407,7 +1407,7 @@ struct ast_sip_endpoint *ast_sip_get_artificial_endpoint(void);
* the next item on the SIP socket(s) can be serviced. On incoming messages,
* Asterisk automatically will push the request to a servant thread. When your
* module callback is called, processing will already be in a servant. However,
- * for other PSJIP events, such as transaction state changes due to timer
+ * for other PJSIP events, such as transaction state changes due to timer
* expirations, your module will be called into from a PJSIP thread. If you
* are called into from a PJSIP thread, then you should push whatever processing
* is needed to a servant as soon as possible. You can discern if you are currently
@@ -1543,28 +1543,92 @@ struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg);
int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
/*!
- * \brief Push a task to SIP servants and wait for it to complete
+ * \brief Push a task to SIP servants and wait for it to complete.
*
- * Like \ref ast_sip_push_task except that it blocks until the task completes.
+ * Like \ref ast_sip_push_task except that it blocks until the task
+ * completes. If the current thread is a SIP servant thread then the
+ * task executes immediately. Otherwise, the specified serializer
+ * executes the task and the current thread waits for it to complete.
*
- * \warning \b Never use this function in a SIP servant thread. This can potentially
- * cause a deadlock. If you are in a SIP servant thread, just call your function
- * in-line.
+ * \note PJPROJECT callbacks tend to have locks already held when
+ * called.
*
- * \warning \b Never hold locks that may be acquired by a SIP servant thread when
- * calling this function. Doing so may cause a deadlock if all SIP servant threads
- * are blocked waiting to acquire the lock while the thread holding the lock is
- * waiting for a free SIP servant thread.
+ * \warning \b Never hold locks that may be acquired by a SIP servant
+ * thread when calling this function. Doing so may cause a deadlock
+ * if all SIP servant threads are blocked waiting to acquire the lock
+ * while the thread holding the lock is waiting for a free SIP servant
+ * thread.
*
- * \param serializer The SIP serializer to which the task belongs. May be NULL.
+ * \warning \b Use of this function in an ao2 destructor callback is a
+ * bad idea. You don't have control over which thread executes the
+ * destructor. Attempting to shift execution to another thread with
+ * this function is likely to cause deadlock.
+ *
+ * \param serializer The SIP serializer to execute the task if the
+ * current thread is not a SIP servant. NULL if any of the default
+ * serializers can be used.
* \param sip_task The task to execute
* \param task_data The parameter to pass to the task when it executes
- * \retval 0 Success
- * \retval -1 Failure
+ *
+ * \note The sip_task() return value may need to be distinguished from
+ * the failure to push the task.
+ *
+ * \return sip_task() return value on success.
+ * \retval -1 Failure to push the task.
+ */
+int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
+
+/*!
+ * \brief Push a task to SIP servants and wait for it to complete.
+ * \deprecated Replaced with ast_sip_push_task_wait_servant().
*/
int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
/*!
+ * \brief Push a task to the serializer and wait for it to complete.
+ *
+ * Like \ref ast_sip_push_task except that it blocks until the task is
+ * completed by the specified serializer. If the specified serializer
+ * is the current thread then the task executes immediately.
+ *
+ * \note PJPROJECT callbacks tend to have locks already held when
+ * called.
+ *
+ * \warning \b Never hold locks that may be acquired by a SIP servant
+ * thread when calling this function. Doing so may cause a deadlock
+ * if all SIP servant threads are blocked waiting to acquire the lock
+ * while the thread holding the lock is waiting for a free SIP servant
+ * thread for the serializer to execute in.
+ *
+ * \warning \b Never hold locks that may be acquired by the serializer
+ * when calling this function. Doing so will cause a deadlock.
+ *
+ * \warning \b Never use this function in the pjsip monitor thread (It
+ * is a SIP servant thread). This is likely to cause a deadlock.
+ *
+ * \warning \b Use of this function in an ao2 destructor callback is a
+ * bad idea. You don't have control over which thread executes the
+ * destructor. Attempting to shift execution to another thread with
+ * this function is likely to cause deadlock.
+ *
+ * \param serializer The SIP serializer to execute the task. NULL if
+ * any of the default serializers can be used.
+ * \param sip_task The task to execute
+ * \param task_data The parameter to pass to the task when it executes
+ *
+ * \note It is generally better to call
+ * ast_sip_push_task_wait_servant() if you pass NULL for the
+ * serializer parameter.
+ *
+ * \note The sip_task() return value may need to be distinguished from
+ * the failure to push the task.
+ *
+ * \return sip_task() return value on success.
+ * \retval -1 Failure to push the task.
+ */
+int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data);
+
+/*!
* \brief Determine if the current thread is a SIP servant thread
*
* \retval 0 This is not a SIP servant thread
@@ -1588,13 +1652,13 @@ enum ast_sip_scheduler_task_flags {
/*!
* Run at a fixed interval.
- * Stop scheduling if the callback returns 0.
+ * Stop scheduling if the callback returns <= 0.
* Any other value is ignored.
*/
AST_SIP_SCHED_TASK_FIXED = (0 << 0),
/*!
* Run at a variable interval.
- * Stop scheduling if the callback returns 0.
+ * Stop scheduling if the callback returns <= 0.
* Any other return value is used as the new interval.
*/
AST_SIP_SCHED_TASK_VARIABLE = (1 << 0),
@@ -1620,16 +1684,23 @@ enum ast_sip_scheduler_task_flags {
*/
AST_SIP_SCHED_TASK_DATA_FREE = ( 1 << 3 ),
- /*! \brief AST_SIP_SCHED_TASK_PERIODIC
- * The task is scheduled at multiples of interval
+ /*!
+ * \brief The task is scheduled at multiples of interval
* \see Interval
*/
AST_SIP_SCHED_TASK_PERIODIC = (0 << 4),
- /*! \brief AST_SIP_SCHED_TASK_DELAY
- * The next invocation of the task is at last finish + interval
+ /*!
+ * \brief The next invocation of the task is at last finish + interval
* \see Interval
*/
AST_SIP_SCHED_TASK_DELAY = (1 << 4),
+ /*!
+ * \brief The scheduled task's events are tracked in the debug log.
+ * \details
+ * Schedule events such as scheduling, running, rescheduling, canceling,
+ * and destroying are logged about the task.
+ */
+ AST_SIP_SCHED_TASK_TRACK = (1 << 5),
};
/*!
@@ -1673,7 +1744,7 @@ struct ast_sip_sched_task;
*
*/
struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *serializer,
- int interval, ast_sip_task sip_task, char *name, void *task_data,
+ int interval, ast_sip_task sip_task, const char *name, void *task_data,
enum ast_sip_scheduler_task_flags flags);
/*!
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index b552948d2..3426b2a1e 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -292,10 +292,16 @@ struct ast_rtp_payload_type {
#define AST_RTP_RTCP_SR 200
/*! Receiver Report */
#define AST_RTP_RTCP_RR 201
+/*! Transport Layer Feed Back (From RFC4585 also RFC5104) */
+#define AST_RTP_RTCP_RTPFB 205
/*! Payload Specific Feed Back (From RFC4585 also RFC5104) */
-#define AST_RTP_RTCP_PSFB 206
+#define AST_RTP_RTCP_PSFB 206
/* Common RTCP feedback message types */
+/*! Generic NACK (From RFC4585 also RFC5104) */
+#define AST_RTP_RTCP_FMT_NACK 1
+/*! Picture loss indication (From RFC4585) */
+#define AST_RTP_RTCP_FMT_PLI 1
/*! Full INTRA-frame Request (From RFC5104) */
#define AST_RTP_RTCP_FMT_FIR 4
/*! REMB Information (From draft-alvestrand-rmcat-remb-03) */
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 4da7fa465..b892cda9e 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -578,6 +578,13 @@ void DO_CRASH_NORETURN __ast_assert_failed(int condition, const char *condition_
#ifdef AST_DEVMODE
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define ast_assert_return(a, ...) \
+({ \
+ if (__builtin_expect(!(a), 1)) { \
+ _ast_assert(0, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ return __VA_ARGS__; \
+ }\
+})
static void force_inline _ast_assert(int condition, const char *condition_str, const char *file, int line, const char *function)
{
if (__builtin_expect(!condition, 1)) {
@@ -586,6 +593,12 @@ static void force_inline _ast_assert(int condition, const char *condition_str, c
}
#else
#define ast_assert(a)
+#define ast_assert_return(a, ...) \
+({ \
+ if (__builtin_expect(!(a), 1)) { \
+ return __VA_ARGS__; \
+ }\
+})
#endif
/*!
diff --git a/main/Makefile b/main/Makefile
index fb985b9b8..bef70e966 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -57,9 +57,9 @@ ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc linux-musl kfreebs
ifneq (x$(CAP_LIB),x)
AST_LIBS+=$(CAP_LIB)
endif
- AST_LIBS+=-lpthread $(EDITLINE_LIB) -lm -lresolv
+ AST_LIBS+=-lpthread -lm -lresolv
else
- AST_LIBS+=$(EDITLINE_LIB) -lm
+ AST_LIBS+=-lm
endif
ifneq ($(findstring BETTER_BACKTRACES,$(MENUSELECT_CFLAGS)),)
@@ -90,11 +90,11 @@ ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
ASTLINK+=-shared -Wl,--out-implib,libasterisk.a
endif
ifeq ($(OSARCH),NetBSD)
- AST_LIBS+=-lpthread -lcrypto -lm -L/usr/pkg/lib $(EDITLINE_LIB)
+ AST_LIBS+=-lpthread -lcrypto -lm -L/usr/pkg/lib
endif
ifeq ($(OSARCH),OpenBSD)
- AST_LIBS+=-lcrypto -lpthread -lm $(EDITLINE_LIB)
+ AST_LIBS+=-lcrypto -lpthread -lm
endif
ifeq ($(OSARCH),SunOS)
@@ -354,7 +354,6 @@ endif
rm -f .libasteriskpj*
rm -f asterisk.exports libasteriskssl.exports libasteriskpj.exports
- @if [ -f editline/Makefile ]; then $(MAKE) -C editline distclean ; fi
@$(MAKE) -C stdtime clean
rm -f libresample/src/*.o
rm -f *.tmp
diff --git a/main/asterisk.c b/main/asterisk.c
index 2e80ffaf6..665b4be84 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -1,7 +1,7 @@
/*
* Asterisk -- An open source telephony toolkit.
*
- * Copyright (C) 1999 - 2016, Digium, Inc.
+ * Copyright (C) 1999 - 2018, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
@@ -53,7 +53,7 @@
*
* \section copyright Copyright and Author
*
- * Copyright (C) 1999 - 2016, Digium, Inc.
+ * Copyright (C) 1999 - 2018, Digium, Inc.
* Asterisk is a <a href="http://www.digium.com/en/company/view-policy.php?id=Trademark-Policy">registered trademark</a>
* of <a rel="nofollow" href="http://www.digium.com">Digium, Inc</a>.
*
@@ -294,13 +294,9 @@ int daemon(int, int); /* defined in libresolv of all places */
#define AST_MAX_CONNECTS 128
#define NUM_MSGS 64
-/*! Default minimum DTMF digit length - 80ms */
-#define AST_MIN_DTMF_DURATION 80
-
-
/*! \brief Welcome message when starting a CLI interface */
#define WELCOME_MESSAGE \
- ast_verbose("Asterisk %s, Copyright (C) 1999 - 2016, Digium, Inc. and others.\n" \
+ ast_verbose("Asterisk %s, Copyright (C) 1999 - 2018, Digium, Inc. and others.\n" \
"Created by Mark Spencer <markster@digium.com>\n" \
"Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.\n" \
"This is free software, with components licensed under the GNU General Public\n" \
@@ -308,40 +304,6 @@ int daemon(int, int); /* defined in libresolv of all places */
"certain conditions. Type 'core show license' for details.\n" \
"=========================================================================\n", ast_get_version()) \
-/*! \defgroup main_options Main Configuration Options
- * \brief Main configuration options from asterisk.conf or OS command line on starting Asterisk.
- * \arg \ref Config_ast "asterisk.conf"
- * \note Some of them can be changed in the CLI
- */
-/*! @{ */
-
-struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
-
-/*! Maximum active system verbosity level. */
-int ast_verb_sys_level;
-
-int option_verbose; /*!< Verbosity level */
-int option_debug; /*!< Debug level */
-int ast_pjproject_max_log_level = -1;/* Default to -1 to know if we have read the level from pjproject yet. */
-int ast_option_pjproject_log_level;
-int ast_option_pjproject_cache_pools;
-double ast_option_maxload; /*!< Max load avg on system */
-int ast_option_maxcalls; /*!< Max number of active calls */
-int ast_option_maxfiles; /*!< Max number of open file handles (files, sockets) */
-unsigned int option_dtmfminduration; /*!< Minimum duration of DTMF. */
-#if defined(HAVE_SYSINFO)
-long option_minmemfree; /*!< Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
-#endif
-int ast_option_rtpusedynamic;
-unsigned int ast_option_rtpptdynamic;
-
-/*! @} */
-
-struct ast_eid ast_eid_default;
-
-/* XXX tmpdir is a subdir of the spool directory, and no way to remap it */
-char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
-
static int ast_socket = -1; /*!< UNIX Socket for allowing remote control */
static int ast_socket_is_sd = 0; /*!< Is socket activation responsible for ast_socket? */
static int ast_consock = -1; /*!< UNIX Socket for controlling another asterisk */
@@ -375,8 +337,6 @@ static char *remotehostname;
struct console consoles[AST_MAX_CONNECTS];
-char ast_defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
-
static int ast_el_add_history(const char *);
static int ast_el_read_history(const char *);
static int ast_el_write_history(const char *);
@@ -386,62 +346,6 @@ static void ast_el_write_default_histfile(void);
static void asterisk_daemon(int isroot, const char *runuser, const char *rungroup);
-#define DEFAULT_MONITOR_DIR DEFAULT_SPOOL_DIR "/monitor"
-#define DEFAULT_RECORDING_DIR DEFAULT_SPOOL_DIR "/recording"
-
-struct _cfg_paths {
- char config_dir[PATH_MAX];
- char module_dir[PATH_MAX];
- char spool_dir[PATH_MAX];
- char monitor_dir[PATH_MAX];
- char recording_dir[PATH_MAX];
- char var_dir[PATH_MAX];
- char data_dir[PATH_MAX];
- char log_dir[PATH_MAX];
- char agi_dir[PATH_MAX];
- char run_dir[PATH_MAX];
- char key_dir[PATH_MAX];
-
- char config_file[PATH_MAX];
- char db_path[PATH_MAX];
- char sbin_dir[PATH_MAX];
- char pid_path[PATH_MAX];
- char socket_path[PATH_MAX];
- char run_user[PATH_MAX];
- char run_group[PATH_MAX];
- char system_name[128];
-};
-
-static struct _cfg_paths cfg_paths;
-
-const char *ast_config_AST_CONFIG_DIR = cfg_paths.config_dir;
-const char *ast_config_AST_CONFIG_FILE = cfg_paths.config_file;
-const char *ast_config_AST_MODULE_DIR = cfg_paths.module_dir;
-const char *ast_config_AST_SPOOL_DIR = cfg_paths.spool_dir;
-const char *ast_config_AST_MONITOR_DIR = cfg_paths.monitor_dir;
-const char *ast_config_AST_RECORDING_DIR = cfg_paths.recording_dir;
-const char *ast_config_AST_VAR_DIR = cfg_paths.var_dir;
-const char *ast_config_AST_DATA_DIR = cfg_paths.data_dir;
-const char *ast_config_AST_LOG_DIR = cfg_paths.log_dir;
-const char *ast_config_AST_AGI_DIR = cfg_paths.agi_dir;
-const char *ast_config_AST_KEY_DIR = cfg_paths.key_dir;
-const char *ast_config_AST_RUN_DIR = cfg_paths.run_dir;
-const char *ast_config_AST_SBIN_DIR = cfg_paths.sbin_dir;
-
-const char *ast_config_AST_DB = cfg_paths.db_path;
-const char *ast_config_AST_PID = cfg_paths.pid_path;
-const char *ast_config_AST_SOCKET = cfg_paths.socket_path;
-const char *ast_config_AST_RUN_USER = cfg_paths.run_user;
-const char *ast_config_AST_RUN_GROUP = cfg_paths.run_group;
-const char *ast_config_AST_SYSTEM_NAME = cfg_paths.system_name;
-
-static char ast_config_AST_CTL_PERMISSIONS[PATH_MAX];
-static char ast_config_AST_CTL_OWNER[PATH_MAX] = "\0";
-static char ast_config_AST_CTL_GROUP[PATH_MAX] = "\0";
-static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl";
-
-extern unsigned int ast_FD_SETSIZE;
-
static char *_argv[256];
typedef enum {
@@ -1803,29 +1707,6 @@ static struct sigaction child_handler = {
.sa_flags = SA_RESTART,
};
-/*! \brief Set maximum open files */
-static void set_ulimit(int value)
-{
- struct rlimit l = {0, 0};
-
- if (value <= 0) {
- ast_log(LOG_WARNING, "Unable to change max files open to invalid value %i\n",value);
- return;
- }
-
- l.rlim_cur = value;
- l.rlim_max = value;
-
- if (setrlimit(RLIMIT_NOFILE, &l)) {
- ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n",strerror(errno));
- return;
- }
-
- ast_log(LOG_NOTICE, "Setting max files open to %d\n",value);
-
- return;
-}
-
/*! \brief Set an X-term or screen title */
static void set_title(char *text)
{
@@ -3441,296 +3322,6 @@ static int show_cli_help(void)
return 0;
}
-static void ast_readconfig(void)
-{
- struct ast_config *cfg;
- struct ast_variable *v;
- char hostname[MAXHOSTNAMELEN] = "";
- struct ast_flags config_flags = { CONFIG_FLAG_NOREALTIME };
- struct {
- unsigned int dbdir:1;
- unsigned int keydir:1;
- } found = { 0, 0 };
- /* Default to false for security */
- int live_dangerously = 0;
- int option_debug_new = 0;
- int option_verbose_new = 0;
-
- /* Set default value */
- option_dtmfminduration = AST_MIN_DTMF_DURATION;
- ast_option_rtpusedynamic = 1;
- ast_option_rtpptdynamic = 35;
-
- /* init with buildtime config */
- ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
- ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
- ast_copy_string(cfg_paths.module_dir, DEFAULT_MODULE_DIR, sizeof(cfg_paths.module_dir));
- ast_copy_string(cfg_paths.monitor_dir, DEFAULT_MONITOR_DIR, sizeof(cfg_paths.monitor_dir));
- ast_copy_string(cfg_paths.recording_dir, DEFAULT_RECORDING_DIR, sizeof(cfg_paths.recording_dir));
- ast_copy_string(cfg_paths.var_dir, DEFAULT_VAR_DIR, sizeof(cfg_paths.var_dir));
- ast_copy_string(cfg_paths.data_dir, DEFAULT_DATA_DIR, sizeof(cfg_paths.data_dir));
- ast_copy_string(cfg_paths.log_dir, DEFAULT_LOG_DIR, sizeof(cfg_paths.log_dir));
- ast_copy_string(cfg_paths.agi_dir, DEFAULT_AGI_DIR, sizeof(cfg_paths.agi_dir));
- ast_copy_string(cfg_paths.db_path, DEFAULT_DB, sizeof(cfg_paths.db_path));
- ast_copy_string(cfg_paths.sbin_dir, DEFAULT_SBIN_DIR, sizeof(cfg_paths.sbin_dir));
- ast_copy_string(cfg_paths.key_dir, DEFAULT_KEY_DIR, sizeof(cfg_paths.key_dir));
- ast_copy_string(cfg_paths.pid_path, DEFAULT_PID, sizeof(cfg_paths.pid_path));
- ast_copy_string(cfg_paths.socket_path, DEFAULT_SOCKET, sizeof(cfg_paths.socket_path));
- ast_copy_string(cfg_paths.run_dir, DEFAULT_RUN_DIR, sizeof(cfg_paths.run_dir));
-
-#ifdef REF_DEBUG
- /* The REF_DEBUG compiler flag is now only used to enable refdebug by default.
- * Support for debugging reference counts is always compiled in. */
- ast_set2_flag(&ast_options, 1, AST_OPT_FLAG_REF_DEBUG);
-#endif
-
- ast_set_default_eid(&ast_eid_default);
-
- cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
-
- /* If AST_OPT_FLAG_EXEC_INCLUDES was previously enabled with -X turn it off now.
- * Using #exec from other configs requires that it be enabled from asterisk.conf. */
- ast_clear_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES);
-
- /* no asterisk.conf? no problem, use buildtime config! */
- if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
- fprintf(stderr, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
- return;
- }
-
- for (v = ast_variable_browse(cfg, "files"); v; v = v->next) {
- if (!strcasecmp(v->name, "astctlpermissions"))
- ast_copy_string(ast_config_AST_CTL_PERMISSIONS, v->value, sizeof(ast_config_AST_CTL_PERMISSIONS));
- else if (!strcasecmp(v->name, "astctlowner"))
- ast_copy_string(ast_config_AST_CTL_OWNER, v->value, sizeof(ast_config_AST_CTL_OWNER));
- else if (!strcasecmp(v->name, "astctlgroup"))
- ast_copy_string(ast_config_AST_CTL_GROUP, v->value, sizeof(ast_config_AST_CTL_GROUP));
- else if (!strcasecmp(v->name, "astctl"))
- ast_copy_string(ast_config_AST_CTL, v->value, sizeof(ast_config_AST_CTL));
- }
-
- for (v = ast_variable_browse(cfg, "directories"); v; v = v->next) {
- if (!strcasecmp(v->name, "astetcdir")) {
- ast_copy_string(cfg_paths.config_dir, v->value, sizeof(cfg_paths.config_dir));
- } else if (!strcasecmp(v->name, "astspooldir")) {
- ast_copy_string(cfg_paths.spool_dir, v->value, sizeof(cfg_paths.spool_dir));
- snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", v->value);
- snprintf(cfg_paths.recording_dir, sizeof(cfg_paths.recording_dir), "%s/recording", v->value);
- } else if (!strcasecmp(v->name, "astvarlibdir")) {
- ast_copy_string(cfg_paths.var_dir, v->value, sizeof(cfg_paths.var_dir));
- if (!found.dbdir)
- snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
- } else if (!strcasecmp(v->name, "astdbdir")) {
- snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
- found.dbdir = 1;
- } else if (!strcasecmp(v->name, "astdatadir")) {
- ast_copy_string(cfg_paths.data_dir, v->value, sizeof(cfg_paths.data_dir));
- if (!found.keydir)
- snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
- } else if (!strcasecmp(v->name, "astkeydir")) {
- snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
- found.keydir = 1;
- } else if (!strcasecmp(v->name, "astlogdir")) {
- ast_copy_string(cfg_paths.log_dir, v->value, sizeof(cfg_paths.log_dir));
- } else if (!strcasecmp(v->name, "astagidir")) {
- ast_copy_string(cfg_paths.agi_dir, v->value, sizeof(cfg_paths.agi_dir));
- } else if (!strcasecmp(v->name, "astrundir")) {
- snprintf(cfg_paths.pid_path, sizeof(cfg_paths.pid_path), "%s/%s", v->value, "asterisk.pid");
- ast_copy_string(cfg_paths.run_dir, v->value, sizeof(cfg_paths.run_dir));
- } else if (!strcasecmp(v->name, "astmoddir")) {
- ast_copy_string(cfg_paths.module_dir, v->value, sizeof(cfg_paths.module_dir));
- } else if (!strcasecmp(v->name, "astsbindir")) {
- ast_copy_string(cfg_paths.sbin_dir, v->value, sizeof(cfg_paths.sbin_dir));
- }
- }
-
- /* Combine astrundir and astctl settings. */
- snprintf(cfg_paths.socket_path, sizeof(cfg_paths.socket_path), "%s/%s",
- ast_config_AST_RUN_DIR, ast_config_AST_CTL);
-
- for (v = ast_variable_browse(cfg, "options"); v; v = v->next) {
- /* verbose level (-v at startup) */
- if (!strcasecmp(v->name, "verbose")) {
- option_verbose_new = atoi(v->value);
- /* whether or not to force timestamping in CLI verbose output. (-T at startup) */
- } else if (!strcasecmp(v->name, "timestamp")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TIMESTAMP);
- /* whether or not to support #exec in config files */
- } else if (!strcasecmp(v->name, "execincludes")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_EXEC_INCLUDES);
- /* debug level (-d at startup) */
- } else if (!strcasecmp(v->name, "debug")) {
- option_debug_new = 0;
- if (sscanf(v->value, "%30d", &option_debug_new) != 1) {
- option_debug_new = ast_true(v->value) ? 1 : 0;
- }
- } else if (!strcasecmp(v->name, "refdebug")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_REF_DEBUG);
-#if HAVE_WORKING_FORK
- /* Disable forking (-f at startup) */
- } else if (!strcasecmp(v->name, "nofork")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_FORK);
- /* Always fork, even if verbose or debug are enabled (-F at startup) */
- } else if (!strcasecmp(v->name, "alwaysfork")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_ALWAYS_FORK);
-#endif
- /* Run quietly (-q at startup ) */
- } else if (!strcasecmp(v->name, "quiet")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_QUIET);
- /* Run as console (-c at startup, implies nofork) */
- } else if (!strcasecmp(v->name, "console")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_CONSOLE);
- /* Run with high priority if the O/S permits (-p at startup) */
- } else if (!strcasecmp(v->name, "highpriority")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIGH_PRIORITY);
- /* Initialize RSA auth keys (IAX2) (-i at startup) */
- } else if (!strcasecmp(v->name, "initcrypto")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INIT_KEYS);
- /* Disable ANSI colors for console (-c at startup) */
- } else if (!strcasecmp(v->name, "nocolor")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_COLOR);
- /* Disable some usage warnings for picky people :p */
- } else if (!strcasecmp(v->name, "dontwarn")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DONT_WARN);
- /* Dump core in case of crash (-g) */
- } else if (!strcasecmp(v->name, "dumpcore")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DUMP_CORE);
- /* Cache recorded sound files to another directory during recording */
- } else if (!strcasecmp(v->name, "cache_record_files")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_RECORD_FILES);
-#if !defined(LOW_MEMORY)
- /* Cache media frames for performance */
- } else if (!strcasecmp(v->name, "cache_media_frames")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_MEDIA_FRAMES);
-#endif
- /* Specify cache directory */
- } else if (!strcasecmp(v->name, "record_cache_dir")) {
- ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN);
- /* Build transcode paths via SLINEAR, instead of directly */
- } else if (!strcasecmp(v->name, "transcode_via_sln")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSCODE_VIA_SLIN);
- /* Transmit SLINEAR silence while a channel is being recorded or DTMF is being generated on a channel */
- } else if (!strcasecmp(v->name, "transmit_silence_during_record") || !strcasecmp(v->name, "transmit_silence")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSMIT_SILENCE);
- /* Enable internal timing */
- } else if (!strcasecmp(v->name, "internal_timing")) {
- if (!ast_opt_remote) {
- fprintf(stderr,
- "NOTICE: The internal_timing option is no longer needed.\n"
- " It will always be enabled if you have a timing module loaded.\n");
- }
- } else if (!strcasecmp(v->name, "mindtmfduration")) {
- if (sscanf(v->value, "%30u", &option_dtmfminduration) != 1) {
- option_dtmfminduration = AST_MIN_DTMF_DURATION;
- }
- } else if (!strcasecmp(v->name, "rtp_use_dynamic")) {
- ast_option_rtpusedynamic = ast_true(v->value);
- /* http://www.iana.org/assignments/rtp-parameters
- * RTP dynamic payload types start at 96 normally; extend down to 0 */
- } else if (!strcasecmp(v->name, "rtp_pt_dynamic")) {
- ast_parse_arg(v->value, PARSE_UINT32|PARSE_IN_RANGE,
- &ast_option_rtpptdynamic, 0, AST_RTP_PT_FIRST_DYNAMIC);
- } else if (!strcasecmp(v->name, "maxcalls")) {
- if ((sscanf(v->value, "%30d", &ast_option_maxcalls) != 1) || (ast_option_maxcalls < 0)) {
- ast_option_maxcalls = 0;
- }
- } else if (!strcasecmp(v->name, "maxload")) {
- double test[1];
-
- if (getloadavg(test, 1) == -1) {
- ast_log(LOG_ERROR, "Cannot obtain load average on this system. 'maxload' option disabled.\n");
- ast_option_maxload = 0.0;
- } else if ((sscanf(v->value, "%30lf", &ast_option_maxload) != 1) || (ast_option_maxload < 0.0)) {
- ast_option_maxload = 0.0;
- }
- /* Set the maximum amount of open files */
- } else if (!strcasecmp(v->name, "maxfiles")) {
- ast_option_maxfiles = atoi(v->value);
- if (!ast_opt_remote) {
- set_ulimit(ast_option_maxfiles);
- }
- /* What user to run as */
- } else if (!strcasecmp(v->name, "runuser")) {
- ast_copy_string(cfg_paths.run_user, v->value, sizeof(cfg_paths.run_user));
- /* What group to run as */
- } else if (!strcasecmp(v->name, "rungroup")) {
- ast_copy_string(cfg_paths.run_group, v->value, sizeof(cfg_paths.run_group));
- } else if (!strcasecmp(v->name, "systemname")) {
- ast_copy_string(cfg_paths.system_name, v->value, sizeof(cfg_paths.system_name));
- } else if (!strcasecmp(v->name, "autosystemname")) {
- if (ast_true(v->value)) {
- if (!gethostname(hostname, sizeof(hostname) - 1))
- ast_copy_string(cfg_paths.system_name, hostname, sizeof(cfg_paths.system_name));
- else {
- if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)){
- ast_copy_string(cfg_paths.system_name, "localhost", sizeof(cfg_paths.system_name));
- }
- ast_log(LOG_ERROR, "Cannot obtain hostname for this system. Using '%s' instead.\n", ast_config_AST_SYSTEM_NAME);
- }
- }
- } else if (!strcasecmp(v->name, "languageprefix")) {
- ast_language_is_prefix = ast_true(v->value);
- } else if (!strcasecmp(v->name, "defaultlanguage")) {
- ast_copy_string(ast_defaultlanguage, v->value, MAX_LANGUAGE);
- } else if (!strcasecmp(v->name, "lockmode")) {
- if (!strcasecmp(v->value, "lockfile")) {
- ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
- } else if (!strcasecmp(v->value, "flock")) {
- ast_set_lock_type(AST_LOCK_TYPE_FLOCK);
- } else {
- ast_log(LOG_WARNING, "'%s' is not a valid setting for the lockmode option, "
- "defaulting to 'lockfile'\n", v->value);
- ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
- }
-#if defined(HAVE_SYSINFO)
- } else if (!strcasecmp(v->name, "minmemfree")) {
- /* specify the minimum amount of free memory to retain. Asterisk should stop accepting new calls
- * if the amount of free memory falls below this watermark */
- if ((sscanf(v->value, "%30ld", &option_minmemfree) != 1) || (option_minmemfree < 0)) {
- option_minmemfree = 0;
- }
-#endif
- } else if (!strcasecmp(v->name, "entityid")) {
- struct ast_eid tmp_eid;
- if (!ast_str_to_eid(&tmp_eid, v->value)) {
- ast_eid_default = tmp_eid;
- } else {
- ast_log(LOG_WARNING, "Invalid Entity ID '%s' provided\n", v->value);
- }
- } else if (!strcasecmp(v->name, "lightbackground")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_LIGHT_BACKGROUND);
- } else if (!strcasecmp(v->name, "forceblackbackground")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_FORCE_BLACK_BACKGROUND);
- } else if (!strcasecmp(v->name, "hideconnect")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIDE_CONSOLE_CONNECT);
- } else if (!strcasecmp(v->name, "lockconfdir")) {
- ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_LOCK_CONFIG_DIR);
- } else if (!strcasecmp(v->name, "stdexten")) {
- /* Choose how to invoke the extensions.conf stdexten */
- if (!strcasecmp(v->value, "gosub")) {
- ast_clear_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
- } else if (!strcasecmp(v->value, "macro")) {
- ast_set_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
- } else {
- ast_log(LOG_WARNING,
- "'%s' is not a valid setting for the stdexten option, defaulting to 'gosub'\n",
- v->value);
- ast_clear_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
- }
- } else if (!strcasecmp(v->name, "live_dangerously")) {
- live_dangerously = ast_true(v->value);
- }
- }
- if (!ast_opt_remote) {
- pbx_live_dangerously(live_dangerously);
- }
-
- option_debug += option_debug_new;
- option_verbose += option_verbose_new;
-
- ast_config_destroy(cfg);
-}
-
static void read_pjproject_startup_options(void)
{
struct ast_config *cfg;
@@ -3915,9 +3506,6 @@ int main(int argc, char *argv[])
}
ast_mainpid = getpid();
- /* Set config file to default before checking arguments for override. */
- ast_copy_string(cfg_paths.config_file, DEFAULT_CONFIG_FILE, sizeof(cfg_paths.config_file));
-
/* Process command-line options that effect asterisk.conf load. */
while ((c = getopt(argc, argv, getopt_settings)) != -1) {
switch (c) {
@@ -3925,7 +3513,7 @@ int main(int argc, char *argv[])
ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES);
break;
case 'C':
- ast_copy_string(cfg_paths.config_file, optarg, sizeof(cfg_paths.config_file));
+ set_asterisk_conf_path(optarg);
break;
case 'd':
option_debug++;
@@ -3954,7 +3542,7 @@ int main(int argc, char *argv[])
/* Initialize env so it is available if #exec is used in asterisk.conf. */
env_init();
- ast_readconfig();
+ load_asterisk_conf();
/* Update env to include any systemname that was set. */
env_init();
@@ -4047,7 +3635,7 @@ int main(int argc, char *argv[])
break;
case 's':
if (ast_opt_remote) {
- ast_copy_string((char *) cfg_paths.socket_path, optarg, sizeof(cfg_paths.socket_path));
+ set_socket_path(optarg);
}
break;
case 'T':
diff --git a/main/bridge.c b/main/bridge.c
index 1109c4b76..2b347fd3f 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -3850,6 +3850,24 @@ void ast_bridge_set_video_update_discard(struct ast_bridge *bridge, unsigned int
ast_bridge_unlock(bridge);
}
+void ast_bridge_set_remb_send_interval(struct ast_bridge *bridge, unsigned int remb_send_interval)
+{
+ ast_assert(bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU);
+
+ ast_bridge_lock(bridge);
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_send_interval = remb_send_interval;
+ ast_bridge_unlock(bridge);
+}
+
+void ast_brige_set_remb_behavior(struct ast_bridge *bridge, enum ast_bridge_video_sfu_remb_behavior behavior)
+{
+ ast_assert(bridge->softmix.video_mode.mode == AST_BRIDGE_VIDEO_MODE_SFU);
+
+ ast_bridge_lock(bridge);
+ bridge->softmix.video_mode.mode_data.sfu_data.remb_behavior = behavior;
+ ast_bridge_unlock(bridge);
+}
+
void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyframe)
{
struct ast_bridge_video_talker_src_data *data;
@@ -4420,7 +4438,7 @@ static void set_transfer_variables_all(struct ast_channel *transferer, struct ao
ao2_iterator_destroy(&iter);
}
-static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
+struct ast_bridge *ast_bridge_transfer_acquire_bridge(struct ast_channel *chan)
{
struct ast_bridge *bridge;
@@ -4461,7 +4479,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
return AST_BRIDGE_TRANSFER_FAIL;
}
- bridge = acquire_bridge(transferer);
+ bridge = ast_bridge_transfer_acquire_bridge(transferer);
if (!bridge) {
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
@@ -4708,8 +4726,8 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
const char *app = NULL;
int hangup_target = 0;
- to_transferee_bridge = acquire_bridge(to_transferee);
- to_target_bridge = acquire_bridge(to_transfer_target);
+ to_transferee_bridge = ast_bridge_transfer_acquire_bridge(to_transferee);
+ to_target_bridge = ast_bridge_transfer_acquire_bridge(to_transfer_target);
transfer_msg = ast_attended_transfer_message_create(1, to_transferee, to_transferee_bridge,
to_transfer_target, to_target_bridge, NULL, NULL);
diff --git a/main/options.c b/main/options.c
new file mode 100644
index 000000000..f6a4e8fd0
--- /dev/null
+++ b/main/options.c
@@ -0,0 +1,475 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2018, CFWare, LLC
+ *
+ * Corey Farrell <git@cfware.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 Symbols related to asterisk.conf options and paths.
+ *
+ * \author Corey Farrell <git@cfware.com>
+ */
+
+/*** MODULEINFO
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+#include "asterisk/_private.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
+#include "asterisk/paths.h"
+#include "asterisk/pbx.h"
+#include "asterisk/rtp_engine.h"
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+
+#include "../defaults.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+
+/*! Default minimum DTMF digit length - 80ms */
+#define AST_MIN_DTMF_DURATION 80
+
+#define DEFAULT_MONITOR_DIR DEFAULT_SPOOL_DIR "/monitor"
+#define DEFAULT_RECORDING_DIR DEFAULT_SPOOL_DIR "/recording"
+
+/*! \defgroup main_options Main Configuration Options
+ * \brief Main configuration options from asterisk.conf or OS command line on starting Asterisk.
+ * \arg \ref Config_ast "asterisk.conf"
+ * \note Some of them can be changed in the CLI
+ */
+/*! @{ */
+
+struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
+
+/*! Maximum active system verbosity level. */
+int ast_verb_sys_level;
+
+/*! Verbosity level */
+int option_verbose;
+/*! Debug level */
+int option_debug;
+/*! Default to -1 to know if we have read the level from pjproject yet. */
+int ast_pjproject_max_log_level = -1;
+int ast_option_pjproject_log_level;
+int ast_option_pjproject_cache_pools;
+/*! Max load avg on system */
+double ast_option_maxload;
+/*! Max number of active calls */
+int ast_option_maxcalls;
+/*! Max number of open file handles (files, sockets) */
+int ast_option_maxfiles;
+/*! Minimum duration of DTMF. */
+unsigned int option_dtmfminduration = AST_MIN_DTMF_DURATION;
+#if defined(HAVE_SYSINFO)
+/*! Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
+long option_minmemfree;
+#endif
+int ast_option_rtpusedynamic = 1;
+unsigned int ast_option_rtpptdynamic = 35;
+
+/*! @} */
+
+struct ast_eid ast_eid_default;
+
+/* XXX tmpdir is a subdir of the spool directory, and no way to remap it */
+char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
+
+char ast_defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
+
+struct _cfg_paths {
+ char config_dir[PATH_MAX];
+ char module_dir[PATH_MAX];
+ char spool_dir[PATH_MAX];
+ char monitor_dir[PATH_MAX];
+ char recording_dir[PATH_MAX];
+ char var_dir[PATH_MAX];
+ char data_dir[PATH_MAX];
+ char log_dir[PATH_MAX];
+ char agi_dir[PATH_MAX];
+ char run_dir[PATH_MAX];
+ char key_dir[PATH_MAX];
+
+ char config_file[PATH_MAX];
+ char db_path[PATH_MAX];
+ char sbin_dir[PATH_MAX];
+ char pid_path[PATH_MAX];
+ char socket_path[PATH_MAX];
+ char run_user[PATH_MAX];
+ char run_group[PATH_MAX];
+ char system_name[128];
+ char ctl_perms[PATH_MAX];
+ char ctl_owner[PATH_MAX];
+ char ctl_group[PATH_MAX];
+ char ctl_file[PATH_MAX];
+};
+
+static struct _cfg_paths cfg_paths = {
+ .config_dir = DEFAULT_CONFIG_DIR,
+ .module_dir = DEFAULT_MODULE_DIR,
+ .spool_dir = DEFAULT_SPOOL_DIR,
+ .monitor_dir = DEFAULT_MONITOR_DIR,
+ .recording_dir = DEFAULT_RECORDING_DIR,
+ .var_dir = DEFAULT_VAR_DIR,
+ .data_dir = DEFAULT_DATA_DIR,
+ .log_dir = DEFAULT_LOG_DIR,
+ .agi_dir = DEFAULT_AGI_DIR,
+ .run_dir = DEFAULT_RUN_DIR,
+ .key_dir = DEFAULT_KEY_DIR,
+
+ .config_file = DEFAULT_CONFIG_FILE,
+ .db_path = DEFAULT_DB,
+ .sbin_dir = DEFAULT_SBIN_DIR,
+ .pid_path = DEFAULT_PID,
+ .socket_path = DEFAULT_SOCKET,
+ .ctl_file = "asterisk.ctl",
+};
+
+const char *ast_config_AST_CONFIG_DIR = cfg_paths.config_dir;
+const char *ast_config_AST_CONFIG_FILE = cfg_paths.config_file;
+const char *ast_config_AST_MODULE_DIR = cfg_paths.module_dir;
+const char *ast_config_AST_SPOOL_DIR = cfg_paths.spool_dir;
+const char *ast_config_AST_MONITOR_DIR = cfg_paths.monitor_dir;
+const char *ast_config_AST_RECORDING_DIR = cfg_paths.recording_dir;
+const char *ast_config_AST_VAR_DIR = cfg_paths.var_dir;
+const char *ast_config_AST_DATA_DIR = cfg_paths.data_dir;
+const char *ast_config_AST_LOG_DIR = cfg_paths.log_dir;
+const char *ast_config_AST_AGI_DIR = cfg_paths.agi_dir;
+const char *ast_config_AST_KEY_DIR = cfg_paths.key_dir;
+const char *ast_config_AST_RUN_DIR = cfg_paths.run_dir;
+const char *ast_config_AST_SBIN_DIR = cfg_paths.sbin_dir;
+
+const char *ast_config_AST_DB = cfg_paths.db_path;
+const char *ast_config_AST_PID = cfg_paths.pid_path;
+const char *ast_config_AST_SOCKET = cfg_paths.socket_path;
+const char *ast_config_AST_RUN_USER = cfg_paths.run_user;
+const char *ast_config_AST_RUN_GROUP = cfg_paths.run_group;
+const char *ast_config_AST_SYSTEM_NAME = cfg_paths.system_name;
+
+const char *ast_config_AST_CTL_PERMISSIONS = cfg_paths.ctl_perms;
+const char *ast_config_AST_CTL_OWNER = cfg_paths.ctl_owner;
+const char *ast_config_AST_CTL_GROUP = cfg_paths.ctl_group;
+const char *ast_config_AST_CTL = cfg_paths.ctl_file;
+
+/*! \brief Set maximum open files */
+static void set_ulimit(int value)
+{
+ struct rlimit l = {0, 0};
+
+ if (value <= 0) {
+ ast_log(LOG_WARNING, "Unable to change max files open to invalid value %i\n",value);
+ return;
+ }
+
+ l.rlim_cur = value;
+ l.rlim_max = value;
+
+ if (setrlimit(RLIMIT_NOFILE, &l)) {
+ ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n",strerror(errno));
+ return;
+ }
+
+ ast_log(LOG_NOTICE, "Setting max files open to %d\n",value);
+
+ return;
+}
+
+void set_asterisk_conf_path(const char *path)
+{
+ ast_copy_string(cfg_paths.config_file, path, sizeof(cfg_paths.config_file));
+}
+
+void set_socket_path(const char *path)
+{
+ ast_copy_string(cfg_paths.socket_path, path, sizeof(cfg_paths.socket_path));
+}
+
+void load_asterisk_conf(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ char hostname[MAXHOSTNAMELEN] = "";
+ struct ast_flags config_flags = { CONFIG_FLAG_NOREALTIME };
+ struct {
+ unsigned int dbdir:1;
+ unsigned int keydir:1;
+ } found = { 0, 0 };
+ /* Default to false for security */
+ int live_dangerously = 0;
+ int option_debug_new = 0;
+ int option_verbose_new = 0;
+
+ /* init with buildtime config */
+#ifdef REF_DEBUG
+ /* The REF_DEBUG compiler flag is now only used to enable refdebug by default.
+ * Support for debugging reference counts is always compiled in. */
+ ast_set2_flag(&ast_options, 1, AST_OPT_FLAG_REF_DEBUG);
+#endif
+
+ ast_set_default_eid(&ast_eid_default);
+
+ cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
+
+ /* If AST_OPT_FLAG_EXEC_INCLUDES was previously enabled with -X turn it off now.
+ * Using #exec from other configs requires that it be enabled from asterisk.conf. */
+ ast_clear_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES);
+
+ /* no asterisk.conf? no problem, use buildtime config! */
+ if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
+ fprintf(stderr, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
+ return;
+ }
+
+ for (v = ast_variable_browse(cfg, "files"); v; v = v->next) {
+ if (!strcasecmp(v->name, "astctlpermissions")) {
+ ast_copy_string(cfg_paths.ctl_perms, v->value, sizeof(cfg_paths.ctl_perms));
+ } else if (!strcasecmp(v->name, "astctlowner")) {
+ ast_copy_string(cfg_paths.ctl_owner, v->value, sizeof(cfg_paths.ctl_owner));
+ } else if (!strcasecmp(v->name, "astctlgroup")) {
+ ast_copy_string(cfg_paths.ctl_group, v->value, sizeof(cfg_paths.ctl_group));
+ } else if (!strcasecmp(v->name, "astctl")) {
+ ast_copy_string(cfg_paths.ctl_file, v->value, sizeof(cfg_paths.ctl_file));
+ }
+ }
+
+ for (v = ast_variable_browse(cfg, "directories"); v; v = v->next) {
+ if (!strcasecmp(v->name, "astetcdir")) {
+ ast_copy_string(cfg_paths.config_dir, v->value, sizeof(cfg_paths.config_dir));
+ } else if (!strcasecmp(v->name, "astspooldir")) {
+ ast_copy_string(cfg_paths.spool_dir, v->value, sizeof(cfg_paths.spool_dir));
+ snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", v->value);
+ snprintf(cfg_paths.recording_dir, sizeof(cfg_paths.recording_dir), "%s/recording", v->value);
+ } else if (!strcasecmp(v->name, "astvarlibdir")) {
+ ast_copy_string(cfg_paths.var_dir, v->value, sizeof(cfg_paths.var_dir));
+ if (!found.dbdir) {
+ snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
+ }
+ } else if (!strcasecmp(v->name, "astdbdir")) {
+ snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
+ found.dbdir = 1;
+ } else if (!strcasecmp(v->name, "astdatadir")) {
+ ast_copy_string(cfg_paths.data_dir, v->value, sizeof(cfg_paths.data_dir));
+ if (!found.keydir) {
+ snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
+ }
+ } else if (!strcasecmp(v->name, "astkeydir")) {
+ snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
+ found.keydir = 1;
+ } else if (!strcasecmp(v->name, "astlogdir")) {
+ ast_copy_string(cfg_paths.log_dir, v->value, sizeof(cfg_paths.log_dir));
+ } else if (!strcasecmp(v->name, "astagidir")) {
+ ast_copy_string(cfg_paths.agi_dir, v->value, sizeof(cfg_paths.agi_dir));
+ } else if (!strcasecmp(v->name, "astrundir")) {
+ snprintf(cfg_paths.pid_path, sizeof(cfg_paths.pid_path), "%s/%s", v->value, "asterisk.pid");
+ ast_copy_string(cfg_paths.run_dir, v->value, sizeof(cfg_paths.run_dir));
+ } else if (!strcasecmp(v->name, "astmoddir")) {
+ ast_copy_string(cfg_paths.module_dir, v->value, sizeof(cfg_paths.module_dir));
+ } else if (!strcasecmp(v->name, "astsbindir")) {
+ ast_copy_string(cfg_paths.sbin_dir, v->value, sizeof(cfg_paths.sbin_dir));
+ }
+ }
+
+ /* Combine astrundir and astctl settings. */
+ snprintf(cfg_paths.socket_path, sizeof(cfg_paths.socket_path), "%s/%s",
+ ast_config_AST_RUN_DIR, ast_config_AST_CTL);
+
+ for (v = ast_variable_browse(cfg, "options"); v; v = v->next) {
+ /* verbose level (-v at startup) */
+ if (!strcasecmp(v->name, "verbose")) {
+ option_verbose_new = atoi(v->value);
+ /* whether or not to force timestamping in CLI verbose output. (-T at startup) */
+ } else if (!strcasecmp(v->name, "timestamp")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TIMESTAMP);
+ /* whether or not to support #exec in config files */
+ } else if (!strcasecmp(v->name, "execincludes")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_EXEC_INCLUDES);
+ /* debug level (-d at startup) */
+ } else if (!strcasecmp(v->name, "debug")) {
+ option_debug_new = 0;
+ if (sscanf(v->value, "%30d", &option_debug_new) != 1) {
+ option_debug_new = ast_true(v->value) ? 1 : 0;
+ }
+ } else if (!strcasecmp(v->name, "refdebug")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_REF_DEBUG);
+#if HAVE_WORKING_FORK
+ /* Disable forking (-f at startup) */
+ } else if (!strcasecmp(v->name, "nofork")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_FORK);
+ /* Always fork, even if verbose or debug are enabled (-F at startup) */
+ } else if (!strcasecmp(v->name, "alwaysfork")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_ALWAYS_FORK);
+#endif
+ /* Run quietly (-q at startup ) */
+ } else if (!strcasecmp(v->name, "quiet")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_QUIET);
+ /* Run as console (-c at startup, implies nofork) */
+ } else if (!strcasecmp(v->name, "console")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_CONSOLE);
+ /* Run with high priority if the O/S permits (-p at startup) */
+ } else if (!strcasecmp(v->name, "highpriority")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIGH_PRIORITY);
+ /* Initialize RSA auth keys (IAX2) (-i at startup) */
+ } else if (!strcasecmp(v->name, "initcrypto")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INIT_KEYS);
+ /* Disable ANSI colors for console (-c at startup) */
+ } else if (!strcasecmp(v->name, "nocolor")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_COLOR);
+ /* Disable some usage warnings for picky people :p */
+ } else if (!strcasecmp(v->name, "dontwarn")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DONT_WARN);
+ /* Dump core in case of crash (-g) */
+ } else if (!strcasecmp(v->name, "dumpcore")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DUMP_CORE);
+ /* Cache recorded sound files to another directory during recording */
+ } else if (!strcasecmp(v->name, "cache_record_files")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_RECORD_FILES);
+#if !defined(LOW_MEMORY)
+ /* Cache media frames for performance */
+ } else if (!strcasecmp(v->name, "cache_media_frames")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_MEDIA_FRAMES);
+#endif
+ /* Specify cache directory */
+ } else if (!strcasecmp(v->name, "record_cache_dir")) {
+ ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN);
+ /* Build transcode paths via SLINEAR, instead of directly */
+ } else if (!strcasecmp(v->name, "transcode_via_sln")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSCODE_VIA_SLIN);
+ /* Transmit SLINEAR silence while a channel is being recorded or DTMF is being generated on a channel */
+ } else if (!strcasecmp(v->name, "transmit_silence_during_record") || !strcasecmp(v->name, "transmit_silence")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSMIT_SILENCE);
+ /* Enable internal timing */
+ } else if (!strcasecmp(v->name, "internal_timing")) {
+ if (!ast_opt_remote) {
+ fprintf(stderr,
+ "NOTICE: The internal_timing option is no longer needed.\n"
+ " It will always be enabled if you have a timing module loaded.\n");
+ }
+ } else if (!strcasecmp(v->name, "mindtmfduration")) {
+ if (sscanf(v->value, "%30u", &option_dtmfminduration) != 1) {
+ option_dtmfminduration = AST_MIN_DTMF_DURATION;
+ }
+ } else if (!strcasecmp(v->name, "rtp_use_dynamic")) {
+ ast_option_rtpusedynamic = ast_true(v->value);
+ /* http://www.iana.org/assignments/rtp-parameters
+ * RTP dynamic payload types start at 96 normally; extend down to 0 */
+ } else if (!strcasecmp(v->name, "rtp_pt_dynamic")) {
+ ast_parse_arg(v->value, PARSE_UINT32|PARSE_IN_RANGE,
+ &ast_option_rtpptdynamic, 0, AST_RTP_PT_FIRST_DYNAMIC);
+ } else if (!strcasecmp(v->name, "maxcalls")) {
+ if ((sscanf(v->value, "%30d", &ast_option_maxcalls) != 1) || (ast_option_maxcalls < 0)) {
+ ast_option_maxcalls = 0;
+ }
+ } else if (!strcasecmp(v->name, "maxload")) {
+ double test[1];
+
+ if (getloadavg(test, 1) == -1) {
+ ast_log(LOG_ERROR, "Cannot obtain load average on this system. 'maxload' option disabled.\n");
+ ast_option_maxload = 0.0;
+ } else if ((sscanf(v->value, "%30lf", &ast_option_maxload) != 1) || (ast_option_maxload < 0.0)) {
+ ast_option_maxload = 0.0;
+ }
+ /* Set the maximum amount of open files */
+ } else if (!strcasecmp(v->name, "maxfiles")) {
+ ast_option_maxfiles = atoi(v->value);
+ if (!ast_opt_remote) {
+ set_ulimit(ast_option_maxfiles);
+ }
+ /* What user to run as */
+ } else if (!strcasecmp(v->name, "runuser")) {
+ ast_copy_string(cfg_paths.run_user, v->value, sizeof(cfg_paths.run_user));
+ /* What group to run as */
+ } else if (!strcasecmp(v->name, "rungroup")) {
+ ast_copy_string(cfg_paths.run_group, v->value, sizeof(cfg_paths.run_group));
+ } else if (!strcasecmp(v->name, "systemname")) {
+ ast_copy_string(cfg_paths.system_name, v->value, sizeof(cfg_paths.system_name));
+ } else if (!strcasecmp(v->name, "autosystemname")) {
+ if (ast_true(v->value)) {
+ if (!gethostname(hostname, sizeof(hostname) - 1)) {
+ ast_copy_string(cfg_paths.system_name, hostname, sizeof(cfg_paths.system_name));
+ } else {
+ if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)){
+ ast_copy_string(cfg_paths.system_name, "localhost", sizeof(cfg_paths.system_name));
+ }
+ ast_log(LOG_ERROR, "Cannot obtain hostname for this system. Using '%s' instead.\n", ast_config_AST_SYSTEM_NAME);
+ }
+ }
+ } else if (!strcasecmp(v->name, "languageprefix")) {
+ ast_language_is_prefix = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "defaultlanguage")) {
+ ast_copy_string(ast_defaultlanguage, v->value, MAX_LANGUAGE);
+ } else if (!strcasecmp(v->name, "lockmode")) {
+ if (!strcasecmp(v->value, "lockfile")) {
+ ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+ } else if (!strcasecmp(v->value, "flock")) {
+ ast_set_lock_type(AST_LOCK_TYPE_FLOCK);
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a valid setting for the lockmode option, "
+ "defaulting to 'lockfile'\n", v->value);
+ ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+ }
+#if defined(HAVE_SYSINFO)
+ } else if (!strcasecmp(v->name, "minmemfree")) {
+ /* specify the minimum amount of free memory to retain. Asterisk should stop accepting new calls
+ * if the amount of free memory falls below this watermark */
+ if ((sscanf(v->value, "%30ld", &option_minmemfree) != 1) || (option_minmemfree < 0)) {
+ option_minmemfree = 0;
+ }
+#endif
+ } else if (!strcasecmp(v->name, "entityid")) {
+ struct ast_eid tmp_eid;
+ if (!ast_str_to_eid(&tmp_eid, v->value)) {
+ ast_eid_default = tmp_eid;
+ } else {
+ ast_log(LOG_WARNING, "Invalid Entity ID '%s' provided\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "lightbackground")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_LIGHT_BACKGROUND);
+ } else if (!strcasecmp(v->name, "forceblackbackground")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_FORCE_BLACK_BACKGROUND);
+ } else if (!strcasecmp(v->name, "hideconnect")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIDE_CONSOLE_CONNECT);
+ } else if (!strcasecmp(v->name, "lockconfdir")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_LOCK_CONFIG_DIR);
+ } else if (!strcasecmp(v->name, "stdexten")) {
+ /* Choose how to invoke the extensions.conf stdexten */
+ if (!strcasecmp(v->value, "gosub")) {
+ ast_clear_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
+ } else if (!strcasecmp(v->value, "macro")) {
+ ast_set_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
+ } else {
+ ast_log(LOG_WARNING,
+ "'%s' is not a valid setting for the stdexten option, defaulting to 'gosub'\n",
+ v->value);
+ ast_clear_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
+ }
+ } else if (!strcasecmp(v->name, "live_dangerously")) {
+ live_dangerously = ast_true(v->value);
+ }
+ }
+ if (!ast_opt_remote) {
+ pbx_live_dangerously(live_dangerously);
+ }
+
+ option_debug += option_debug_new;
+ option_verbose += option_verbose_new;
+
+ ast_config_destroy(cfg);
+}
diff --git a/makeopts.in b/makeopts.in
index 4d3cc5db6..1063316dd 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -143,12 +143,6 @@ CODEC2_LIB=@CODEC2_LIB@
CURL_INCLUDE=@CURL_INCLUDE@
CURL_LIB=@CURL_LIB@
-CURSES_INCLUDE=@CURSES_INCLUDE@
-CURSES_LIB=@CURSES_LIB@
-CURSES_DIR=@CURSES_DIR@
-
-EDITLINE_LIB=@EDITLINE_LIB@
-
FREETDS_INCLUDE=@FREETDS_INCLUDE@
FREETDS_LIB=@FREETDS_LIB@
@@ -210,10 +204,6 @@ MYSQLCLIENT_LIB=@MYSQLCLIENT_LIB@
NBS_INCLUDE=@NBS_INCLUDE@
NBS_LIB=@NBS_LIB@
-NCURSES_INCLUDE=@NCURSES_INCLUDE@
-NCURSES_LIB=@NCURSES_LIB@
-NCURSES_DIR=@NCURSES_DIR@
-
NEON_INCLUDE=@NEON_INCLUDE@
NEON_LIB=@NEON_LIB@
NEON29_INCLUDE=@NEON_INCLUDE@
@@ -364,20 +354,12 @@ CAP_INCLUDE=@CAP_INCLUDE@
BKTR_INCLUDE=@BKTR_INCLUDE@
BKTR_LIB=@BKTR_LIB@
-TERMCAP_INCLUDE=@TERMCAP_INCLUDE@
-TERMCAP_LIB=@TERMCAP_LIB@
-TERMCAP_DIR=@TERMCAP_DIR@
-
LIBXML2_INCLUDE=@LIBXML2_INCLUDE@
LIBXML2_LIB=@LIBXML2_LIB@
LIBXSLT_INCLUDE=@LIBXSLT_INCLUDE@
LIBXSLT_LIB=@LIBXSLT_LIB@
-TINFO_INCLUDE=@TINFO_INCLUDE@
-TINFO_LIB=@TINFO_LIB@
-TINFO_DIR=@TINFO_DIR@
-
# if poll is not present, let the makefile know.
POLL_AVAILABLE=@HAS_POLL@
TIMERFD_INCLUDE=@TIMERFD_INCLUDE@
diff --git a/menuselect/configure b/menuselect/configure
index 6e5331edd..a0aa10928 100755
--- a/menuselect/configure
+++ b/menuselect/configure
@@ -4392,7 +4392,7 @@ fi
if test ! "x${CONFIG_LIBXML2}" = xNo; then
LIBXML2_INCLUDE=$(${CONFIG_LIBXML2} --cflags)
- LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g")
+ LIBXML2_INCLUDE=$(echo ${LIBXML2_INCLUDE} | $SED -e "s|-I|-I${LIBXML2_DIR}|g" -e "s|-std=c99||g")
LIBXML2_LIB=$(${CONFIG_LIBXML2} --libs)
LIBXML2_LIB=$(echo ${LIBXML2_LIB} | $SED -e "s|-L|-L${LIBXML2_DIR}|g")
@@ -4633,7 +4633,7 @@ else
$as_echo "yes" >&6; }
PBX_GTK2=1
- GTK2_INCLUDE="$GTK2_CFLAGS"
+ GTK2_INCLUDE=$(echo ${GTK2_CFLAGS} | $SED -e "s|-std=c99||g")
GTK2_LIB="$GTK2_LIBS"
$as_echo "#define HAVE_GTK2 1" >>confdefs.h
diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c
index 55b14c934..1c8728cf7 100644
--- a/res/res_musiconhold.c
+++ b/res/res_musiconhold.c
@@ -333,7 +333,6 @@ static int ast_moh_files_next(struct ast_channel *chan)
}
} else {
state->announcement = 0;
- state->samples = 0;
}
if (!state->class->total_files) {
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 935a5598e..19e6e1d13 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -1366,12 +1366,17 @@
If <literal>0</literal> no timeout. Time in fractional seconds.
</para></description>
</configOption>
- <configOption name="authenticate_qualify" default="no">
- <synopsis>Authenticates a qualify request if needed</synopsis>
- <description><para>
- If true and a qualify request receives a challenge or authenticate response
+ <configOption name="authenticate_qualify">
+ <synopsis>Authenticates a qualify challenge response if needed</synopsis>
+ <description>
+ <para>If true and a qualify request receives a challenge response then
authentication is attempted before declaring the contact available.
- </para></description>
+ </para>
+ <note><para>This option does nothing as we will always complete
+ the challenge response authentication if the qualify request is
+ challenged.
+ </para></note>
+ </description>
</configOption>
<configOption name="outbound_proxy">
<synopsis>Outbound proxy used when sending OPTIONS request</synopsis>
@@ -1565,12 +1570,17 @@
If <literal>0</literal> no timeout. Time in fractional seconds.
</para></description>
</configOption>
- <configOption name="authenticate_qualify" default="no">
- <synopsis>Authenticates a qualify request if needed</synopsis>
- <description><para>
- If true and a qualify request receives a challenge or authenticate response
+ <configOption name="authenticate_qualify">
+ <synopsis>Authenticates a qualify challenge response if needed</synopsis>
+ <description>
+ <para>If true and a qualify request receives a challenge response then
authentication is attempted before declaring the contact available.
- </para></description>
+ </para>
+ <note><para>This option does nothing as we will always complete
+ the challenge response authentication if the qualify request is
+ challenged.
+ </para></note>
+ </description>
</configOption>
<configOption name="outbound_proxy">
<synopsis>Outbound proxy used when sending OPTIONS request</synopsis>
@@ -2733,7 +2743,7 @@ static int register_service(void *data)
int ast_sip_register_service(pjsip_module *module)
{
- return ast_sip_push_task_synchronous(NULL, register_service, &module);
+ return ast_sip_push_task_wait_servant(NULL, register_service, &module);
}
static int unregister_service(void *data)
@@ -2749,7 +2759,7 @@ static int unregister_service(void *data)
void ast_sip_unregister_service(pjsip_module *module)
{
- ast_sip_push_task_synchronous(NULL, unregister_service, &module);
+ ast_sip_push_task_wait_servant(NULL, unregister_service, &module);
}
static struct ast_sip_authenticator *registered_authenticator;
@@ -2999,7 +3009,7 @@ static char *cli_dump_endpt(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
return CLI_SHOWUSAGE;
}
- ast_sip_push_task_synchronous(NULL, do_cli_dump_endpt, a);
+ ast_sip_push_task_wait_servant(NULL, do_cli_dump_endpt, a);
return CLI_SUCCESS;
}
@@ -3791,8 +3801,6 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
{
const pjsip_method *pmethod = get_pjsip_method(method);
- ast_assert(endpoint != NULL);
-
if (!pmethod) {
ast_log(LOG_WARNING, "Unknown method '%s'. Cannot send request\n", method);
return -1;
@@ -3801,6 +3809,7 @@ int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
if (dlg) {
return create_in_dialog_request(pmethod, dlg, tdata);
} else {
+ ast_assert(endpoint != NULL);
return create_out_of_dialog_request(pmethod, endpoint, uri, contact, tdata);
}
}
@@ -4475,21 +4484,30 @@ static int serializer_pool_setup(void)
return 0;
}
+static struct ast_taskprocessor *serializer_pool_pick(void)
+{
+ struct ast_taskprocessor *serializer;
+
+ unsigned int pos;
+
+ /*
+ * Pick a serializer to use from the pool.
+ *
+ * Note: We don't care about any reentrancy behavior
+ * when incrementing serializer_pool_pos. If it gets
+ * incorrectly incremented it doesn't matter.
+ */
+ pos = serializer_pool_pos++;
+ pos %= SERIALIZER_POOL_SIZE;
+ serializer = serializer_pool[pos];
+
+ return serializer;
+}
+
int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
{
if (!serializer) {
- unsigned int pos;
-
- /*
- * Pick a serializer to use from the pool.
- *
- * Note: We don't care about any reentrancy behavior
- * when incrementing serializer_pool_pos. If it gets
- * incorrectly incremented it doesn't matter.
- */
- pos = serializer_pool_pos++;
- pos %= SERIALIZER_POOL_SIZE;
- serializer = serializer_pool[pos];
+ serializer = serializer_pool_pick();
}
return ast_taskprocessor_push(serializer, sip_task, task_data);
@@ -4513,9 +4531,8 @@ static int sync_task(void *data)
/*
* Once we unlock std->lock after signaling, we cannot access
- * std again. The thread waiting within
- * ast_sip_push_task_synchronous() is free to continue and
- * release its local variable (std).
+ * std again. The thread waiting within ast_sip_push_task_wait()
+ * is free to continue and release its local variable (std).
*/
ast_mutex_lock(&std->lock);
std->complete = 1;
@@ -4525,15 +4542,11 @@ static int sync_task(void *data)
return ret;
}
-int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+static int ast_sip_push_task_wait(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
{
/* This method is an onion */
struct sync_task_data std;
- if (ast_sip_thread_is_servant()) {
- return sip_task(task_data);
- }
-
memset(&std, 0, sizeof(std));
ast_mutex_init(&std.lock);
ast_cond_init(&std.cond, NULL);
@@ -4557,6 +4570,42 @@ int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*si
return std.fail;
}
+int ast_sip_push_task_wait_servant(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+{
+ if (ast_sip_thread_is_servant()) {
+ return sip_task(task_data);
+ }
+
+ return ast_sip_push_task_wait(serializer, sip_task, task_data);
+}
+
+int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+{
+ return ast_sip_push_task_wait_servant(serializer, sip_task, task_data);
+}
+
+int ast_sip_push_task_wait_serializer(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data)
+{
+ if (!serializer) {
+ /* Caller doesn't care which PJSIP serializer the task executes under. */
+ serializer = serializer_pool_pick();
+ if (!serializer) {
+ /* No serializer picked to execute the task */
+ return -1;
+ }
+ }
+ if (ast_taskprocessor_is_task(serializer)) {
+ /*
+ * We are the requested serializer so we must execute
+ * the task now or deadlock waiting on ourself to
+ * execute it.
+ */
+ return sip_task(task_data);
+ }
+
+ return ast_sip_push_task_wait(serializer, sip_task, task_data);
+}
+
void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
{
size_t chars_to_copy = MIN(size - 1, pj_strlen(src));
@@ -5182,7 +5231,7 @@ static int reload_module(void)
* We must wait for the reload to complete so multiple
* reloads cannot happen at the same time.
*/
- if (ast_sip_push_task_synchronous(NULL, reload_configuration_task, NULL)) {
+ if (ast_sip_push_task_wait_servant(NULL, reload_configuration_task, NULL)) {
ast_log(LOG_WARNING, "Failed to reload PJSIP\n");
return -1;
}
@@ -5199,7 +5248,7 @@ static int unload_module(void)
/* The thread this is called from cannot call PJSIP/PJLIB functions,
* so we have to push the work to the threadpool to handle
*/
- ast_sip_push_task_synchronous(NULL, unload_pjsip, NULL);
+ ast_sip_push_task_wait_servant(NULL, unload_pjsip, NULL);
ast_sip_destroy_scheduler();
serializer_pool_shutdown();
ast_threadpool_shutdown(sip_threadpool);
diff --git a/res/res_pjsip/config_system.c b/res/res_pjsip/config_system.c
index dfd92404b..ed2b5d232 100644
--- a/res/res_pjsip/config_system.c
+++ b/res/res_pjsip/config_system.c
@@ -282,5 +282,5 @@ static int system_create_resolver_and_set_nameservers(void *data)
void ast_sip_initialize_dns(void)
{
- ast_sip_push_task_synchronous(NULL, system_create_resolver_and_set_nameservers, NULL);
+ ast_sip_push_task_wait_servant(NULL, system_create_resolver_and_set_nameservers, NULL);
}
diff --git a/res/res_pjsip/config_transport.c b/res/res_pjsip/config_transport.c
index 15c03769b..dd7c7049d 100644
--- a/res/res_pjsip/config_transport.c
+++ b/res/res_pjsip/config_transport.c
@@ -267,7 +267,7 @@ static void sip_transport_state_destroy(void *obj)
{
struct ast_sip_transport_state *state = obj;
- ast_sip_push_task_synchronous(NULL, destroy_sip_transport_state, state);
+ ast_sip_push_task_wait_servant(NULL, destroy_sip_transport_state, state);
}
/*! \brief Destructor for ast_sip_transport state information */
diff --git a/res/res_pjsip/pjsip_scheduler.c b/res/res_pjsip/pjsip_scheduler.c
index e4459da66..bbf666fd7 100644
--- a/res/res_pjsip/pjsip_scheduler.c
+++ b/res/res_pjsip/pjsip_scheduler.c
@@ -28,6 +28,7 @@
#include "asterisk/res_pjsip.h"
#include "include/res_pjsip_private.h"
#include "asterisk/res_pjsip_cli.h"
+#include "asterisk/taskprocessor.h"
#define TASK_BUCKETS 53
@@ -36,31 +37,31 @@ static struct ao2_container *tasks;
static int task_count;
struct ast_sip_sched_task {
- /*! ast_sip_sched task id */
- uint32_t task_id;
- /*! ast_sched scheudler id */
- int current_scheduler_id;
- /*! task is currently running */
- int is_running;
- /*! task */
- ast_sip_task task;
+ /*! The serializer to be used (if any) (Holds a ref) */
+ struct ast_taskprocessor *serializer;
/*! task data */
void *task_data;
- /*! reschedule interval in milliseconds */
- int interval;
- /*! the time the task was queued */
+ /*! task function */
+ ast_sip_task task;
+ /*! the time the task was originally scheduled/queued */
struct timeval when_queued;
/*! the last time the task was started */
struct timeval last_start;
/*! the last time the task was ended */
struct timeval last_end;
+ /*! When the periodic task is next expected to run */
+ struct timeval next_periodic;
+ /*! reschedule interval in milliseconds */
+ int interval;
+ /*! ast_sched scheudler id */
+ int current_scheduler_id;
+ /*! task is currently running */
+ int is_running;
/*! times run */
int run_count;
/*! the task reschedule, cleanup and policy flags */
enum ast_sip_scheduler_task_flags flags;
- /*! the serializer to be used (if any) */
- struct ast_taskprocessor *serializer;
- /* A name to be associated with the task */
+ /*! A name to be associated with the task */
char name[0];
};
@@ -76,14 +77,22 @@ static int push_to_serializer(const void *data);
*/
static int run_task(void *data)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, ao2_bump(data), ao2_cleanup);
+ RAII_VAR(struct ast_sip_sched_task *, schtd, data, ao2_cleanup);
int res;
int delay;
+ if (!schtd->interval) {
+ /* Task was cancelled while waiting to be executed by the serializer */
+ return -1;
+ }
+
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Running %s\n", schtd, schtd->name);
+ }
ao2_lock(schtd);
schtd->last_start = ast_tvnow();
schtd->is_running = 1;
- schtd->run_count++;
+ ++schtd->run_count;
ao2_unlock(schtd);
res = schtd->task(schtd->task_data);
@@ -93,10 +102,10 @@ static int run_task(void *data)
schtd->last_end = ast_tvnow();
/*
- * Don't restart if the task returned 0 or if the interval
+ * Don't restart if the task returned <= 0 or if the interval
* was set to 0 while the task was running
*/
- if (!res || !schtd->interval) {
+ if (res <= 0 || !schtd->interval) {
schtd->interval = 0;
ao2_unlock(schtd);
ao2_unlink(tasks, schtd);
@@ -110,18 +119,31 @@ static int run_task(void *data)
if (schtd->flags & AST_SIP_SCHED_TASK_DELAY) {
delay = schtd->interval;
} else {
- delay = schtd->interval - (ast_tvdiff_ms(schtd->last_end, schtd->last_start) % schtd->interval);
+ int64_t diff;
+
+ /* Determine next periodic interval we need to expire. */
+ do {
+ schtd->next_periodic = ast_tvadd(schtd->next_periodic,
+ ast_samp2tv(schtd->interval, 1000));
+ diff = ast_tvdiff_ms(schtd->next_periodic, schtd->last_end);
+ } while (diff <= 0);
+ delay = diff;
}
- schtd->current_scheduler_id = ast_sched_add(scheduler_context, delay, push_to_serializer, (const void *)schtd);
+ schtd->current_scheduler_id = ast_sched_add(scheduler_context, delay, push_to_serializer, schtd);
if (schtd->current_scheduler_id < 0) {
schtd->interval = 0;
ao2_unlock(schtd);
+ ast_log(LOG_ERROR, "Sched %p: Failed to reschedule task %s\n", schtd, schtd->name);
ao2_unlink(tasks, schtd);
return -1;
}
ao2_unlock(schtd);
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Rescheduled %s for %d ms\n", schtd, schtd->name,
+ delay);
+ }
return 0;
}
@@ -133,9 +155,32 @@ static int run_task(void *data)
static int push_to_serializer(const void *data)
{
struct ast_sip_sched_task *schtd = (struct ast_sip_sched_task *)data;
+ int sched_id;
+ ao2_lock(schtd);
+ sched_id = schtd->current_scheduler_id;
+ schtd->current_scheduler_id = -1;
+ ao2_unlock(schtd);
+ if (sched_id < 0) {
+ /* Task was cancelled while waiting on the lock */
+ return 0;
+ }
+
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Ready to run %s\n", schtd, schtd->name);
+ }
+ ao2_t_ref(schtd, +1, "Give ref to run_task()");
if (ast_sip_push_task(schtd->serializer, run_task, schtd)) {
- ao2_ref(schtd, -1);
+ /*
+ * Oh my. Have to cancel the scheduled item because we
+ * unexpectedly cannot run it anymore.
+ */
+ ao2_unlink(tasks, schtd);
+ ao2_lock(schtd);
+ schtd->interval = 0;
+ ao2_unlock(schtd);
+
+ ao2_t_ref(schtd, -1, "Failed so release run_task() ref");
}
return 0;
@@ -144,48 +189,54 @@ static int push_to_serializer(const void *data)
int ast_sip_sched_task_cancel(struct ast_sip_sched_task *schtd)
{
int res;
+ int sched_id;
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
-
- if (schtd->current_scheduler_id < 0 || schtd->interval <= 0) {
- ao2_unlock_and_unref(schtd);
- return 0;
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Canceling %s\n", schtd, schtd->name);
}
+ /*
+ * Prevent any tasks in the serializer queue from
+ * running and restarting the scheduled item on us
+ * first.
+ */
+ ao2_lock(schtd);
schtd->interval = 0;
- ao2_unlock_and_unref(schtd);
+
+ sched_id = schtd->current_scheduler_id;
+ schtd->current_scheduler_id = -1;
+ ao2_unlock(schtd);
+ res = ast_sched_del(scheduler_context, sched_id);
+
ao2_unlink(tasks, schtd);
- res = ast_sched_del(scheduler_context, schtd->current_scheduler_id);
return res;
}
int ast_sip_sched_task_cancel_by_name(const char *name)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int res;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return -1;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return -1;
}
- return ast_sip_sched_task_cancel(schtd);
+ res = ast_sip_sched_task_cancel(schtd);
+ ao2_ref(schtd, -1);
+ return res;
}
int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd,
struct timeval *queued, struct timeval *last_start, struct timeval *last_end)
{
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
-
+ ao2_lock(schtd);
if (queued) {
memcpy(queued, &schtd->when_queued, sizeof(struct timeval));
}
@@ -195,8 +246,7 @@ int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd,
if (last_end) {
memcpy(last_end, &schtd->last_end, sizeof(struct timeval));
}
-
- ao2_unlock_and_unref(schtd);
+ ao2_unlock(schtd);
return 0;
}
@@ -204,18 +254,21 @@ int ast_sip_sched_task_get_times(struct ast_sip_sched_task *schtd,
int ast_sip_sched_task_get_times_by_name(const char *name,
struct timeval *queued, struct timeval *last_start, struct timeval *last_end)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int res;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return -1;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return -1;
}
- return ast_sip_sched_task_get_times(schtd, queued, last_start, last_end);
+ res = ast_sip_sched_task_get_times(schtd, queued, last_start, last_end);
+ ao2_ref(schtd, -1);
+ return res;
}
int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, size_t maxlen)
@@ -224,13 +277,9 @@ int ast_sip_sched_task_get_name(struct ast_sip_sched_task *schtd, char *name, si
return -1;
}
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
-
+ ao2_lock(schtd);
ast_copy_string(name, schtd->name, maxlen);
-
- ao2_unlock_and_unref(schtd);
+ ao2_unlock(schtd);
return 0;
}
@@ -241,9 +290,7 @@ int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd)
struct timeval since_when;
struct timeval now;
- if (!ao2_ref_and_lock(schtd)) {
- return -1;
- }
+ ao2_lock(schtd);
if (schtd->interval) {
delay = schtd->interval;
@@ -262,103 +309,136 @@ int ast_sip_sched_task_get_next_run(struct ast_sip_sched_task *schtd)
delay = -1;
}
- ao2_unlock_and_unref(schtd);
+ ao2_unlock(schtd);
return delay;
}
int ast_sip_sched_task_get_next_run_by_name(const char *name)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int next_run;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return -1;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return -1;
}
- return ast_sip_sched_task_get_next_run(schtd);
+ next_run = ast_sip_sched_task_get_next_run(schtd);
+ ao2_ref(schtd, -1);
+ return next_run;
}
int ast_sip_sched_is_task_running(struct ast_sip_sched_task *schtd)
{
- if (!schtd) {
- return 0;
- }
-
- return schtd->is_running;
+ return schtd ? schtd->is_running : 0;
}
int ast_sip_sched_is_task_running_by_name(const char *name)
{
- RAII_VAR(struct ast_sip_sched_task *, schtd, NULL, ao2_cleanup);
+ int is_running;
+ struct ast_sip_sched_task *schtd;
if (ast_strlen_zero(name)) {
return 0;
}
- schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+ schtd = ao2_find(tasks, name, OBJ_SEARCH_KEY);
if (!schtd) {
return 0;
}
- return schtd->is_running;
+ is_running = schtd->is_running;
+ ao2_ref(schtd, -1);
+ return is_running;
}
-static void schtd_destructor(void *data)
+static void schtd_dtor(void *data)
{
struct ast_sip_sched_task *schtd = data;
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Destructor %s\n", schtd, schtd->name);
+ }
if (schtd->flags & AST_SIP_SCHED_TASK_DATA_AO2) {
/* release our own ref, then release the callers if asked to do so */
ao2_ref(schtd->task_data, (schtd->flags & AST_SIP_SCHED_TASK_DATA_FREE) ? -2 : -1);
} else if (schtd->task_data && (schtd->flags & AST_SIP_SCHED_TASK_DATA_FREE)) {
ast_free(schtd->task_data);
}
+ ast_taskprocessor_unreference(schtd->serializer);
}
struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *serializer,
- int interval, ast_sip_task sip_task, char *name, void *task_data, enum ast_sip_scheduler_task_flags flags)
+ int interval, ast_sip_task sip_task, const char *name, void *task_data,
+ enum ast_sip_scheduler_task_flags flags)
{
#define ID_LEN 13 /* task_deadbeef */
struct ast_sip_sched_task *schtd;
int res;
- if (interval < 0) {
+ if (interval <= 0) {
return NULL;
}
- schtd = ao2_alloc((sizeof(*schtd) + (!ast_strlen_zero(name) ? strlen(name) : ID_LEN) + 1), schtd_destructor);
+ schtd = ao2_alloc((sizeof(*schtd) + (!ast_strlen_zero(name) ? strlen(name) : ID_LEN) + 1),
+ schtd_dtor);
if (!schtd) {
return NULL;
}
- schtd->task_id = ast_atomic_fetchadd_int(&task_count, 1);
- schtd->serializer = serializer;
+ schtd->serializer = ao2_bump(serializer);
+ schtd->task_data = task_data;
schtd->task = sip_task;
+ schtd->interval = interval;
+ schtd->flags = flags;
if (!ast_strlen_zero(name)) {
strcpy(schtd->name, name); /* Safe */
} else {
- sprintf(schtd->name, "task_%08x", schtd->task_id);
+ uint32_t task_id;
+
+ task_id = ast_atomic_fetchadd_int(&task_count, 1);
+ sprintf(schtd->name, "task_%08x", task_id);
+ }
+ if (schtd->flags & AST_SIP_SCHED_TASK_TRACK) {
+ ast_log(LOG_DEBUG, "Sched %p: Scheduling %s for %d ms\n", schtd, schtd->name,
+ interval);
}
- schtd->task_data = task_data;
- schtd->flags = flags;
- schtd->interval = interval;
schtd->when_queued = ast_tvnow();
+ if (!(schtd->flags & AST_SIP_SCHED_TASK_DELAY)) {
+ schtd->next_periodic = ast_tvadd(schtd->when_queued,
+ ast_samp2tv(schtd->interval, 1000));
+ }
if (flags & AST_SIP_SCHED_TASK_DATA_AO2) {
ao2_ref(task_data, +1);
}
- res = ast_sched_add(scheduler_context, interval, push_to_serializer, (const void *)schtd);
+
+ /*
+ * We must put it in the 'tasks' container before scheduling
+ * the task because we don't want the push_to_serializer()
+ * sched task to "remove" it on failure before we even put
+ * it in. If this happens then nothing would remove it from
+ * the 'tasks' container.
+ */
+ ao2_link(tasks, schtd);
+
+ /*
+ * Lock so we are guaranteed to get the sched id set before
+ * the push_to_serializer() sched task can clear it.
+ */
+ ao2_lock(schtd);
+ res = ast_sched_add(scheduler_context, interval, push_to_serializer, schtd);
+ schtd->current_scheduler_id = res;
+ ao2_unlock(schtd);
if (res < 0) {
+ ao2_unlink(tasks, schtd);
ao2_ref(schtd, -1);
return NULL;
- } else {
- schtd->current_scheduler_id = res;
- ao2_link(tasks, schtd);
}
return schtd;
@@ -367,16 +447,17 @@ struct ast_sip_sched_task *ast_sip_schedule_task(struct ast_taskprocessor *seria
static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- struct ao2_iterator i;
+ struct ao2_iterator iter;
+ struct ao2_container *sorted_tasks;
struct ast_sip_sched_task *schtd;
- const char *log_format = ast_logger_get_dateformat();
+ const char *log_format;
struct ast_tm tm;
char queued[32];
char last_start[32];
char next_start[32];
int datelen;
- struct timeval now = ast_tvnow();
- const char *separator = "======================================";
+ struct timeval now;
+ static const char separator[] = "=============================================";
switch (cmd) {
case CLI_INIT:
@@ -392,26 +473,47 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
return CLI_SHOWUSAGE;
}
+ /* Get a sorted snapshot of the scheduled tasks */
+ sorted_tasks = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ ast_sip_sched_task_sort_fn, NULL);
+ if (!sorted_tasks) {
+ return CLI_SUCCESS;
+ }
+ if (ao2_container_dup(sorted_tasks, tasks, 0)) {
+ ao2_ref(sorted_tasks, -1);
+ return CLI_SUCCESS;
+ }
+
+ now = ast_tvnow();
+ log_format = ast_logger_get_dateformat();
+
ast_localtime(&now, &tm, NULL);
datelen = ast_strftime(queued, sizeof(queued), log_format, &tm);
ast_cli(a->fd, "PJSIP Scheduled Tasks:\n\n");
- ast_cli(a->fd, " %1$-24s %2$-9s %3$-9s %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s %9$7s\n",
+ ast_cli(a->fd, "%1$-45s %2$-9s %3$-9s %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s %9$7s\n",
"Task Name", "Interval", "Times Run", "State",
datelen, "Queued", "Last Started", "Next Start", "( secs)");
- ast_cli(a->fd, " %1$-24.24s %2$-9.9s %3$-9.9s %4$-5.5s %6$-*5$.*5$s %7$-*5$.*5$s %9$-*8$.*8$s\n",
+ ast_cli(a->fd, "%1$-45.45s %2$-9.9s %3$-9.9s %4$-5.5s %6$-*5$.*5$s %7$-*5$.*5$s %9$-*8$.*8$s\n",
separator, separator, separator, separator,
datelen, separator, separator, datelen + 8, separator);
+ iter = ao2_iterator_init(sorted_tasks, AO2_ITERATOR_UNLINK);
+ for (; (schtd = ao2_iterator_next(&iter)); ao2_ref(schtd, -1)) {
+ int next_run_sec;
+ struct timeval next;
+
+ ao2_lock(schtd);
- ao2_ref(tasks, +1);
- ao2_rdlock(tasks);
- i = ao2_iterator_init(tasks, 0);
- while ((schtd = ao2_iterator_next(&i))) {
- int next_run_sec = ast_sip_sched_task_get_next_run(schtd) / 1000;
- struct timeval next = ast_tvadd(now, (struct timeval) {next_run_sec, 0});
+ next_run_sec = ast_sip_sched_task_get_next_run(schtd) / 1000;
+ if (next_run_sec < 0) {
+ /* Scheduled task is now canceled */
+ ao2_unlock(schtd);
+ continue;
+ }
+ next = ast_tvadd(now, ast_tv(next_run_sec, 0));
ast_localtime(&schtd->when_queued, &tm, NULL);
ast_strftime(queued, sizeof(queued), log_format, &tm);
@@ -426,7 +528,7 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
ast_localtime(&next, &tm, NULL);
ast_strftime(next_start, sizeof(next_start), log_format, &tm);
- ast_cli(a->fd, " %1$-24.24s %2$9.3f %3$9d %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s (%9$5d)\n",
+ ast_cli(a->fd, "%1$-46.46s%2$9.3f %3$9d %4$-5s %6$-*5$s %7$-*5$s %8$-*5$s (%9$5d)\n",
schtd->name,
schtd->interval / 1000.0,
schtd->run_count,
@@ -434,11 +536,10 @@ static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
datelen, queued, last_start,
next_start,
next_run_sec);
- ao2_cleanup(schtd);
+ ao2_unlock(schtd);
}
- ao2_iterator_destroy(&i);
- ao2_unlock(tasks);
- ao2_ref(tasks, -1);
+ ao2_iterator_destroy(&iter);
+ ao2_ref(sorted_tasks, -1);
ast_cli(a->fd, "\n");
return CLI_SUCCESS;
@@ -450,7 +551,8 @@ static struct ast_cli_entry cli_commands[] = {
int ast_sip_initialize_scheduler(void)
{
- if (!(scheduler_context = ast_sched_context_create())) {
+ scheduler_context = ast_sched_context_create();
+ if (!scheduler_context) {
ast_log(LOG_ERROR, "Failed to create scheduler. Aborting load\n");
return -1;
}
@@ -461,8 +563,9 @@ int ast_sip_initialize_scheduler(void)
return -1;
}
- tasks = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
- TASK_BUCKETS, ast_sip_sched_task_hash_fn, ast_sip_sched_task_sort_fn, ast_sip_sched_task_cmp_fn);
+ tasks = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_RWLOCK,
+ AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, TASK_BUCKETS, ast_sip_sched_task_hash_fn,
+ ast_sip_sched_task_sort_fn, ast_sip_sched_task_cmp_fn);
if (!tasks) {
ast_log(LOG_ERROR, "Failed to allocate task container. Aborting load\n");
ast_sched_context_destroy(scheduler_context);
@@ -479,7 +582,21 @@ int ast_sip_destroy_scheduler(void)
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
if (scheduler_context) {
+ if (tasks) {
+ struct ao2_iterator iter;
+ struct ast_sip_sched_task *schtd;
+
+ /* Cancel all scheduled tasks */
+ iter = ao2_iterator_init(tasks, 0);
+ while ((schtd = ao2_iterator_next(&iter))) {
+ ast_sip_sched_task_cancel(schtd);
+ ao2_ref(schtd, -1);
+ }
+ ao2_iterator_destroy(&iter);
+ }
+
ast_sched_context_destroy(scheduler_context);
+ scheduler_context = NULL;
}
ao2_cleanup(tasks);
diff --git a/res/res_pjsip_header_funcs.c b/res/res_pjsip_header_funcs.c
index 6c0f9151d..798a1cde6 100644
--- a/res/res_pjsip_header_funcs.c
+++ b/res/res_pjsip_header_funcs.c
@@ -153,7 +153,7 @@ static const struct ast_datastore_info header_datastore = {
.type = "header_datastore",
};
-/*! \brief Data structure used for ast_sip_push_task_synchronous */
+/*! \brief Data structure used for ast_sip_push_task_wait_serializer */
struct header_data {
struct ast_sip_channel_pvt *channel;
char *header_name;
@@ -480,11 +480,11 @@ static int func_read_header(struct ast_channel *chan, const char *function, char
header_data.len = len;
if (!strcasecmp(args.action, "read")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, read_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ read_header, &header_data);
} else if (!strcasecmp(args.action, "remove")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, remove_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ remove_header, &header_data);
} else {
ast_log(AST_LOG_ERROR,
"Unknown action '%s' is not valid, must be 'read' or 'remove'.\n",
@@ -539,14 +539,14 @@ static int func_write_header(struct ast_channel *chan, const char *cmd, char *da
header_data.len = 0;
if (!strcasecmp(args.action, "add")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, add_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ add_header, &header_data);
} else if (!strcasecmp(args.action, "update")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, update_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ update_header, &header_data);
} else if (!strcasecmp(args.action, "remove")) {
- return ast_sip_push_task_synchronous(channel->session->serializer, remove_header,
- &header_data);
+ return ast_sip_push_task_wait_serializer(channel->session->serializer,
+ remove_header, &header_data);
} else {
ast_log(AST_LOG_ERROR,
"Unknown action '%s' is not valid, must be 'add', 'update', or 'remove'.\n",
diff --git a/res/res_pjsip_history.c b/res/res_pjsip_history.c
index ab035a296..eed06eed8 100644
--- a/res/res_pjsip_history.c
+++ b/res/res_pjsip_history.c
@@ -1385,7 +1385,7 @@ static int unload_module(void)
ast_cli_unregister_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip));
ast_sip_unregister_service(&logging_module);
- ast_sip_push_task_synchronous(NULL, clear_history_entries, NULL);
+ ast_sip_push_task_wait_servant(NULL, clear_history_entries, NULL);
AST_VECTOR_FREE(&vector_history);
ast_pjproject_caching_pool_destroy(&cachingpool);
diff --git a/res/res_pjsip_notify.c b/res/res_pjsip_notify.c
index 253cf9ac8..98a75c964 100644
--- a/res/res_pjsip_notify.c
+++ b/res/res_pjsip_notify.c
@@ -25,6 +25,7 @@
#include "asterisk.h"
#include <pjsip.h>
+#include <pjsip_ua.h>
#include "asterisk/cli.h"
#include "asterisk/config.h"
@@ -32,12 +33,13 @@
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
#include "asterisk/sorcery.h"
/*** DOCUMENTATION
<manager name="PJSIPNotify" language="en_US">
<synopsis>
- Send a NOTIFY to either an endpoint or an arbitrary URI.
+ Send a NOTIFY to either an endpoint, an arbitrary URI, or inside a SIP dialog.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
@@ -47,6 +49,9 @@
<parameter name="URI" required="false">
<para>Abritrary URI to which to send the NOTIFY.</para>
</parameter>
+ <parameter name="channel" required="false">
+ <para>Channel name to send the NOTIFY. Must be a PJSIP channel.</para>
+ </parameter>
<parameter name="Variable" required="true">
<para>Appends variables as headers/content to the NOTIFY. If the variable is
named <literal>Content</literal>, then the value will compose the body
@@ -55,14 +60,14 @@
</parameter>
</syntax>
<description>
- <para>Sends a NOTIFY to an endpoint or an arbitrary URI.</para>
+ <para>Sends a NOTIFY to an endpoint, an arbitrary URI, or inside a SIP dialog.</para>
<para>All parameters for this event must be specified in the body of this
request via multiple <literal>Variable: name=value</literal> sequences.</para>
- <note><para>One (and only one) of <literal>Endpoint</literal> or
- <literal>URI</literal> must be specified. If <literal>URI</literal> is used,
- the default outbound endpoint will be used to send the message. If the default
- outbound endpoint isn't configured, this command can not send to an arbitrary
- URI.</para></note>
+ <note><para>One (and only one) of <literal>Endpoint</literal>,
+ <literal>URI</literal>, or <literal>Channel</literal> must be specified.
+ If <literal>URI</literal> is used, the default outbound endpoint will be used
+ to send the message. If the default outbound endpoint isn't configured, this command
+ can not send to an arbitrary URI.</para></note>
</description>
</manager>
<configInfo name="res_pjsip_notify" language="en_US">
@@ -289,6 +294,16 @@ struct notify_uri_data {
void (*build_notify)(pjsip_tx_data *, void *);
};
+/*!
+ * \internal
+ * \brief Structure to hold task data for notifications (channel variant)
+ */
+struct notify_channel_data {
+ struct ast_sip_session *session;
+ void *info;
+ void (*build_notify)(pjsip_tx_data *, void *);
+};
+
static void notify_cli_uri_data_destroy(void *obj)
{
struct notify_uri_data *data = obj;
@@ -381,6 +396,19 @@ static void notify_ami_uri_data_destroy(void *obj)
ast_variables_destroy(info);
}
+/*!
+ * \internal
+ * \brief Destroy the notify AMI channel data releasing any resources.
+ */
+static void notify_ami_channel_data_destroy(void *obj)
+{
+ struct notify_channel_data *data = obj;
+ struct ast_variable *info = data->info;
+
+ ao2_cleanup(data->session);
+ ast_variables_destroy(info);
+}
+
static void build_ami_notify(pjsip_tx_data *tdata, void *info);
/*!
@@ -432,6 +460,28 @@ static struct notify_uri_data* notify_ami_uri_data_create(
/*!
* \internal
+ * \brief Construct a notify channel data object for AMI.
+ */
+static struct notify_channel_data *notify_ami_channel_data_create(
+ struct ast_sip_session *session, void *info)
+{
+ struct notify_channel_data *data;
+
+ data = ao2_alloc_options(sizeof(*data), notify_ami_channel_data_destroy,
+ AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!data) {
+ return NULL;
+ }
+
+ data->session = session;
+ data->info = info;
+ data->build_notify = build_ami_notify;
+
+ return data;
+}
+
+/*!
+ * \internal
* \brief Checks if the given header name is not allowed.
*
* \details Some headers are not allowed to be set by the user within the
@@ -672,9 +722,45 @@ static int notify_uri(void *obj)
return 0;
}
+/*!
+ * \internal
+ * \brief Send a notify request to a channel.
+ */
+static int notify_channel(void *obj)
+{
+ RAII_VAR(struct notify_channel_data *, data, obj, ao2_cleanup);
+ pjsip_tx_data *tdata;
+ struct pjsip_dialog *dlg;
+
+ if (!data->session->channel
+ || !data->session->inv_session
+ || data->session->inv_session->state < PJSIP_INV_STATE_EARLY
+ || data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+ return -1;
+ }
+
+ ast_debug(1, "Sending notify on channel %s\n", ast_channel_name(data->session->channel));
+
+ dlg = data->session->inv_session->dlg;
+
+ if (ast_sip_create_request("NOTIFY", dlg, NULL, NULL, NULL, &tdata)) {
+ return -1;
+ }
+
+ ast_sip_add_header(tdata, "Subscription-State", "terminated");
+ data->build_notify(tdata, data->info);
+
+ if (ast_sip_send_request(tdata, dlg, NULL, NULL, NULL)) {
+ return -1;
+ }
+
+ return 0;
+}
+
enum notify_result {
SUCCESS,
INVALID_ENDPOINT,
+ INVALID_CHANNEL,
ALLOC_ERROR,
TASK_PUSH_ERROR
};
@@ -684,6 +770,10 @@ typedef struct notify_data *(*task_data_create)(
typedef struct notify_uri_data *(*task_uri_data_create)(
const char *uri, void *info);
+
+typedef struct notify_channel_data *(*task_channel_data_create)(
+ struct ast_sip_session *session, void *info);
+
/*!
* \internal
* \brief Send a NOTIFY request to the endpoint within a threaded task.
@@ -734,6 +824,68 @@ static enum notify_result push_notify_uri(const char *uri, void *info,
/*!
* \internal
+ * \brief Send a NOTIFY request in a channel within an threaded task.
+ */
+static enum notify_result push_notify_channel(const char *channel_name, void *info,
+ task_channel_data_create data_create)
+{
+ struct notify_channel_data *data;
+ struct ast_channel *ch;
+ struct ast_sip_session *session;
+ struct ast_sip_channel_pvt *ch_pvt;
+
+ /* note: this increases the refcount of the channel */
+ ch = ast_channel_get_by_name(channel_name);
+ if (!ch) {
+ ast_debug(1, "No channel found with name %s", channel_name);
+ return INVALID_CHANNEL;
+ }
+
+ if (strcmp(ast_channel_tech(ch)->type, "PJSIP")) {
+ ast_log(LOG_WARNING, "Channel was a non-PJSIP channel: %s\n", channel_name);
+ ast_channel_unref(ch);
+ return INVALID_CHANNEL;
+ }
+
+ ast_channel_lock(ch);
+ ch_pvt = ast_channel_tech_pvt(ch);
+ session = ch_pvt->session;
+
+ if (!session || !session->inv_session
+ || session->inv_session->state < PJSIP_INV_STATE_EARLY
+ || session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+ ast_debug(1, "No active session for channel %s\n", channel_name);
+ ast_channel_unlock(ch);
+ ast_channel_unref(ch);
+ return INVALID_CHANNEL;
+ }
+
+ ao2_ref(session, +1);
+ ast_channel_unlock(ch);
+
+ /* don't keep a reference to the channel, we've got a reference to the session */
+ ast_channel_unref(ch);
+
+ /*
+ * data_create will take ownership of the session,
+ * and take care of releasing the ref.
+ */
+ data = data_create(session, info);
+ if (!data) {
+ ao2_ref(session, -1);
+ return ALLOC_ERROR;
+ }
+
+ if (ast_sip_push_task(session->serializer, notify_channel, data)) {
+ ao2_ref(data, -1);
+ return TASK_PUSH_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+/*!
+ * \internal
* \brief Do completion on the endpoint.
*/
static char *cli_complete_endpoint(const char *word, int state)
@@ -915,6 +1067,10 @@ static void manager_notify_endpoint(struct mansession *s,
}
switch (push_notify(endpoint_name, vars, notify_ami_data_create)) {
+ case INVALID_CHANNEL:
+ /* Shouldn't be possible. */
+ ast_assert(0);
+ break;
case INVALID_ENDPOINT:
ast_variables_destroy(vars);
astman_send_error_va(s, m, "Unable to retrieve endpoint %s",
@@ -944,6 +1100,10 @@ static void manager_notify_uri(struct mansession *s,
struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
switch (push_notify_uri(uri, vars, notify_ami_uri_data_create)) {
+ case INVALID_CHANNEL:
+ /* Shouldn't be possible. */
+ ast_assert(0);
+ break;
case INVALID_ENDPOINT:
/* Shouldn't be possible. */
ast_assert(0);
@@ -964,22 +1124,70 @@ static void manager_notify_uri(struct mansession *s,
/*!
* \internal
+ * \brief Completes SIPNotify AMI command in channel mode.
+ */
+static void manager_notify_channel(struct mansession *s,
+ const struct message *m, const char *channel)
+{
+ struct ast_variable *vars = astman_get_variables_order(m, ORDER_NATURAL);
+
+ switch (push_notify_channel(channel, vars, notify_ami_channel_data_create)) {
+ case INVALID_CHANNEL:
+ ast_variables_destroy(vars);
+ astman_send_error(s, m, "Channel not found");
+ break;
+ case INVALID_ENDPOINT:
+ /* Shouldn't be possible. */
+ ast_assert(0);
+ break;
+ case ALLOC_ERROR:
+ ast_variables_destroy(vars);
+ astman_send_error(s, m, "Unable to allocate NOTIFY task data");
+ break;
+ case TASK_PUSH_ERROR:
+ /* Don't need to destroy vars since it is handled by cleanup in push_notify_channel */
+ astman_send_error(s, m, "Unable to push Notify task");
+ break;
+ case SUCCESS:
+ astman_send_ack(s, m, "NOTIFY sent");
+ break;
+ }
+}
+
+/*!
+ * \internal
* \brief AMI entry point to send a SIP notify to an endpoint.
*/
static int manager_notify(struct mansession *s, const struct message *m)
{
const char *endpoint_name = astman_get_header(m, "Endpoint");
const char *uri = astman_get_header(m, "URI");
+ const char *channel = astman_get_header(m, "Channel");
+ int count = 0;
+
+ if (!ast_strlen_zero(endpoint_name)) {
+ ++count;
+ }
+ if (!ast_strlen_zero(uri)) {
+ ++count;
+ }
+ if (!ast_strlen_zero(channel)) {
+ ++count;
+ }
- if (!ast_strlen_zero(endpoint_name) && !ast_strlen_zero(uri)) {
- astman_send_error(s, m, "PJSIPNotify action can not handle a request specifying "
- "both 'URI' and 'Endpoint'. You must use only one of the two.\n");
+ if (1 < count) {
+ astman_send_error(s, m,
+ "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel. "
+ "You must use only one of them.");
} else if (!ast_strlen_zero(endpoint_name)) {
manager_notify_endpoint(s, m, endpoint_name);
} else if (!ast_strlen_zero(uri)) {
manager_notify_uri(s, m, uri);
+ } else if (!ast_strlen_zero(channel)) {
+ manager_notify_channel(s, m, channel);
} else {
- astman_send_error(s, m, "PJSIPNotify requires either an endpoint name or a SIP URI.");
+ astman_send_error(s, m,
+ "PJSIPNotify requires either an endpoint name, a SIP URI, or a channel.");
}
return 0;
diff --git a/res/res_pjsip_outbound_publish.c b/res/res_pjsip_outbound_publish.c
index 75e74a26f..4894e55d1 100644
--- a/res/res_pjsip_outbound_publish.c
+++ b/res/res_pjsip_outbound_publish.c
@@ -362,7 +362,8 @@ static struct ast_sip_event_publisher_handler *find_publisher_handler_for_event_
/*! \brief Helper function which cancels the refresh timer on a publisher */
static void cancel_publish_refresh(struct sip_outbound_publisher *publisher)
{
- if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &publisher->timer)) {
+ if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+ &publisher->timer, 0)) {
/* The timer was successfully cancelled, drop the refcount of the publisher */
ao2_ref(publisher, -1);
}
@@ -1069,7 +1070,7 @@ static struct sip_outbound_publisher *sip_outbound_publisher_alloc(
return NULL;
}
- if (ast_sip_push_task_synchronous(NULL, sip_outbound_publisher_init, publisher)) {
+ if (ast_sip_push_task_wait_servant(NULL, sip_outbound_publisher_init, publisher)) {
ast_log(LOG_ERROR, "Unable to create publisher for outbound publish '%s'\n",
ast_sorcery_object_get_id(client->publish));
ao2_ref(publisher, -1);
@@ -1513,8 +1514,8 @@ static int current_state_reusable(struct ast_sip_outbound_publish *publish,
*/
old_publish = current_state->client->publish;
current_state->client->publish = publish;
- if (ast_sip_push_task_synchronous(
- NULL, sip_outbound_publisher_reinit_all, current_state->client->publishers)) {
+ if (ast_sip_push_task_wait_servant(NULL, sip_outbound_publisher_reinit_all,
+ current_state->client->publishers)) {
/*
* If the state object fails to re-initialize then swap
* the old publish info back in.
diff --git a/res/res_pjsip_outbound_registration.c b/res/res_pjsip_outbound_registration.c
index 011186321..0d815ad39 100644
--- a/res/res_pjsip_outbound_registration.c
+++ b/res/res_pjsip_outbound_registration.c
@@ -512,7 +512,8 @@ static struct ast_sip_endpoint_identifier line_identifier = {
/*! \brief Helper function which cancels the timer on a client */
static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
{
- if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
+ if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+ &client_state->timer, client_state->timer.id)) {
/* The timer was successfully cancelled, drop the refcount of client_state */
ao2_ref(client_state, -1);
}
@@ -1131,8 +1132,8 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
}
state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
- state->client_state->timer.user_data = state->client_state;
- state->client_state->timer.cb = sip_outbound_registration_timer_cb;
+ pj_timer_entry_init(&state->client_state->timer, 0, state->client_state,
+ sip_outbound_registration_timer_cb);
state->client_state->transport_name = ast_strdup(registration->transport);
state->client_state->registration_name =
ast_strdup(ast_sorcery_object_get_id(registration));
@@ -1481,7 +1482,7 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
return -1;
}
- if (ast_sip_push_task_synchronous(new_state->client_state->serializer,
+ if (ast_sip_push_task_wait_serializer(new_state->client_state->serializer,
sip_outbound_registration_regc_alloc, new_state)) {
return -1;
}
@@ -1851,8 +1852,7 @@ static int ami_outbound_registration_detail(void *obj, void *arg, int flags)
struct sip_ami_outbound *ami = arg;
ami->registration = obj;
- return ast_sip_push_task_synchronous(
- NULL, ami_outbound_registration_task, ami);
+ return ast_sip_push_task_wait_servant(NULL, ami_outbound_registration_task, ami);
}
static int ami_show_outbound_registrations(struct mansession *s,
diff --git a/res/res_pjsip_pubsub.c b/res/res_pjsip_pubsub.c
index f26acb355..d98491495 100644
--- a/res/res_pjsip_pubsub.c
+++ b/res/res_pjsip_pubsub.c
@@ -1355,7 +1355,8 @@ static void subscription_tree_destructor(void *obj)
destroy_subscriptions(sub_tree->root);
if (sub_tree->dlg) {
- ast_sip_push_task_synchronous(sub_tree->serializer, subscription_unreference_dialog, sub_tree);
+ ast_sip_push_task_wait_servant(sub_tree->serializer,
+ subscription_unreference_dialog, sub_tree);
}
ao2_cleanup(sub_tree->endpoint);
@@ -1702,7 +1703,8 @@ static int subscription_persistence_recreate(void *obj, void *arg, int flags)
}
recreate_data.persistence = persistence;
recreate_data.rdata = &rdata;
- if (ast_sip_push_task_synchronous(serializer, sub_persistence_recreate, &recreate_data)) {
+ if (ast_sip_push_task_wait_serializer(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);
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 7d892f653..1e6ca7f46 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -316,7 +316,15 @@ static void refer_progress_on_evsub_state(pjsip_evsub *sub, pjsip_event *event)
/* It's possible that a task is waiting to remove us already, so bump the refcount of progress so it doesn't get destroyed */
ao2_ref(progress, +1);
pjsip_dlg_dec_lock(progress->dlg);
- ast_sip_push_task_synchronous(progress->serializer, refer_progress_terminate, progress);
+ /*
+ * XXX We are always going to execute this inline rather than
+ * in the serializer because this function is a PJPROJECT
+ * callback and thus has to be a SIP servant thread.
+ *
+ * The likely remedy is to push most of this function into
+ * refer_progress_terminate() with ast_sip_push_task().
+ */
+ ast_sip_push_task_wait_servant(progress->serializer, refer_progress_terminate, progress);
pjsip_dlg_inc_lock(progress->dlg);
ao2_ref(progress, -1);
@@ -917,10 +925,7 @@ static int invite_replaces(void *data)
ast_channel_ref(invite->session->channel);
invite->channel = invite->session->channel;
- ast_channel_lock(invite->channel);
- invite->bridge = ast_channel_get_bridge(invite->channel);
- ast_channel_unlock(invite->channel);
-
+ invite->bridge = ast_bridge_transfer_acquire_bridge(invite->channel);
return 0;
}
@@ -963,7 +968,8 @@ static int refer_incoming_invite_request(struct ast_sip_session *session, struct
invite.session = other_session;
- if (ast_sip_push_task_synchronous(other_session->serializer, invite_replaces, &invite)) {
+ if (ast_sip_push_task_wait_serializer(other_session->serializer, invite_replaces,
+ &invite)) {
response = 481;
goto inv_replace_failed;
}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 03d37652f..14ed3b186 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -1096,6 +1096,9 @@ static void add_rtcp_fb_to_stream(struct ast_sip_session *session,
attr = pjmedia_sdp_attr_create(pool, "rtcp-fb", pj_cstr(&stmp, "* goog-remb"));
pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
+
+ attr = pjmedia_sdp_attr_create(pool, "rtcp-fb", pj_cstr(&stmp, "* nack"));
+ pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
}
/*! \brief Function which negotiates an incoming media stream */
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index f25201731..49ab87568 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -2702,10 +2702,8 @@ int ast_sip_session_defer_termination(struct ast_sip_session *session)
session->defer_end = 1;
session->ended_while_deferred = 0;
- session->scheduled_termination.id = 0;
ao2_ref(session, +1);
- session->scheduled_termination.user_data = session;
- session->scheduled_termination.cb = session_termination_cb;
+ pj_timer_entry_init(&session->scheduled_termination, 0, session, session_termination_cb);
res = (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(),
&session->scheduled_termination, &delay) != PJ_SUCCESS) ? -1 : 0;
@@ -2727,8 +2725,8 @@ int ast_sip_session_defer_termination(struct ast_sip_session *session)
*/
static void sip_session_defer_termination_stop_timer(struct ast_sip_session *session)
{
- if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
- &session->scheduled_termination)) {
+ if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+ &session->scheduled_termination, session->scheduled_termination.id)) {
ao2_ref(session, -1);
}
}
@@ -4155,7 +4153,7 @@ static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_trans
if (ast_sip_transport_is_local(transport_state, &our_sdp_addr) || !transport_state->localnet) {
ast_debug(5, "Setting external media address to %s\n", ast_sockaddr_stringify_host(&transport_state->external_media_address));
pj_strdup2(tdata->pool, &sdp->conn->addr, ast_sockaddr_stringify_host(&transport_state->external_media_address));
- pj_strdup2(tdata->pool, &sdp->origin.addr, transport->external_media_address);
+ pj_strassign(&sdp->origin.addr, &sdp->conn->addr);
}
}
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 333295fe6..eba0b36e8 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -142,7 +142,8 @@ static void t38_change_state(struct ast_sip_session *session, struct ast_sip_ses
new_state, old_state,
session->channel ? ast_channel_name(session->channel) : "<gone>");
- if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &state->timer)) {
+ if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
+ &state->timer, 0)) {
ast_debug(2, "Automatic T.38 rejection on channel '%s' terminated\n",
session->channel ? ast_channel_name(session->channel) : "<gone>");
ao2_ref(session, -1);
@@ -248,8 +249,7 @@ static struct t38_state *t38_state_get_or_alloc(struct ast_sip_session *session)
state = datastore->data;
/* This will get bumped up before scheduling */
- state->timer.user_data = session;
- state->timer.cb = t38_automatic_reject_timer_cb;
+ pj_timer_entry_init(&state->timer, 0, session, t38_automatic_reject_timer_cb);
datastore->data = state;
diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c
index 974b15087..633594359 100644
--- a/res/res_pjsip_transport_websocket.c
+++ b/res/res_pjsip_transport_websocket.c
@@ -377,7 +377,7 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
create_data.ws_session = session;
- if (ast_sip_push_task_synchronous(serializer, transport_create, &create_data)) {
+ if (ast_sip_push_task_wait_serializer(serializer, transport_create, &create_data)) {
ast_log(LOG_ERROR, "Could not create WebSocket transport.\n");
ast_taskprocessor_unreference(serializer);
ast_websocket_unref(session);
@@ -396,13 +396,13 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
}
if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
- ast_sip_push_task_synchronous(serializer, transport_read, &read_data);
+ ast_sip_push_task_wait_serializer(serializer, transport_read, &read_data);
} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
break;
}
}
- ast_sip_push_task_synchronous(serializer, transport_shutdown, transport);
+ ast_sip_push_task_wait_serializer(serializer, transport_shutdown, transport);
ast_taskprocessor_unreference(serializer);
ast_websocket_unref(session);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index b010f6c51..4ac20d551 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -71,6 +71,7 @@
#include "asterisk/smoother.h"
#include "asterisk/uuid.h"
#include "asterisk/test.h"
+#include "asterisk/data_buffer.h"
#ifdef HAVE_PJPROJECT
#include "asterisk/res_pjproject.h"
#endif
@@ -92,6 +93,8 @@
#define TURN_STATE_WAIT_TIME 2000
+#define DEFAULT_RTP_BUFFER_SIZE 250
+
/*! Full INTRA-frame Request / Fast Update Request (From RFC2032) */
#define RTCP_PT_FUR 192
/*! Sender Report (From RFC3550) */
@@ -373,6 +376,8 @@ struct ast_rtp {
struct rtp_red *red;
+ struct ast_data_buffer *send_buffer; /*!< Buffer for storing sent packets for retransmission */
+
#ifdef HAVE_PJPROJECT
ast_cond_t cond; /*!< ICE/TURN condition for signaling */
@@ -509,6 +514,12 @@ struct rtp_red {
long int prev_ts;
};
+/*! \brief Structure for storing RTP packets for retransmission */
+struct ast_rtp_rtcp_nack_payload {
+ size_t size; /*!< The size of the payload */
+ unsigned char buf[0]; /*!< The payload data */
+};
+
AST_LIST_HEAD_NOLOCK(frame_list, ast_frame);
/* Forward Declarations */
@@ -3675,6 +3686,11 @@ static int ast_rtp_destroy(struct ast_rtp_instance *instance)
rtp->red = NULL;
}
+ /* Destroy the send buffer if it was being used */
+ if (rtp->send_buffer) {
+ ast_data_buffer_free(rtp->send_buffer);
+ }
+
ao2_cleanup(rtp->lasttxformat);
ao2_cleanup(rtp->lastrxformat);
ao2_cleanup(rtp->f.subclass.format);
@@ -4369,7 +4385,7 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
} else {
/* This is the first frame with sequence number we've seen, so start keeping track */
rtp->expectedseqno = frame->seqno + 1;
- }
+ }
} else {
rtp->expectedseqno = -1;
}
@@ -4383,13 +4399,27 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
/* If we know the remote address construct a packet and send it out */
if (!ast_sockaddr_isnull(&remote_address)) {
int hdrlen = 12, res, ice;
+ int packet_len = frame->datalen + hdrlen;
unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen);
put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (seqno) | (mark << 23)));
put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
- if ((res = rtp_sendto(instance, (void *)rtpheader, frame->datalen + hdrlen, 0, &remote_address, &ice)) < 0) {
+ /* If retransmissions are enabled, we need to store this packet for future use */
+ if (rtp->send_buffer) {
+ struct ast_rtp_rtcp_nack_payload *payload;
+
+ payload = ast_malloc(sizeof(*payload) + packet_len);
+ if (payload) {
+ payload->size = packet_len;
+ memcpy(payload->buf, rtpheader, packet_len);
+ ast_data_buffer_put(rtp->send_buffer, rtp->seqno, payload);
+ }
+ }
+
+ res = rtp_sendto(instance, (void *)rtpheader, packet_len, 0, &remote_address, &ice);
+ if (res < 0) {
if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) || (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT) && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
ast_debug(1, "RTP Transmission error of packet %d to %s: %s\n",
rtp->seqno,
@@ -4473,6 +4503,94 @@ static struct ast_frame *red_t140_to_red(struct rtp_red *red)
return &red->t140red;
}
+static void rtp_write_rtcp_fir(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_sockaddr *remote_address)
+{
+ unsigned int *rtcpheader;
+ char bdata[1024];
+ int len = 20;
+ int ice;
+ int res;
+
+ if (!rtp || !rtp->rtcp) {
+ return;
+ }
+
+ if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
+ /*
+ * RTCP was stopped.
+ */
+ return;
+ }
+
+ if (!rtp->themssrc_valid) {
+ /* We don't know their SSRC value so we don't know who to update. */
+ return;
+ }
+
+ /* Prepare RTCP FIR (PT=206, FMT=4) */
+ rtp->rtcp->firseq++;
+ if(rtp->rtcp->firseq == 256) {
+ rtp->rtcp->firseq = 0;
+ }
+
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[0] = htonl((2 << 30) | (4 << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
+ rtcpheader[1] = htonl(rtp->ssrc);
+ rtcpheader[2] = htonl(rtp->themssrc);
+ rtcpheader[3] = htonl(rtp->themssrc); /* FCI: SSRC */
+ rtcpheader[4] = htonl(rtp->rtcp->firseq << 24); /* FCI: Sequence number */
+ res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? remote_address : &rtp->rtcp->them, &ice);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno));
+ }
+}
+
+static void rtp_write_rtcp_psfb(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_frame *frame, struct ast_sockaddr *remote_address)
+{
+ struct ast_rtp_rtcp_feedback *feedback = frame->data.ptr;
+ unsigned int *rtcpheader;
+ char bdata[1024];
+ int len = 24;
+ int ice;
+ int res;
+
+ if (feedback->fmt != AST_RTP_RTCP_FMT_REMB) {
+ ast_debug(1, "Provided an RTCP feedback frame of format %d to write on RTP instance '%p' but only REMB is supported\n",
+ feedback->fmt, instance);
+ return;
+ }
+
+ if (!rtp || !rtp->rtcp) {
+ return;
+ }
+
+ /* If REMB support is not enabled don't send this RTCP packet */
+ if (!ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_REMB)) {
+ ast_debug(1, "Provided an RTCP feedback REMB report to write on RTP instance '%p' but REMB support not enabled\n",
+ instance);
+ return;
+ }
+
+ if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
+ /*
+ * RTCP was stopped.
+ */
+ return;
+ }
+
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[0] = htonl((2 << 30) | (AST_RTP_RTCP_FMT_REMB << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
+ rtcpheader[1] = htonl(rtp->ssrc);
+ rtcpheader[2] = htonl(0); /* Per the draft this should always be 0 */
+ rtcpheader[3] = htonl(('R' << 24) | ('E' << 16) | ('M' << 8) | ('B')); /* Unique identifier 'R' 'E' 'M' 'B' */
+ rtcpheader[4] = htonl((1 << 24) | (feedback->remb.br_exp << 18) | (feedback->remb.br_mantissa)); /* Number of SSRCs / BR Exp / BR Mantissa */
+ rtcpheader[5] = htonl(rtp->ssrc); /* The SSRC this feedback message applies to */
+ res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? remote_address : &rtp->rtcp->them, &ice);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP PSFB transmission error: %s\n", strerror(errno));
+ }
+}
+
/*! \pre instance is locked */
static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
{
@@ -4491,42 +4609,11 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr
/* VP8: is this a request to send a RTCP FIR? */
if (frame->frametype == AST_FRAME_CONTROL && frame->subclass.integer == AST_CONTROL_VIDUPDATE) {
- unsigned int *rtcpheader;
- char bdata[1024];
- int len = 20;
- int ice;
- int res;
-
- if (!rtp || !rtp->rtcp) {
- return 0;
- }
-
- if (ast_sockaddr_isnull(&rtp->rtcp->them) || rtp->rtcp->schedid < 0) {
- /*
- * RTCP was stopped.
- */
- return 0;
- }
- if (!rtp->themssrc_valid) {
- /* We don't know their SSRC value so we don't know who to update. */
- return 0;
- }
-
- /* Prepare RTCP FIR (PT=206, FMT=4) */
- rtp->rtcp->firseq++;
- if(rtp->rtcp->firseq == 256) {
- rtp->rtcp->firseq = 0;
- }
-
- rtcpheader = (unsigned int *)bdata;
- rtcpheader[0] = htonl((2 << 30) | (4 << 24) | (RTCP_PT_PSFB << 16) | ((len/4)-1));
- rtcpheader[1] = htonl(rtp->ssrc);
- rtcpheader[2] = htonl(rtp->themssrc);
- rtcpheader[3] = htonl(rtp->themssrc); /* FCI: SSRC */
- rtcpheader[4] = htonl(rtp->rtcp->firseq << 24); /* FCI: Sequence number */
- res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? &remote_address : &rtp->rtcp->them, &ice);
- if (res < 0) {
- ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno));
+ rtp_write_rtcp_fir(instance, rtp, &remote_address);
+ return 0;
+ } else if (frame->frametype == AST_FRAME_RTCP) {
+ if (frame->subclass.integer == AST_RTP_RTCP_PSFB) {
+ rtp_write_rtcp_psfb(instance, rtp, frame, &remote_address);
}
return 0;
}
@@ -5088,8 +5175,8 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
}
/*! \pre instance is locked */
-static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance,
- struct ast_rtp *rtp, unsigned int ssrc)
+static struct ast_rtp_instance *__rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance,
+ struct ast_rtp *rtp, unsigned int ssrc, int source)
{
int index;
@@ -5101,8 +5188,9 @@ static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instanc
/* Find the bundled child instance */
for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {
struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);
+ unsigned int mapping_ssrc = source ? ast_rtp_get_ssrc(mapping->instance) : mapping->ssrc;
- if (mapping->ssrc_valid && mapping->ssrc == ssrc) {
+ if (mapping->ssrc_valid && mapping_ssrc == ssrc) {
return mapping->instance;
}
}
@@ -5114,6 +5202,20 @@ static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instanc
return NULL;
}
+/*! \pre instance is locked */
+static struct ast_rtp_instance *rtp_find_instance_by_packet_source_ssrc(struct ast_rtp_instance *instance,
+ struct ast_rtp *rtp, unsigned int ssrc)
+{
+ return __rtp_find_instance_by_ssrc(instance, rtp, ssrc, 0);
+}
+
+/*! \pre instance is locked */
+static struct ast_rtp_instance *rtp_find_instance_by_media_source_ssrc(struct ast_rtp_instance *instance,
+ struct ast_rtp *rtp, unsigned int ssrc)
+{
+ return __rtp_find_instance_by_ssrc(instance, rtp, ssrc, 1);
+}
+
static const char *rtcp_payload_type2str(unsigned int pt)
{
const char *str;
@@ -5146,6 +5248,69 @@ static const char *rtcp_payload_type2str(unsigned int pt)
return str;
}
+/*! \pre instance is locked */
+static int ast_rtp_rtcp_handle_nack(struct ast_rtp_instance *instance, unsigned int *nackdata, unsigned int position,
+ unsigned int length)
+{
+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ int res = 0;
+ int blp_index;
+ int packet_index;
+ int ice;
+ struct ast_rtp_rtcp_nack_payload *payload;
+ unsigned int current_word;
+ unsigned int pid; /* Packet ID which refers to seqno of lost packet */
+ unsigned int blp; /* Bitmask of following lost packets */
+ struct ast_sockaddr remote_address = { {0,} };
+
+ if (!rtp->send_buffer) {
+ ast_debug(1, "Tried to handle NACK request, but we don't have a RTP packet storage!\n");
+ return res;
+ }
+
+ ast_rtp_instance_get_remote_address(instance, &remote_address);
+
+ /*
+ * We use index 3 because with feedback messages, the FCI (Feedback Control Information)
+ * does not begin until after the version, packet SSRC, and media SSRC words.
+ */
+ for (packet_index = 3; packet_index < length; packet_index++) {
+ current_word = ntohl(nackdata[position + packet_index]);
+ pid = current_word >> 16;
+ /* We know the remote end is missing this packet. Go ahead and send it if we still have it. */
+ payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, pid);
+ if (payload) {
+ res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
+ } else {
+ ast_debug(1, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid);
+ }
+ /*
+ * The bitmask. Denoting the least significant bit as 1 and its most significant bit
+ * as 16, then bit i of the bitmask is set to 1 if the receiver has not received RTP
+ * packet (pid+i)(modulo 2^16). Otherwise, it is set to 0. We cannot assume bits set
+ * to 0 after a bit set to 1 have actually been received.
+ */
+ blp = current_word & 0xFF;
+ blp_index = 1;
+ while (blp) {
+ if (blp & 1) {
+ /* Packet (pid + i)(modulo 2^16) is missing too. */
+ unsigned int seqno = (pid + blp_index) % 65536;
+ payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, seqno);
+ if (payload) {
+ res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
+ } else {
+ ast_debug(1, "Remote end also requested RTP packet with seqno %d, but we don't have it\n", seqno);
+ }
+ }
+ blp >>= 1;
+ blp_index++;
+ }
+ }
+
+ return res;
+}
+
/*
* Unshifted RTCP header bit field masks
*/
@@ -5185,7 +5350,8 @@ static const char *rtcp_payload_type2str(unsigned int pt)
#define RTCP_SR_BLOCK_WORD_LENGTH 5
#define RTCP_RR_BLOCK_WORD_LENGTH 6
#define RTCP_HEADER_SSRC_LENGTH 2
-#define RTCP_FB_REMB_BLOCK_WORD_LENGTH 5
+#define RTCP_FB_REMB_BLOCK_WORD_LENGTH 4
+#define RTCP_FB_NACK_BLOCK_WORD_LENGTH 2
static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
{
@@ -5262,6 +5428,8 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
unsigned int ssrc_valid;
unsigned int length;
unsigned int min_length;
+ /*! Always use packet source SSRC to find the rtp instance unless explicitly told not to. */
+ unsigned int use_packet_source = 1;
struct ast_json *message_blob;
RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);
@@ -5284,9 +5452,20 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
/* fall through */
case RTCP_PT_RR:
min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
+ use_packet_source = 0;
break;
case RTCP_PT_FUR:
break;
+ case AST_RTP_RTCP_RTPFB:
+ switch (rc) {
+ case AST_RTP_RTCP_FMT_NACK:
+ min_length += RTCP_FB_NACK_BLOCK_WORD_LENGTH;
+ break;
+ default:
+ break;
+ }
+ use_packet_source = 0;
+ break;
case RTCP_PT_PSFB:
switch (rc) {
case AST_RTP_RTCP_FMT_REMB:
@@ -5335,13 +5514,16 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
}
rtcp_report->reception_report_count = rc;
- ssrc = ntohl(rtcpheader[i + 1]);
+ ssrc = ntohl(rtcpheader[i + 2]);
rtcp_report->ssrc = ssrc;
break;
case RTCP_PT_FUR:
case RTCP_PT_PSFB:
ssrc = ntohl(rtcpheader[i + 1]);
break;
+ case AST_RTP_RTCP_RTPFB:
+ ssrc = ntohl(rtcpheader[i + 2]);
+ break;
case RTCP_PT_SDES:
case RTCP_PT_BYE:
default:
@@ -5360,7 +5542,15 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
/* Determine the appropriate instance for this */
if (ssrc_valid) {
- child = rtp_find_instance_by_ssrc(transport, transport_rtp, ssrc);
+ /*
+ * Depending on the payload type, either the packet source or media source
+ * SSRC is used.
+ */
+ if (use_packet_source) {
+ child = rtp_find_instance_by_packet_source_ssrc(transport, transport_rtp, ssrc);
+ } else {
+ child = rtp_find_instance_by_media_source_ssrc(transport, transport_rtp, ssrc);
+ }
if (child && child != transport) {
/*
* It is safe to hold the child lock while holding the parent lock.
@@ -5381,7 +5571,7 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
}
if (ssrc_valid && rtp->themssrc_valid) {
- if (ssrc != rtp->themssrc) {
+ if (ssrc != rtp->themssrc && use_packet_source) {
/*
* Skip over this RTCP record as it does not contain the
* correct SSRC. We should not act upon RTCP records
@@ -5524,11 +5714,30 @@ static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, c
transport_rtp->f.src = "RTP";
f = &transport_rtp->f;
break;
+ case AST_RTP_RTCP_RTPFB:
+ switch (rc) {
+ case AST_RTP_RTCP_FMT_NACK:
+ /* If retransmissions are not enabled ignore this message */
+ if (!rtp->send_buffer) {
+ break;
+ }
+
+ if (rtcp_debug_test_addr(addr)) {
+ ast_verbose("Received generic RTCP NACK message\n");
+ }
+
+ ast_rtp_rtcp_handle_nack(instance, rtcpheader, position, length);
+ break;
+ default:
+ break;
+ }
+ break;
case RTCP_PT_FUR:
- /* Handle RTCP FUR as FIR by setting the format to 4 */
+ /* Handle RTCP FUR as FIR by setting the format to 4 */
rc = AST_RTP_RTCP_FMT_FIR;
case RTCP_PT_PSFB:
switch (rc) {
+ case AST_RTP_RTCP_FMT_PLI:
case AST_RTP_RTCP_FMT_FIR:
if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an RTCP Fast Update Request\n");
@@ -5923,7 +6132,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
ssrc = ntohl(rtpheader[2]);
/* Determine the appropriate instance for this */
- child = rtp_find_instance_by_ssrc(instance, rtp, ssrc);
+ child = rtp_find_instance_by_packet_source_ssrc(instance, rtp, ssrc);
if (!child) {
/* Neither the bundled parent nor any child has this SSRC */
return &ast_null_frame;
@@ -6556,6 +6765,8 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
}
} else if (property == AST_RTP_PROPERTY_ASYMMETRIC_CODEC) {
rtp->asymmetric_codec = value;
+ } else if (property == AST_RTP_PROPERTY_RETRANS_SEND) {
+ rtp->send_buffer = ast_data_buffer_alloc(ast_free_ptr, DEFAULT_RTP_BUFFER_SIZE);
}
}
diff --git a/third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch b/third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch
new file mode 100644
index 000000000..3aafd69e8
--- /dev/null
+++ b/third-party/pjproject/patches/0070-os_core_unix-Set-mutex-NULL-in-atomic-destroy-and-ad.patch
@@ -0,0 +1,114 @@
+From 67485f3a6c711f67a40ff46288cb6be1658023bd Mon Sep 17 00:00:00 2001
+From: nanang <nanang@localhost>
+Date: Mon, 26 Mar 2018 10:33:50 +0000
+Subject: [PATCH] Close #2101: - set atomic's mutex to NULL in atomic destroy
+ - added few sanity checks to the atomic functions.
+
+---
+ pjlib/src/pj/os_core_unix.c | 20 ++++++++++++++++++--
+ pjlib/src/pj/os_core_win32.c | 4 ++++
+ 2 files changed, 22 insertions(+), 2 deletions(-)
+
+diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c
+index ebfe84348..c17ad4ef0 100644
+--- a/pjlib/src/pj/os_core_unix.c
++++ b/pjlib/src/pj/os_core_unix.c
+@@ -879,9 +879,16 @@ PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool,
+ */
+ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+ {
++ pj_status_t status;
++
+ PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
++
+ #if PJ_HAS_THREADS
+- return pj_mutex_destroy( atomic_var->mutex );
++ status = pj_mutex_destroy( atomic_var->mutex );
++ if (status == PJ_SUCCESS) {
++ atomic_var->mutex = NULL;
++ }
++ return status;
+ #else
+ return 0;
+ #endif
+@@ -892,10 +899,16 @@ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+ */
+ PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value)
+ {
++ pj_status_t status;
++
+ PJ_CHECK_STACK();
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+
+ #if PJ_HAS_THREADS
+- pj_mutex_lock( atomic_var->mutex );
++ status = pj_mutex_lock( atomic_var->mutex );
++ if (status != PJ_SUCCESS) {
++ return;
++ }
+ #endif
+ atomic_var->value = value;
+ #if PJ_HAS_THREADS
+@@ -946,6 +959,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_inc_and_get(atomic_var);
+ }
+
+@@ -974,6 +988,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_dec_and_get(atomic_var);
+ }
+
+@@ -1005,6 +1020,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
+ PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
+ pj_atomic_value_t value )
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_add_and_get(atomic_var, value);
+ }
+
+diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
+index 1cb6004d3..8c934b34d 100644
+--- a/pjlib/src/pj/os_core_win32.c
++++ b/pjlib/src/pj/os_core_win32.c
+@@ -750,6 +750,7 @@ PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+ PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
+ {
+ PJ_CHECK_STACK();
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+
+ InterlockedExchange(&atomic_var->value, value);
+ }
+@@ -784,6 +785,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_inc_and_get(atomic_var);
+ }
+
+@@ -806,6 +808,7 @@ PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+ */
+ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ pj_atomic_dec_and_get(atomic_var);
+ }
+
+@@ -815,6 +818,7 @@ PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+ PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
+ pj_atomic_value_t value )
+ {
++ PJ_ASSERT_ON_FAIL(atomic_var, return);
+ #if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+ InterlockedExchangeAdd( &atomic_var->value, value );
+ #else
+--
+2.14.3
+
diff --git a/third-party/pjproject/patches/0080-timer-Clean-up-usage-of-timer-heap.patch b/third-party/pjproject/patches/0080-timer-Clean-up-usage-of-timer-heap.patch
new file mode 100644
index 000000000..6c139ba18
--- /dev/null
+++ b/third-party/pjproject/patches/0080-timer-Clean-up-usage-of-timer-heap.patch
@@ -0,0 +1,434 @@
+From 853005378de2ffecee7774e095d8cbfbfa0ab706 Mon Sep 17 00:00:00 2001
+From: George Joseph <gjoseph@digium.com>
+Date: Tue, 2 Jan 2018 06:36:46 -0700
+Subject: [PATCH] timer: Clean up usage of timer heap
+
+Added a new pj_timer_entry_reset function that resets a timer_entry
+for re-use.
+
+Changed direct settings of timer_entry fields to use
+pj_timer_entry_init and pj_timer_entry_reset.
+
+Fixed issues where timers were being rescheduled incorrectly.
+---
+ pjlib/include/pj/timer.h | 14 ++++++++++++++
+ pjlib/src/pj/ssl_sock_ossl.c | 8 +++++---
+ pjlib/src/pj/timer.c | 12 ++++++++++++
+ pjnath/src/pjnath/ice_session.c | 9 ++++++++-
+ pjnath/src/pjnath/nat_detect.c | 2 ++
+ pjnath/src/pjnath/stun_sock.c | 2 +-
+ pjnath/src/pjnath/stun_transaction.c | 10 +++++-----
+ pjnath/src/pjnath/turn_session.c | 3 +++
+ pjnath/src/pjnath/turn_sock.c | 1 +
+ pjnath/src/pjturn-srv/allocation.c | 4 ++--
+ pjnath/src/pjturn-srv/listener_tcp.c | 2 +-
+ pjsip/src/pjsip-simple/evsub.c | 6 +++---
+ pjsip/src/pjsip/sip_endpoint.c | 4 +++-
+ pjsip/src/pjsip/sip_transaction.c | 9 +++------
+ pjsip/src/pjsip/sip_transport.c | 3 +--
+ 15 files changed, 64 insertions(+), 25 deletions(-)
+
+diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h
+index df6155a81..90fc8ac85 100644
+--- a/pjlib/include/pj/timer.h
++++ b/pjlib/include/pj/timer.h
+@@ -212,6 +212,20 @@ PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+ void *user_data,
+ pj_timer_heap_callback *cb );
+
++/**
++ * Reset a timer entry. Application should call this function before reusing
++ * the timer entry.
++ *
++ * @param entry The timer entry to be initialized.
++ * @param id Arbitrary ID assigned by the user/owner of this entry.
++ * Applications can use this ID to distinguish multiple
++ * timer entries that share the same callback and user_data.
++ *
++ * @return The timer entry itself.
++ */
++PJ_DECL(pj_timer_entry*) pj_timer_entry_reset( pj_timer_entry *entry,
++ int id);
++
+ /**
+ * Queries whether a timer entry is currently running.
+ *
+diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
+index 969cc1420..ead1a8fbb 100644
+--- a/pjlib/src/pj/ssl_sock_ossl.c
++++ b/pjlib/src/pj/ssl_sock_ossl.c
+@@ -291,6 +291,7 @@ struct pj_ssl_cert_t
+ static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len);
+ static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata);
+ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
++static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te);
+
+ /*
+ *******************************************************************
+@@ -1621,7 +1622,8 @@ static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
+ pj_timer_heap_cancel(ssock->param.timer_heap,
+ &ssock->timer);
+ }
+- ssock->timer.id = TIMER_CLOSE;
++
++ pj_timer_entry_reset(&ssock->timer, TIMER_CLOSE);
+ pj_time_val_normalize(&interval);
+ if (pj_timer_heap_schedule(ssock->param.timer_heap,
+ &ssock->timer, &interval) != 0)
+@@ -2387,7 +2389,7 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
+ ssock->param.timeout.msec != 0))
+ {
+ pj_assert(ssock->timer.id == TIMER_NONE);
+- ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
++ pj_timer_entry_reset(&ssock->timer, TIMER_HANDSHAKE_TIMEOUT);
+ status = pj_timer_heap_schedule(ssock->param.timer_heap,
+ &ssock->timer,
+ &ssock->param.timeout);
+@@ -3405,7 +3407,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_connect( pj_ssl_sock_t *ssock,
+ ssock->param.timeout.msec != 0))
+ {
+ pj_assert(ssock->timer.id == TIMER_NONE);
+- ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
++ pj_timer_entry_reset(&ssock->timer, TIMER_HANDSHAKE_TIMEOUT);
+ status = pj_timer_heap_schedule(ssock->param.timer_heap,
+ &ssock->timer,
+ &ssock->param.timeout);
+diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c
+index 90a95e37b..13126116f 100644
+--- a/pjlib/src/pj/timer.c
++++ b/pjlib/src/pj/timer.c
+@@ -472,6 +472,18 @@ PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+ return entry;
+ }
+
++PJ_DEF(pj_timer_entry*) pj_timer_entry_reset( pj_timer_entry *entry,
++ int id)
++{
++ entry->id = id;
++ entry->_grp_lock = NULL;
++ entry->_timer_id = -1;
++ entry->_timer_value = (pj_time_val){0, 0};
++
++ return entry;
++}
++
++
+ PJ_DEF(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry )
+ {
+ return (entry->_timer_id >= 1);
+diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
+index 63a0d1c9c..6d0e6abc9 100644
+--- a/pjnath/src/pjnath/ice_session.c
++++ b/pjnath/src/pjnath/ice_session.c
+@@ -1246,6 +1246,7 @@ done:
+ ice->comp_cnt;
+ pj_time_val_normalize(&delay);
+
++ pj_timer_entry_reset(&ice->timer, TIMER_KEEP_ALIVE);
+ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->timer, &delay,
+ TIMER_KEEP_ALIVE,
+@@ -1276,7 +1277,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
+ /* Call callback */
+ if (ice->cb.on_ice_complete) {
+ pj_time_val delay = {0, 0};
+-
++ pj_timer_entry_reset(&ice->timer, TIMER_COMPLETION_CALLBACK);
+ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->timer, &delay,
+ TIMER_COMPLETION_CALLBACK,
+@@ -1507,6 +1508,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
+ delay.sec = 0;
+ delay.msec = ice->opt.controlled_agent_want_nom_timeout;
+ pj_time_val_normalize(&delay);
++ pj_timer_entry_reset(&ice->timer, TIMER_CONTROLLED_WAIT_NOM);
+
+ pj_timer_heap_schedule_w_grp_lock(
+ ice->stun_cfg.timer_heap,
+@@ -1597,6 +1599,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
+ delay.sec = 0;
+ delay.msec = ice->opt.nominated_check_delay;
+ pj_time_val_normalize(&delay);
++ pj_timer_entry_reset(&ice->timer, TIMER_START_NOMINATED_CHECK);
+
+ pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->timer, &delay,
+@@ -1929,6 +1932,8 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
+ pj_time_val timeout = {0, PJ_ICE_TA_VAL};
+
+ pj_time_val_normalize(&timeout);
++ pj_timer_entry_reset(&ice->timer, PJ_TRUE);
++
+ pj_timer_heap_schedule_w_grp_lock(th, te, &timeout, PJ_TRUE,
+ ice->grp_lock);
+ }
+@@ -1986,6 +1991,7 @@ static void start_nominated_check(pj_ice_sess *ice)
+ &ice->clist.timer, PJ_FALSE);
+
+ delay.sec = delay.msec = 0;
++ pj_timer_entry_reset(&ice->timer, PJ_TRUE);
+ status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &ice->clist.timer, &delay,
+ PJ_TRUE,
+@@ -2125,6 +2131,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
+ * return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer);
+ */
+ delay.sec = delay.msec = 0;
++ pj_timer_entry_reset(&ice->timer, PJ_TRUE);
+ status = pj_timer_heap_schedule_w_grp_lock(ice->stun_cfg.timer_heap,
+ &clist->timer, &delay,
+ PJ_TRUE, ice->grp_lock);
+diff --git a/pjnath/src/pjnath/nat_detect.c b/pjnath/src/pjnath/nat_detect.c
+index 8a2408374..7bb364798 100644
+--- a/pjnath/src/pjnath/nat_detect.c
++++ b/pjnath/src/pjnath/nat_detect.c
+@@ -414,6 +414,7 @@ static void end_session(nat_detect_session *sess,
+ delay.msec = 0;
+
+ sess->timer.id = TIMER_DESTROY;
++ pj_timer_entry_init(&sess->timer, TIMER_DESTROY, sess, &on_sess_timer);
+ pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
+ }
+
+@@ -933,6 +934,7 @@ static void on_sess_timer(pj_timer_heap_t *th,
+
+ if (next_timer) {
+ pj_time_val delay = {0, TEST_INTERVAL};
++ pj_timer_entry_reset(te, TIMER_TEST);
+ pj_timer_heap_schedule(th, te, &delay);
+ } else {
+ te->id = 0;
+diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c
+index 6028e0c47..3aab27a1d 100644
+--- a/pjnath/src/pjnath/stun_sock.c
++++ b/pjnath/src/pjnath/stun_sock.c
+@@ -864,7 +864,7 @@ static void start_ka_timer(pj_stun_sock *stun_sock)
+
+ delay.sec = stun_sock->ka_interval;
+ delay.msec = 0;
+-
++ pj_timer_entry_reset(&stun_sock->ka_timer, PJ_TRUE);
+ pj_timer_heap_schedule_w_grp_lock(stun_sock->stun_cfg.timer_heap,
+ &stun_sock->ka_timer,
+ &delay, PJ_TRUE,
+diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c
+index 28f623005..ad87b7b6c 100644
+--- a/pjnath/src/pjnath/stun_transaction.c
++++ b/pjnath/src/pjnath/stun_transaction.c
+@@ -86,11 +86,8 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
+ tsx->grp_lock = grp_lock;
+ pj_memcpy(&tsx->cb, cb, sizeof(*cb));
+
+- tsx->retransmit_timer.cb = &retransmit_timer_callback;
+- tsx->retransmit_timer.user_data = tsx;
+-
+- tsx->destroy_timer.cb = &destroy_timer_callback;
+- tsx->destroy_timer.user_data = tsx;
++ pj_timer_entry_init(&tsx->retransmit_timer, 0, tsx, &retransmit_timer_callback);
++ pj_timer_entry_init(&tsx->destroy_timer, 0, tsx, &destroy_timer_callback);
+
+ pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "utsx%p", tsx);
+
+@@ -120,6 +117,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy(
+ pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
+ TIMER_INACTIVE);
+
++ pj_timer_entry_reset(&tsx->destroy_timer, TIMER_ACTIVE);
+ status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+ &tsx->destroy_timer, delay,
+ TIMER_ACTIVE, tsx->grp_lock);
+@@ -237,6 +235,7 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx,
+ * cancel it (as opposed to when schedule_timer() failed we cannot
+ * cancel transmission).
+ */;
++ pj_timer_entry_reset(&tsx->retransmit_timer, TIMER_ACTIVE);
+ status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+ &tsx->retransmit_timer,
+ &tsx->retransmit_time,
+@@ -315,6 +314,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
+ * cancel it (as opposed to when schedule_timer() failed we cannot
+ * cancel transmission).
+ */;
++ pj_timer_entry_reset(&tsx->retransmit_timer, TIMER_ACTIVE);
+ status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
+ &tsx->retransmit_timer,
+ &tsx->retransmit_time,
+diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c
+index bbea027f3..e4685e625 100644
+--- a/pjnath/src/pjnath/turn_session.c
++++ b/pjnath/src/pjnath/turn_session.c
+@@ -431,6 +431,7 @@ static void sess_shutdown(pj_turn_session *sess,
+
+ pj_timer_heap_cancel_if_active(sess->timer_heap, &sess->timer,
+ TIMER_NONE);
++ pj_timer_entry_reset(&sess->timer, TIMER_DESTROY);
+ pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer,
+ &delay, TIMER_DESTROY,
+ sess->grp_lock);
+@@ -1434,6 +1435,7 @@ static void on_allocate_success(pj_turn_session *sess,
+ timeout.sec = sess->ka_interval;
+ timeout.msec = 0;
+
++ pj_timer_entry_reset(&sess->timer, TIMER_KEEP_ALIVE);
+ pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer,
+ &timeout, TIMER_KEEP_ALIVE,
+ sess->grp_lock);
+@@ -2080,6 +2082,7 @@ static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e)
+ delay.sec = sess->ka_interval;
+ delay.msec = 0;
+
++ pj_timer_entry_reset(&sess->timer, TIMER_KEEP_ALIVE);
+ pj_timer_heap_schedule_w_grp_lock(sess->timer_heap, &sess->timer,
+ &delay, TIMER_KEEP_ALIVE,
+ sess->grp_lock);
+diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c
+index a30ab5153..507858048 100644
+--- a/pjnath/src/pjnath/turn_sock.c
++++ b/pjnath/src/pjnath/turn_sock.c
+@@ -928,6 +928,7 @@ static void turn_on_state(pj_turn_session *sess,
+
+ pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap,
+ &turn_sock->timer, 0);
++ pj_timer_entry_reset(&turn_sock->timer, TIMER_DESTROY);
+ pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap,
+ &turn_sock->timer,
+ &delay, TIMER_DESTROY,
+diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c
+index 6c9c9ce11..88533926b 100644
+--- a/pjnath/src/pjturn-srv/allocation.c
++++ b/pjnath/src/pjturn-srv/allocation.c
+@@ -513,7 +513,7 @@ static void alloc_shutdown(pj_turn_allocation *alloc)
+ */
+
+ /* Schedule destroy timer */
+- alloc->relay.timer.id = TIMER_ID_DESTROY;
++ pj_timer_entry_reset(&alloc->relay.timer, TIMER_ID_DESTROY);
+ pj_timer_heap_schedule(alloc->server->core.timer_heap,
+ &alloc->relay.timer, &destroy_delay);
+ }
+@@ -538,7 +538,7 @@ static pj_status_t resched_timeout(pj_turn_allocation *alloc)
+ delay.sec = alloc->relay.lifetime;
+ delay.msec = 0;
+
+- alloc->relay.timer.id = TIMER_ID_TIMEOUT;
++ pj_timer_entry_reset(&alloc->relay.timer, TIMER_ID_TIMEOUT);
+ status = pj_timer_heap_schedule(alloc->server->core.timer_heap,
+ &alloc->relay.timer, &delay);
+ if (status != PJ_SUCCESS) {
+diff --git a/pjnath/src/pjturn-srv/listener_tcp.c b/pjnath/src/pjturn-srv/listener_tcp.c
+index 796ed471b..4a9550c2e 100644
+--- a/pjnath/src/pjturn-srv/listener_tcp.c
++++ b/pjnath/src/pjturn-srv/listener_tcp.c
+@@ -475,7 +475,7 @@ static void tcp_dec_ref(pj_turn_transport *tp,
+
+ if (tcp->ref_cnt == 0 && tcp->timer.id == TIMER_NONE) {
+ pj_time_val delay = { SHUTDOWN_DELAY, 0 };
+- tcp->timer.id = TIMER_DESTROY;
++ pj_timer_entry_reset(&tcp->timer, TIMER_DESTROY);
+ pj_timer_heap_schedule(tcp->base.listener->server->core.timer_heap,
+ &tcp->timer, &delay);
+ }
+diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c
+index eb666654f..7748853d2 100644
+--- a/pjsip/src/pjsip-simple/evsub.c
++++ b/pjsip/src/pjsip-simple/evsub.c
+@@ -518,6 +518,7 @@ static void set_timer( pjsip_evsub *sub, int timer_id,
+ timeout.sec = seconds;
+ timeout.msec = 0;
+
++ pj_timer_entry_reset(&sub->timer, timer_id);
+ pj_timer_heap_schedule_w_grp_lock(
+ pjsip_endpt_get_timer_heap(sub->endpt),
+ &sub->timer, &timeout, timer_id, sub->grp_lock);
+@@ -655,7 +656,7 @@ static void on_timer( pj_timer_heap_t *timer_heap,
+ /* If this timer entry has just been rescheduled or cancelled
+ * while waiting for dialog mutex, just return (see #1885 scenario 1).
+ */
+- if (pj_timer_entry_running(entry) || entry->id == TIMER_TYPE_NONE) {
++ if (entry->id == TIMER_TYPE_NONE) {
+ pjsip_dlg_dec_lock(sub->dlg);
+ return;
+ }
+@@ -786,8 +787,7 @@ static pj_status_t evsub_create( pjsip_dialog *dlg,
+ pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
+ pj_list_init(&sub->sub_hdr_list);
+
+- sub->timer.user_data = sub;
+- sub->timer.cb = &on_timer;
++ pj_timer_entry_init(&sub->timer, 0, sub, &on_timer);
+
+ /* Set name. */
+ pj_ansi_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
+diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
+index d810781d5..5c98a5bf6 100644
+--- a/pjsip/src/pjsip/sip_endpoint.c
++++ b/pjsip/src/pjsip/sip_endpoint.c
+@@ -788,6 +788,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_dbg(pjsip_endpoint *endpt,
+ {
+ PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
+ entry, delay->sec, delay->msec));
++ pj_timer_entry_reset(entry, entry->id);
+ return pj_timer_heap_schedule_dbg(endpt->timer_heap, entry, delay,
+ src_file, src_line);
+ }
+@@ -798,6 +799,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+ {
+ PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
+ entry, delay->sec, delay->msec));
++ pj_timer_entry_reset(entry, entry->id);
+ return pj_timer_heap_schedule( endpt->timer_heap, entry, delay );
+ }
+ #endif
+@@ -809,7 +811,7 @@ PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry )
+ {
+ PJ_LOG(6, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry));
+- pj_timer_heap_cancel( endpt->timer_heap, entry );
++ pj_timer_heap_cancel_if_active( endpt->timer_heap, entry, 0 );
+ }
+
+ /*
+diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
+index d52b12a72..5236e63f7 100644
+--- a/pjsip/src/pjsip/sip_transaction.c
++++ b/pjsip/src/pjsip/sip_transaction.c
+@@ -978,6 +978,7 @@ static pj_status_t tsx_schedule_timer(pjsip_transaction *tsx,
+ pj_status_t status;
+
+ pj_assert(active_id != 0);
++ pj_timer_entry_reset(entry, active_id);
+ status = pj_timer_heap_schedule_w_grp_lock(timer_heap, entry,
+ delay, active_id,
+ tsx->grp_lock);
+@@ -1019,12 +1020,8 @@ static pj_status_t tsx_create( pjsip_module *tsx_user,
+ pj_memcpy(pool->obj_name, tsx->obj_name, sizeof(pool->obj_name));
+
+ tsx->handle_200resp = 1;
+- tsx->retransmit_timer.id = TIMER_INACTIVE;
+- tsx->retransmit_timer.user_data = tsx;
+- tsx->retransmit_timer.cb = &tsx_timer_callback;
+- tsx->timeout_timer.id = TIMER_INACTIVE;
+- tsx->timeout_timer.user_data = tsx;
+- tsx->timeout_timer.cb = &tsx_timer_callback;
++ pj_timer_entry_init(&tsx->retransmit_timer, TIMER_INACTIVE, tsx, &tsx_timer_callback);
++ pj_timer_entry_init(&tsx->timeout_timer, TIMER_INACTIVE, tsx, &tsx_timer_callback);
+
+ if (grp_lock) {
+ tsx->grp_lock = grp_lock;
+diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
+index 3d27d5d71..834babeae 100644
+--- a/pjsip/src/pjsip/sip_transport.c
++++ b/pjsip/src/pjsip/sip_transport.c
+@@ -1092,8 +1092,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
+ /* Init. */
+ tp->tpmgr = mgr;
+ pj_bzero(&tp->idle_timer, sizeof(tp->idle_timer));
+- tp->idle_timer.user_data = tp;
+- tp->idle_timer.cb = &transport_idle_callback;
++ pj_timer_entry_init(&tp->idle_timer, 0, tp, &transport_idle_callback);
+
+ /*
+ * Register to hash table (see Trac ticket #42).
+--
+2.14.3
+
diff --git a/third-party/pjproject/patches/0090-sip_transaction-In-tsx_timer_callback-check-if-tsx-i.patch b/third-party/pjproject/patches/0090-sip_transaction-In-tsx_timer_callback-check-if-tsx-i.patch
new file mode 100644
index 000000000..12df3469c
--- /dev/null
+++ b/third-party/pjproject/patches/0090-sip_transaction-In-tsx_timer_callback-check-if-tsx-i.patch
@@ -0,0 +1,31 @@
+From beaa7874ff8e3b1d2951218c94e7e6bbba9c0531 Mon Sep 17 00:00:00 2001
+From: George Joseph <gjoseph@digium.com>
+Date: Sun, 25 Mar 2018 12:30:05 -0600
+Subject: [PATCH] sip_transaction: In tsx_timer_callback, check if tsx is
+ already gone
+
+There have been cases that when the transaction timer callback is called
+the tsx is already destroyed. This causes a crash. We now check the
+tsx state and return if the tsx is already destroyed.
+---
+ pjsip/src/pjsip/sip_transaction.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
+index d52b12a72..6d4cdc65f 100644
+--- a/pjsip/src/pjsip/sip_transaction.c
++++ b/pjsip/src/pjsip/sip_transaction.c
+@@ -1119,6 +1119,10 @@ static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
+
+ PJ_UNUSED_ARG(theap);
+
++ if (tsx->state >= PJSIP_TSX_STATE_DESTROYED) {
++ return;
++ }
++
+ if (entry->id == TRANSPORT_ERR_TIMER) {
+ /* Posted transport error event */
+ entry->id = 0;
+--
+2.14.3
+
diff --git a/utils/Makefile b/utils/Makefile
index d62d45f4f..ae2af08e2 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -164,7 +164,7 @@ aelparse.c: $(ASTTOPDIR)/res/ael/ael_lex.c
$(CMD_PREFIX) mv "$@.new" "$@"
aelparse.o: _ASTCFLAGS+=-I$(ASTTOPDIR)/res -Wno-unused
-aelparse: LIBS+=-lm
+aelparse: LIBS+=-lm $(AST_CLANG_BLOCKS_LIBS)
aelparse: aelparse.o aelbison.o pbx_ael.o hashtab.o lock.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o pval.o extconf.o astmm.o
threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c
@@ -174,6 +174,7 @@ threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c
extconf.o: extconf.c
+conf2ael: LIBS+=$(AST_CLANG_BLOCKS_LIBS)
conf2ael: conf2ael.o ast_expr2f.o ast_expr2.o hashtab.o lock.o aelbison.o aelparse.o pbx_ael.o pval.o extconf.o strcompat.o astmm.o
check_expr2: $(ASTTOPDIR)/main/ast_expr2f.c $(ASTTOPDIR)/main/ast_expr2.c $(ASTTOPDIR)/main/ast_expr2.h astmm.o