2 * Asterisk -- A telephony toolkit for Linux.
4 * True call queues with optional send URL on answer
6 * Copyright (C) 1999-2004, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * 2004-11-25: Persistent Dynamic Members added by:
11 * NetNation Communications (www.netnation.com)
12 * Kevin Lindsay <kevinl@netnation.com>
14 * Each dynamic agent in each queue is now stored in the astdb.
15 * When asterisk is restarted, each agent will be automatically
16 * readded into their recorded queues. This feature can be
17 * configured with the 'peristent_members=<1|0>' KVP under the
18 * '[general]' group in queues.conf. The default is on.
20 * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
22 * These features added by David C. Troy <dave@toad.net>:
23 * - Per-queue holdtime calculation
24 * - Estimated holdtime announcement
25 * - Position announcement
26 * - Abandoned/completed call counters
27 * - Failout timer passed as optional app parameter
28 * - Optional monitoring of calls, started when call is answered
30 * Patch Version 1.07 2003-12-24 01
32 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
33 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
35 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
36 * by Matthew Enger <m.enger@xi.com.au>
38 * This program is free software, distributed under the terms of
39 * the GNU General Public License
42 #include <asterisk/lock.h>
43 #include <asterisk/file.h>
44 #include <asterisk/logger.h>
45 #include <asterisk/channel.h>
46 #include <asterisk/pbx.h>
47 #include <asterisk/options.h>
48 #include <asterisk/module.h>
49 #include <asterisk/translate.h>
50 #include <asterisk/say.h>
51 #include <asterisk/features.h>
52 #include <asterisk/musiconhold.h>
53 #include <asterisk/cli.h>
54 #include <asterisk/manager.h>
55 #include <asterisk/config.h>
56 #include <asterisk/monitor.h>
57 #include <asterisk/utils.h>
58 #include <asterisk/causes.h>
59 #include <asterisk/astdb.h>
67 #include <sys/signal.h>
68 #include <netinet/in.h>
70 #include "../astconf.h"
72 #define QUEUE_STRATEGY_RINGALL 0
73 #define QUEUE_STRATEGY_ROUNDROBIN 1
74 #define QUEUE_STRATEGY_LEASTRECENT 2
75 #define QUEUE_STRATEGY_FEWESTCALLS 3
76 #define QUEUE_STRATEGY_RANDOM 4
77 #define QUEUE_STRATEGY_RRMEMORY 5
79 static struct strategy {
83 { QUEUE_STRATEGY_RINGALL, "ringall" },
84 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
85 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
86 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
87 { QUEUE_STRATEGY_RANDOM, "random" },
88 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
91 #define DEFAULT_RETRY 5
92 #define DEFAULT_TIMEOUT 15
93 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
95 #define RES_OKAY 0 /* Action completed */
96 #define RES_EXISTS (-1) /* Entry already exists */
97 #define RES_OUTOFMEMORY (-2) /* Out of memory */
98 #define RES_NOSUCHQUEUE (-3) /* No such queue */
100 static char *tdesc = "True Call Queueing";
102 static char *app = "Queue";
104 static char *synopsis = "Queue a call for a call queue";
106 static char *descrip =
107 " Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
108 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
109 " This application returns -1 if the originating channel hangs up, or if the\n"
110 "call is bridged and either of the parties in the bridge terminate the call.\n"
111 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
112 "The option string may contain zero or more of the following characters:\n"
113 " 't' -- allow the called user transfer the calling user\n"
114 " 'T' -- to allow the calling user to transfer the call.\n"
115 " 'd' -- data-quality (modem) call (minimum delay).\n"
116 " 'h' -- allow callee to hang up by hitting *.\n"
117 " 'H' -- allow caller to hang up by hitting *.\n"
118 " 'n' -- no retries on the timeout; will exit this application and go to the next step.\n"
119 " 'r' -- ring instead of playing MOH\n"
120 " In addition to transferring the call, a call may be parked and then picked\n"
121 "up by another user.\n"
122 " The optional URL will be sent to the called party if the channel supports\n"
124 " The timeout will cause the queue to fail out after a specified number of\n"
125 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n";
128 static char *app_aqm = "AddQueueMember" ;
129 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
130 static char *app_aqm_descrip =
131 " AddQueueMember(queuename[|interface[|penalty]]):\n"
132 "Dynamically adds interface to an existing queue.\n"
133 "If the interface is already in the queue and there exists an n+101 priority\n"
134 "then it will then jump to this priority. Otherwise it will return an error\n"
135 "Returns -1 if there is an error.\n"
136 "Example: AddQueueMember(techsupport|SIP/3000)\n"
139 static char *app_rqm = "RemoveQueueMember" ;
140 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
141 static char *app_rqm_descrip =
142 " RemoveQueueMember(queuename[|interface]):\n"
143 "Dynamically removes interface to an existing queue\n"
144 "If the interface is NOT in the queue and there exists an n+101 priority\n"
145 "then it will then jump to this priority. Otherwise it will return an error\n"
146 "Returns -1 if there is an error.\n"
147 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
150 /* Persistent Members astdb family */
151 static const char *pm_family = "/Queue/PersistentMembers";
152 /* The maximum lengh of each persistent member queue database entry */
153 #define PM_MAX_LEN 2048
154 /* queues.conf [general] option */
155 static int queue_persistent_members = 0;
157 #define QUEUE_FLAG_RINGBACKONLY (1 << 0)
158 #define QUEUE_FLAG_MUSICONHOLD (1 << 1)
159 #define QUEUE_FLAG_DATAQUALITY (1 << 2)
160 #define QUEUE_FLAG_REDIR_IN (1 << 3)
161 #define QUEUE_FLAG_REDIR_OUT (1 << 4)
162 #define QUEUE_FLAG_DISCON_IN (1 << 5)
163 #define QUEUE_FLAG_DISCON_OUT (1 << 6)
164 #define QUEUE_FLAG_MONJOIN (1 << 7) /* Should we join the two files when we are done with the call */
165 #define QUEUE_FLAG_DEAD (1 << 8) /* Whether the queue is dead or not */
166 #define QUEUE_FLAG_JOINEMPTY (1 << 9) /* Do we care if the queue has no members? */
167 #define QUEUE_FLAG_EVENTWHENCALLED (1 << 10) /* Generate an event when the agent is called (before pickup) */
168 #define QUEUE_FLAG_LEAVEWHENEMPTY (1 << 11) /* If all agents leave the queue, remove callers from the queue */
169 #define QUEUE_FLAG_REPORTHOLDTIME (1 << 12) /* Should we report caller hold time to answering member? */
170 #define QUEUE_FLAG_WRAPPED (1 << 13) /* Round Robin - wrapped around? */
172 /* We define a customer "local user" structure because we
173 use it not only for keeping track of what is in use but
174 also for keeping track of who we're dialing. */
177 struct ast_channel *chan;
182 int flags; /* flag bits */
184 struct member *member;
185 struct localuser *next;
191 struct ast_call_queue *parent; /* What queue is our parent */
192 char moh[80]; /* Name of musiconhold to be used */
193 char announce[80]; /* Announcement to play for member when call is answered */
194 char context[80]; /* Context when user exits queue */
195 int pos; /* Where we are in the queue */
196 int prio; /* Our priority */
197 int last_pos_said; /* Last position we told the user */
198 time_t last_pos; /* Last time we told the user their position */
199 int opos; /* Where we started in the queue */
200 int handled; /* Whether our call was handled */
201 time_t start; /* When we started holding */
202 time_t expire; /* When this entry should expire (time out of queue) */
203 struct ast_channel *chan; /* Our channel */
204 struct queue_ent *next; /* The next queue entry */
208 char interface[80]; /* Technology/Location */
209 int penalty; /* Are we a last resort? */
210 int calls; /* Number of calls serviced by this member */
211 int dynamic; /* Are we dynamically added? */
212 int status; /* Status of queue member */
213 time_t lastcall; /* When last successful call was hungup */
214 struct member *next; /* Next member */
217 struct ast_call_queue {
219 char name[80]; /* Name of the queue */
220 char moh[80]; /* Name of musiconhold to be used */
221 char announce[80]; /* Announcement to play when call is answered */
222 char context[80]; /* Context for this queue */
223 int flags; /* flag bits */
224 int strategy; /* Queueing strategy */
225 int announcefrequency; /* How often to announce their position */
226 int roundingseconds; /* How many seconds do we round to? */
227 int announceholdtime; /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
228 int holdtime; /* Current avg holdtime for this queue, based on recursive boxcar filter */
229 int callscompleted; /* Number of queue calls completed */
230 int callsabandoned; /* Number of queue calls abandoned */
231 int servicelevel; /* seconds setting for servicelevel*/
232 int callscompletedinsl; /* Number of queue calls answererd with servicelevel*/
233 char monfmt[8]; /* Format to use when recording calls */
234 char sound_next[80]; /* Sound file: "Your call is now first in line" (def. queue-youarenext) */
235 char sound_thereare[80]; /* Sound file: "There are currently" (def. queue-thereare) */
236 char sound_calls[80]; /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
237 char sound_holdtime[80]; /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
238 char sound_minutes[80]; /* Sound file: "minutes." (def. queue-minutes) */
239 char sound_lessthan[80]; /* Sound file: "less-than" (def. queue-lessthan) */
240 char sound_seconds[80]; /* Sound file: "seconds." (def. queue-seconds) */
241 char sound_thanks[80]; /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
242 char sound_reporthold[80]; /* Sound file: "Hold time" (def. queue-reporthold) */
244 int count; /* How many entries are in the queue */
245 int maxlen; /* Max number of entries in queue */
246 int wrapuptime; /* Wrapup Time */
248 int retry; /* Retry calling everyone after this amount of time */
249 int timeout; /* How long to wait for an answer */
251 /* Queue strategy things */
253 int rrpos; /* Round Robin - position */
254 int memberdelay; /* Seconds to delay connecting member to caller */
256 struct member *members; /* Member channels to be tried */
257 struct queue_ent *head; /* Start of the actual queue */
258 struct ast_call_queue *next; /* Next call queue */
261 static struct ast_call_queue *queues = NULL;
262 AST_MUTEX_DEFINE_STATIC(qlock);
264 static char *int2strat(int strategy)
267 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
268 if (strategy == strategies[x].strategy)
269 return strategies[x].name;
274 static int strat2int(char *strategy)
277 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
278 if (!strcasecmp(strategy, strategies[x].name))
279 return strategies[x].strategy;
284 /* Insert the 'new' entry after the 'prev' entry of queue 'q' */
285 static inline void insert_entry(struct ast_call_queue *q,
286 struct queue_ent *prev, struct queue_ent *new, int *pos)
288 struct queue_ent *cur;
305 static int has_no_members(struct ast_call_queue *q)
307 struct member *member;
310 while(empty && member) {
311 switch(member->status) {
312 case AST_DEVICE_UNAVAILABLE:
313 case AST_DEVICE_INVALID:
314 /* Not logged on, etc */
320 member = member->next;
330 static void *changethread(void *data)
332 struct ast_call_queue *q;
333 struct statechange *sc = data;
336 loc = strchr(sc->dev, '/');
341 ast_log(LOG_WARNING, "Can't change device '%s' with no technology!\n", sc->dev);
346 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", sc->dev, loc, sc->state);
347 ast_mutex_lock(&qlock);
348 for (q = queues; q; q = q->next) {
349 ast_mutex_lock(&q->lock);
352 if (!strcasecmp(sc->dev, cur->interface)) {
353 if (cur->status != sc->state) {
354 cur->status = sc->state;
355 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
363 q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
364 cur->penalty, cur->calls, cur->lastcall, cur->status);
369 ast_mutex_unlock(&q->lock);
371 ast_mutex_unlock(&qlock);
372 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", sc->dev, loc, sc->state);
377 static int statechange_queue(const char *dev, int state, void *ign)
379 /* Avoid potential for deadlocks by spawning a new thread to handle
381 struct statechange *sc;
384 sc = malloc(sizeof(struct statechange) + strlen(dev) + 1);
387 strcpy(sc->dev, dev);
388 pthread_attr_init(&attr);
389 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
390 if (ast_pthread_create(&t, &attr, changethread, sc)) {
391 ast_log(LOG_WARNING, "Failed to create update thread!\n");
398 static int join_queue(char *queuename, struct queue_ent *qe)
400 struct ast_call_queue *q;
401 struct queue_ent *cur, *prev = NULL;
406 ast_mutex_lock(&qlock);
407 for (q = queues; q; q = q->next) {
408 if (!strcasecmp(q->name, queuename)) {
409 /* This is our one */
410 ast_mutex_lock(&q->lock);
411 if ((!has_no_members(q) || ast_test_flag(q, QUEUE_FLAG_JOINEMPTY)) && (!q->maxlen || (q->count < q->maxlen))) {
412 /* There's space for us, put us at the right position inside
414 * Take into account the priority of the calling user */
419 /* We have higher priority than the current user, enter
420 * before him, after all the other users with priority
421 * higher or equal to our priority. */
422 if ((!inserted) && (qe->prio > cur->prio)) {
423 insert_entry(q, prev, qe, &pos);
430 /* No luck, join at the end of the queue */
432 insert_entry(q, prev, qe, &pos);
433 strncpy(qe->moh, q->moh, sizeof(qe->moh) - 1);
434 strncpy(qe->announce, q->announce, sizeof(qe->announce) - 1);
435 strncpy(qe->context, q->context, sizeof(qe->context) - 1);
438 manager_event(EVENT_FLAG_CALL, "Join",
439 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
441 qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
442 qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
443 q->name, qe->pos, q->count );
445 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
448 ast_mutex_unlock(&q->lock);
452 ast_mutex_unlock(&qlock);
456 static void free_members(struct ast_call_queue *q, int all)
458 /* Free non-dynamic members */
459 struct member *curm, *next, *prev;
464 if (all || !curm->dynamic) {
476 static void destroy_queue(struct ast_call_queue *q)
478 struct ast_call_queue *cur, *prev = NULL;
479 ast_mutex_lock(&qlock);
480 for (cur = queues; cur; cur = cur->next) {
483 prev->next = cur->next;
490 ast_mutex_unlock(&qlock);
492 ast_mutex_destroy(&q->lock);
496 static int play_file(struct ast_channel *chan, char *filename)
500 ast_stopstream(chan);
501 res = ast_streamfile(chan, filename, chan->language);
504 res = ast_waitstream(chan, "");
509 ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
512 ast_stopstream(chan);
517 static int say_position(struct queue_ent *qe)
519 int res = 0, avgholdmins, avgholdsecs;
522 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
524 if ( (now - qe->last_pos) < 15 )
527 /* If either our position has changed, or we are over the freq timer, say position */
528 if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
531 ast_moh_stop(qe->chan);
532 /* Say we're next, if we are */
534 res += play_file(qe->chan, qe->parent->sound_next);
537 res += play_file(qe->chan, qe->parent->sound_thereare);
538 res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
539 res += play_file(qe->chan, qe->parent->sound_calls);
541 /* Round hold time to nearest minute */
542 avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60);
544 /* If they have specified a rounding then round the seconds as well */
545 if(qe->parent->roundingseconds) {
546 avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds;
547 avgholdsecs*= qe->parent->roundingseconds;
552 if (option_verbose > 2)
553 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
555 /* If the hold time is >1 min, if it's enabled, and if it's not
556 supposed to be only once and we have already said it, say it */
557 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
558 res += play_file(qe->chan, qe->parent->sound_holdtime);
560 if (avgholdmins < 2) {
561 res += play_file(qe->chan, qe->parent->sound_lessthan);
562 res += ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL);
564 res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
565 res += play_file(qe->chan, qe->parent->sound_minutes);
568 res += ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL);
569 res += play_file(qe->chan, qe->parent->sound_seconds);
575 /* Set our last_pos indicators */
577 qe->last_pos_said = qe->pos;
579 if (option_verbose > 2)
580 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
581 res += play_file(qe->chan, qe->parent->sound_thanks);
582 ast_moh_start(qe->chan, qe->moh);
587 static void record_abandoned(struct queue_ent *qe)
589 ast_mutex_lock(&qe->parent->lock);
590 qe->parent->callsabandoned++;
591 ast_mutex_unlock(&qe->parent->lock);
594 static void recalc_holdtime(struct queue_ent *qe)
596 int oldvalue, newvalue;
598 /* Calculate holdtime using a recursive boxcar filter */
599 /* Thanks to SRT for this contribution */
600 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
602 newvalue = time(NULL) - qe->start;
604 ast_mutex_lock(&qe->parent->lock);
605 if (newvalue <= qe->parent->servicelevel)
606 qe->parent->callscompletedinsl++;
607 oldvalue = qe->parent->holdtime;
608 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
609 ast_mutex_unlock(&qe->parent->lock);
613 static void leave_queue(struct queue_ent *qe)
615 struct ast_call_queue *q;
616 struct queue_ent *cur, *prev = NULL;
621 ast_mutex_lock(&q->lock);
629 /* Take us out of the queue */
630 manager_event(EVENT_FLAG_CALL, "Leave",
631 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
632 qe->chan->name, q->name, q->count);
634 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
636 /* Take us out of the queue */
638 prev->next = cur->next;
642 /* Renumber the people after us in the queue based on a new count */
648 ast_mutex_unlock(&q->lock);
649 if (ast_test_flag(q, QUEUE_FLAG_DEAD) && !q->count) {
650 /* It's dead and nobody is in it, so kill it */
655 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
657 /* Hang up a tree of stuff */
658 struct localuser *oo;
660 /* Hangup any existing lines we have open */
661 if (outgoing->chan && (outgoing->chan != exception))
662 ast_hangup(outgoing->chan);
664 outgoing=outgoing->next;
669 static int update_status(struct ast_call_queue *q, struct member *member, int status)
672 /* Since a reload could have taken place, we have to traverse the list to
673 be sure it's still valid */
674 ast_mutex_lock(&q->lock);
678 cur->status = status;
679 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
687 q->name, cur->interface, cur->dynamic ? "dynamic" : "static",
688 cur->penalty, cur->calls, cur->lastcall, cur->status);
694 ast_mutex_unlock(&q->lock);
698 static int update_dial_status(struct ast_call_queue *q, struct member *member, int status)
700 if (status == AST_CAUSE_BUSY)
701 status = AST_DEVICE_BUSY;
702 else if (status == AST_CAUSE_UNREGISTERED)
703 status = AST_DEVICE_UNAVAILABLE;
704 else if (status == AST_CAUSE_NOSUCHDRIVER)
705 status = AST_DEVICE_INVALID;
707 status = AST_DEVICE_UNKNOWN;
708 return update_status(q, member, status);
711 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
718 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
719 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
721 ast_cdr_busy(qe->chan->cdr);
726 strncpy(tech, tmp->interface, sizeof(tech) - 1);
727 if ((location = strchr(tech, '/')))
732 /* Request the peer */
733 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
734 if (!tmp->chan) { /* If we can't, just go on to the next call */
736 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
739 ast_cdr_busy(qe->chan->cdr);
741 update_dial_status(qe->parent, tmp->member, status);
743 } else if (status != tmp->oldstatus)
744 update_dial_status(qe->parent, tmp->member, status);
746 tmp->chan->appl = "AppQueue";
747 tmp->chan->data = "(Outgoing Line)";
748 tmp->chan->whentohangup = 0;
749 if (tmp->chan->cid.cid_num)
750 free(tmp->chan->cid.cid_num);
751 tmp->chan->cid.cid_num = NULL;
752 if (tmp->chan->cid.cid_name)
753 free(tmp->chan->cid.cid_name);
754 tmp->chan->cid.cid_name = NULL;
755 if (tmp->chan->cid.cid_ani)
756 free(tmp->chan->cid.cid_ani);
757 tmp->chan->cid.cid_ani = NULL;
758 if (qe->chan->cid.cid_num)
759 tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num);
760 if (qe->chan->cid.cid_name)
761 tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name);
762 if (qe->chan->cid.cid_ani)
763 tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani);
764 /* Presense of ADSI CPE on outgoing channel follows ours */
765 tmp->chan->adsicpe = qe->chan->adsicpe;
766 /* Place the call, but don't wait on the answer */
767 res = ast_call(tmp->chan, location, 0);
769 /* Again, keep going even if there's an error */
771 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
772 else if (option_verbose > 2)
773 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
774 ast_hangup(tmp->chan);
779 if (ast_test_flag(qe->parent, QUEUE_FLAG_EVENTWHENCALLED)) {
780 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
781 "AgentCalled: %s\r\n"
782 "ChannelCalling: %s\r\n"
784 "CallerIDName: %s\r\n"
788 tmp->interface, qe->chan->name,
789 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
790 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
791 qe->chan->context, qe->chan->exten, qe->chan->priority);
793 if (option_verbose > 2)
794 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
799 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
801 struct localuser *cur;
802 struct localuser *best;
808 if (cur->stillgoing && /* Not already done */
809 !cur->chan && /* Isn't already going */
810 (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
811 bestmetric = cur->metric;
817 if (!qe->parent->strategy) {
818 /* Ring everyone who shares this best metric (for ringall) */
821 if (cur->stillgoing && !cur->chan && (cur->metric == bestmetric)) {
822 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
828 /* Ring just the best channel */
830 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
831 ring_entry(qe, best);
834 } while (best && !best->chan);
837 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
843 static int store_next(struct queue_ent *qe, struct localuser *outgoing)
845 struct localuser *cur;
846 struct localuser *best;
851 if (cur->stillgoing && /* Not already done */
852 !cur->chan && /* Isn't already going */
853 (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
854 bestmetric = cur->metric;
860 /* Ring just the best channel */
861 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
862 qe->parent->rrpos = best->metric % 1000;
864 /* Just increment rrpos */
865 if (!ast_test_flag(qe->parent, QUEUE_FLAG_WRAPPED)) {
866 /* No more channels, start over */
867 qe->parent->rrpos = 0;
869 /* Prioritize next entry */
873 ast_clear_flag(qe->parent, QUEUE_FLAG_WRAPPED);
877 static int valid_exit(struct queue_ent *qe, char digit)
880 if (ast_strlen_zero(qe->context))
884 if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->cid.cid_num)) {
885 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
886 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
887 qe->chan->priority = 0;
893 #define AST_MAX_WATCHERS 256
895 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, struct ast_flags *flags, char *digit)
897 char *queue = qe->parent->name;
907 struct localuser *peer = NULL;
908 struct ast_channel *watchers[AST_MAX_WATCHERS];
910 struct ast_channel *winner;
911 struct ast_channel *in = qe->chan;
913 while(*to && !peer) {
920 /* Keep track of important channels */
921 if (o->stillgoing && o->chan) {
922 watchers[pos++] = o->chan;
929 if (numlines == (numbusies + numnochan)) {
930 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
932 ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
937 winner = ast_waitfor_n(watchers, pos, to);
940 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
942 if (option_verbose > 2)
943 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
945 ast_copy_flags(flags, o, QUEUE_FLAG_REDIR_IN & QUEUE_FLAG_REDIR_OUT & QUEUE_FLAG_DISCON_IN & QUEUE_FLAG_DISCON_OUT);
947 } else if (o->chan && (o->chan == winner)) {
948 if (!ast_strlen_zero(o->chan->call_forward)) {
949 char tmpchan[256]="";
952 strncpy(tmpchan, o->chan->call_forward, sizeof(tmpchan) - 1);
953 if ((stuff = strchr(tmpchan, '/'))) {
958 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
962 /* Before processing channel, go ahead and check for forwarding */
963 if (option_verbose > 2)
964 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
965 /* Setup parameters */
966 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
967 if (status != o->oldstatus)
968 update_dial_status(qe->parent, o->member, status);
970 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
974 if (o->chan->cid.cid_num)
975 free(o->chan->cid.cid_num);
976 o->chan->cid.cid_num = NULL;
977 if (o->chan->cid.cid_name)
978 free(o->chan->cid.cid_name);
979 o->chan->cid.cid_name = NULL;
981 if (in->cid.cid_num) {
982 o->chan->cid.cid_num = strdup(in->cid.cid_num);
983 if (!o->chan->cid.cid_num)
984 ast_log(LOG_WARNING, "Out of memory\n");
986 if (in->cid.cid_name) {
987 o->chan->cid.cid_name = strdup(in->cid.cid_name);
988 if (!o->chan->cid.cid_name)
989 ast_log(LOG_WARNING, "Out of memory\n");
991 strncpy(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode) - 1);
992 o->chan->cdrflags = in->cdrflags;
994 if (in->cid.cid_ani) {
995 if (o->chan->cid.cid_ani)
996 free(o->chan->cid.cid_ani);
997 o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
998 if (o->chan->cid.cid_ani)
999 strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
1001 ast_log(LOG_WARNING, "Out of memory\n");
1003 if (o->chan->cid.cid_rdnis)
1004 free(o->chan->cid.cid_rdnis);
1005 if (!ast_strlen_zero(in->macroexten))
1006 o->chan->cid.cid_rdnis = strdup(in->macroexten);
1008 o->chan->cid.cid_rdnis = strdup(in->exten);
1009 if (ast_call(o->chan, tmpchan, 0)) {
1010 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
1012 ast_hangup(o->chan);
1017 /* Hangup the original channel now, in case we needed it */
1021 f = ast_read(winner);
1023 if (f->frametype == AST_FRAME_CONTROL) {
1024 switch(f->subclass) {
1025 case AST_CONTROL_ANSWER:
1026 /* This is our guy if someone answered. */
1028 if (option_verbose > 2)
1029 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
1031 ast_copy_flags(flags, o, QUEUE_FLAG_REDIR_IN & QUEUE_FLAG_REDIR_OUT & QUEUE_FLAG_DISCON_IN & QUEUE_FLAG_DISCON_OUT);
1034 case AST_CONTROL_BUSY:
1035 if (option_verbose > 2)
1036 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
1039 ast_cdr_busy(in->cdr);
1040 ast_hangup(o->chan);
1042 if (qe->parent->strategy)
1043 ring_one(qe, outgoing);
1046 case AST_CONTROL_CONGESTION:
1047 if (option_verbose > 2)
1048 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
1051 ast_cdr_busy(in->cdr);
1052 ast_hangup(o->chan);
1054 if (qe->parent->strategy)
1055 ring_one(qe, outgoing);
1058 case AST_CONTROL_RINGING:
1059 if (option_verbose > 2)
1060 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
1063 ast_indicate(in, AST_CONTROL_RINGING);
1068 case AST_CONTROL_OFFHOOK:
1069 /* Ignore going off hook */
1072 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
1078 ast_hangup(o->chan);
1080 if (qe->parent->strategy)
1081 ring_one(qe, outgoing);
1089 if (f && (f->frametype != AST_FRAME_VOICE))
1090 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
1091 else if (!f || (f->frametype != AST_FRAME_VOICE))
1092 printf("Hangup received on %s\n", in->name);
1094 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
1099 if (f && (f->frametype == AST_FRAME_DTMF) && ast_test_flag(flags, QUEUE_FLAG_DISCON_OUT) && (f->subclass == '*')) {
1100 if (option_verbose > 3)
1101 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
1105 if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
1106 if (option_verbose > 3)
1107 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
1113 if (!*to && (option_verbose > 2))
1114 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
1121 static int is_our_turn(struct queue_ent *qe)
1123 struct queue_ent *ch;
1126 /* Atomically read the parent head -- does not need a lock */
1127 ch = qe->parent->head;
1128 /* If we are now at the top of the head, break out */
1131 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
1135 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
1141 static int wait_our_turn(struct queue_ent *qe, int ringing)
1145 /* This is the holding pen for callers 2 through maxlen */
1147 if (is_our_turn(qe))
1150 /* If we have timed out, break out */
1151 if (qe->expire && (time(NULL) > qe->expire))
1154 /* leave the queue if no agents, if enabled */
1155 if (ast_test_flag(qe->parent, QUEUE_FLAG_LEAVEWHENEMPTY) && has_no_members(qe->parent)) {
1160 /* Make a position announcement, if enabled */
1161 if (qe->parent->announcefrequency && !ringing)
1164 /* Wait a second before checking again */
1165 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
1172 static int update_queue(struct ast_call_queue *q, struct member *member)
1175 /* Since a reload could have taken place, we have to traverse the list to
1176 be sure it's still valid */
1177 ast_mutex_lock(&q->lock);
1180 if (member == cur) {
1181 time(&cur->lastcall);
1187 q->callscompleted++;
1188 ast_mutex_unlock(&q->lock);
1192 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
1194 switch (q->strategy) {
1195 case QUEUE_STRATEGY_RINGALL:
1196 /* Everyone equal, except for penalty */
1197 tmp->metric = mem->penalty * 1000000;
1199 case QUEUE_STRATEGY_ROUNDROBIN:
1201 if (!ast_test_flag(q, QUEUE_FLAG_WRAPPED)) {
1202 /* No more channels, start over */
1205 /* Prioritize next entry */
1208 ast_clear_flag(q, QUEUE_FLAG_WRAPPED);
1211 case QUEUE_STRATEGY_RRMEMORY:
1212 if (pos < q->rrpos) {
1213 tmp->metric = 1000 + pos;
1215 if (pos > q->rrpos) {
1216 /* Indicate there is another priority */
1217 ast_set_flag(q, QUEUE_FLAG_WRAPPED);
1221 tmp->metric += mem->penalty * 1000000;
1223 case QUEUE_STRATEGY_RANDOM:
1224 tmp->metric = rand() % 1000;
1225 tmp->metric += mem->penalty * 1000000;
1227 case QUEUE_STRATEGY_FEWESTCALLS:
1228 tmp->metric = mem->calls;
1229 tmp->metric += mem->penalty * 1000000;
1231 case QUEUE_STRATEGY_LEASTRECENT:
1235 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
1236 tmp->metric += mem->penalty * 1000000;
1239 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
1245 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url, int *go_on)
1248 struct localuser *outgoing=NULL, *tmp = NULL;
1250 struct ast_flags flags;
1251 char restofit[AST_MAX_EXTENSION];
1252 char oldexten[AST_MAX_EXTENSION]="";
1253 char oldcontext[AST_MAX_EXTENSION]="";
1254 char queuename[256]="";
1256 char *monitorfilename;
1257 struct ast_channel *peer;
1258 struct localuser *lpeer;
1259 struct member *member;
1260 int res = 0, bridge = 0;
1263 char *announce = NULL;
1267 struct ast_bridge_config config;
1268 /* Hold the lock while we setup the outgoing calls */
1269 ast_mutex_lock(&qe->parent->lock);
1271 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
1273 strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
1275 cur = qe->parent->members;
1276 if (!ast_strlen_zero(qe->announce))
1277 announce = qe->announce;
1278 if (announceoverride && !ast_strlen_zero(announceoverride))
1279 announce = announceoverride;
1281 /* Get a technology/[device:]number pair */
1282 tmp = malloc(sizeof(struct localuser));
1284 ast_mutex_unlock(&qe->parent->lock);
1285 ast_log(LOG_WARNING, "Out of memory\n");
1288 memset(tmp, 0, sizeof(struct localuser));
1289 tmp->stillgoing = -1;
1290 for (; options && *options; options++)
1293 ast_set_flag(tmp, QUEUE_FLAG_REDIR_IN);
1296 ast_set_flag(tmp, QUEUE_FLAG_REDIR_OUT);
1299 ast_set_flag(tmp, QUEUE_FLAG_RINGBACKONLY);
1302 ast_set_flag(tmp, QUEUE_FLAG_MUSICONHOLD);
1305 ast_set_flag(tmp, QUEUE_FLAG_DATAQUALITY);
1308 ast_set_flag(tmp, QUEUE_FLAG_DISCON_IN);
1311 ast_set_flag(tmp, QUEUE_FLAG_DISCON_OUT);
1314 if ((now - qe->start >= qe->parent->timeout))
1320 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
1322 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
1325 tmp->member = cur; /* Never directly dereference! Could change on reload */
1326 tmp->oldstatus = cur->status;
1327 tmp->lastcall = cur->lastcall;
1328 strncpy(tmp->interface, cur->interface, sizeof(tmp->interface)-1);
1329 /* If we're dialing by extension, look at the extension to know what to dial */
1330 if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) {
1332 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1);
1333 snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit);
1335 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface);
1337 /* Special case: If we ring everyone, go ahead and ring them, otherwise
1338 just calculate their metric for the appropriate strategy */
1339 calc_metric(qe->parent, cur, x++, qe, tmp);
1340 /* Put them in the list of outgoing thingies... We're ready now.
1341 XXX If we're forcibly removed, these outgoing calls won't get
1343 tmp->next = outgoing;
1345 /* If this line is up, don't try anybody else */
1346 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
1351 if (qe->parent->timeout)
1352 to = qe->parent->timeout * 1000;
1355 ring_one(qe, outgoing);
1356 ast_mutex_unlock(&qe->parent->lock);
1357 lpeer = wait_for_answer(qe, outgoing, &to, &flags, &digit);
1358 ast_mutex_lock(&qe->parent->lock);
1359 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
1360 store_next(qe, outgoing);
1362 ast_mutex_unlock(&qe->parent->lock);
1369 /* Musta gotten hung up */
1370 record_abandoned(qe);
1373 if (digit && valid_exit(qe, digit))
1376 /* Nobody answered, next please? */
1380 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
1384 /* Ah ha! Someone answered within the desired timeframe. Of course after this
1385 we will always return with -1 so that it is hung up properly after the
1388 if (!strcmp(qe->chan->type,"Zap")) {
1389 zapx = !ast_test_flag(tmp, QUEUE_FLAG_DATAQUALITY);
1390 ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1392 if (!strcmp(peer->type,"Zap")) {
1393 zapx = !ast_test_flag(tmp, QUEUE_FLAG_DATAQUALITY);
1394 ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
1396 /* Update parameters for the queue */
1397 recalc_holdtime(qe);
1398 member = lpeer->member;
1399 hanguptree(outgoing, peer);
1401 if (announce || ast_test_flag(qe->parent, QUEUE_FLAG_REPORTHOLDTIME) || qe->parent->memberdelay) {
1403 res2 = ast_autoservice_start(qe->chan);
1405 if (qe->parent->memberdelay) {
1406 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
1407 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
1409 if (!res2 && announce) {
1410 if (play_file(peer, announce))
1411 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
1413 if (!res2 && ast_test_flag(qe->parent, QUEUE_FLAG_REPORTHOLDTIME)) {
1414 if (!play_file(peer, qe->parent->sound_reporthold)) {
1419 holdtime = abs((now - qe->start) / 60);
1421 play_file(peer, qe->parent->sound_lessthan);
1422 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
1424 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
1425 play_file(peer, qe->parent->sound_minutes);
1429 res2 |= ast_autoservice_stop(qe->chan);
1431 /* Agent must have hung up */
1432 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
1433 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
1438 /* Stop music on hold */
1439 ast_moh_stop(qe->chan);
1440 /* If appropriate, log that we have a destination channel */
1442 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
1443 /* Make sure channels are compatible */
1444 res = ast_channel_make_compatible(qe->chan, peer);
1446 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
1447 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
1451 /* Begin Monitoring */
1452 if (qe->parent->monfmt && *qe->parent->monfmt) {
1453 monitorfilename = pbx_builtin_getvar_helper( qe->chan, "MONITOR_FILENAME");
1454 if(monitorfilename) {
1455 ast_monitor_start( peer, qe->parent->monfmt, monitorfilename, 1 );
1457 ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
1459 if(ast_test_flag(qe->parent, QUEUE_FLAG_MONJOIN)) {
1460 ast_monitor_setjoinfiles( peer, 1);
1463 /* Drop out of the queue at this point, to prepare for next caller */
1465 if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
1467 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
1468 ast_channel_sendurl( peer, url );
1470 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
1471 strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
1472 strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
1475 memset(&config,0,sizeof(struct ast_bridge_config));
1476 config.allowredirect_in = ast_test_flag(&flags, QUEUE_FLAG_REDIR_IN);
1477 config.allowredirect_out = ast_test_flag(&flags, QUEUE_FLAG_REDIR_OUT);
1478 config.allowdisconnect_in = ast_test_flag(&flags, QUEUE_FLAG_DISCON_IN);
1479 config.allowdisconnect_out = ast_test_flag(&flags, QUEUE_FLAG_DISCON_OUT);
1480 bridge = ast_bridge_call(qe->chan,peer,&config);
1482 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
1483 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
1484 } else if (qe->chan->_softhangup) {
1485 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1487 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1490 if(bridge != AST_PBX_NO_HANGUP_PEER)
1492 update_queue(qe->parent, member);
1493 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
1494 else res = bridge; /* bridge error, stay in the queue */
1497 hanguptree(outgoing, NULL);
1501 static int wait_a_bit(struct queue_ent *qe)
1503 /* Don't need to hold the lock while we setup the outgoing calls */
1504 int retrywait = qe->parent->retry * 1000;
1505 return ast_waitfordigit(qe->chan, retrywait);
1508 /* [PHM 06/26/03] */
1510 static struct member * interface_exists(struct ast_call_queue *q, char *interface)
1515 for (mem = q->members; mem; mem = mem->next)
1516 if (!strcmp(interface, mem->interface))
1523 static struct member *create_queue_node(char *interface, int penalty)
1527 /* Add a new member */
1529 cur = malloc(sizeof(struct member));
1532 memset(cur, 0, sizeof(struct member));
1533 cur->penalty = penalty;
1534 strncpy(cur->interface, interface, sizeof(cur->interface) - 1);
1535 if (!strchr(cur->interface, '/'))
1536 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
1537 cur->status = ast_device_state(interface);
1543 /* Dump all members in a specific queue to the databse
1545 * <pm_family>/<queuename> = <interface>;<penalty>;...
1548 static void dump_queue_members(struct ast_call_queue *pm_queue)
1550 struct member *cur_member = NULL;
1551 char value[PM_MAX_LEN];
1555 memset(value, 0, sizeof(value));
1558 cur_member = pm_queue->members;
1559 while (cur_member) {
1560 if (cur_member->dynamic) {
1561 value_len = strlen(value);
1562 res = snprintf(value+value_len, sizeof(value)-value_len, "%s;%d;", cur_member->interface, cur_member->penalty);
1563 if (res != strlen(value + value_len)) {
1564 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
1568 cur_member = cur_member->next;
1571 if (!ast_strlen_zero(value) && !cur_member) {
1572 if (ast_db_put(pm_family, pm_queue->name, value))
1573 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
1575 /* Delete the entry if the queue is empty or there is an error */
1576 ast_db_del(pm_family, pm_queue->name);
1583 static int remove_from_queue(char *queuename, char *interface)
1585 struct ast_call_queue *q;
1586 struct member *last_member, *look;
1587 int res = RES_NOSUCHQUEUE;
1589 ast_mutex_lock(&qlock);
1590 for (q = queues ; q ; q = q->next) {
1591 ast_mutex_lock(&q->lock);
1592 if (!strcmp(q->name, queuename)) {
1593 if ((last_member = interface_exists(q, interface))) {
1594 if ((look = q->members) == last_member) {
1595 q->members = last_member->next;
1597 while (look != NULL) {
1598 if (look->next == last_member) {
1599 look->next = last_member->next;
1606 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
1609 q->name, last_member->interface);
1612 if (queue_persistent_members)
1613 dump_queue_members(q);
1619 ast_mutex_unlock(&q->lock);
1622 ast_mutex_unlock(&q->lock);
1624 ast_mutex_unlock(&qlock);
1628 static int add_to_queue(char *queuename, char *interface, int penalty)
1630 struct ast_call_queue *q;
1631 struct member *new_member;
1632 int res = RES_NOSUCHQUEUE;
1634 ast_mutex_lock(&qlock);
1635 for (q = queues ; q ; q = q->next) {
1636 ast_mutex_lock(&q->lock);
1637 if (!strcmp(q->name, queuename)) {
1638 if (interface_exists(q, interface) == NULL) {
1639 new_member = create_queue_node(interface, penalty);
1641 if (new_member != NULL) {
1642 new_member->dynamic = 1;
1643 new_member->next = q->members;
1644 q->members = new_member;
1645 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
1648 "Membership: %s\r\n"
1650 "CallsTaken: %d\r\n"
1653 q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static",
1654 new_member->penalty, new_member->calls, new_member->lastcall, new_member->status);
1656 if (queue_persistent_members)
1657 dump_queue_members(q);
1661 res = RES_OUTOFMEMORY;
1666 ast_mutex_unlock(&q->lock);
1669 ast_mutex_unlock(&q->lock);
1671 ast_mutex_unlock(&qlock);
1675 /* Add members saved in the queue members DB file saves
1676 * created by dump_queue_members(), back into the queues */
1677 static void reload_queue_members(void)
1680 char *pm_queue_name;
1682 char *pm_penalty_tok;
1684 struct ast_db_entry *pm_db_tree = NULL;
1685 int pm_family_len = 0;
1686 struct ast_call_queue *cur_queue = NULL;
1687 char queue_data[PM_MAX_LEN];
1689 pm_db_tree = ast_db_gettree(pm_family, NULL);
1691 pm_family_len = strlen(pm_family);
1692 ast_mutex_lock(&qlock);
1693 /* Each key in 'pm_family' is the name of a specific queue in which
1694 * we will reload members into. */
1695 while (pm_db_tree) {
1696 pm_queue_name = pm_db_tree->key+pm_family_len+2;
1700 ast_mutex_lock(&cur_queue->lock);
1702 if (strcmp(pm_queue_name, cur_queue->name) == 0)
1705 ast_mutex_unlock(&cur_queue->lock);
1707 cur_queue = cur_queue->next;
1711 /* If the queue no longer exists, remove it from the
1713 ast_db_del(pm_family, pm_queue_name);
1714 pm_db_tree = pm_db_tree->next;
1717 ast_mutex_unlock(&cur_queue->lock);
1719 if (!ast_db_get(pm_family, pm_queue_name, queue_data, PM_MAX_LEN)) {
1720 /* Parse each <interface>;<penalty>; from the value of the
1721 * queuename key and add it to the respective queue */
1722 cur_pm_ptr = queue_data;
1723 while ((pm_interface = strsep(&cur_pm_ptr, ";"))) {
1724 if (!(pm_penalty_tok = strsep(&cur_pm_ptr, ";"))) {
1725 ast_log(LOG_WARNING, "Error parsing corrupted Queue DB string for '%s'\n", pm_queue_name);
1728 pm_penalty = strtol(pm_penalty_tok, NULL, 10);
1729 if (errno == ERANGE) {
1730 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", pm_penalty_tok);
1735 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d\n", pm_queue_name, pm_interface, pm_penalty);
1737 if (add_to_queue(pm_queue_name, pm_interface, pm_penalty) == RES_OUTOFMEMORY) {
1738 ast_log(LOG_ERROR, "Out of Memory\n");
1744 pm_db_tree = pm_db_tree->next;
1747 ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n");
1748 ast_mutex_unlock(&qlock);
1750 ast_db_freetree(pm_db_tree);
1755 static int rqm_exec(struct ast_channel *chan, void *data)
1758 struct localuser *u;
1759 char *info, *queuename;
1760 char tmpchan[256]="";
1761 char *interface = NULL;
1764 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface])\n");
1768 info = ast_strdupa((char *)data);
1770 ast_log(LOG_ERROR, "Out of memory\n");
1778 interface = strchr(queuename, '|');
1784 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1785 interface = strrchr(tmpchan, '-');
1788 interface = tmpchan;
1792 switch (remove_from_queue(queuename, interface)) {
1794 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", interface, queuename);
1798 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
1799 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
1800 chan->priority += 100;
1804 case RES_NOSUCHQUEUE:
1805 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
1808 case RES_OUTOFMEMORY:
1809 ast_log(LOG_ERROR, "Out of memory\n");
1813 LOCAL_USER_REMOVE(u);
1817 static int aqm_exec(struct ast_channel *chan, void *data)
1820 struct localuser *u;
1823 char tmpchan[512]="";
1824 char *interface=NULL;
1825 char *penaltys=NULL;
1829 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface][|penalty]])\n");
1833 info = ast_strdupa((char *)data);
1835 ast_log(LOG_ERROR, "Out of memory\n");
1842 interface = strchr(queuename, '|');
1848 penaltys = strchr(interface, '|');
1854 if (!interface || ast_strlen_zero(interface)) {
1855 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1856 interface = strrchr(tmpchan, '-');
1859 interface = tmpchan;
1861 if (penaltys && strlen(penaltys)) {
1862 if ((sscanf(penaltys, "%d", &penalty) != 1) || penalty < 0) {
1863 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", penaltys);
1869 switch (add_to_queue(queuename, interface, penalty)) {
1871 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1875 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
1876 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
1877 chan->priority += 100;
1881 case RES_NOSUCHQUEUE:
1882 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1885 case RES_OUTOFMEMORY:
1886 ast_log(LOG_ERROR, "Out of memory\n");
1890 LOCAL_USER_REMOVE(u);
1894 static int queue_exec(struct ast_channel *chan, void *data)
1898 struct localuser *u;
1901 char *info_ptr = info;
1902 char *options = NULL;
1904 char *announceoverride = NULL;
1905 char *user_priority;
1907 char *queuetimeoutstr = NULL;
1909 /* whether to exit Queue application after the timeout hits */
1912 /* Our queue entry */
1913 struct queue_ent qe;
1916 ast_log(LOG_WARNING, "Queue requires an argument (queuename[|[timeout][|URL]])\n");
1922 /* Setup our queue entry */
1923 memset(&qe, 0, sizeof(qe));
1924 qe.start = time(NULL);
1926 /* Parse our arguments XXX Check for failure XXX */
1927 strncpy(info, (char *) data, sizeof(info) - 1);
1928 queuename = strsep(&info_ptr, "|");
1929 options = strsep(&info_ptr, "|");
1930 url = strsep(&info_ptr, "|");
1931 announceoverride = strsep(&info_ptr, "|");
1932 queuetimeoutstr = info_ptr;
1934 /* set the expire time based on the supplied timeout; */
1935 if (queuetimeoutstr)
1936 qe.expire = qe.start + atoi(queuetimeoutstr);
1940 /* Get the priority from the variable ${QUEUE_PRIO} */
1941 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
1942 if (user_priority) {
1943 if (sscanf(user_priority, "%d", &prio) == 1) {
1945 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
1948 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
1949 user_priority, chan->name);
1954 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
1959 if (strchr(options, 'r')) {
1964 /* if (option_debug) */
1965 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
1966 queuename, options, url, announceoverride, (long)qe.expire, (int)prio);
1969 qe.prio = (int)prio;
1970 qe.last_pos_said = 0;
1972 if (!join_queue(queuename, &qe)) {
1973 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->cid.cid_num ? chan->cid.cid_num : "");
1974 /* Start music on hold */
1977 ast_indicate(chan, AST_CONTROL_RINGING);
1979 ast_moh_start(chan, qe.moh);
1982 /* This is the wait loop for callers 2 through maxlen */
1984 res = wait_our_turn(&qe, ringing);
1985 /* If they hungup, return immediately */
1987 /* Record this abandoned call */
1988 record_abandoned(&qe);
1989 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1990 if (option_verbose > 2) {
1991 ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1998 if (valid_exit(&qe, res)) {
1999 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
2004 int makeannouncement = 0;
2006 /* This is the wait loop for the head caller*/
2007 /* To exit, they may get their call answered; */
2008 /* they may dial a digit from the queue context; */
2009 /* or, they may timeout. */
2011 /* Leave if we have exceeded our queuetimeout */
2012 if (qe.expire && (time(NULL) > qe.expire)) {
2017 if (makeannouncement) {
2018 /* Make a position announcement, if enabled */
2019 if (qe.parent->announcefrequency && !ringing)
2022 makeannouncement = 1;
2024 /* Try calling all queue members for 'timeout' seconds */
2025 res = try_calling(&qe, options, announceoverride, url, &go_on);
2029 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
2031 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
2035 /* leave the queue if no agents, if enabled */
2036 if (ast_test_flag(qe.parent, QUEUE_FLAG_LEAVEWHENEMPTY) && has_no_members(qe.parent)) {
2041 /* Leave if we have exceeded our queuetimeout */
2042 if (qe.expire && (time(NULL) > qe.expire)) {
2047 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
2048 res = wait_a_bit(&qe);
2050 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
2051 if (option_verbose > 2) {
2052 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
2057 if (res && valid_exit(&qe, res)) {
2058 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
2061 /* exit after 'timeout' cycle if 'n' option enabled */
2063 if (option_verbose > 2) {
2064 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
2067 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
2071 /* Since this is a priority queue and
2072 * it is not sure that we are still at the head
2073 * of the queue, go and check for our turn again.
2075 if (!is_our_turn(&qe)) {
2076 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
2082 /* Don't allow return code > 0 */
2083 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
2086 ast_indicate(chan, -1);
2090 ast_stopstream(chan);
2094 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
2097 LOCAL_USER_REMOVE(u);
2101 static void reload_queues(void)
2103 struct ast_call_queue *q, *ql, *qn;
2104 struct ast_config *cfg;
2106 struct ast_variable *var;
2107 struct member *prev, *cur;
2109 char *general_val = NULL;
2111 cfg = ast_load("queues.conf");
2113 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
2116 ast_mutex_lock(&qlock);
2117 /* Mark all queues as dead for the moment */
2120 ast_set_flag(q, QUEUE_FLAG_DEAD);
2123 /* Chug through config file */
2124 cat = ast_category_browse(cfg, NULL);
2126 if (strcasecmp(cat, "general")) {
2127 /* Look for an existing one */
2130 if (!strcmp(q->name, cat))
2136 q = malloc(sizeof(struct ast_call_queue));
2139 memset(q, 0, sizeof(struct ast_call_queue));
2140 ast_mutex_init(&q->lock);
2141 strncpy(q->name, cat, sizeof(q->name) - 1);
2148 ast_mutex_lock(&q->lock);
2149 /* Re-initialize the queue */
2150 ast_clear_flag(q, QUEUE_FLAG_DEAD);
2154 q->announcefrequency = 0;
2155 q->announceholdtime = 0;
2156 q->roundingseconds = 0; /* Default - don't announce seconds */
2158 q->callscompleted = 0;
2159 q->callsabandoned = 0;
2160 q->callscompletedinsl = 0;
2161 q->servicelevel = 0;
2165 q->announce[0] = '\0';
2166 q->context[0] = '\0';
2167 q->monfmt[0] = '\0';
2168 strncpy(q->sound_next, "queue-youarenext", sizeof(q->sound_next) - 1);
2169 strncpy(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare) - 1);
2170 strncpy(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls) - 1);
2171 strncpy(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime) - 1);
2172 strncpy(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes) - 1);
2173 strncpy(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds) - 1);
2174 strncpy(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks) - 1);
2175 strncpy(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan) - 1);
2176 strncpy(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold) - 1);
2179 /* find the end of any dynamic members */
2183 var = ast_variable_browse(cfg, cat);
2185 if (!strcasecmp(var->name, "member")) {
2186 /* Add a new member */
2187 cur = malloc(sizeof(struct member));
2189 memset(cur, 0, sizeof(struct member));
2190 strncpy(cur->interface, var->value, sizeof(cur->interface) - 1);
2191 if ((tmp = strchr(cur->interface, ','))) {
2194 cur->penalty = atoi(tmp);
2195 if (cur->penalty < 0)
2198 if (!strchr(cur->interface, '/'))
2199 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
2206 } else if (!strcasecmp(var->name, "music") || !strcasecmp(var->name, "musiconhold")) {
2207 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
2208 } else if (!strcasecmp(var->name, "announce")) {
2209 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
2210 } else if (!strcasecmp(var->name, "context")) {
2211 strncpy(q->context, var->value, sizeof(q->context) - 1);
2212 } else if (!strcasecmp(var->name, "timeout")) {
2213 q->timeout = atoi(var->value);
2214 } else if (!strcasecmp(var->name, "monitor-join")) {
2215 ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_MONJOIN);
2216 } else if (!strcasecmp(var->name, "monitor-format")) {
2217 strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
2218 } else if (!strcasecmp(var->name, "queue-youarenext")) {
2219 strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
2220 } else if (!strcasecmp(var->name, "queue-thereare")) {
2221 strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
2222 } else if (!strcasecmp(var->name, "queue-callswaiting")) {
2223 strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
2224 } else if (!strcasecmp(var->name, "queue-holdtime")) {
2225 strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
2226 } else if (!strcasecmp(var->name, "queue-minutes")) {
2227 strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
2228 } else if (!strcasecmp(var->name, "queue-seconds")) {
2229 strncpy(q->sound_seconds, var->value, sizeof(q->sound_seconds) - 1);
2230 } else if (!strcasecmp(var->name, "queue-lessthan")) {
2231 strncpy(q->sound_lessthan, var->value, sizeof(q->sound_lessthan) - 1);
2232 } else if (!strcasecmp(var->name, "queue-thankyou")) {
2233 strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
2234 } else if (!strcasecmp(var->name, "queue-reporthold")) {
2235 strncpy(q->sound_reporthold, var->value, sizeof(q->sound_reporthold) - 1);
2236 } else if (!strcasecmp(var->name, "announce-frequency")) {
2237 q->announcefrequency = atoi(var->value);
2238 } else if (!strcasecmp(var->name, "announce-round-seconds")) {
2239 q->roundingseconds = atoi(var->value);
2240 if(q->roundingseconds>60 || q->roundingseconds<0) {
2241 ast_log(LOG_WARNING, "'%s' isn't a valid value for queue-rounding-seconds using 0 instead at line %d of queue.conf\n", var->value, var->lineno);
2242 q->roundingseconds=0;
2244 } else if (!strcasecmp(var->name, "announce-holdtime")) {
2245 q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
2246 } else if (!strcasecmp(var->name, "retry")) {
2247 q->retry = atoi(var->value);
2248 } else if (!strcasecmp(var->name, "wrapuptime")) {
2249 q->wrapuptime = atoi(var->value);
2250 } else if (!strcasecmp(var->name, "maxlen")) {
2251 q->maxlen = atoi(var->value);
2252 } else if (!strcasecmp(var->name, "servicelevel")) {
2253 q->servicelevel= atoi(var->value);
2254 } else if (!strcasecmp(var->name, "strategy")) {
2255 q->strategy = strat2int(var->value);
2256 if (q->strategy < 0) {
2257 ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
2260 } else if (!strcasecmp(var->name, "joinempty")) {
2261 ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_JOINEMPTY);
2262 } else if (!strcasecmp(var->name, "leavewhenempty")) {
2263 ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_LEAVEWHENEMPTY);
2264 } else if (!strcasecmp(var->name, "eventwhencalled")) {
2265 ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_EVENTWHENCALLED);
2266 } else if (!strcasecmp(var->name, "reportholdtime")) {
2267 ast_set2_flag(q, ast_true(var->value), QUEUE_FLAG_REPORTHOLDTIME);
2268 } else if (!strcasecmp(var->name, "memberdelay")) {
2269 q->memberdelay = atoi(var->value);
2271 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
2276 q->retry = DEFAULT_RETRY;
2278 q->timeout = DEFAULT_TIMEOUT;
2282 ast_mutex_unlock(&q->lock);
2289 /* Initialize global settings */
2290 queue_persistent_members = 0;
2291 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
2292 queue_persistent_members = ast_true(general_val);
2294 cat = ast_category_browse(cfg, cat);
2301 if (ast_test_flag(q, QUEUE_FLAG_DEAD)) {
2309 ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n");
2311 for (cur = q->members; cur; cur = cur->next)
2312 cur->status = ast_device_state(cur->interface);
2317 ast_mutex_unlock(&qlock);
2320 static char *status2str(int status, char *buf, int buflen)
2323 case AST_DEVICE_UNKNOWN:
2324 strncpy(buf, "unknown", buflen - 1);
2326 case AST_DEVICE_NOT_INUSE:
2327 strncpy(buf, "notinuse", buflen - 1);
2329 case AST_DEVICE_INUSE:
2330 strncpy(buf, "inuse", buflen - 1);
2332 case AST_DEVICE_BUSY:
2333 strncpy(buf, "busy", buflen - 1);
2335 case AST_DEVICE_INVALID:
2336 strncpy(buf, "invalid", buflen - 1);
2338 case AST_DEVICE_UNAVAILABLE:
2339 strncpy(buf, "unavailable", buflen - 1);
2342 snprintf(buf, buflen, "unknown status %d", status);
2347 static int __queues_show(int fd, int argc, char **argv, int queue_show)
2349 struct ast_call_queue *q;
2350 struct queue_ent *qe;
2355 char calls[80] = "";
2356 char tmpbuf[80] = "";
2360 if ((!queue_show && argc != 2) || (queue_show && argc != 3))
2361 return RESULT_SHOWUSAGE;
2362 ast_mutex_lock(&qlock);
2365 ast_mutex_unlock(&qlock);
2367 ast_cli(fd, "No such queue: %s.\n",argv[2]);
2369 ast_cli(fd, "No queues.\n");
2370 return RESULT_SUCCESS;
2373 ast_mutex_lock(&q->lock);
2375 if (strcasecmp(q->name, argv[2]) != 0) {
2376 ast_mutex_unlock(&q->lock);
2379 ast_cli(fd, "No such queue: %s.\n",argv[2]);
2386 snprintf(max, sizeof(max), "%d", q->maxlen);
2388 strncpy(max, "unlimited", sizeof(max) - 1);
2390 if(q->callscompleted > 0)
2391 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
2392 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), C:%d, A:%d, SL:%2.1f%% within %ds\n",
2393 q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
2395 ast_cli(fd, " Members: \n");
2396 for (mem = q->members; mem; mem = mem->next) {
2398 snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
2402 strncat(max, " (dynamic)", sizeof(max) - strlen(max) - 1);
2404 snprintf(max + strlen(max), sizeof(max) - strlen(max), " (%s)", status2str(mem->status, tmpbuf, sizeof(tmpbuf)));
2406 snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
2407 mem->calls, (long)(time(NULL) - mem->lastcall));
2409 strncpy(calls, " has taken no calls yet", sizeof(calls) - 1);
2410 ast_cli(fd, " %s%s%s\n", mem->interface, max, calls);
2413 ast_cli(fd, " No Members\n");
2416 ast_cli(fd, " Callers: \n");
2417 for (qe = q->head; qe; qe = qe->next)
2418 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++, qe->chan->name,
2419 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio);
2421 ast_cli(fd, " No Callers\n");
2423 ast_mutex_unlock(&q->lock);
2428 ast_mutex_unlock(&qlock);
2429 return RESULT_SUCCESS;
2432 static int queues_show(int fd, int argc, char **argv)
2434 return __queues_show(fd, argc, argv, 0);
2437 static int queue_show(int fd, int argc, char **argv)
2439 return __queues_show(fd, argc, argv, 1);
2442 static char *complete_queue(char *line, char *word, int pos, int state)
2444 struct ast_call_queue *q;
2447 ast_mutex_lock(&qlock);
2448 for (q = queues; q; q = q->next) {
2449 if (!strncasecmp(word, q->name, strlen(word))) {
2450 if (++which > state)
2454 ast_mutex_unlock(&qlock);
2455 return q ? strdup(q->name) : NULL;
2458 /* JDG: callback to display queues status in manager */
2459 static int manager_queues_show( struct mansession *s, struct message *m )
2461 char *a[] = { "show", "queues" };
2462 return queues_show( s->fd, 2, a );
2466 /* Dump queue status */
2467 static int manager_queues_status( struct mansession *s, struct message *m )
2471 char *id = astman_get_header(m,"ActionID");
2472 char idText[256] = "";
2473 struct ast_call_queue *q;
2474 struct queue_ent *qe;
2477 astman_send_ack(s, m, "Queue status will follow");
2479 ast_mutex_lock(&qlock);
2480 if (!ast_strlen_zero(id)) {
2481 snprintf(idText,256,"ActionID: %s\r\n",id);
2483 for (q = queues; q; q = q->next) {
2484 ast_mutex_lock(&q->lock);
2486 /* List queue properties */
2487 if(q->callscompleted > 0)
2488 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
2489 ast_mutex_lock(&s->lock);
2490 ast_cli(s->fd, "Event: QueueParams\r\n"
2497 "ServiceLevel: %d\r\n"
2498 "ServicelevelPerf: %2.1f\r\n"
2501 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
2502 q->callsabandoned, q->servicelevel, sl, idText);
2504 /* List Queue Members */
2505 for (mem = q->members; mem; mem = mem->next)
2506 ast_cli(s->fd, "Event: QueueMember\r\n"
2509 "Membership: %s\r\n"
2511 "CallsTaken: %d\r\n"
2516 q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
2517 mem->penalty, mem->calls, mem->lastcall, mem->status, idText);
2519 /* List Queue Entries */
2522 for (qe = q->head; qe; qe = qe->next)
2523 ast_cli(s->fd, "Event: QueueEntry\r\n"
2528 "CallerIDName: %s\r\n"
2532 q->name, pos++, qe->chan->name,
2533 qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown",
2534 qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown",
2535 (long)(now - qe->start), idText);
2536 ast_mutex_unlock(&s->lock);
2537 ast_mutex_unlock(&q->lock);
2539 ast_mutex_unlock(&qlock);
2540 return RESULT_SUCCESS;
2543 static int manager_add_queue_member(struct mansession *s, struct message *m)
2545 char *queuename, *interface, *penalty_s;
2548 queuename = astman_get_header(m, "Queue");
2549 interface = astman_get_header(m, "Interface");
2550 penalty_s = astman_get_header(m, "Penalty");
2552 if (ast_strlen_zero(queuename)) {
2553 astman_send_error(s, m, "'Queue' not specified.");
2557 if (ast_strlen_zero(interface)) {
2558 astman_send_error(s, m, "'Interface' not specified.");
2562 if (ast_strlen_zero(penalty_s))
2564 else if (sscanf(penalty_s, "%d", &penalty) != 1) {
2568 switch (add_to_queue(queuename, interface, penalty)) {
2570 astman_send_ack(s, m, "Added interface to queue");
2573 astman_send_error(s, m, "Unable to add interface: Already there");
2575 case RES_NOSUCHQUEUE:
2576 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
2578 case RES_OUTOFMEMORY:
2579 astman_send_error(s, m, "Out of memory");
2585 static int manager_remove_queue_member(struct mansession *s, struct message *m)
2587 char *queuename, *interface;
2589 queuename = astman_get_header(m, "Queue");
2590 interface = astman_get_header(m, "Interface");
2592 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
2593 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
2597 switch (remove_from_queue(queuename, interface)) {
2599 astman_send_ack(s, m, "Removed interface from queue");
2602 astman_send_error(s, m, "Unable to remove interface: Not there");
2604 case RES_NOSUCHQUEUE:
2605 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
2607 case RES_OUTOFMEMORY:
2608 astman_send_error(s, m, "Out of memory");
2614 static int handle_add_queue_member(int fd, int argc, char *argv[])
2616 char *queuename, *interface;
2619 if ((argc != 6) && (argc != 8)) {
2620 return RESULT_SHOWUSAGE;
2621 } else if (strcmp(argv[4], "to")) {
2622 return RESULT_SHOWUSAGE;
2623 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
2624 return RESULT_SHOWUSAGE;
2627 queuename = argv[5];
2628 interface = argv[3];
2630 if (sscanf(argv[7], "%d", &penalty) == 1) {
2632 ast_cli(fd, "Penalty must be >= 0\n");
2636 ast_cli(fd, "Penalty must be an integer >= 0\n");
2643 switch (add_to_queue(queuename, interface, penalty)) {
2645 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
2646 return RESULT_SUCCESS;
2648 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
2649 return RESULT_FAILURE;
2650 case RES_NOSUCHQUEUE:
2651 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
2652 return RESULT_FAILURE;
2653 case RES_OUTOFMEMORY:
2654 ast_cli(fd, "Out of memory\n");
2655 return RESULT_FAILURE;
2657 return RESULT_FAILURE;
2661 static char *complete_add_queue_member(char *line, char *word, int pos, int state)
2663 /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty> */
2666 /* Don't attempt to complete name of member (infinite possibilities) */
2670 return strdup("to");
2675 /* No need to duplicate code */
2676 return complete_queue(line, word, pos, state);
2679 return strdup("penalty");
2684 if (state < 100) { /* 0-99 */
2685 char *num = malloc(3);
2687 sprintf(num, "%d", state);
2698 static int handle_remove_queue_member(int fd, int argc, char *argv[])
2700 char *queuename, *interface;
2703 return RESULT_SHOWUSAGE;
2704 } else if (strcmp(argv[4], "from")) {
2705 return RESULT_SHOWUSAGE;
2708 queuename = argv[5];
2709 interface = argv[3];
2711 switch (remove_from_queue(queuename, interface)) {
2713 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
2714 return RESULT_SUCCESS;
2716 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
2717 return RESULT_FAILURE;
2718 case RES_NOSUCHQUEUE:
2719 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
2720 return RESULT_FAILURE;
2721 case RES_OUTOFMEMORY:
2722 ast_cli(fd, "Out of memory\n");
2723 return RESULT_FAILURE;
2725 return RESULT_FAILURE;
2729 static char *complete_remove_queue_member(char *line, char *word, int pos, int state)
2732 struct ast_call_queue *q;
2735 /* 0 - add; 1 - queue; 2 - member; 3 - <member>; 4 - to; 5 - <queue> */
2736 if ((pos > 5) || (pos < 3)) {
2741 return strdup("from");
2748 /* No need to duplicate code */
2749 return complete_queue(line, word, pos, state);
2752 if (queues != NULL) {
2753 for (q = queues ; q ; q = q->next) {
2754 ast_mutex_lock(&q->lock);
2755 for (m = q->members ; m ; m = m->next) {
2756 if (++which > state) {
2757 ast_mutex_unlock(&q->lock);
2758 return strdup(m->interface);
2761 ast_mutex_unlock(&q->lock);
2767 static char show_queues_usage[] =
2768 "Usage: show queues\n"
2769 " Provides summary information on call queues.\n";
2771 static struct ast_cli_entry cli_show_queues = {
2772 { "show", "queues", NULL }, queues_show,
2773 "Show status of queues", show_queues_usage, NULL };
2775 static char show_queue_usage[] =
2776 "Usage: show queue\n"
2777 " Provides summary information on a specified queue.\n";
2779 static struct ast_cli_entry cli_show_queue = {
2780 { "show", "queue", NULL }, queue_show,
2781 "Show status of a specified queue", show_queue_usage, complete_queue };
2783 static char aqm_cmd_usage[] =
2784 "Usage: add queue member <channel> to <queue> [penalty <penalty>]\n";
2786 static struct ast_cli_entry cli_add_queue_member = {
2787 { "add", "queue", "member", NULL }, handle_add_queue_member,
2788 "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member };