Index: apps/app_queue.c =================================================================== RCS file: /usr/cvsroot/asterisk/apps/app_queue.c,v retrieving revision 1.68 diff -u -r1.68 app_queue.c --- apps/app_queue.c 9 Jun 2004 01:45:07 -0000 1.68 +++ apps/app_queue.c 10 Jun 2004 16:00:36 -0000 @@ -7,6 +7,8 @@ * * Mark Spencer * + * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr). + * * These features added by David C. Troy : * - Per-queue holdtime calculation * - Estimated holdtime announcement @@ -156,7 +158,8 @@ char moh[80]; /* Name of musiconhold to be used */ char announce[80]; /* Announcement to play for member when call is answered */ char context[80]; /* Context when user exits queue */ - int pos; /* Where we are in the queue */ + int pos; /* Where we are in the queue */ + int prio; /* Our priority */ int last_pos_said; /* Last position we told the user */ time_t last_pos; /* Last time we told the user their position */ int opos; /* Where we started in the queue */ @@ -168,12 +171,12 @@ }; struct member { - char tech[80]; /* Technology */ - char loc[256]; /* Location */ - int penalty; /* Are we a last resort? */ - int calls; - int dynamic; /* Are we dynamically added? */ - time_t lastcall; /* When last successful call was hungup */ + char tech[80]; /* Technology */ + char loc[256]; /* Location */ + int penalty; /* Are we a last resort? */ + int calls; /* Number of calls serviced by this member */ + int dynamic; /* Are we dynamically added? */ + time_t lastcall; /* When last successful call was hungup */ struct member *next; /* Next member */ }; @@ -206,6 +209,7 @@ int dead; /* Whether this queue is dead or not */ int retry; /* Retry calling everyone after this amount of time */ int timeout; /* How long to wait for an answer */ + int callattempt; /* Whether a client is trying to reach a queue member */ /* Queue strategy things */ @@ -246,6 +250,8 @@ struct queue_ent *cur, *prev = NULL; int res = -1; int pos = 0; + int inserted = 0; + ast_mutex_lock(&qlock); q = queues; while(q) { @@ -253,32 +259,51 @@ /* This is our one */ ast_mutex_lock(&q->lock); if (q->members && (!q->maxlen || (q->count < q->maxlen))) { - /* There's space for us, put us at the end */ + /* 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 */ + if ((!inserted) && (qe->prio > cur->prio)) { + if (prev) + prev->next = qe; + else + q->head = qe; + qe->next = cur; + qe->parent = q; + qe->pos = ++pos; + qe->opos = pos; + inserted = 1; + } cur->pos = ++pos; prev = cur; cur = cur->next; } - if (prev) - prev->next = qe; - else - q->head = qe; - /* Fix additional pointers and - information */ - qe->next = NULL; - qe->parent = q; - qe->pos = ++pos; - qe->opos = pos; + /* No luck, join at the end of the queue */ + if (!inserted) { + if (prev) + prev->next = qe; + else + q->head = qe; + /* Fix additional pointers and + information */ + qe->next = NULL; + qe->parent = q; + qe->pos = ++pos; + qe->opos = pos; + } strncpy(qe->moh, q->moh, sizeof(qe->moh)); strncpy(qe->announce, q->announce, sizeof(qe->announce)); strncpy(qe->context, q->context, sizeof(qe->context)); q->count++; res = 0; manager_event(EVENT_FLAG_CALL, "Join", - "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", - qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count ); + "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", + qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count ); #if 0 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); #endif @@ -449,8 +474,8 @@ /* Take us out of the queue */ manager_event(EVENT_FLAG_CALL, "Leave", - "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n", - qe->chan->name, q->name, q->count); + "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n", + qe->chan->name, q->name, q->count); #if 0 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); #endif @@ -566,13 +591,16 @@ } } else { /* Ring just the best channel */ - ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric); + if (option_debug) + ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", + best->tech, best->numsubst, best->metric); ring_entry(qe, best); } } } while (best && !best->chan); if (!best) { - ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); + if (option_debug) + ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); return 0; } return 1; @@ -785,6 +813,26 @@ } +static int is_our_turn(struct queue_ent *qe) +{ + struct queue_ent *ch; + int res; + + /* Atomically read the parent head -- does not need a lock */ + ch = qe->parent->head; + /* If we are now at the top of the head, break out */ + if (ch == qe) { + if (option_debug) + ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); + res = 1; + } else { + if (option_debug) + ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); + res = 0; + } + return res; +} + static int wait_our_turn(struct queue_ent *qe, int ringing) { struct queue_ent *ch; @@ -797,8 +845,11 @@ ch = qe->parent->head; /* If we are now at the top of the head, break out */ - if (qe == ch) + if (ch == qe) { + if (option_debug) + ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); break; + } /* If we have timed out, break out */ if ( qe->queuetimeout ) { @@ -811,7 +862,6 @@ if (qe->parent->announcefrequency && !ringing) say_position(qe); - /* Wait a second before checking again */ res = ast_waitfordigit(qe->chan, RECHECK * 1000); if (res) @@ -920,6 +970,17 @@ struct ast_bridge_config config; /* Hold the lock while we setup the outgoing calls */ ast_mutex_lock(&qe->parent->lock); + if (qe->parent->callattempt) { + /* Someone else is already trying, go back */ + if (option_debug) + ast_log(LOG_WARNING, "%s: Hmm, someone else is already trying to call a queue member.\n", + qe->chan->name); + ast_mutex_unlock(&qe->parent->lock); + return 0; + } + if (option_debug) + ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", + qe->chan->name); strncpy(queuename, qe->parent->name, sizeof(queuename) - 1); time(&now); cur = qe->parent->members; @@ -953,10 +1014,12 @@ if ((strchr(options, 'n')) && (now - qe->start >= qe->parent->timeout)) *go_on = 1; } - if (url) { - ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url); - } else - ast_log(LOG_DEBUG, "Simple queue (no URL)\n"); + if (option_debug) { + if (url) + ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url); + else + ast_log(LOG_DEBUG, "Simple queue (no URL)\n"); + } tmp->member = cur; /* Never directly dereference! Could change on reload */ strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1); @@ -986,7 +1049,11 @@ to = qe->parent->timeout * 1000; else to = -1; + /* Indicate our call attempt */ + qe->parent->callattempt = 1; ring_one(qe, outgoing); + /* Clear our call attempt */ + qe->parent->callattempt = 0; ast_mutex_unlock(&qe->parent->lock); lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit); ast_mutex_lock(&qe->parent->lock); @@ -1010,6 +1077,8 @@ /* Nobody answered, next please? */ res=0; } + if (option_debug) + ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name); goto out; } if (peer) { @@ -1079,7 +1148,8 @@ /* Drop out of the queue at this point, to prepare for next caller */ leave_queue(qe); if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) { - ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); + if (option_debug) + ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); ast_channel_sendurl( peer, url ); } ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start); @@ -1367,12 +1437,13 @@ char *options = NULL; char *url = NULL; char *announceoverride = NULL; + char *user_priority; + long int prio; char *queuetimeoutstr = NULL; + /* whether to exit Queue application after the timeout hits */ int go_on = 0; - - /* Our queue entry */ struct queue_ent qe; @@ -1409,29 +1480,46 @@ qe.queuetimeout = atoi(queuetimeoutstr); } else { qe.queuetimeout = 0; + } } } } } + + /* Get the priority from the variable ${QUEUE_PRIO} */ + user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); + if (user_priority) { + prio = strtol(user_priority, NULL, 10); + if (prio == LONG_MAX || prio == LONG_MIN) { + ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s\n", + user_priority, chan->name); + prio = 0; + } + } else { + if (option_debug) + ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); + prio = 0; } if (options) { if (strchr(options, 'r')) { ringing = 1; - } - } - - printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n", - queuename, options, url, announceoverride, qe.queuetimeout); + } + } +// if (option_debug) + ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, timeout: %d, priority: %d\n", + queuename, options, url, announceoverride, qe.queuetimeout, (int)prio); qe.chan = chan; qe.start = time(NULL); + qe.prio = (int)prio; qe.last_pos_said = 0; qe.last_pos = 0; if (!join_queue(queuename, &qe)) { ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : ""); /* Start music on hold */ +check_turns: if (ringing) { ast_indicate(chan, AST_CONTROL_RINGING); } else { @@ -1517,7 +1605,15 @@ res = 0; break; } - + /* Since this is a priority queue and + * it is not sure that we are still at the head + * of the queue, go and check for our turn again. + */ + if (!is_our_turn(&qe)) { + ast_log(LOG_DEBUG, "Damn priorities, going back in queue (%s)!\n", + qe.chan->name); + goto check_turns; + } } } /* Don't allow return code > 0 */ @@ -1590,6 +1686,7 @@ q->retry = 0; q->timeout = -1; q->maxlen = 0; + q->callattempt = 0; q->announcefrequency = 0; q->announceholdtime = 0; q->holdtime = 0; @@ -1718,7 +1815,7 @@ if (!q->count) { free(q); } else - ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n"); + ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n"); } else ql = q; q = qn; @@ -1794,8 +1891,8 @@ pos = 1; ast_cli(fd, " Callers: \n"); for (qe = q->head; qe; qe = qe->next) - ast_cli(fd, " %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name, - (long)(now - qe->start) / 60, (long)(now - qe->start) % 60); + ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++, qe->chan->name, + (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio); } else ast_cli(fd, " No Callers\n"); ast_cli(fd, "\n");