summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES5
-rw-r--r--apps/app_queue.c10
-rw-r--r--include/asterisk/channel.h38
-rw-r--r--main/autoservice.c66
-rw-r--r--main/channel.c102
-rw-r--r--main/channel_internal_api.c6
-rw-r--r--res/res_agi.c38
-rw-r--r--res/res_pjsip_registrar_expire.c2
8 files changed, 141 insertions, 126 deletions
diff --git a/CHANGES b/CHANGES
index c550f3fda..463c88427 100644
--- a/CHANGES
+++ b/CHANGES
@@ -49,6 +49,11 @@ RTP
disabled and the range is 96-127 because any number below might be rejected
by a remote implementation; although no such broken implementation is known.
+Queue
+------------------
+ * A new dialplan variable, ABANDONED, is set when the call is not answered
+ by an agent.
+
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ----------
------------------------------------------------------------------------------
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 5fe932486..c9f3aee63 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -259,7 +259,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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>
@@ -271,6 +271,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<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>
@@ -4594,6 +4598,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}",
@@ -7925,6 +7931,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/include/asterisk/channel.h b/include/asterisk/channel.h
index a18591fb1..a76e606f9 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -966,11 +966,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 */
@@ -4686,37 +4681,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 f26d8fc9b..305ab23b1 100644
--- a/main/autoservice.c
+++ b/main/autoservice.c
@@ -61,6 +61,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;
};
@@ -75,13 +79,19 @@ static int as_chan_list_state;
static void *autoservice_run(void *ign)
{
struct ast_callid *callid = NULL;
+ 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);
@@ -96,6 +106,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");
@@ -126,9 +137,51 @@ static void *autoservice_run(void *ign)
}
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(NULL);
@@ -167,7 +220,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);
@@ -201,6 +253,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);
@@ -252,7 +305,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);
free(as);
diff --git a/main/channel.c b/main/channel.c
index 020d4d06e..278104cc9 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1061,25 +1061,6 @@ struct ast_channel *ast_dummy_channel_alloc(void)
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;
@@ -1543,18 +1524,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;
}
@@ -1569,7 +1551,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);
+ }
}
}
@@ -1578,8 +1571,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;
@@ -3883,32 +3885,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
@@ -10240,15 +10216,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;
@@ -10296,15 +10266,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;
@@ -10345,15 +10309,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;
@@ -10394,15 +10352,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 4bc770d3b..90f59d64a 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -223,7 +223,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 */
@@ -1699,8 +1698,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 969c62dd0..06df75293 100644
--- a/res/res_agi.c
+++ b/res/res_agi.c
@@ -4093,6 +4093,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;
@@ -4111,6 +4128,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");
@@ -4172,8 +4192,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);
@@ -4221,6 +4253,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);
@@ -4243,6 +4277,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 aeefc3fbc..e802733bb 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);
}