Example #1
0
static int app_control_silence_start(struct stasis_app_control *control,
	struct ast_channel *chan, void *data)
{
	if (ast_channel_state(chan) != AST_STATE_UP) {
		ast_indicate(chan, AST_CONTROL_PROGRESS);
	}

	if (control->silgen) {
		/* We have a silence generator, but it may have been implicitly
		 * disabled by media actions (music on hold, playing media,
		 * etc.) Just stop it and restart a new one.
		 */
		ast_channel_stop_silence_generator(
			control->channel, control->silgen);
	}

	ast_debug(3, "%s: Starting silence generator\n",
		stasis_app_control_get_channel_id(control));
	control->silgen = ast_channel_start_silence_generator(control->channel);

	if (!control->silgen) {
		ast_log(LOG_WARNING,
			"%s: Failed to start silence generator.\n",
			stasis_app_control_get_channel_id(control));
	}

	return 0;
}
Example #2
0
static int app_control_remove_channel_from_bridge(
    struct stasis_app_control *control,
    struct ast_channel *chan, void *data)
{
    struct ast_bridge *bridge = data;

    if (!control) {
        return -1;
    }

    /* We should only depart from our own bridge */
    ast_debug(3, "%s: Departing bridge %s\n",
              stasis_app_control_get_channel_id(control),
              bridge->uniqueid);

    if (bridge != stasis_app_get_bridge(control)) {
        ast_log(LOG_WARNING, "%s: Not in bridge %s; not removing\n",
                stasis_app_control_get_channel_id(control),
                bridge->uniqueid);
        return -1;
    }

    ast_bridge_depart(chan);
    return 0;
}
static void recording_publish(struct stasis_app_recording *recording, const char *cause)
{
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	ast_assert(recording != NULL);

	json = stasis_app_recording_to_json(recording);
	if (json == NULL) {
		return;
	}

	if (!ast_strlen_zero(cause)) {
		struct ast_json *failure_cause = ast_json_string_create(cause);

		if (!failure_cause) {
			return;
		}

		if (ast_json_object_set(json, "cause", failure_cause)) {
			return;
		}
	}

	message = ast_channel_blob_create_from_cache(
		stasis_app_control_get_channel_id(recording->control),
		stasis_app_recording_snapshot_type(), json);
	if (message == NULL) {
		return;
	}

	stasis_app_control_publish(recording->control, message);
}
static int app_control_answer(struct stasis_app_control *control,
	struct ast_channel *chan, void *data)
{
	ast_debug(3, "%s: Answering\n",
		stasis_app_control_get_channel_id(control));
	return ast_raw_answer(chan);
}
int stasis_app_control_answer(struct stasis_app_control *control)
{
	int retval;

	ast_debug(3, "%s: Sending answer command\n",
		stasis_app_control_get_channel_id(control));

	retval = stasis_app_send_command(control, app_control_answer, NULL);

	if (retval != 0) {
		ast_log(LOG_WARNING, "%s: Failed to answer channel\n",
			stasis_app_control_get_channel_id(control));
		return -1;
	}

	return 0;
}
Example #6
0
static void *app_control_answer(struct stasis_app_control *control,
	struct ast_channel *chan, void *data)
{
	const int delay = 0;
	ast_debug(3, "%s: Answering",
		stasis_app_control_get_channel_id(control));
	return __ast_answer(chan, delay) == 0 ? &OK : &FAIL;
}
Example #7
0
int stasis_app_control_remove_channel_from_bridge(
    struct stasis_app_control *control, struct ast_bridge *bridge)
{
    ast_debug(3, "%s: Sending channel remove_from_bridge command\n",
              stasis_app_control_get_channel_id(control));
    return app_send_command_on_condition(
               control, app_control_remove_channel_from_bridge, bridge,
               app_control_can_remove_channel_from_bridge);
}
Example #8
0
int stasis_app_control_remove_channel_from_bridge(
	struct stasis_app_control *control, struct ast_bridge *bridge)
{
	int *res;
	ast_debug(3, "%s: Sending channel remove_from_bridge command\n",
			stasis_app_control_get_channel_id(control));
	res = stasis_app_send_command(control,
		app_control_remove_channel_from_bridge, bridge);
	return *res;
}
Example #9
0
void control_silence_stop_now(struct stasis_app_control *control)
{
	if (control->silgen) {
		ast_debug(3, "%s: Stopping silence generator\n",
			stasis_app_control_get_channel_id(control));
		ast_channel_stop_silence_generator(
			control->channel, control->silgen);
		control->silgen = NULL;
	}
}
Example #10
0
int stasis_app_control_add_channel_to_bridge(
	struct stasis_app_control *control, struct ast_bridge *bridge)
{
	ast_debug(3, "%s: Sending channel add_to_bridge command\n",
			stasis_app_control_get_channel_id(control));

	return app_send_command_on_condition(
		control, control_add_channel_to_bridge, bridge, NULL,
		app_control_can_add_channel_to_bridge);
}
Example #11
0
static int app_control_silence_stop(struct stasis_app_control *control,
                                    struct ast_channel *chan, void *data)
{
    if (control->silgen) {
        ast_debug(3, "%s: Stopping silence generator\n",
                  stasis_app_control_get_channel_id(control));
        ast_channel_stop_silence_generator(
            control->channel, control->silgen);
        control->silgen = NULL;
    }

    return 0;
}
Example #12
0
static int check_add_remove_channel(struct ast_ari_response *response,
				    struct stasis_app_control *control,
				    enum stasis_app_control_channel_result result)
{
	switch (result) {
	case STASIS_APP_CHANNEL_RECORDING :
		ast_ari_response_error(
			response, 409, "Conflict", "Channel %s currently recording",
			stasis_app_control_get_channel_id(control));
		return -1;
	case STASIS_APP_CHANNEL_OKAY:
		return 0;
	}
	return 0;
}
Example #13
0
struct ast_channel_snapshot *stasis_app_control_get_snapshot(
    const struct stasis_app_control *control)
{
    RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
    struct ast_channel_snapshot *snapshot;

    msg = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(),
                           stasis_app_control_get_channel_id(control));
    if (!msg) {
        return NULL;
    }

    snapshot = stasis_message_data(msg);
    ast_assert(snapshot != NULL);

    ao2_ref(snapshot, +1);
    return snapshot;
}
Example #14
0
static void recording_publish(struct stasis_app_recording *recording)
{
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);

	ast_assert(recording != NULL);

	json = stasis_app_recording_to_json(recording);
	if (json == NULL) {
		return;
	}

	message = ast_channel_blob_create_from_cache(
		stasis_app_control_get_channel_id(recording->control),
		stasis_app_recording_snapshot_type(), json);
	if (message == NULL) {
		return;
	}

	stasis_app_control_publish(recording->control, message);
}
Example #15
0
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap)
{
	int res;

	if (!control || !bridge) {
		return -1;
	}

	ast_debug(3, "%s: Adding to bridge %s\n",
		stasis_app_control_get_channel_id(control),
		bridge->uniqueid);

	ast_assert(chan != NULL);

	/* Depart whatever Stasis bridge we're currently in. */
	if (stasis_app_get_bridge(control)) {
		/* Note that it looks like there's a race condition here, since
		 * we don't have control locked. But this happens from the
		 * control callback thread, so there won't be any other
		 * concurrent attempts to bridge.
		 */
		ast_bridge_depart(chan);
	}


	res = ast_bridge_set_after_callback(chan, bridge_after_cb,
		bridge_after_cb_failed, control);
	if (res != 0) {
		ast_log(LOG_ERROR, "Error setting after-bridge callback\n");
		return -1;
	}

	{
		/* pbx and bridge are modified by the bridging impart thread.
		 * It shouldn't happen concurrently, but we still need to lock
		 * for the memory fence.
		 */
		SCOPED_AO2LOCK(lock, control);

		/* Ensure the controlling application is subscribed early enough
		 * to receive the ChannelEnteredBridge message. This works in concert
		 * with the subscription handled in the Stasis application execution
		 * loop */
		app_subscribe_bridge(control->app, bridge);

		/* Save off the channel's PBX */
		ast_assert(control->pbx == NULL);
		if (!control->pbx) {
			control->pbx = ast_channel_pbx(chan);
			ast_channel_pbx_set(chan, NULL);
		}

		res = ast_bridge_impart(bridge,
			chan,
			swap,
			NULL, /* features */
			AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
		if (res != 0) {
			ast_log(LOG_ERROR, "Error adding channel to bridge\n");
			ast_channel_pbx_set(chan, control->pbx);
			control->pbx = NULL;
			return -1;
		}

		ast_assert(stasis_app_get_bridge(control) == NULL);
		control->bridge = bridge;

		ast_channel_lock(chan);
		set_interval_hook(chan);
		ast_channel_unlock(chan);
	}
	return 0;
}
struct stasis_app_recording *stasis_app_control_record(
	struct stasis_app_control *control,
	struct stasis_app_recording_options *options)
{
	struct stasis_app_recording *recording;
	char *last_slash;

	errno = 0;

	if (options == NULL ||
		ast_strlen_zero(options->name) ||
		ast_strlen_zero(options->format) ||
		options->max_silence_seconds < 0 ||
		options->max_duration_seconds < 0) {
		errno = EINVAL;
		return NULL;
	}

	ast_debug(3, "%s: Sending record(%s.%s) command\n",
		stasis_app_control_get_channel_id(control), options->name,
		options->format);

	recording = ao2_alloc(sizeof(*recording), recording_dtor);
	if (!recording) {
		errno = ENOMEM;
		return NULL;
	}
	recording->duration.total = -1;
	recording->duration.energy_only = -1;

	ast_asprintf(&recording->absolute_name, "%s/%s",
		ast_config_AST_RECORDING_DIR, options->name);

	if (recording->absolute_name == NULL) {
		errno = ENOMEM;
		ao2_ref(recording, -1);
		return NULL;
	}

	if ((last_slash = strrchr(recording->absolute_name, '/'))) {
		*last_slash = '\0';
		if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR,
				recording->absolute_name, 0777) != 0) {
			/* errno set by ast_mkdir */
			ao2_ref(recording, -1);
			return NULL;
		}
		*last_slash = '/';
	}

	ao2_ref(options, +1);
	recording->options = options;
	recording->control = control;
	recording->state = STASIS_APP_RECORDING_STATE_QUEUED;

	if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) &&
			(ast_fileexists(recording->absolute_name, NULL, NULL))) {
		ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n",
			recording->absolute_name);
		errno = EEXIST;
		ao2_ref(recording, -1);
		return NULL;
	}

	{
		RAII_VAR(struct stasis_app_recording *, old_recording, NULL,
			ao2_cleanup);

		SCOPED_AO2LOCK(lock, recordings);

		old_recording = ao2_find(recordings, options->name,
			OBJ_KEY | OBJ_NOLOCK);
		if (old_recording) {
			ast_log(LOG_WARNING,
				"Recording %s already in progress\n",
				recording->options->name);
			errno = EEXIST;
			ao2_ref(recording, -1);
			return NULL;
		}
		ao2_link(recordings, recording);
	}

	stasis_app_control_register_add_rule(control, &rule_recording);

	stasis_app_send_command_async(control, record_file, ao2_bump(recording), recording_cleanup);

	return recording;
}
Example #17
0
struct stasis_app_recording *stasis_app_control_record(
	struct stasis_app_control *control,
	struct stasis_app_recording_options *options)
{
	RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
	char *last_slash;

	errno = 0;

	if (options == NULL ||
		ast_strlen_zero(options->name) ||
		ast_strlen_zero(options->format) ||
		options->max_silence_seconds < 0 ||
		options->max_duration_seconds < 0) {
		errno = EINVAL;
		return NULL;
	}

	ast_debug(3, "%s: Sending record(%s.%s) command\n",
		stasis_app_control_get_channel_id(control), options->name,
		options->format);

	recording = ao2_alloc(sizeof(*recording), recording_dtor);
	if (!recording) {
		errno = ENOMEM;
		return NULL;
	}

	ast_asprintf(&recording->absolute_name, "%s/%s",
		ast_config_AST_RECORDING_DIR, options->name);

	if (recording->absolute_name == NULL) {
		errno = ENOMEM;
		return NULL;
	}

	if ((last_slash = strrchr(recording->absolute_name, '/'))) {
		*last_slash = '\0';
		if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR,
				recording->absolute_name, 0777) != 0) {
			/* errno set by ast_mkdir */
			return NULL;
		}
		*last_slash = '/';
	}

	ao2_ref(options, +1);
	recording->options = options;
	recording->control = control;
	recording->state = STASIS_APP_RECORDING_STATE_QUEUED;

	{
		RAII_VAR(struct stasis_app_recording *, old_recording, NULL,
			ao2_cleanup);

		SCOPED_AO2LOCK(lock, recordings);

		old_recording = ao2_find(recordings, options->name,
			OBJ_KEY | OBJ_NOLOCK);
		if (old_recording) {
			ast_log(LOG_WARNING,
				"Recording %s already in progress\n",
				recording->options->name);
			errno = EEXIST;
			return NULL;
		}
		ao2_link(recordings, recording);
	}

	/* A ref is kept in the recordings container; no need to bump */
	stasis_app_send_command_async(control, record_file, recording);

	/* Although this should be bumped for the caller */
	ao2_ref(recording, +1);
	return recording;
}
Example #18
0
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap)
{
	int res;
	struct ast_bridge_features *features;

	if (!control || !bridge) {
		return -1;
	}

	ast_debug(3, "%s: Adding to bridge %s\n",
		stasis_app_control_get_channel_id(control),
		bridge->uniqueid);

	ast_assert(chan != NULL);

	/* Depart whatever Stasis bridge we're currently in. */
	if (stasis_app_get_bridge(control)) {
		/* Note that it looks like there's a race condition here, since
		 * we don't have control locked. But this happens from the
		 * control callback thread, so there won't be any other
		 * concurrent attempts to bridge.
		 */
		ast_bridge_depart(chan);
	}


	res = ast_bridge_set_after_callback(chan, bridge_after_cb,
		bridge_after_cb_failed, control);
	if (res != 0) {
		ast_log(LOG_ERROR, "Error setting after-bridge callback\n");
		return -1;
	}

	ao2_lock(control);

	/* Ensure the controlling application is subscribed early enough
	 * to receive the ChannelEnteredBridge message. This works in concert
	 * with the subscription handled in the Stasis application execution
	 * loop */
	app_subscribe_bridge(control->app, bridge);

	/* Save off the channel's PBX */
	ast_assert(control->pbx == NULL);
	if (!control->pbx) {
		control->pbx = ast_channel_pbx(chan);
		ast_channel_pbx_set(chan, NULL);
	}

	/* Pull bridge features from the control */
	features = control->bridge_features;
	control->bridge_features = NULL;

	ast_assert(stasis_app_get_bridge(control) == NULL);
	/* We need to set control->bridge here since bridge_after_cb may be run
	 * before ast_bridge_impart returns.  bridge_after_cb gets a reason
	 * code so it can tell if the bridge is actually valid or not.
	 */
	control->bridge = bridge;

	/* We can't be holding the control lock while impart is running
	 * or we could create a deadlock with bridge_after_cb which also
	 * tries to lock control.
	 */
	ao2_unlock(control);
	res = ast_bridge_impart(bridge,
		chan,
		swap,
		features, /* features */
		AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
	if (res != 0) {
		/* ast_bridge_impart failed before it could spawn the depart
		 * thread.  The callbacks aren't called in this case.
		 * The impart could still fail even if ast_bridge_impart returned
		 * ok but that's handled by bridge_after_cb.
		 */
		ast_log(LOG_ERROR, "Error adding channel to bridge\n");
		ao2_lock(control);
		ast_channel_pbx_set(chan, control->pbx);
		control->pbx = NULL;
		control->bridge = NULL;
		ao2_unlock(control);
	} else {
		ast_channel_lock(chan);
		set_interval_hook(chan);
		ast_channel_unlock(chan);
	}

	return res;
}
Example #19
0
static int app_control_add_channel_to_bridge(
    struct stasis_app_control *control,
    struct ast_channel *chan, void *data)
{
    struct ast_bridge *bridge = data;
    int res;

    if (!control || !bridge) {
        return -1;
    }

    ast_debug(3, "%s: Adding to bridge %s\n",
              stasis_app_control_get_channel_id(control),
              bridge->uniqueid);

    ast_assert(chan != NULL);

    /* Depart whatever Stasis bridge we're currently in. */
    if (stasis_app_get_bridge(control)) {
        /* Note that it looks like there's a race condition here, since
         * we don't have control locked. But this happens from the
         * control callback thread, so there won't be any other
         * concurrent attempts to bridge.
         */
        ast_bridge_depart(chan);
    }


    res = ast_bridge_set_after_callback(chan, bridge_after_cb,
                                        bridge_after_cb_failed, control);
    if (res != 0) {
        ast_log(LOG_ERROR, "Error setting after-bridge callback\n");
        return -1;
    }

    {
        /* pbx and bridge are modified by the bridging impart thread.
         * It shouldn't happen concurrently, but we still need to lock
         * for the memory fence.
         */
        SCOPED_AO2LOCK(lock, control);

        /* Save off the channel's PBX */
        ast_assert(control->pbx == NULL);
        if (!control->pbx) {
            control->pbx = ast_channel_pbx(chan);
            ast_channel_pbx_set(chan, NULL);
        }

        res = ast_bridge_impart(bridge,
                                chan,
                                NULL, /* swap channel */
                                NULL, /* features */
                                AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
        if (res != 0) {
            ast_log(LOG_ERROR, "Error adding channel to bridge\n");
            ast_channel_pbx_set(chan, control->pbx);
            control->pbx = NULL;
            return -1;
        }

        ast_assert(stasis_app_get_bridge(control) == NULL);
        control->bridge = bridge;
    }
    return 0;
}