Fix stuck channel in ARI through the introduction of synchronous bridge actions.
authorMark Michelson <mmichelson@digium.com>
Mon, 17 Mar 2014 17:22:12 +0000 (17:22 +0000)
committerMark Michelson <mmichelson@digium.com>
Mon, 17 Mar 2014 17:22:12 +0000 (17:22 +0000)
Playing back a file to a channel in an ARI bridge would attempt to wait until
the playback concluded before returning. The method used involved signaling the
waiting thread in the ARI custom playback function.

The problem with this is that there were some corner cases that were not accounted for:
* If a bridge channel could not be found, then we never would attempt the playback but
  would still attempt to wait for the playback to complete.
* If the bridge playfile action failed to queue, we would still attempt to wait for the
  playback to complete.
* If the bridge playfile action were queued but some circumstance caused the playback
  not to occur (the bridge dies, the channel is removed from the bridge), then we would
  never be notified.

The solution to this is to move the waiting logic into the bridge code. A new bridge
API function is added to queue a synchronous action on a bridge. The waiting thread
is notified when the queued frame has been freed, either due to an error occurring
or due to successful playback. As a failsafe, the waiting thread has a 10 minute
timeout just in case there is a frame leak somewhere.

Review: https://reviewboard.asterisk.org/r/3338
........

Merged revisions 410673 from http://svn.asterisk.org/svn/asterisk/branches/12

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410684 65c4cc65-6c06-0410-ace0-fbb531ad65f3

17 files changed:
bridges/bridge_softmix.c
configs/sorcery.conf.sample
funcs/func_frame_trace.c
include/asterisk/bridge_channel.h
include/asterisk/frame.h
include/asterisk/sorcery.h
main/bridge_channel.c
main/channel.c
main/frame.c
main/sorcery.c
res/res_mwi_external.c
res/res_pjsip/config_system.c
res/res_pjsip/pjsip_configuration.c
res/res_stasis_playback.c
tests/test_sorcery.c
tests/test_sorcery_astdb.c
tests/test_sorcery_realtime.c

index 271f856..e2ab213 100644 (file)
@@ -661,6 +661,9 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha
        case AST_FRAME_BRIDGE_ACTION:
                res = ast_bridge_queue_everyone_else(bridge, bridge_channel, frame);
                break;
+       case AST_FRAME_BRIDGE_ACTION_SYNC:
+               ast_log(LOG_ERROR, "Synchronous bridge action written to a softmix bridge.\n");
+               ast_assert(0);
        default:
                ast_debug(3, "Frame type %d unsupported\n", frame->frametype);
                /* "Accept" the frame and discard it. */
index ee13003..7406214 100644 (file)
@@ -41,7 +41,7 @@
 ;
 ; The following object mappings are used by the unit test to test certain functionality of sorcery.
 ;
-[test_sorcery]
+[test_sorcery_section]
 test=memory
 
 [test_sorcery_cache]
index 17f7c89..bdbc596 100644 (file)
@@ -392,6 +392,10 @@ static void print_frame(struct ast_frame *frame)
                ast_verbose("FrameType: Bridge\n");
                ast_verbose("SubClass: %d\n", frame->subclass.integer);
                break;
+       case AST_FRAME_BRIDGE_ACTION_SYNC:
+               ast_verbose("Frametype: Synchronous Bridge\n");
+               ast_verbose("Subclass: %d\n", frame->subclass.integer);
+               break;
        }
 
        ast_verbose("Src: %s\n", ast_strlen_zero(frame->src) ? "NOT PRESENT" : frame->src);
index 73c6570..c8aaf89 100644 (file)
@@ -529,6 +529,28 @@ int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel,
 int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
 
 /*!
+ * \brief Synchronously queue a bridge action play file frame onto the bridge channel.
+ * \since 12.2.0
+ *
+ * \param bridge_channel Which channel to put the frame onto.
+ * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
+ * \param playfile Sound filename to play.
+ * \param moh_class MOH class to request bridge peers to hear while file is played.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * This function will block until the queued frame has been destroyed. This will happen
+ * either if an error occurs or if the queued playback finishes.
+ *
+ * \note No locks may be held when calling this function.
+ *
+ * \retval 0 The playback was successfully queued.
+ * \retval -1 The playback could not be queued.
+ */
+int ast_bridge_channel_queue_playfile_sync(struct ast_bridge_channel *bridge_channel,
+               ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
+
+/*!
  * \brief Custom callback run on a bridge channel.
  *
  * \param bridge_channel Which channel to operate on.
index 5338383..846832a 100644 (file)
@@ -122,6 +122,12 @@ enum ast_frame_type {
        AST_FRAME_DTMF_BEGIN,
        /*! Internal bridge module action. */
        AST_FRAME_BRIDGE_ACTION,
+       /*! Internal synchronous bridge module action.
+        * Synchronous bridge actions may be queued onto bridge
+        * channels, but they absolutely must not ever be written
+        * directly into bridges.
+        */
+       AST_FRAME_BRIDGE_ACTION_SYNC,
 };
 #define AST_FRAME_DTMF AST_FRAME_DTMF_END
 
index 026fb40..22616d5 100644 (file)
  * object types to their respective wizards (object storage modules). If the developer would like
  * to allow the user to configure this using the sorcery.conf configuration file the
  * \ref ast_sorcery_apply_config API call can be used to read in the configuration file and apply the
- * mappings. If the storage of the object types are such that a default wizard can be used this can
+ * mappings. \ref ast_sorcery_open will automatically call \ref ast_sorcery_apply_config to allow
+ * for configuration of objects using the same category name as the module that is opening the
+ * sorcery instance. Direct calls to \ref ast_sorcery_apply_config should only be performed if a
+ * module wishes to allow for additional configuration sections in sorcery.conf to be used.
+ * If the storage of the object types are such that a default wizard can be used this can
  * be applied using the \ref ast_sorcery_apply_default API call. Note that the default mappings will not
  * override configured mappings. They are only used in the case where no configured mapping exists.
  *
@@ -322,6 +326,9 @@ int ast_sorcery_wizard_unregister(const struct ast_sorcery_wizard *interface);
  *
  * \param module The module name (AST_MODULE)
  *
+ * When called, this will automatically also call __ast_sorcery_apply_config()
+ * with the module name as the configuration section.
+ *
  * \retval non-NULL success
  * \retval NULL if allocation failed
  */
@@ -343,6 +350,17 @@ struct ast_sorcery *__ast_sorcery_open(const char *module);
  */
 struct ast_sorcery *ast_sorcery_retrieve_by_module_name(const char *module);
 
+enum ast_sorcery_apply_result {
+       /*! Sorcery wizard failed to apply. */
+       AST_SORCERY_APPLY_FAIL = -1,
+       /*! Sorcery wizard applied successfully. */
+       AST_SORCERY_APPLY_SUCCESS = 0,
+       /*! Sorcery wizard has already been applied to the object type. */
+       AST_SORCERY_APPLY_DUPLICATE = 1,
+       /*! Default sorcery wizard is unnecessary since a wizard has already been applied to the object type. */
+       AST_SORCERY_APPLY_DEFAULT_UNNECESSARY = 2,
+};
+
 /*!
  * \brief Apply configured wizard mappings
  *
@@ -350,10 +368,17 @@ struct ast_sorcery *ast_sorcery_retrieve_by_module_name(const char *module);
  * \param name Name of the category to use within the configuration file, normally the module name
  * \param module The module name (AST_MODULE)
  *
- * \retval 0 success
- * \retval -1 failure
+ * This function is called automatically by __ast_sorcery_open() using the module name as the
+ * configuration category. The only reason you should call this function is if your module
+ * wishes to apply configuration from additional sections of sorcery.conf.
+ *
+ * If a configuration section attempts to apply the same sorcery wizard to an object type
+ * more than once, the wizard will only be applied one time.
+ *
+ * \return What happened when attempting to apply the default.
  */
-int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module);
+enum ast_sorcery_apply_result __ast_sorcery_apply_config(struct ast_sorcery *sorcery,
+               const char *name, const char *module);
 
 #define ast_sorcery_apply_config(sorcery, name) \
        __ast_sorcery_apply_config((sorcery), (name), AST_MODULE)
@@ -367,14 +392,14 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co
  * \param name Name of the wizard to use
  * \param data Data to be passed to wizard
  *
- * \retval 0 success
- * \retval -1 failure
+ * \return What occurred when applying the default
  *
  * \note This should be called *after* applying configuration sourced mappings
  *
  * \note Only a single default can exist per object type
  */
-int __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data);
+enum ast_sorcery_apply_result __ast_sorcery_apply_default(struct ast_sorcery *sorcery,
+               const char *type, const char *module, const char *name, const char *data);
 
 #define ast_sorcery_apply_default(sorcery, type, name, data) \
        __ast_sorcery_apply_default((sorcery), (type), AST_MODULE, (name), (data))
index 06da4ee..75008fe 100644 (file)
@@ -35,6 +35,7 @@
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <signal.h>
+#include <semaphore.h>
 
 #include "asterisk/heap.h"
 #include "asterisk/astobj2.h"
@@ -70,6 +71,142 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  */
 typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen);
 
+/*!
+ * \brief Counter used for assigning synchronous bridge action IDs
+ */
+static int sync_ids;
+
+/*!
+ * \brief Frame payload for synchronous bridge actions.
+ *
+ * The payload serves as a wrapper around the actual payload of the
+ * frame, with the addition of an id used to find the associated
+ * bridge_sync object.
+ */
+struct sync_payload {
+       /*! Unique ID for this synchronous action */
+       unsigned int id;
+       /*! Actual frame data to process */
+       unsigned char data[0];
+};
+
+/*!
+ * \brief Synchronous bridge action object.
+ *
+ * Synchronous bridge actions require the ability for one thread to wait
+ * and for another thread to indicate that the action has completed. This
+ * structure facilitates that goal by providing synchronization structures.
+ */
+struct bridge_sync {
+       /*! Unique ID of this synchronization object. Corresponds with ID in synchronous frame payload */
+       unsigned int id;
+       /*! Semaphore used for synchronization */
+       sem_t sem;
+       /*! Pointer to next entry in the list */
+       AST_LIST_ENTRY(bridge_sync) list;
+};
+
+/*!
+ * \brief List holding active synchronous action objects.
+ */
+static AST_RWLIST_HEAD_STATIC(sync_structs, bridge_sync);
+
+/*!
+ * \brief initialize a synchronous bridge object.
+ *
+ * This both initializes the structure and adds it to the list of
+ * synchronization structures.
+ *
+ * \param sync_struct The synchronization object to initialize.
+ * \param id ID to assign to the synchronization object.
+ */
+static void bridge_sync_init(struct bridge_sync *sync_struct, unsigned int id)
+{
+       memset(sync_struct, 0, sizeof(*sync_struct));
+       sync_struct->id = id;
+       sem_init(&sync_struct->sem, 0, 0);
+
+       AST_RWLIST_WRLOCK(&sync_structs);
+       AST_RWLIST_INSERT_TAIL(&sync_structs, sync_struct, list);
+       AST_RWLIST_UNLOCK(&sync_structs);
+}
+
+/*!
+ * \brief Clean up a syncrhonization bridge object.
+ *
+ * This frees fields within the synchronization object and removes
+ * it from the list of active synchronization objects.
+ *
+ * Since synchronization objects are stack-allocated, it is vital
+ * that this is called before the synchronization object goes
+ * out of scope.
+ *
+ * \param sync_struct Synchronization object to clean up.
+ */
+static void bridge_sync_cleanup(struct bridge_sync *sync_struct)
+{
+       struct bridge_sync *iter;
+
+       AST_RWLIST_WRLOCK(&sync_structs);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&sync_structs, iter, list) {
+               if (iter->id == sync_struct->id) {
+                       AST_LIST_REMOVE_CURRENT(list);
+                       break;
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+       AST_RWLIST_UNLOCK(&sync_structs);
+
+       sem_destroy(&sync_struct->sem);
+}
+
+/*!
+ * \brief Failsafe for synchronous bridge action waiting.
+ *
+ * When waiting for a synchronous bridge action to complete,
+ * if there is a frame resource leak somewhere, it is possible
+ * that we will never get notified that the synchronous action
+ * completed.
+ *
+ * If a significant amount of time passes, then we will abandon
+ * waiting for the synchrnous bridge action to complete.
+ *
+ * This constant represents the number of milliseconds we will
+ * wait for the bridge action to complete.
+ */
+#define PLAYBACK_TIMEOUT (600 * 1000)
+
+/*!
+ * \brief Wait for a synchronous bridge action to complete.
+ *
+ * \param sync_struct Synchronization object corresponding to the bridge action.
+ */
+static void bridge_sync_wait(struct bridge_sync *sync_struct)
+{
+       struct timeval timeout_val = ast_tvadd(ast_tvnow(), ast_samp2tv(PLAYBACK_TIMEOUT, 1000));
+       struct timespec timeout_spec = {
+               .tv_sec = timeout_val.tv_sec,
+               .tv_nsec = timeout_val.tv_usec * 1000,
+       };
+
+       sem_timedwait(&sync_struct->sem, &timeout_spec);
+}
+
+/*!
+ * \brief Signal that waiting for a synchronous bridge action is no longer necessary.
+ *
+ * This may occur for several reasons
+ * \li The synchronous bridge action has completed.
+ * \li The bridge channel has been removed from the bridge.
+ * \li The synchronous bridge action could not be queued.
+ *
+ * \param sync_struct Synchronization object corresponding to the bridge action.
+ */
+static void bridge_sync_signal(struct bridge_sync *sync_struct)
+{
+       sem_post(&sync_struct->sem);
+}
+
 void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
 {
        struct ast_bridge *bridge;
@@ -342,6 +479,8 @@ void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int caus
  */
 static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
 {
+       ast_assert(frame->frametype != AST_FRAME_BRIDGE_ACTION_SYNC);
+
        ast_bridge_channel_lock_bridge(bridge_channel);
 /*
  * XXX need to implement a deferred write queue for when there
@@ -493,7 +632,8 @@ static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen)
+static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel,
+       enum bridge_channel_action_type action, const void *data, size_t datalen)
 {
        struct ast_frame frame = {
                .frametype = AST_FRAME_BRIDGE_ACTION,
@@ -507,6 +647,52 @@ static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_ch
 
 /*!
  * \internal
+ * \brief Queue an action frame onto the bridge channel with data synchronously.
+ * \since 12.2.0
+ *
+ * The function will not return until the queued frame is freed.
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int bridge_channel_queue_action_data_sync(struct ast_bridge_channel *bridge_channel,
+       enum bridge_channel_action_type action, const void *data, size_t datalen)
+{
+       struct sync_payload *sync_payload;
+       int sync_payload_len = sizeof(*sync_payload) + datalen;
+       struct bridge_sync sync_struct;
+       struct ast_frame frame = {
+               .frametype = AST_FRAME_BRIDGE_ACTION_SYNC,
+               .subclass.integer = action,
+       };
+
+       /* Make sure we don't end up trying to wait on ourself to deliver the frame */
+       ast_assert(!pthread_equal(pthread_self(), bridge_channel->thread));
+
+       sync_payload = ast_alloca(sync_payload_len);
+       sync_payload->id = ast_atomic_fetchadd_int(&sync_ids, +1);
+       memcpy(sync_payload->data, data, datalen);
+
+       frame.datalen = sync_payload_len;
+       frame.data.ptr = sync_payload;
+
+       bridge_sync_init(&sync_struct, sync_payload->id);
+       if (ast_bridge_channel_queue_frame(bridge_channel, &frame)) {
+               bridge_sync_cleanup(&sync_struct);
+               return -1;
+       }
+
+       bridge_sync_wait(&sync_struct);
+       bridge_sync_cleanup(&sync_struct);
+       return 0;
+}
+/*!
+ * \internal
  * \brief Write an action frame onto the bridge channel with data.
  * \since 12.0.0
  *
@@ -518,7 +704,8 @@ static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_ch
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen)
+static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel,
+       enum bridge_channel_action_type action, const void *data, size_t datalen)
 {
        struct ast_frame frame = {
                .frametype = AST_FRAME_BRIDGE_ACTION,
@@ -530,6 +717,27 @@ static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_ch
        return bridge_channel_write_frame(bridge_channel, &frame);
 }
 
+static void bridge_frame_free(struct ast_frame *frame)
+{
+       if (frame->frametype == AST_FRAME_BRIDGE_ACTION_SYNC) {
+               struct sync_payload *sync_payload = frame->data.ptr;
+               struct bridge_sync *sync;
+
+               AST_RWLIST_RDLOCK(&sync_structs);
+               AST_RWLIST_TRAVERSE(&sync_structs, sync, list) {
+                       if (sync->id == sync_payload->id) {
+                               break;
+                       }
+               }
+               if (sync) {
+                       bridge_sync_signal(sync);
+               }
+               AST_RWLIST_UNLOCK(&sync_structs);
+       }
+
+       ast_frfree(frame);
+}
+
 int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
 {
        struct ast_frame *dup;
@@ -557,7 +765,7 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st
        if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) {
                /* Drop frames on channels leaving the bridge. */
                ast_bridge_channel_unlock(bridge_channel);
-               ast_frfree(dup);
+               bridge_frame_free(dup);
                return 0;
        }
 
@@ -816,7 +1024,7 @@ static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it,
        size_t len_payload = sizeof(*payload) + len_name + len_moh;
 
        /* Fill in play file frame data. */
-       payload = alloca(len_payload);
+       payload = ast_alloca(len_payload);
        payload->custom_play = custom_play;
        payload->moh_offset = len_moh ? len_name : 0;
        strcpy(payload->playfile, playfile);/* Safe */
@@ -839,6 +1047,13 @@ int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel,
                bridge_channel, custom_play, playfile, moh_class);
 }
 
+int ast_bridge_channel_queue_playfile_sync(struct ast_bridge_channel *bridge_channel,
+               ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
+{
+       return payload_helper_playfile(bridge_channel_queue_action_data_sync,
+               bridge_channel, custom_play, playfile, moh_class);
+}
+
 struct bridge_custom_callback {
        /*! Call this function on the bridge channel thread. */
        ast_bridge_custom_callback_fn callback;
@@ -1389,53 +1604,55 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c
  *
  * \param bridge_channel Channel to execute the action on.
  * \param action What to do.
+ * \param data data from the action.
  *
  * \return Nothing
  */
-static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action)
+static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel,
+       enum bridge_channel_action_type action, void *data)
 {
-       switch (action->subclass.integer) {
+       switch (action) {
        case BRIDGE_CHANNEL_ACTION_DTMF_STREAM:
                bridge_channel_suspend(bridge_channel);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-               bridge_channel_dtmf_stream(bridge_channel, action->data.ptr);
+               bridge_channel_dtmf_stream(bridge_channel, data);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                bridge_channel_unsuspend(bridge_channel);
                break;
        case BRIDGE_CHANNEL_ACTION_TALKING_START:
        case BRIDGE_CHANNEL_ACTION_TALKING_STOP:
                bridge_channel_talking(bridge_channel,
-                       action->subclass.integer == BRIDGE_CHANNEL_ACTION_TALKING_START);
+                       action == BRIDGE_CHANNEL_ACTION_TALKING_START);
                break;
        case BRIDGE_CHANNEL_ACTION_PLAY_FILE:
                bridge_channel_suspend(bridge_channel);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-               bridge_channel_playfile(bridge_channel, action->data.ptr);
+               bridge_channel_playfile(bridge_channel, data);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                bridge_channel_unsuspend(bridge_channel);
                break;
        case BRIDGE_CHANNEL_ACTION_RUN_APP:
                bridge_channel_suspend(bridge_channel);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-               bridge_channel_run_app(bridge_channel, action->data.ptr);
+               bridge_channel_run_app(bridge_channel, data);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                bridge_channel_unsuspend(bridge_channel);
                break;
        case BRIDGE_CHANNEL_ACTION_CALLBACK:
-               bridge_channel_do_callback(bridge_channel, action->data.ptr);
+               bridge_channel_do_callback(bridge_channel, data);
                break;
        case BRIDGE_CHANNEL_ACTION_PARK:
                bridge_channel_suspend(bridge_channel);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-               bridge_channel_park(bridge_channel, action->data.ptr);
+               bridge_channel_park(bridge_channel, data);
                ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
                bridge_channel_unsuspend(bridge_channel);
                break;
        case BRIDGE_CHANNEL_ACTION_BLIND_TRANSFER:
-               bridge_channel_blind_transfer(bridge_channel, action->data.ptr);
+               bridge_channel_blind_transfer(bridge_channel, data);
                break;
        case BRIDGE_CHANNEL_ACTION_ATTENDED_TRANSFER:
-               bridge_channel_attended_transfer(bridge_channel, action->data.ptr);
+               bridge_channel_attended_transfer(bridge_channel, data);
                break;
        default:
                break;
@@ -1700,6 +1917,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
 {
        struct ast_frame *fr;
        char nudge;
+       struct sync_payload *sync_payload;
 
        ast_bridge_channel_lock(bridge_channel);
        if (read(bridge_channel->alert_pipe[0], &nudge, sizeof(nudge)) < 0) {
@@ -1715,7 +1933,11 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
        }
        switch (fr->frametype) {
        case AST_FRAME_BRIDGE_ACTION:
-               bridge_channel_handle_action(bridge_channel, fr);
+               bridge_channel_handle_action(bridge_channel, fr->subclass.integer, fr->data.ptr);
+               break;
+       case AST_FRAME_BRIDGE_ACTION_SYNC:
+               sync_payload = fr->data.ptr;
+               bridge_channel_handle_action(bridge_channel, fr->subclass.integer, sync_payload->data);
                break;
        case AST_FRAME_CONTROL:
                bridge_channel_handle_control(bridge_channel, fr);
@@ -1728,7 +1950,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe
                ast_write(bridge_channel->chan, fr);
                break;
        }
-       ast_frfree(fr);
+       bridge_frame_free(fr);
 }
 
 /*! \brief Internal function to handle DTMF from a channel */
@@ -1745,7 +1967,7 @@ static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_ch
        if (hook) {
                enum ast_frame_type frametype = frame->frametype;
 
-               ast_frfree(frame);
+               bridge_frame_free(frame);
                frame = NULL;
 
                ao2_ref(hook, -1);
@@ -1805,7 +2027,7 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
                switch (frame->subclass.integer) {
                case AST_CONTROL_HANGUP:
                        ast_bridge_channel_kick(bridge_channel, 0);
-                       ast_frfree(frame);
+                       bridge_frame_free(frame);
                        return;
                default:
                        break;
@@ -1818,7 +2040,7 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
                        return;
                }
                if (!bridge_channel->features->dtmf_passthrough) {
-                       ast_frfree(frame);
+                       bridge_frame_free(frame);
                        return;
                }
                break;
@@ -1828,7 +2050,7 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
 
        /* Simply write the frame out to the bridge technology. */
        bridge_channel_write_frame(bridge_channel, frame);
-       ast_frfree(frame);
+       bridge_frame_free(frame);
 }
 
 /*!
@@ -2205,7 +2427,7 @@ static void bridge_channel_destroy(void *obj)
 
        /* Flush any unhandled wr_queue frames. */
        while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) {
-               ast_frfree(fr);
+               bridge_frame_free(fr);
        }
        pipe_close(bridge_channel->alert_pipe);
 
index 6145e35..b264a21 100644 (file)
@@ -1531,6 +1531,7 @@ int ast_is_deferrable_frame(const struct ast_frame *frame)
         */
        switch (frame->frametype) {
        case AST_FRAME_BRIDGE_ACTION:
+       case AST_FRAME_BRIDGE_ACTION_SYNC:
        case AST_FRAME_CONTROL:
        case AST_FRAME_TEXT:
        case AST_FRAME_IMAGE:
@@ -2875,6 +2876,7 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay)
                                case AST_FRAME_CONTROL:
                                case AST_FRAME_IAX:
                                case AST_FRAME_BRIDGE_ACTION:
+                               case AST_FRAME_BRIDGE_ACTION_SYNC:
                                case AST_FRAME_NULL:
                                case AST_FRAME_CNG:
                                        break;
index 8713ce4..1b13abc 100644 (file)
@@ -639,6 +639,10 @@ void ast_frame_subclass2str(struct ast_frame *f, char *subclass, size_t slen, ch
                /* Should never happen */
                snprintf(subclass, slen, "Bridge Frametype %d", f->subclass.integer);
                break;
+       case AST_FRAME_BRIDGE_ACTION_SYNC:
+               /* Should never happen */
+               snprintf(subclass, slen, "Synchronous Bridge Frametype %d", f->subclass.integer);
+               break;
        case AST_FRAME_TEXT:
                ast_copy_string(subclass, "N/A", slen);
                if (moreinfo) {
@@ -730,6 +734,10 @@ void ast_frame_type2str(enum ast_frame_type frame_type, char *ftype, size_t len)
                /* Should never happen */
                ast_copy_string(ftype, "Bridge Specific", len);
                break;
+       case AST_FRAME_BRIDGE_ACTION_SYNC:
+               /* Should never happen */
+               ast_copy_string(ftype, "Bridge Specific", len);
+               break;
        case AST_FRAME_TEXT:
                ast_copy_string(ftype, "Text", len);
                break;
index 2ab4933..94a1110 100644 (file)
@@ -580,6 +580,14 @@ struct ast_sorcery *__ast_sorcery_open(const char *module_name)
        }
 
        strcpy(sorcery->module_name, module_name); /* Safe */
+
+       if (__ast_sorcery_apply_config(sorcery, module_name, module_name) == AST_SORCERY_APPLY_FAIL) {
+               ast_log(LOG_ERROR, "Error attempting to apply configuration %s to sorcery.", module_name);
+               ao2_cleanup(sorcery);
+               sorcery = NULL;
+               goto done;
+       }
+
        ao2_link_flags(instances, sorcery, OBJ_NOLOCK);
 
 done:
@@ -623,7 +631,7 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
        }
 
        /* Order matters for object wizards */
-       if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
+       if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, sorcery_wizard_cmp))) {
                ao2_ref(object_type, -1);
                return NULL;
        }
@@ -683,7 +691,8 @@ static void sorcery_object_wizard_destructor(void *obj)
 }
 
 /*! \brief Internal function which creates an object type and adds a wizard mapping */
-static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+static enum ast_sorcery_apply_result sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
+               const char *type, const char *module, const char *name, const char *data, unsigned int caching)
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
        RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
@@ -691,18 +700,30 @@ static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char
        int created = 0;
 
        if (!wizard || !object_wizard) {
-               return -1;
+               return AST_SORCERY_APPLY_FAIL;
        }
 
        if (!object_type) {
                if (!(object_type = sorcery_object_type_alloc(type, module))) {
-                       return -1;
+                       return AST_SORCERY_APPLY_FAIL;
                }
                created = 1;
        }
 
+       if (!created) {
+               struct ast_sorcery_wizard *found;
+
+               found = ao2_find(object_type->wizards, wizard, OBJ_SEARCH_OBJECT);
+               if (found) {
+                       ast_debug(1, "Wizard %s already applied to object type %s\n",
+                                       wizard->name, object_type->name);
+                       ao2_cleanup(found);
+                       return AST_SORCERY_APPLY_DUPLICATE;
+               }
+       }
+
        if (wizard->open && !(object_wizard->data = wizard->open(data))) {
-               return -1;
+               return AST_SORCERY_APPLY_FAIL;
        }
 
        ast_module_ref(wizard->module);
@@ -716,18 +737,18 @@ static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char
                ao2_link(sorcery->types, object_type);
        }
 
-       return 0;
+       return AST_SORCERY_APPLY_SUCCESS;
 }
 
-int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
+enum ast_sorcery_apply_result  __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
 {
        struct ast_flags flags = { 0 };
        struct ast_config *config = ast_config_load2("sorcery.conf", "sorcery", flags);
        struct ast_variable *mapping;
-       int res = 0;
+       int res = AST_SORCERY_APPLY_SUCCESS;
 
        if (!config || config == CONFIG_STATUS_FILEINVALID) {
-               return -1;
+               return AST_SORCERY_APPLY_FAIL;
        }
 
        for (mapping = ast_variable_browse(config, name); mapping; mapping = mapping->next) {
@@ -750,8 +771,8 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co
                }
 
                /* Any error immediately causes us to stop */
-               if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, data, caching)) {
-                       res = -1;
+               if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, data, caching) == AST_SORCERY_APPLY_FAIL) {
+                       res = AST_SORCERY_APPLY_FAIL;
                        break;
                }
        }
@@ -761,13 +782,13 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co
        return res;
 }
 
-int __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data)
+enum ast_sorcery_apply_result __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data)
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 
        /* Defaults can not be added if any existing mapping exists */
        if (object_type) {
-               return -1;
+               return AST_SORCERY_APPLY_DEFAULT_UNNECESSARY;
        }
 
        return sorcery_apply_wizard_mapping(sorcery, type, module, name, data, 0);
index c3fc0ea..875f327 100644 (file)
@@ -163,10 +163,8 @@ static int mwi_sorcery_init(void)
        }
 
        /* Map the external MWI wizards. */
-       res = !!ast_sorcery_apply_config(mwi_sorcery, "res_mwi_external");
-       res &= !!ast_sorcery_apply_default(mwi_sorcery, MWI_MAILBOX_TYPE, "astdb",
-               MWI_ASTDB_PREFIX);
-       if (res) {
+       if (ast_sorcery_apply_default(mwi_sorcery, MWI_MAILBOX_TYPE, "astdb",
+                       MWI_ASTDB_PREFIX) == AST_SORCERY_APPLY_FAIL) {
                ast_log(LOG_ERROR, "MWI external: Sorcery could not setup wizards.\n");
                return -1;
        }
index bdf5314..8fae6b7 100644 (file)
@@ -116,8 +116,6 @@ int ast_sip_initialize_system(void)
                return -1;
        }
 
-       ast_sorcery_apply_config(system_sorcery, "res_pjsip");
-
        ast_sorcery_apply_default(system_sorcery, "system", "config", "pjsip.conf,criteria=type=system");
 
        if (ast_sorcery_object_register_no_reload(system_sorcery, "system", system_alloc, NULL, system_apply)) {
index 1f289ad..0a34334 100644 (file)
@@ -1619,8 +1619,6 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
                return -1;
        }
 
-       ast_sorcery_apply_config(sip_sorcery, "res_pjsip");
-
        ast_sip_initialize_cli();
 
        if (ast_sip_initialize_sorcery_auth()) {
index ee4a20b..299cb44 100644 (file)
@@ -78,14 +78,10 @@ struct stasis_app_playback {
        long offsetms;
        /*! Number of milliseconds to skip for forward/reverse operations */
        int skipms;
-       /*! Condition for waiting on done to be set */
-       ast_cond_t done_cond;
        /*! Number of milliseconds of media that has been played */
        long playedms;
        /*! Current playback state */
        enum stasis_app_playback_state state;
-       /*! Set when playback has been completed */
-       unsigned int done:1;
        /*! Set when the playback can be controlled */
        unsigned int controllable:1;
 };
@@ -121,7 +117,6 @@ static void playback_dtor(void *obj)
        struct stasis_app_playback *playback = obj;
 
        ast_string_field_free_memory(playback);
-       ast_cond_destroy(&playback->done_cond);
 }
 
 static struct stasis_app_playback *playback_create(
@@ -129,7 +124,6 @@ static struct stasis_app_playback *playback_create(
 {
        RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
        char uuid[AST_UUID_STR_LEN];
-       int res;
 
        if (!control) {
                return NULL;
@@ -140,13 +134,6 @@ static struct stasis_app_playback *playback_create(
                return NULL;
        }
 
-       res = ast_cond_init(&playback->done_cond, NULL);
-       if (res != 0) {
-               ast_log(LOG_ERROR, "Error creating done condition: %s\n",
-                       strerror(errno));
-               return NULL;
-       }
-
        if (!ast_strlen_zero(id)) {
                ast_string_field_set(playback, id, id);
        } else {
@@ -266,21 +253,9 @@ static void playback_final_update(struct stasis_app_playback *playback,
        playback_publish(playback);
 }
 
-/*!
- * \brief RAII_VAR function to mark a playback as done when leaving scope.
- */
-static void mark_as_done(struct stasis_app_playback *playback)
-{
-       SCOPED_AO2LOCK(lock, playback);
-       playback->done = 1;
-       ast_cond_broadcast(&playback->done_cond);
-}
-
 static void play_on_channel(struct stasis_app_playback *playback,
        struct ast_channel *chan)
 {
-       RAII_VAR(struct stasis_app_playback *, mark_when_done, playback,
-               mark_as_done);
        int res;
        long offsetms;
 
@@ -399,7 +374,6 @@ static int play_uri(struct stasis_app_control *control,
        RAII_VAR(struct stasis_app_playback *, playback, NULL,
                remove_from_playbacks);
        struct ast_bridge *bridge;
-       int res;
 
        playback = data;
 
@@ -413,28 +387,16 @@ static int play_uri(struct stasis_app_control *control,
 
                /* Queue up playback on the bridge */
                ast_bridge_lock(bridge);
-               bridge_chan = bridge_find_channel(bridge, chan);
+               bridge_chan = ao2_bump(bridge_find_channel(bridge, chan));
+               ast_bridge_unlock(bridge);
                if (bridge_chan) {
-                       ast_bridge_channel_queue_playfile(
+                       ast_bridge_channel_queue_playfile_sync(
                                bridge_chan,
                                play_on_channel_in_bridge,
                                playback->id,
                                NULL); /* moh_class */
                }
-               ast_bridge_unlock(bridge);
-
-               /* Wait for playback to complete */
-               ao2_lock(playback);
-               while (!playback->done) {
-                       res = ast_cond_wait(&playback->done_cond,
-                               ao2_object_get_lockaddr(playback));
-                       if (res != 0) {
-                               ast_log(LOG_ERROR,
-                                       "Error waiting for playback to complete: %s\n",
-                                       strerror(errno));
-                       }
-               }
-               ao2_unlock(playback);
+               ao2_cleanup(bridge_chan);
        } else {
                play_on_channel(playback, chan);
        }
index ed4d604..aa34a11 100644 (file)
@@ -306,7 +306,7 @@ static struct ast_sorcery *alloc_and_initialize_sorcery(void)
                return NULL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
+       if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
                ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
                ast_sorcery_unref(sorcery);
                return NULL;
@@ -452,17 +452,17 @@ AST_TEST_DEFINE(apply_default)
                return AST_TEST_FAIL;
        }
 
-       if (!ast_sorcery_apply_default(sorcery, "test", "dummy", NULL)) {
+       if (ast_sorcery_apply_default(sorcery, "test", "dummy", NULL) != AST_SORCERY_APPLY_FAIL) {
                ast_test_status_update(test, "Successfully set a default wizard that doesn't exist\n");
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Failed to set a known wizard as a default\n");
                return AST_TEST_FAIL;
        }
 
-       if (!ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_DEFAULT_UNNECESSARY) {
                ast_test_status_update(test, "Successfully set a default wizard on a type twice\n");
                return AST_TEST_FAIL;
        }
@@ -493,7 +493,7 @@ AST_TEST_DEFINE(apply_config)
                return AST_TEST_NOT_RUN;
        }
 
-       if (!ast_category_get(config, "test_sorcery")) {
+       if (!ast_category_get(config, "test_sorcery_section")) {
                ast_test_status_update(test, "Sorcery configuration file does not have test_sorcery section\n");
                ast_config_destroy(config);
                return AST_TEST_NOT_RUN;
@@ -506,7 +506,7 @@ AST_TEST_DEFINE(apply_config)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_config(sorcery, "test_sorcery")) {
+       if (ast_sorcery_apply_config(sorcery, "test_sorcery_section") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Failed to apply configured object mappings\n");
                return AST_TEST_FAIL;
        }
@@ -535,7 +535,7 @@ AST_TEST_DEFINE(object_register)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Failed to set a known wizard as a default\n");
                return AST_TEST_FAIL;
        }
@@ -608,7 +608,7 @@ AST_TEST_DEFINE(object_field_register)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Failed to set a known wizard as a default\n");
                return AST_TEST_FAIL;
        }
@@ -657,7 +657,7 @@ AST_TEST_DEFINE(object_fields_register)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Failed to set a known wizard as a default\n");
                return AST_TEST_FAIL;
        }
@@ -1192,7 +1192,7 @@ AST_TEST_DEFINE(objectset_create_regex)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
+       if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
            ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
                ast_test_status_update(test, "Failed to register 'test' object type\n");
                return AST_TEST_FAIL;
@@ -1292,7 +1292,7 @@ AST_TEST_DEFINE(objectset_apply_handler)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
+       if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
            ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
                ast_test_status_update(test, "Failed to register 'test' object type\n");
                return AST_TEST_FAIL;
@@ -1387,7 +1387,7 @@ AST_TEST_DEFINE(objectset_transform)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Failed to set a known wizard as a default\n");
                return AST_TEST_FAIL;
        }
@@ -1453,7 +1453,7 @@ AST_TEST_DEFINE(objectset_apply_fields)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
+       if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
            ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
                ast_test_status_update(test, "Failed to register 'test' object type\n");
                return AST_TEST_FAIL;
@@ -2244,7 +2244,7 @@ AST_TEST_DEFINE(caching_wizard_behavior)
                goto end;
        }
 
-       if (ast_sorcery_apply_config(sorcery, "test_sorcery_cache")) {
+       if (ast_sorcery_apply_config(sorcery, "test_sorcery_cache") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Failed to apply configured object mappings\n");
                goto end;
        }
@@ -2489,7 +2489,7 @@ AST_TEST_DEFINE(configuration_file_wizard)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
+       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
                return AST_TEST_NOT_RUN;
        }
@@ -2552,7 +2552,7 @@ AST_TEST_DEFINE(configuration_file_wizard_with_file_integrity)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,integrity=file")) {
+       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,integrity=file") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
                return AST_TEST_NOT_RUN;
        }
@@ -2606,7 +2606,7 @@ AST_TEST_DEFINE(configuration_file_wizard_with_criteria)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,criteria=type=zombies")) {
+       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,criteria=type=zombies") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
                return AST_TEST_NOT_RUN;
        }
@@ -2665,7 +2665,7 @@ AST_TEST_DEFINE(configuration_file_wizard_retrieve_field)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
+       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
                return AST_TEST_NOT_RUN;
        }
@@ -2728,7 +2728,7 @@ AST_TEST_DEFINE(configuration_file_wizard_retrieve_multiple)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
+       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
                return AST_TEST_NOT_RUN;
        }
@@ -2799,7 +2799,7 @@ AST_TEST_DEFINE(configuration_file_wizard_retrieve_multiple_all)
                return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
+       if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
                ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
                return AST_TEST_NOT_RUN;
        }
index 41e7adb..b87ed74 100644 (file)
@@ -60,7 +60,7 @@ static struct ast_sorcery *alloc_and_initialize_sorcery(void)
                return NULL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "astdb", "test") ||
+       if ((ast_sorcery_apply_default(sorcery, "test", "astdb", "test") != AST_SORCERY_APPLY_SUCCESS) ||
                ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
                ast_sorcery_unref(sorcery);
                return NULL;
index 7cf42d2..e3d0a4b 100644 (file)
@@ -212,7 +212,7 @@ static struct ast_sorcery *alloc_and_initialize_sorcery(void)
                return NULL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "realtime", "sorcery_realtime_test") ||
+       if ((ast_sorcery_apply_default(sorcery, "test", "realtime", "sorcery_realtime_test") != AST_SORCERY_APPLY_SUCCESS) ||
                ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL) ||
                !(realtime_objects = ast_config_new())) {
                ast_sorcery_unref(sorcery);