diff --git a/apps/app_originate.c b/apps/app_originate.c index b196194e44daefca3e8a692a90909aefc68f50d6..9fceb08498e9904f2e783fc9421f0ebf7ac63e70 100644 --- a/apps/app_originate.c +++ b/apps/app_originate.c @@ -177,14 +177,14 @@ static int originate_exec(struct ast_channel *chan, const char *data) chantech, chandata, args.arg1, exten, priority); ast_pbx_outgoing_exten(chantech, cap_slin, chandata, - timeout * 1000, args.arg1, exten, priority, &outgoing_status, 0, NULL, + timeout * 1000, args.arg1, exten, priority, &outgoing_status, 1, NULL, NULL, NULL, NULL, NULL, 0, NULL); } else if (!strcasecmp(args.type, "app")) { ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n", chantech, chandata, args.arg1, S_OR(args.arg2, "")); ast_pbx_outgoing_app(chantech, cap_slin, chandata, - timeout * 1000, args.arg1, args.arg2, &outgoing_status, 0, NULL, + timeout * 1000, args.arg1, args.arg2, &outgoing_status, 1, NULL, NULL, NULL, NULL, NULL, NULL); } else { ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n", diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h index 784b976ccded3b0f3bdb69328786148b347d8c66..795af05842aa4e118994b8cee4ac43428254e68d 100644 --- a/include/asterisk/pbx.h +++ b/include/asterisk/pbx.h @@ -1102,7 +1102,8 @@ int ast_async_goto(struct ast_channel *chan, const char *context, const char *ex */ int ast_async_goto_by_name(const char *chan, const char *context, const char *exten, int priority); -/*! \brief Synchronously or asynchronously make an outbound call and send it to a +/*! + * \brief Synchronously or asynchronously make an outbound call and send it to a * particular extension * * \param type The channel technology to create @@ -1112,27 +1113,34 @@ int ast_async_goto_by_name(const char *chan, const char *context, const char *ex * \param context The destination context for the outbound channel * \param exten The destination extension for the outbound channel * \param priority The destination priority for the outbound channel - * \param reason Optional. If provided, the hangup cause code of the outbound channel if - * it failed - * \param sync If non-zero, block until the outbound channel answers + * \param reason Optional. If provided, the dialed status of the outgoing channel. + * Codes are AST_CONTROL_xxx values. Valid only if synchronous is non-zero. + * \param synchronous If zero then don't wait for anything. + * If one then block until the outbound channel answers or the call fails. + * If greater than one then wait for the call to complete or if the call doesn't + * answer and failed@context exists then run a channel named OutgoingSpoolFailed + * at failed@context. * \param cid_num The caller ID number to set on the outbound channel * \param cid_name The caller ID name to set on the outbound channel * \param vars Variables to set on the outbound channel * \param account The accountcode for the outbound channel - * \param locked_channel Optional. The outbound channel that was created. This is returned - * both locked and reference bumped. If a caller provides a channel parameter, it must - * unlock the channel and decrement the reference count. - * \param assignedid Optional. The uniqueid to assign the channel that was created. - * \param assignedid2 Optional. The uniqueid to assign the second local channel. - * \param early_media If non-zero, allow early-media on the originated channel + * \param locked_channel Optional. The outbound channel that was created if success + * is returned. Otherwise it is set to NULL. This is returned both locked + * and reference bumped. + * \param early_media If non-zero the channel "answers" when progress is indicated. + * \param assignedids Optional. The uniqueid(s) to assign the channel(s) that are created. + * + * \retval 0 on success + * \retval -1 on failure */ int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, - int timeout, const char *context, const char *exten, int priority, int *reason, - int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, - const char *account, struct ast_channel **locked_channel, int early_media, + int timeout, const char *context, const char *exten, int priority, int *reason, + int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, + const char *account, struct ast_channel **locked_channel, int early_media, const struct ast_assigned_ids *assignedids); -/*! \brief Synchronously or asynchronously make an outbound call and execute an +/*! + * \brief Synchronously or asynchronously make an outbound call and execute an * application on the channel. * * Note that when the application stops executing, the channel is hungup. @@ -1143,23 +1151,27 @@ int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const c * \param timeout How long we should attempt to dial the outbound channel * \param app The name of the application to execute * \param appdata Data to pass to the application - * \param reason Optional. If provided, the hangup cause code of the outbound channel if - * it failed - * \param sync If non-zero, block until the outbound channel answers + * \param reason Optional. If provided, the dialed status of the outgoing channel. + * Codes are AST_CONTROL_xxx values. Valid only if synchronous is non-zero. + * \param synchronous If zero then don't wait for anything. + * If one then block until the outbound channel answers or the call fails. + * If greater than one then wait for the call to complete. * \param cid_num The caller ID number to set on the outbound channel * \param cid_name The caller ID name to set on the outbound channel * \param vars Variables to set on the outbound channel * \param account The accountcode for the outbound channel - * \param locked_channel Optional. The outbound channel that was created. This is returned - * \param assignedid Optional. The uniqueid to assign the channel that was created. - * \param assignedid2 Optional. The uniqueid to assign the second local channel. - * both locked and reference bumped. If a caller provides a channel parameter, it must - * unlock the channel and decrement the reference count. + * \param locked_channel Optional. The outbound channel that was created if success + * is returned. Otherwise it is set to NULL. This is returned both locked + * and reference bumped. + * \param assignedids Optional. The uniqueid(s) to assign the channel(s) that are created. + * + * \retval 0 on success + * \retval -1 on failure */ int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr, - int timeout, const char *app, const char *appdata, int *reason, int sync, - const char *cid_num, const char *cid_name, struct ast_variable *vars, - const char *account, struct ast_channel **locked_channel, + int timeout, const char *app, const char *appdata, int *reason, int synchronous, + const char *cid_num, const char *cid_name, struct ast_variable *vars, + const char *account, struct ast_channel **locked_channel, const struct ast_assigned_ids *assignedids); /*! diff --git a/main/dial.c b/main/dial.c index bf0d51c5e21c0f2728628745bbc5430a0ff9d2d4..2563c707b5cc7445f5e7cedaec098c4cc66eb1c5 100644 --- a/main/dial.c +++ b/main/dial.c @@ -541,12 +541,14 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel ast_verb(3, "%s is busy\n", ast_channel_name(channel->owner)); ast_channel_publish_dial(chan, channel->owner, channel->device, "BUSY"); ast_hangup(channel->owner); + channel->cause = AST_CAUSE_USER_BUSY; channel->owner = NULL; break; case AST_CONTROL_CONGESTION: ast_verb(3, "%s is circuit-busy\n", ast_channel_name(channel->owner)); ast_channel_publish_dial(chan, channel->owner, channel->device, "CONGESTION"); ast_hangup(channel->owner); + channel->cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; channel->owner = NULL; break; case AST_CONTROL_INCOMPLETE: @@ -593,7 +595,7 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel break; case AST_CONTROL_HOLD: ast_verb(3, "Call on %s placed on hold\n", ast_channel_name(chan)); - ast_indicate(chan, AST_CONTROL_HOLD); + ast_indicate_data(chan, AST_CONTROL_HOLD, fr->data.ptr, fr->datalen); break; case AST_CONTROL_UNHOLD: ast_verb(3, "Call on %s left from hold\n", ast_channel_name(chan)); @@ -613,8 +615,6 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel break; } } - - return; } /*! \brief Helper function that handles control frames WITHOUT owner */ @@ -638,12 +638,25 @@ static void handle_frame_ownerless(struct ast_dial *dial, struct ast_dial_channe ast_verb(3, "%s is busy\n", ast_channel_name(channel->owner)); ast_channel_publish_dial(NULL, channel->owner, channel->device, "BUSY"); ast_hangup(channel->owner); + channel->cause = AST_CAUSE_USER_BUSY; channel->owner = NULL; break; case AST_CONTROL_CONGESTION: ast_verb(3, "%s is circuit-busy\n", ast_channel_name(channel->owner)); ast_channel_publish_dial(NULL, channel->owner, channel->device, "CONGESTION"); ast_hangup(channel->owner); + channel->cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; + channel->owner = NULL; + break; + case AST_CONTROL_INCOMPLETE: + /* + * Nothing to do but abort the call since we have no + * controlling channel to ask for more digits. + */ + ast_verb(3, "%s dialed Incomplete extension %s\n", + ast_channel_name(channel->owner), ast_channel_exten(channel->owner)); + ast_hangup(channel->owner); + channel->cause = AST_CAUSE_UNALLOCATED; channel->owner = NULL; break; case AST_CONTROL_RINGING: @@ -661,8 +674,6 @@ static void handle_frame_ownerless(struct ast_dial *dial, struct ast_dial_channe default: break; } - - return; } /*! \brief Helper function to handle when a timeout occurs on dialing attempt */ @@ -686,6 +697,7 @@ static int handle_timeout_trip(struct ast_dial *dial, struct timeval start) AST_LIST_TRAVERSE(&dial->channels, channel, list) { if (dial->state == AST_DIAL_RESULT_TIMEOUT || diff >= channel->timeout) { ast_hangup(channel->owner); + channel->cause = AST_CAUSE_NO_ANSWER; channel->owner = NULL; } else if ((lowest_timeout == -1) || (lowest_timeout > channel->timeout)) { lowest_timeout = channel->timeout; @@ -835,6 +847,7 @@ static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_chann ast_poll_channel_del(chan, channel->owner); ast_channel_publish_dial(chan, channel->owner, channel->device, "CANCEL"); ast_hangup(channel->owner); + channel->cause = AST_CAUSE_ANSWERED_ELSEWHERE; channel->owner = NULL; } AST_LIST_UNLOCK(&dial->channels); @@ -859,6 +872,7 @@ static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_chann ast_poll_channel_del(chan, channel->owner); ast_channel_publish_dial(chan, channel->owner, channel->device, "CANCEL"); ast_hangup(channel->owner); + channel->cause = AST_CAUSE_NORMAL_CLEARING; channel->owner = NULL; } AST_LIST_UNLOCK(&dial->channels); diff --git a/main/pbx.c b/main/pbx.c index d1611174618ba556ca2e30526c222f82880f07d2..c8421bd83e0e23062ffea05a37d5364da1cbd59b 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -10092,8 +10092,6 @@ static int ast_add_extension2_lockopt(struct ast_context *con, struct pbx_outgoing { /*! \brief Dialing structure being used */ struct ast_dial *dial; - /*! \brief Mutex lock for synchronous dialing */ - ast_mutex_t lock; /*! \brief Condition for synchronous dialing */ ast_cond_t cond; /*! \brief Application to execute */ @@ -10123,7 +10121,6 @@ static void pbx_outgoing_destroy(void *obj) ast_dial_destroy(outgoing->dial); } - ast_mutex_destroy(&outgoing->lock); ast_cond_destroy(&outgoing->cond); ast_free(outgoing->appdata); @@ -10136,12 +10133,12 @@ static void *pbx_outgoing_exec(void *data) enum ast_dial_result res; /* Notify anyone interested that dialing is complete */ - ast_mutex_lock(&outgoing->lock); res = ast_dial_run(outgoing->dial, NULL, 0); + ao2_lock(outgoing); outgoing->dial_res = res; outgoing->dialed = 1; ast_cond_signal(&outgoing->cond); - ast_mutex_unlock(&outgoing->lock); + ao2_unlock(outgoing); /* If the outgoing leg was not answered we can immediately return and go no further */ if (res != AST_DIAL_RESULT_ANSWERED) { @@ -10182,10 +10179,10 @@ static void *pbx_outgoing_exec(void *data) } /* Notify anyone else again that may be interested that execution is complete */ - ast_mutex_lock(&outgoing->lock); + ao2_lock(outgoing); outgoing->executed = 1; ast_cond_signal(&outgoing->cond); - ast_mutex_unlock(&outgoing->lock); + ao2_unlock(outgoing); return NULL; } @@ -10209,21 +10206,69 @@ static void pbx_outgoing_state_callback(struct ast_dial *dial) ast_queue_control(channel, AST_CONTROL_ANSWER); } -static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, - const char *exten, int priority, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, - const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, int early_media, const struct ast_assigned_ids *assignedids) +/*! + * \brief Attempt to convert disconnect cause to old originate reason. + * + * \todo XXX The old originate reasons need to be trashed and replaced + * with normal disconnect cause codes if the call was not answered. + * The internal consumers of the reason values would also need to be + * updated: app_originate, call files, and AMI OriginateResponse. + */ +static enum ast_control_frame_type pbx_dial_reason(enum ast_dial_result dial_result, int cause) +{ + enum ast_control_frame_type pbx_reason; + + if (dial_result == AST_DIAL_RESULT_ANSWERED) { + /* Remote end answered. */ + pbx_reason = AST_CONTROL_ANSWER; + } else if (dial_result == AST_DIAL_RESULT_HANGUP) { + /* Caller hungup */ + pbx_reason = AST_CONTROL_HANGUP; + } else { + switch (cause) { + case AST_CAUSE_USER_BUSY: + pbx_reason = AST_CONTROL_BUSY; + break; + case AST_CAUSE_CALL_REJECTED: + case AST_CAUSE_NETWORK_OUT_OF_ORDER: + case AST_CAUSE_DESTINATION_OUT_OF_ORDER: + case AST_CAUSE_NORMAL_TEMPORARY_FAILURE: + case AST_CAUSE_SWITCH_CONGESTION: + case AST_CAUSE_NORMAL_CIRCUIT_CONGESTION: + pbx_reason = AST_CONTROL_CONGESTION; + break; + case AST_CAUSE_ANSWERED_ELSEWHERE: + case AST_CAUSE_NO_ANSWER: + /* Remote end was ringing (but isn't anymore) */ + pbx_reason = AST_CONTROL_RINGING; + break; + case AST_CAUSE_UNALLOCATED: + default: + /* Call Failure (not BUSY, and not NO_ANSWER, maybe Circuit busy or down?) */ + pbx_reason = 0; + break; + } + } + + return pbx_reason; +} + +static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, + const char *addr, int timeout, const char *context, const char *exten, int priority, + const char *app, const char *appdata, int *reason, int synchronous, + const char *cid_num, const char *cid_name, struct ast_variable *vars, + const char *account, struct ast_channel **locked_channel, int early_media, + const struct ast_assigned_ids *assignedids) { - RAII_VAR(struct pbx_outgoing *, outgoing, ao2_alloc(sizeof(*outgoing), pbx_outgoing_destroy), ao2_cleanup); + RAII_VAR(struct pbx_outgoing *, outgoing, NULL, ao2_cleanup); struct ast_channel *dialed; pthread_t thread; + outgoing = ao2_alloc(sizeof(*outgoing), pbx_outgoing_destroy); if (!outgoing) { return -1; } - - if (channel) { - *channel = NULL; - } + ast_cond_init(&outgoing->cond, NULL); if (!ast_strlen_zero(app)) { ast_copy_string(outgoing->app, app, sizeof(outgoing->app)); @@ -10245,6 +10290,10 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co ast_dial_set_global_timeout(outgoing->dial, timeout); if (ast_dial_prerun(outgoing->dial, NULL, cap)) { + if (synchronous && reason) { + *reason = pbx_dial_reason(AST_DIAL_RESULT_FAILED, + ast_dial_reason(outgoing->dial, 0)); + } return -1; } @@ -10257,7 +10306,6 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co if (vars) { ast_set_variables(dialed, vars); } - if (account) { ast_channel_accountcode_set(dialed, account); } @@ -10295,107 +10343,148 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co ast_dial_set_state_callback(outgoing->dial, pbx_outgoing_state_callback); } - if (channel) { - *channel = dialed; - ast_channel_ref(*channel); - ast_channel_lock(*channel); + if (locked_channel) { + /* + * Keep a dialed channel ref since the caller wants + * the channel returned. We must get the ref before + * spawning off pbx_outgoing_exec(). + */ + ast_channel_ref(dialed); + if (!synchronous) { + /* + * Lock it now to hold off pbx_outgoing_exec() in case the + * calling function needs the channel state/snapshot before + * dialing actually happens. + */ + ast_channel_lock(dialed); + } } - ast_mutex_init(&outgoing->lock); - ast_cond_init(&outgoing->cond, NULL); - ao2_ref(outgoing, +1); - - ast_mutex_lock(&outgoing->lock); - if (ast_pthread_create_detached(&thread, NULL, pbx_outgoing_exec, outgoing)) { ast_log(LOG_WARNING, "Unable to spawn dialing thread for '%s/%s'\n", type, addr); - if (channel) { - ast_channel_unlock(*channel); - ast_channel_unref(*channel); - } - ast_mutex_unlock(&outgoing->lock); ao2_ref(outgoing, -1); + if (locked_channel) { + if (!synchronous) { + ast_channel_unlock(dialed); + } + ast_channel_unref(dialed); + } return -1; } - /* Wait for dialing to complete */ if (synchronous) { - if (channel && *channel) { - ast_channel_unlock(*channel); - } + ao2_lock(outgoing); + /* Wait for dialing to complete */ while (!outgoing->dialed) { - ast_cond_wait(&outgoing->cond, &outgoing->lock); + ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing)); + } + if (1 < synchronous + && outgoing->dial_res == AST_DIAL_RESULT_ANSWERED) { + /* Wait for execution to complete */ + while (!outgoing->executed) { + ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing)); + } + } + ao2_unlock(outgoing); - if (outgoing->dial_res != AST_DIAL_RESULT_ANSWERED) { - ast_mutex_unlock(&outgoing->lock); - /* The dial operation failed. */ - return -1; + /* Determine the outcome of the dialing attempt up to it being answered. */ + if (reason) { + *reason = pbx_dial_reason(outgoing->dial_res, + ast_dial_reason(outgoing->dial, 0)); + } + + if (outgoing->dial_res != AST_DIAL_RESULT_ANSWERED) { + /* The dial operation failed. */ + if (locked_channel) { + ast_channel_unref(dialed); } + return -1; } - if (channel && *channel) { - ast_channel_lock(*channel); + if (locked_channel) { + ast_channel_lock(dialed); } } - /* Wait for execution to complete */ - if (synchronous > 1) { - while (!outgoing->executed) { - ast_cond_wait(&outgoing->cond, &outgoing->lock); - } + if (locked_channel) { + *locked_channel = dialed; } + return 0; +} - ast_mutex_unlock(&outgoing->lock); +int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, + int timeout, const char *context, const char *exten, int priority, int *reason, + int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, + const char *account, struct ast_channel **locked_channel, int early_media, + const struct ast_assigned_ids *assignedids) +{ + int res; + int my_reason; - if (reason) { - *reason = ast_dial_reason(outgoing->dial, 0); + if (!reason) { + reason = &my_reason; + } + *reason = 0; + if (locked_channel) { + *locked_channel = NULL; } - if ((synchronous > 1) && ast_dial_state(outgoing->dial) != AST_DIAL_RESULT_ANSWERED && - ast_strlen_zero(app) && ast_exists_extension(NULL, context, "failed", 1, NULL)) { - struct ast_channel *failed = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", NULL, NULL, 0, "OutgoingSpoolFailed"); + res = pbx_outgoing_attempt(type, cap, addr, timeout, context, exten, priority, + NULL, NULL, reason, synchronous, cid_num, cid_name, vars, account, locked_channel, + early_media, assignedids); - if (failed) { - char failed_reason[4] = ""; + if (res < 0 /* Call failed to get connected for some reason. */ + && 1 < synchronous + && ast_exists_extension(NULL, context, "failed", 1, NULL)) { + struct ast_channel *failed; - if (!ast_strlen_zero(context)) { - ast_channel_context_set(failed, context); - } + /* We do not have to worry about a locked_channel if dialing failed. */ + ast_assert(!locked_channel || !*locked_channel); - if (account) { - ast_channel_accountcode_set(failed, account); - } + /*! + * \todo XXX Not good. The channel name is not unique if more than + * one originate fails at a time. + */ + failed = ast_channel_alloc(0, AST_STATE_DOWN, cid_num, cid_name, account, + "failed", context, NULL, NULL, 0, "OutgoingSpoolFailed"); + if (failed) { + char failed_reason[12]; - set_ext_pri(failed, "failed", 1); ast_set_variables(failed, vars); - snprintf(failed_reason, sizeof(failed_reason), "%d", ast_dial_reason(outgoing->dial, 0)); + snprintf(failed_reason, sizeof(failed_reason), "%d", *reason); pbx_builtin_setvar_helper(failed, "REASON", failed_reason); ast_channel_unlock(failed); if (ast_pbx_run(failed)) { - ast_log(LOG_ERROR, "Unable to run PBX on '%s'\n", ast_channel_name(failed)); + ast_log(LOG_ERROR, "Unable to run PBX on '%s'\n", + ast_channel_name(failed)); ast_hangup(failed); } } } - return 0; -} - -int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, int early_media, const struct ast_assigned_ids *assignedids) -{ - return pbx_outgoing_attempt(type, cap, addr, timeout, context, exten, priority, NULL, NULL, reason, synchronous, cid_num, - cid_name, vars, account, channel, early_media, assignedids); + return res; } -int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, const struct ast_assigned_ids *assignedids) +int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr, + int timeout, const char *app, const char *appdata, int *reason, int synchronous, + const char *cid_num, const char *cid_name, struct ast_variable *vars, + const char *account, struct ast_channel **locked_channel, + const struct ast_assigned_ids *assignedids) { + if (reason) { + *reason = 0; + } + if (locked_channel) { + *locked_channel = NULL; + } if (ast_strlen_zero(app)) { return -1; } - return pbx_outgoing_attempt(type, cap, addr, timeout, NULL, NULL, 0, app, appdata, reason, synchronous, cid_num, - cid_name, vars, account, locked_channel, 0, assignedids); + return pbx_outgoing_attempt(type, cap, addr, timeout, NULL, NULL, 0, app, appdata, + reason, synchronous, cid_num, cid_name, vars, account, locked_channel, 0, + assignedids); } /* this is the guts of destroying a context --