aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortwilson <twilson@f38db490-d61c-443f-a65b-d21fe96a405b>2008-10-29 20:13:22 +0000
committertwilson <twilson@f38db490-d61c-443f-a65b-d21fe96a405b>2008-10-29 20:13:22 +0000
commit106dc897f64acdbd6ec4720f3129ad7392b2555c (patch)
tree052397c0630997742b89b7f5ad06c2c6801c28e1
parent97f887edc6a5fd89858425319cae0abdca8e7421 (diff)
Small modification to putnopvut's patch to fix this issue. Thanks for all the help, putnopvut!
(closes issue #12884) Reported by: bcnit Patches: 12884v4-1.6.0-branch.patch uploaded by otherwiseguy (license 396) Tested by: otherwiseguy git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.0@152644 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--apps/app_queue.c162
1 files changed, 107 insertions, 55 deletions
diff --git a/apps/app_queue.c b/apps/app_queue.c
index bb83ea876..1af45ad19 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -511,6 +511,8 @@ AST_LIST_HEAD_STATIC(rule_lists, rule_list);
static struct ao2_container *queues;
+static void copy_rules(struct queue_ent *qe, const char *rulename);
+static void update_qe_rule(struct queue_ent *qe);
static void update_realtime_members(struct call_queue *q);
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
@@ -1666,7 +1668,7 @@ static void update_realtime_members(struct call_queue *q)
ast_config_destroy(member_config);
}
-static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
+static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, const char *overriding_rule)
{
struct call_queue *q;
struct queue_ent *cur, *prev = NULL;
@@ -1674,6 +1676,7 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
int pos = 0;
int inserted = 0;
enum queue_member_status stat;
+ int exit = 0;
if (!(q = load_realtime_queue(queuename)))
return res;
@@ -1681,50 +1684,63 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
ao2_lock(queues);
ao2_lock(q);
+ copy_rules(qe, S_OR(overriding_rule, q->defaultrule));
+ qe->pr = AST_LIST_FIRST(&qe->qe_rules);
+
/* This is our one */
- stat = get_member_status(q, qe->max_penalty, qe->min_penalty);
- if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
- *reason = QUEUE_JOINEMPTY;
- else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
- *reason = QUEUE_JOINUNAVAIL;
- else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
- *reason = QUEUE_JOINUNAVAIL;
- else if (q->maxlen && (q->count >= q->maxlen))
- *reason = QUEUE_FULL;
- else {
- /* There's space for us, put us at the right position inside
- * the queue.
- * Take into account the priority of the calling user */
- inserted = 0;
- prev = NULL;
- cur = q->head;
- while (cur) {
- /* We have higher priority than the current user, enter
- * before him, after all the other users with priority
- * higher or equal to our priority. */
- if ((!inserted) && (qe->prio > cur->prio)) {
- insert_entry(q, prev, qe, &pos);
- inserted = 1;
+ while (!exit) {
+ stat = get_member_status(q, qe->max_penalty, qe->min_penalty);
+ if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
+ *reason = QUEUE_JOINEMPTY;
+ else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
+ *reason = QUEUE_JOINUNAVAIL;
+ else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
+ *reason = QUEUE_JOINUNAVAIL;
+ else if (q->maxlen && (q->count >= q->maxlen))
+ *reason = QUEUE_FULL;
+ else {
+ /* There's space for us, put us at the right position inside
+ * the queue.
+ * Take into account the priority of the calling user */
+ inserted = 0;
+ prev = NULL;
+ cur = q->head;
+ while (cur) {
+ /* We have higher priority than the current user, enter
+ * before him, after all the other users with priority
+ * higher or equal to our priority. */
+ if ((!inserted) && (qe->prio > cur->prio)) {
+ insert_entry(q, prev, qe, &pos);
+ inserted = 1;
+ }
+ cur->pos = ++pos;
+ prev = cur;
+ cur = cur->next;
}
- cur->pos = ++pos;
- prev = cur;
- cur = cur->next;
- }
- /* No luck, join at the end of the queue */
- if (!inserted)
- insert_entry(q, prev, qe, &pos);
- ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
- ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
- ast_copy_string(qe->context, q->context, sizeof(qe->context));
- q->count++;
- res = 0;
- manager_event(EVENT_FLAG_CALL, "Join",
- "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
- qe->chan->name,
- S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
- S_OR(qe->chan->cid.cid_name, "unknown"),
- q->name, qe->pos, q->count, qe->chan->uniqueid );
- ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
+ /* No luck, join at the end of the queue */
+ if (!inserted)
+ insert_entry(q, prev, qe, &pos);
+ ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
+ ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
+ ast_copy_string(qe->context, q->context, sizeof(qe->context));
+ q->count++;
+ res = 0;
+ manager_event(EVENT_FLAG_CALL, "Join",
+ "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
+ qe->chan->name,
+ S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
+ S_OR(qe->chan->cid.cid_name, "unknown"),
+ q->name, qe->pos, q->count, qe->chan->uniqueid );
+ ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
+ }
+ if (!exit && qe->pr && res) {
+ /* We failed to join the queue, but perhaps we can join if we move
+ * to the next defined penalty rule
+ */
+ update_qe_rule(qe);
+ } else {
+ exit = 1;
+ }
}
ao2_unlock(q);
ao2_unlock(queues);
@@ -2794,7 +2810,8 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
/* This is the holding pen for callers 2 through maxlen */
for (;;) {
- enum queue_member_status stat;
+ enum queue_member_status stat = QUEUE_NORMAL;
+ int exit = 0;
if (is_our_turn(qe))
break;
@@ -2805,9 +2822,27 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
break;
}
- stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
+ /* If we are going to exit due to a leavewhenempty condition, we should
+ * actually attempt to keep the caller in the queue until we have
+ * exhausted all penalty rules.
+ */
+ for (; !exit || qe->pr; update_qe_rule(qe)) {
+ stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
+
+ if (!qe->pr || stat == QUEUE_NORMAL) {
+ break;
+ }
+
+ /* leave the queue if no agents, if enabled */
+ if ((qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) ||
+ ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
+ ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS))) {
+ continue;
+ } else {
+ exit = 1;
+ }
+ }
- /* leave the queue if no agents, if enabled */
if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
*reason = QUEUE_LEAVEEMPTY;
ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
@@ -4455,7 +4490,10 @@ static void copy_rules(struct queue_ent *qe, const char *rulename)
{
struct penalty_rule *pr_iter;
struct rule_list *rl_iter;
- const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
+ const char *tmp = rulename;
+ if (ast_strlen_zero(tmp)) {
+ return;
+ }
AST_LIST_LOCK(&rule_lists);
AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
if (!strcasecmp(rl_iter->name, tmp))
@@ -4599,15 +4637,13 @@ static int queue_exec(struct ast_channel *chan, void *data)
qe.last_periodic_announce_time = time(NULL);
qe.last_periodic_announce_sound = 0;
qe.valid_digits = 0;
- if (join_queue(args.queuename, &qe, &reason)) {
+ if (join_queue(args.queuename, &qe, &reason, args.rule)) {
ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
set_queue_result(chan, reason);
return 0;
}
ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
S_OR(chan->cid.cid_num, ""));
- copy_rules(&qe, args.rule);
- qe.pr = AST_LIST_FIRST(&qe.qe_rules);
check_turns:
if (ringing) {
ast_indicate(chan, AST_CONTROL_RINGING);
@@ -4629,7 +4665,8 @@ check_turns:
/* they may dial a digit from the queue context; */
/* or, they may timeout. */
- enum queue_member_status stat;
+ enum queue_member_status stat = QUEUE_NORMAL;
+ int exit = 0;
/* Leave if we have exceeded our queuetimeout */
if (qe.expire && (time(NULL) >= qe.expire)) {
@@ -4674,8 +4711,6 @@ check_turns:
goto stop;
}
- stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
-
/* exit after 'timeout' cycle if 'n' option enabled */
if (noption && tries >= qe.parent->membercount) {
ast_verb(3, "Exiting on time-out cycle\n");
@@ -4686,6 +4721,22 @@ check_turns:
break;
}
+ for (; !exit || qe.pr; update_qe_rule(&qe)) {
+ stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
+
+ if (!qe.pr || stat == QUEUE_NORMAL) {
+ break;
+ }
+
+ if ((qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) ||
+ ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
+ ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS))) {
+ continue;
+ } else {
+ exit = 1;
+ }
+ }
+
/* leave the queue if no agents, if enabled */
if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
record_abandoned(&qe);
@@ -5169,10 +5220,11 @@ static int reload_queue_rules(int reload)
ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
- if(!strcasecmp(rulevar->name, "penaltychange"))
+ if(!strcasecmp(rulevar->name, "penaltychange")) {
insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
- else
+ } else {
ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
+ }
}
}
AST_LIST_UNLOCK(&rule_lists);