diff options
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | apps/app_queue.c | 10 | ||||
-rw-r--r-- | channels/chan_sip.c | 40 | ||||
-rw-r--r-- | include/asterisk/channel.h | 38 | ||||
-rw-r--r-- | main/autoservice.c | 66 | ||||
-rw-r--r-- | main/channel.c | 102 | ||||
-rw-r--r-- | main/channel_internal_api.c | 6 | ||||
-rw-r--r-- | res/res_agi.c | 38 | ||||
-rw-r--r-- | res/res_pjsip_registrar_expire.c | 2 |
9 files changed, 164 insertions, 143 deletions
@@ -90,6 +90,11 @@ CLI Commands information will be sent to the Asterisk CLI. +Queue +------------------ + * A new dialplan variable, ABANDONED, is set when the call is not answered + by an agent. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ---------- ------------------------------------------------------------------------------ diff --git a/apps/app_queue.c b/apps/app_queue.c index b40c1db30..0abd512fa 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -256,7 +256,7 @@ any of the join options cause the caller to not enter the queue.</para> <para>This application does not automatically answer and should be preceeded by an application such as Answer(), Progress(), or Ringing().</para> - <para>This application sets the following channel variable upon completion:</para> + <para>This application sets the following channel variables upon completion:</para> <variablelist> <variable name="QUEUESTATUS"> <para>The status of the call as a text string.</para> @@ -268,6 +268,10 @@ <value name="LEAVEUNAVAIL" /> <value name="CONTINUE" /> </variable> + <variable name="ABANDONED"> + <para>If the call was not answered by an agent this variable will be TRUE.</para> + <value name="TRUE" /> + </variable> </variablelist> </description> <see-also> @@ -4617,6 +4621,8 @@ static void record_abandoned(struct queue_ent *qe) { RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + pbx_builtin_setvar_helper(qe->chan, "ABANDONED", "TRUE"); + set_queue_variables(qe->parent, qe->chan); ao2_lock(qe->parent); blob = ast_json_pack("{s: s, s: i, s: i, s: i}", @@ -7961,6 +7967,8 @@ static int queue_exec(struct ast_channel *chan, const char *data) /* Setup our queue entry */ qe.start = time(NULL); + pbx_builtin_setvar_helper(chan, "ABANDONED", NULL); + /* set the expire time based on the supplied timeout; */ if (!ast_strlen_zero(args.queuetimeoutstr)) { qe.expire = qe.start + atoi(args.queuetimeoutstr); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 32b2a3611..d6d2df69b 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -199,23 +199,29 @@ The session-timers parameter in sip.conf defines the mode of operation of SIP session-timers feature in Asterisk. The Asterisk can be configured in one of the following three modes: - 1. Accept :: In the "accept" mode, the Asterisk server honors session-timers requests - made by remote end-points. A remote end-point can request Asterisk to engage - session-timers by either sending it an INVITE request with a "Supported: timer" - header in it or by responding to Asterisk's INVITE with a 200 OK that contains - Session-Expires: header in it. In this mode, the Asterisk server does not - request session-timers from remote end-points. This is the default mode. - 2. Originate :: In the "originate" mode, the Asterisk server requests the remote - end-points to activate session-timers in addition to honoring such requests - made by the remote end-pints. In order to get as much protection as possible - against hanging SIP channels due to network or end-point failures, Asterisk - resends periodic re-INVITEs even if a remote end-point does not support - the session-timers feature. - 3. Refuse :: In the "refuse" mode, Asterisk acts as if it does not support session- - timers for inbound or outbound requests. If a remote end-point requests - session-timers in a dialog, then Asterisk ignores that request unless it's - noted as a requirement (Require: header), in which case the INVITE is - rejected with a 420 Bad Extension response. + 1. Accept :: In the "accept" mode, the Asterisk server honors + session-timers requests made by remote end-points. A remote + end-point can request Asterisk to engage session-timers by either + sending it an INVITE request with a "Supported: timer" header in + it or by responding to Asterisk's INVITE with a 200 OK that + contains Session-Expires: header in it. In this mode, the Asterisk + server does not request session-timers from remote + end-points. This is the default mode. + + 2. Originate :: In the "originate" mode, the Asterisk server + requests the remote end-points to activate session-timers in + addition to honoring such requests made by the remote + end-points. In order to get as much protection as possible against + hanging SIP channels due to network or end-point failures, + Asterisk resends periodic re-INVITEs even if a remote end-point + does not support the session-timers feature. + + 3. Refuse :: In the "refuse" mode, Asterisk acts as if it does not + support session- timers for inbound or outbound requests. If a + remote end-point requests session-timers in a dialog, then + Asterisk ignores that request unless it's noted as a requirement + (Require: header), in which case the INVITE is rejected with a 420 + Bad Extension response. */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index e5f53f082..ff92cc878 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -967,11 +967,6 @@ enum { * The channel is executing a subroutine or macro */ AST_FLAG_SUBROUTINE_EXEC = (1 << 27), - /*! - * The channel is currently in an operation where - * frames should be deferred. - */ - AST_FLAG_DEFER_FRAMES = (1 << 28), }; /*! \brief ast_bridge_config flags */ @@ -4676,37 +4671,4 @@ enum ast_channel_error { */ enum ast_channel_error ast_channel_errno(void); -/*! - * \brief Retrieve the deferred read queue. - */ -struct ast_readq_list *ast_channel_deferred_readq(struct ast_channel *chan); - -/*! - * \brief Start deferring deferrable frames on this channel - * - * Sometimes, a channel gets entered into a mode where a "main" application - * is tasked with servicing frames on the channel, but that application does - * not need to act on those frames. However, it would be imprudent to simply - * drop important frames. This function can be called so that important frames - * will be deferred, rather than placed in the channel frame queue as normal. - * - * \pre chan MUST be locked before calling - * - * \param chan The channel on which frames should be deferred - */ -void ast_channel_start_defer_frames(struct ast_channel *chan); - -/*! - * \brief Stop deferring deferrable frames on this channel - * - * When it is time to stop deferring frames on the channel, all deferred frames - * will be queued onto the channel's read queue so that the next servicer of - * the channel can handle those frames as necessary. - * - * \pre chan MUST be locked before calling - * - * \param chan The channel on which to stop deferring frames. - */ -void ast_channel_stop_defer_frames(struct ast_channel *chan); - #endif /* _ASTERISK_CHANNEL_H */ diff --git a/main/autoservice.c b/main/autoservice.c index c8a59a639..11c9eab96 100644 --- a/main/autoservice.c +++ b/main/autoservice.c @@ -59,6 +59,10 @@ struct asent { unsigned int use_count; unsigned int orig_end_dtmf_flag:1; unsigned int ignore_frame_types; + /*! Frames go on at the head of deferred_frames, so we have the frames + * from newest to oldest. As we put them at the head of the readq, we'll + * end up with them in the right order for the channel's readq. */ + AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames; AST_LIST_ENTRY(asent) list; }; @@ -73,13 +77,19 @@ static int as_chan_list_state; static void *autoservice_run(void *ign) { ast_callid callid = 0; + struct ast_frame hangup_frame = { + .frametype = AST_FRAME_CONTROL, + .subclass.integer = AST_CONTROL_HANGUP, + }; while (!asexit) { struct ast_channel *mons[MAX_AUTOMONS]; + struct asent *ents[MAX_AUTOMONS]; struct ast_channel *chan; struct asent *as; - int x = 0, ms = 50; + int i, x = 0, ms = 50; struct ast_frame *f = NULL; + struct ast_frame *defer_frame = NULL; AST_LIST_LOCK(&aslist); @@ -94,6 +104,7 @@ static void *autoservice_run(void *ign) AST_LIST_TRAVERSE(&aslist, as, list) { if (!ast_check_hangup(as->chan)) { if (x < MAX_AUTOMONS) { + ents[x] = as; mons[x++] = as->chan; } else { ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n"); @@ -121,9 +132,51 @@ static void *autoservice_run(void *ign) ast_callid_threadassoc_change(callid); f = ast_read(chan); - if (f) { + + if (!f) { + /* No frame means the channel has been hung up. + * A hangup frame needs to be queued here as ast_waitfor() may + * never return again for the condition to be detected outside + * of autoservice. So, we'll leave a HANGUP queued up so the + * thread in charge of this channel will know. */ + + defer_frame = &hangup_frame; + } else if (ast_is_deferrable_frame(f)) { + defer_frame = f; + } else { + /* Can't defer. Discard and continue with next. */ ast_frfree(f); + continue; } + + for (i = 0; i < x; i++) { + struct ast_frame *dup_f; + + if (mons[i] != chan) { + continue; + } + + if (!f) { /* defer_frame == &hangup_frame */ + if ((dup_f = ast_frdup(defer_frame))) { + AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list); + } + } else { + if ((dup_f = ast_frisolate(defer_frame))) { + AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list); + } + if (dup_f != defer_frame) { + ast_frfree(defer_frame); + } + } + + break; + } + /* The ast_waitfor_n() call will only read frames from + * the channels' file descriptors. If ast_waitfor_n() + * returns non-NULL, then one of the channels in the + * mons array must have triggered the return. It's + * therefore impossible that we got here while (i >= x). + * If we did, we'd need to ast_frfree(f) if (f). */ } ast_callid_threadassoc_change(0); @@ -162,7 +215,6 @@ int ast_autoservice_start(struct ast_channel *chan) as->orig_end_dtmf_flag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY) ? 1 : 0; if (!as->orig_end_dtmf_flag) ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY); - ast_channel_start_defer_frames(chan); ast_channel_unlock(chan); AST_LIST_LOCK(&aslist); @@ -196,6 +248,7 @@ int ast_autoservice_stop(struct ast_channel *chan) { int res = -1; struct asent *as, *removed = NULL; + struct ast_frame *f; int chan_list_state; AST_LIST_LOCK(&aslist); @@ -247,7 +300,12 @@ int ast_autoservice_stop(struct ast_channel *chan) } ast_channel_lock(chan); - ast_channel_stop_defer_frames(chan); + while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) { + if (!((1 << f->frametype) & as->ignore_frame_types)) { + ast_queue_frame_head(chan, f); + } + ast_frfree(f); + } ast_channel_unlock(chan); ast_free(as); diff --git a/main/channel.c b/main/channel.c index 872661c5a..cdb6569c3 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1062,25 +1062,6 @@ struct ast_channel *__ast_dummy_channel_alloc(const char *file, int line, const return tmp; } -void ast_channel_start_defer_frames(struct ast_channel *chan) -{ - ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES); -} - -void ast_channel_stop_defer_frames(struct ast_channel *chan) -{ - ast_clear_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES); - - /* Move the deferred frames onto the channel read queue, ahead of other queued frames */ - ast_queue_frame_head(chan, AST_LIST_FIRST(ast_channel_deferred_readq(chan))); - /* ast_frfree will mosey down the list and free them all */ - if (!AST_LIST_EMPTY(ast_channel_deferred_readq(chan))) { - ast_frfree(AST_LIST_FIRST(ast_channel_deferred_readq(chan))); - } - /* Reset the list to be empty */ - AST_LIST_HEAD_INIT_NOLOCK(ast_channel_deferred_readq(chan)); -} - static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after) { struct ast_frame *f; @@ -1544,18 +1525,19 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c int res = 0; struct timeval start; int ms; + AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames; + + AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames); /* If no other generator is present, start silencegen while waiting */ if (ast_opt_transmit_silence && !ast_channel_generatordata(chan)) { silgen = ast_channel_start_silence_generator(chan); } - ast_channel_lock(chan); - ast_channel_start_defer_frames(chan); - ast_channel_unlock(chan); - start = ast_tvnow(); while ((ms = ast_remaining_ms(start, timeout_ms))) { + struct ast_frame *dup_f = NULL; + if (cond && ((*cond)(data) == 0)) { break; } @@ -1570,7 +1552,18 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c res = -1; break; } - ast_frfree(f); + + if (!ast_is_deferrable_frame(f)) { + ast_frfree(f); + continue; + } + + if ((dup_f = ast_frisolate(f))) { + if (dup_f != f) { + ast_frfree(f); + } + AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list); + } } } @@ -1579,8 +1572,17 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c ast_channel_stop_silence_generator(chan, silgen); } + /* We need to free all the deferred frames, but we only need to + * queue the deferred frames if there was no error and no + * hangup was received + */ ast_channel_lock(chan); - ast_channel_stop_defer_frames(chan); + while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) { + if (!res) { + ast_queue_frame_head(chan, f); + } + ast_frfree(f); + } ast_channel_unlock(chan); return res; @@ -3881,32 +3883,6 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) if (!AST_LIST_EMPTY(ast_channel_readq(chan))) { int skip_dtmf = should_skip_dtmf(chan); - if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_FRAMES)) { - AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) { - if (ast_is_deferrable_frame(f)) { - if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_HANGUP) { - struct ast_frame *dup; - - /* Hangup is a special case. We want to defer the frame, but we also do not - * want to remove it from the frame queue. So rather than just moving the frame - * over, we duplicate it and move the copy to the deferred readq. - * - * The reason for this? This way, whoever calls ast_read() will get a NULL return - * immediately and can tell the channel has hung up and do what it needs to. Also, - * when frame deferral finishes, then whoever calls ast_read() next will also get - * the hangup. - */ - dup = ast_frdup(f); - AST_LIST_INSERT_TAIL(ast_channel_deferred_readq(chan), dup, frame_list); - } else { - AST_LIST_INSERT_TAIL(ast_channel_deferred_readq(chan), f, frame_list); - AST_LIST_REMOVE_CURRENT(frame_list); - } - } - } - AST_LIST_TRAVERSE_SAFE_END; - } - AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) { /* We have to be picky about which frame we pull off of the readq because * there are cases where we want to leave DTMF frames on the queue until @@ -10262,15 +10238,9 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc ast_party_connected_line_copy(ast_channel_connected(macro_chan), connected); } - ast_channel_start_defer_frames(macro_chan); ast_channel_unlock(macro_chan); retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args); - - ast_channel_lock(macro_chan); - ast_channel_stop_defer_frames(macro_chan); - ast_channel_unlock(macro_chan); - if (!retval) { struct ast_party_connected_line saved_connected; @@ -10318,15 +10288,9 @@ int ast_channel_redirecting_macro(struct ast_channel *autoservice_chan, struct a ast_party_redirecting_copy(ast_channel_redirecting(macro_chan), redirecting); } - ast_channel_start_defer_frames(macro_chan); ast_channel_unlock(macro_chan); retval = ast_app_run_macro(autoservice_chan, macro_chan, macro, macro_args); - - ast_channel_lock(macro_chan); - ast_channel_stop_defer_frames(macro_chan); - ast_channel_unlock(macro_chan); - if (!retval) { struct ast_party_redirecting saved_redirecting; @@ -10367,15 +10331,9 @@ int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct ast_party_connected_line_copy(ast_channel_connected(sub_chan), connected); } - ast_channel_start_defer_frames(sub_chan); ast_channel_unlock(sub_chan); retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0); - - ast_channel_lock(sub_chan); - ast_channel_stop_defer_frames(sub_chan); - ast_channel_unlock(sub_chan); - if (!retval) { struct ast_party_connected_line saved_connected; @@ -10416,15 +10374,9 @@ int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast ast_party_redirecting_copy(ast_channel_redirecting(sub_chan), redirecting); } - ast_channel_start_defer_frames(sub_chan); ast_channel_unlock(sub_chan); retval = ast_app_run_sub(autoservice_chan, sub_chan, sub, sub_args, 0); - - ast_channel_lock(sub_chan); - ast_channel_stop_defer_frames(sub_chan); - ast_channel_unlock(sub_chan); - if (!retval) { struct ast_party_redirecting saved_redirecting; diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index 50f6c5da9..1cb91e7c3 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -221,7 +221,6 @@ struct ast_channel { struct stasis_cp_single *topics; /*!< Topic for all channel's events */ struct stasis_forward *endpoint_forward; /*!< Subscription for event forwarding to endpoint's topic */ struct stasis_forward *endpoint_cache_forward; /*!< Subscription for cache updates to endpoint's topic */ - struct ast_readq_list deferred_readq; }; /*! \brief The monotonically increasing integer counter for channel uniqueids */ @@ -1682,8 +1681,3 @@ enum ast_channel_error ast_channel_internal_errno(void) return *error_code; } - -struct ast_readq_list *ast_channel_deferred_readq(struct ast_channel *chan) -{ - return &chan->deferred_readq; -} diff --git a/res/res_agi.c b/res/res_agi.c index 5e047d4c3..06e8a03e2 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -4091,6 +4091,23 @@ static enum agi_result agi_handle_command(struct ast_channel *chan, AGI *agi, ch return AGI_RESULT_SUCCESS; } +AST_LIST_HEAD_NOLOCK(deferred_frames, ast_frame); + +static void queue_deferred_frames(struct deferred_frames *deferred_frames, + struct ast_channel *chan) +{ + struct ast_frame *f; + + if (!AST_LIST_EMPTY(deferred_frames)) { + ast_channel_lock(chan); + while ((f = AST_LIST_REMOVE_HEAD(deferred_frames, frame_list))) { + ast_queue_frame_head(chan, f); + ast_frfree(f); + } + ast_channel_unlock(chan); + } +} + static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[]) { struct ast_channel *c; @@ -4109,6 +4126,9 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi const char *sighup_str; const char *exit_on_hangup_str; int exit_on_hangup; + struct deferred_frames deferred_frames; + + AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames); ast_channel_lock(chan); sighup_str = pbx_builtin_getvar_helper(chan, "AGISIGHUP"); @@ -4170,8 +4190,20 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi /* Write, ignoring errors */ if (write(agi->audio, f->data.ptr, f->datalen) < 0) { } + ast_frfree(f); + } else if (ast_is_deferrable_frame(f)) { + struct ast_frame *dup_f; + + if ((dup_f = ast_frisolate(f))) { + AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list); + } + + if (dup_f != f) { + ast_frfree(f); + } + } else { + ast_frfree(f); } - ast_frfree(f); } } else if (outfd > -1) { size_t len = sizeof(buf); @@ -4219,6 +4251,8 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi buf[buflen - 1] = '\0'; } + queue_deferred_frames(&deferred_frames, chan); + if (agidebug) ast_verbose("<%s>AGI Rx << %s\n", ast_channel_name(chan), buf); cmd_status = agi_handle_command(chan, agi, buf, dead); @@ -4241,6 +4275,8 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi } } + queue_deferred_frames(&deferred_frames, chan); + if (agi->speech) { ast_speech_destroy(agi->speech); } diff --git a/res/res_pjsip_registrar_expire.c b/res/res_pjsip_registrar_expire.c index 61841a054..fe4a60da8 100644 --- a/res/res_pjsip_registrar_expire.c +++ b/res/res_pjsip_registrar_expire.c @@ -82,7 +82,7 @@ static void *check_expiration_thread(void *data) ast_variables_destroy(var); if (contacts) { - ast_debug(3, "Expiring %d contacts\n\n", ao2_container_count(contacts)); + ast_debug(3, "Expiring %d contacts\n", ao2_container_count(contacts)); ao2_callback(contacts, OBJ_NODATA, expire_contact, NULL); ao2_ref(contacts, -1); } |