diff options
53 files changed, 2890 insertions, 1727 deletions
@@ -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 ------------ ------------------------------------------------------------------------------ @@ -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 @@ -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 |