2 * Asterisk -- A telephony toolkit for Linux.
4 * True call queues with optional send URL on answer
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * These features added by David C. Troy <dave@toad.net>:
11 * - Per-queue holdtime calculation
12 * - Estimated holdtime announcement
13 * - Position announcement
14 * - Abandoned/completed call counters
15 * - Failout timer passed as optional app parameter
16 * - Optional monitoring of calls, started when call is answered
18 * Patch Version 1.07 2003-12-24 01
20 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
22 * Fixed ot work with CVS as of 2004-02-25 and released as 1.07a
23 * by Matthew Enger <m.enger@xi.com.au>
25 * This program is free software, distributed under the terms of
26 * the GNU General Public License
29 #include <asterisk/lock.h>
30 #include <asterisk/file.h>
31 #include <asterisk/logger.h>
32 #include <asterisk/channel.h>
33 #include <asterisk/pbx.h>
34 #include <asterisk/options.h>
35 #include <asterisk/module.h>
36 #include <asterisk/translate.h>
37 #include <asterisk/say.h>
38 #include <asterisk/parking.h>
39 #include <asterisk/musiconhold.h>
40 #include <asterisk/cli.h>
41 #include <asterisk/manager.h> /* JDG */
42 #include <asterisk/config.h>
43 #include <asterisk/monitor.h>
51 #include <sys/signal.h>
52 #include <netinet/in.h>
54 #include "../astconf.h"
58 #define QUEUE_STRATEGY_RINGALL 0
59 #define QUEUE_STRATEGY_ROUNDROBIN 1
60 #define QUEUE_STRATEGY_LEASTRECENT 2
61 #define QUEUE_STRATEGY_FEWESTCALLS 3
62 #define QUEUE_STRATEGY_RANDOM 4
64 static struct strategy {
68 { QUEUE_STRATEGY_RINGALL, "ringall" },
69 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
70 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
71 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
72 { QUEUE_STRATEGY_RANDOM, "random" },
75 #define DEFAULT_RETRY 5
76 #define DEFAULT_TIMEOUT 15
77 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
79 static char *tdesc = "True Call Queueing";
81 static char *app = "Queue";
83 static char *synopsis = "Queue a call for a call queue";
85 static char *descrip =
86 " Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n"
87 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
88 " This application returns -1 if the originating channel hangs up, or if the\n"
89 "call is bridged and either of the parties in the bridge terminate the call.\n"
90 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
91 "The option string may contain zero or more of the following characters:\n"
92 " 't' -- allow the called user transfer the calling user\n"
93 " 'T' -- to allow the calling user to transfer the call.\n"
94 " 'd' -- data-quality (modem) call (minimum delay).\n"
95 " 'H' -- allow caller to hang up by hitting *.\n"
96 " 'n' -- no retries on the timeout; will exit this application and go to the next step.\n"
97 " In addition to transferring the call, a call may be parked and then picked\n"
98 "up by another user.\n"
99 " The optional URL will be sent to the called party if the channel supports\n"
101 " The timeout will cause the queue to fail out after a specified number of\n"
102 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n";
105 static char *app_aqm = "AddQueueMember" ;
106 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
107 static char *app_aqm_descrip =
108 " AddQueueMember(queuename[|interface]):\n"
109 "Dynamically adds interface to an existing queue\n"
110 "Returns -1 if there is an error.\n"
111 "Example: AddQueueMember(techsupport|SIP/3000)\n"
114 static char *app_rqm = "RemoveQueueMember" ;
115 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
116 static char *app_rqm_descrip =
117 " RemoveQueueMember(queuename[|interface]):\n"
118 "Dynamically removes interface to an existing queue\n"
119 "Returns -1 if there is an error.\n"
120 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
123 /* We define a customer "local user" structure because we
124 use it not only for keeping track of what is in use but
125 also for keeping track of who we're dialing. */
128 struct ast_channel *chan;
133 int allowredirect_in;
134 int allowredirect_out;
139 struct member *member;
140 struct localuser *next;
146 struct ast_call_queue *parent; /* What queue is our parent */
147 char moh[80]; /* Name of musiconhold to be used */
148 char announce[80]; /* Announcement to play for member when call is answered */
149 char context[80]; /* Context when user exits queue */
150 int pos; /* Where we are in the queue */
151 int last_pos_said; /* Last position we told the user */
152 time_t last_pos; /* Last time we told the user their position */
153 int opos; /* Where we started in the queue */
154 int handled; /* Whether our call was handled */
155 time_t start; /* When we started holding */
156 int queuetimeout; /* How many seconds before timing out of queue */
157 struct ast_channel *chan; /* Our channel */
158 struct queue_ent *next; /* The next queue entry */
162 char tech[80]; /* Technology */
163 char loc[256]; /* Location */
164 int penalty; /* Are we a last resort? */
166 int dynamic; /* Are we dynamically added? */
167 time_t lastcall; /* When last successful call was hungup */
168 struct member *next; /* Next member */
171 struct ast_call_queue {
173 char name[80]; /* Name of the queue */
174 char moh[80]; /* Name of musiconhold to be used */
175 char announce[80]; /* Announcement to play when call is answered */
176 char context[80]; /* Context for this queue */
177 int strategy; /* Queueing strategy */
178 int announcefrequency; /* How often to announce their position */
179 int announceholdtime; /* When to announce holdtime: 0 = never, -1 = every announcement, 1 = only once */
180 int holdtime; /* Current avg holdtime for this queue, based on recursive boxcar filter */
181 int callscompleted; /* Number of queue calls completed */
182 int callsabandoned; /* Number of queue calls abandoned */
183 int servicelevel; /* seconds setting for servicelevel*/
184 int callscompletedinsl; /* Number of queue calls answererd with servicelevel*/
185 char monfmt[8]; /* Format to use when recording calls */
186 char sound_next[80]; /* Sound file: "Your call is now first in line" (def. queue-youarenext) */
187 char sound_thereare[80]; /* Sound file: "There are currently" (def. queue-thereare) */
188 char sound_calls[80]; /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
189 char sound_holdtime[80]; /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
190 char sound_minutes[80]; /* Sound file: "minutes." (def. queue-minutes) */
191 char sound_thanks[80]; /* Sound file: "Thank you for your patience." (def. queue-thankyou) */
193 int count; /* How many entries are in the queue */
194 int maxlen; /* Max number of entries in queue */
196 int dead; /* Whether this queue is dead or not */
197 int retry; /* Retry calling everyone after this amount of time */
198 int timeout; /* How long to wait for an answer */
200 /* Queue strategy things */
202 int rrpos; /* Round Robin - position */
203 int wrapped; /* Round Robin - wrapped around? */
205 struct member *members; /* Member channels to be tried */
206 struct queue_ent *head; /* Start of the actual queue */
207 struct ast_call_queue *next; /* Next call queue */
210 static struct ast_call_queue *queues = NULL;
211 static ast_mutex_t qlock = AST_MUTEX_INITIALIZER;
213 static char *int2strat(int strategy)
216 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
217 if (strategy == strategies[x].strategy)
218 return strategies[x].name;
223 static int strat2int(char *strategy)
226 for (x=0;x<sizeof(strategies) / sizeof(strategies[0]);x++) {
227 if (!strcasecmp(strategy, strategies[x].name))
228 return strategies[x].strategy;
233 static int join_queue(char *queuename, struct queue_ent *qe)
235 struct ast_call_queue *q;
236 struct queue_ent *cur, *prev = NULL;
239 ast_mutex_lock(&qlock);
242 if (!strcasecmp(q->name, queuename)) {
243 /* This is our one */
244 ast_mutex_lock(&q->lock);
245 if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
246 /* There's space for us, put us at the end */
258 /* Fix additional pointers and
264 strncpy(qe->moh, q->moh, sizeof(qe->moh));
265 strncpy(qe->announce, q->announce, sizeof(qe->announce));
266 strncpy(qe->context, q->context, sizeof(qe->context));
269 manager_event(EVENT_FLAG_CALL, "Join",
270 "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
271 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : "unknown"), q->name, qe->pos, q->count );
273 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
276 ast_mutex_unlock(&q->lock);
281 ast_mutex_unlock(&qlock);
285 static void free_members(struct ast_call_queue *q, int all)
287 /* Free non-dynamic members */
288 struct member *curm, *next, *prev;
293 if (all || !curm->dynamic) {
305 static void destroy_queue(struct ast_call_queue *q)
307 struct ast_call_queue *cur, *prev = NULL;
308 ast_mutex_lock(&qlock);
313 prev->next = cur->next;
321 ast_mutex_unlock(&qlock);
326 static int play_file(struct ast_channel *chan, char *filename)
330 ast_stopstream(chan);
331 res = ast_streamfile(chan, filename, chan->language);
334 res = ast_waitstream(chan, "");
339 ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name);
342 ast_stopstream(chan);
347 static int say_position(struct queue_ent *qe)
349 int res = 0, avgholdmins;
352 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
354 if ( (now - qe->last_pos) < 15 )
357 /* If either our position has changed, or we are over the freq timer, say position */
358 if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) )
361 ast_moh_stop(qe->chan);
362 /* Say we're next, if we are */
364 res += play_file(qe->chan, qe->parent->sound_next);
367 res += play_file(qe->chan, qe->parent->sound_thereare);
368 res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language);
369 res += play_file(qe->chan, qe->parent->sound_calls);
372 /* Round hold time to nearest minute */
373 avgholdmins = ( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60;
374 if (option_verbose > 2)
375 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes\n", qe->parent->name, avgholdmins);
377 /* If the hold time is >1 min, if it's enabled, and if it's not
378 supposed to be only once and we have already said it, say it */
379 if (avgholdmins > 1 && (qe->parent->announceholdtime) && (!(qe->parent->announceholdtime==1 && qe->last_pos)) ) {
380 res += play_file(qe->chan, qe->parent->sound_holdtime);
381 res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language);
382 res += play_file(qe->chan, qe->parent->sound_minutes);
386 /* Set our last_pos indicators */
388 qe->last_pos_said = qe->pos;
390 if (option_verbose > 2)
391 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos);
392 res += play_file(qe->chan, qe->parent->sound_thanks);
393 ast_moh_start(qe->chan, qe->moh);
398 static void record_abandoned(struct queue_ent *qe)
400 ast_mutex_lock(&qe->parent->lock);
401 qe->parent->callsabandoned++;
402 ast_mutex_unlock(&qe->parent->lock);
405 static void recalc_holdtime(struct queue_ent *qe)
407 int oldvalue, newvalue;
409 /* Calculate holdtime using a recursive boxcar filter */
410 /* Thanks to SRT for this contribution */
411 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
413 newvalue = time(NULL) - qe->start;
415 ast_mutex_lock(&qe->parent->lock);
416 if (newvalue <= qe->parent->servicelevel)
417 qe->parent->callscompletedinsl++;
418 oldvalue = qe->parent->holdtime;
419 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
420 ast_mutex_unlock(&qe->parent->lock);
424 static void leave_queue(struct queue_ent *qe)
426 struct ast_call_queue *q;
427 struct queue_ent *cur, *prev = NULL;
432 ast_mutex_lock(&q->lock);
440 /* Take us out of the queue */
441 manager_event(EVENT_FLAG_CALL, "Leave",
442 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
443 qe->chan->name, q->name, q->count);
445 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
447 /* Take us out of the queue */
449 prev->next = cur->next;
453 /* Renumber the people after us in the queue based on a new count */
459 ast_mutex_unlock(&q->lock);
460 if (q->dead && !q->count) {
461 /* It's dead and nobody is in it, so kill it */
466 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
468 /* Hang up a tree of stuff */
469 struct localuser *oo;
471 /* Hangup any existing lines we have open */
472 if (outgoing->chan && (outgoing->chan != exception))
473 ast_hangup(outgoing->chan);
475 outgoing=outgoing->next;
480 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
483 /* Request the peer */
484 tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
485 if (!tmp->chan) { /* If we can't, just go on to the next call */
487 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
490 ast_cdr_busy(qe->chan->cdr);
494 tmp->chan->appl = "AppQueue";
495 tmp->chan->data = "(Outgoing Line)";
496 tmp->chan->whentohangup = 0;
497 if (tmp->chan->callerid)
498 free(tmp->chan->callerid);
500 free(tmp->chan->ani);
501 if (qe->chan->callerid)
502 tmp->chan->callerid = strdup(qe->chan->callerid);
504 tmp->chan->callerid = NULL;
506 tmp->chan->ani = strdup(qe->chan->ani);
508 tmp->chan->ani = NULL;
509 /* Presense of ADSI CPE on outgoing channel follows ours */
510 tmp->chan->adsicpe = qe->chan->adsicpe;
511 /* Place the call, but don't wait on the answer */
512 res = ast_call(tmp->chan, tmp->numsubst, 0);
514 /* Again, keep going even if there's an error */
516 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
517 else if (option_verbose > 2)
518 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
519 ast_hangup(tmp->chan);
524 if (option_verbose > 2)
525 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
529 static int ring_one(struct queue_ent *qe, struct localuser *outgoing)
531 struct localuser *cur;
532 struct localuser *best;
538 if (cur->stillgoing && /* Not already done */
539 !cur->chan && /* Isn't already going */
540 (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */
541 bestmetric = cur->metric;
547 if (!qe->parent->strategy) {
548 /* Ring everyone who shares this best metric (for ringall) */
551 if (cur->stillgoing && !cur->chan && (cur->metric == bestmetric)) {
552 ast_log(LOG_DEBUG, "(Parallel) Trying '%s/%s' with metric %d\n", cur->tech, cur->numsubst, cur->metric);
558 /* Ring just the best channel */
559 ast_log(LOG_DEBUG, "Trying '%s/%s' with metric %d\n", best->tech, best->numsubst, best->metric);
560 ring_entry(qe, best);
563 } while (best && !best->chan);
565 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
571 static int valid_exit(struct queue_ent *qe, char digit)
574 if (!strlen(qe->context))
578 if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
579 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
580 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
581 qe->chan->priority = 0;
589 static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *digit)
591 char *queue = qe->parent->name;
599 struct localuser *peer = NULL;
600 struct ast_channel *watchers[MAX];
602 struct ast_channel *winner;
603 struct ast_channel *in = qe->chan;
605 while(*to && !peer) {
612 /* Keep track of important channels */
613 if (o->stillgoing && o->chan) {
614 watchers[pos++] = o->chan;
621 if (numlines == numbusies) {
622 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
624 ast_log(LOG_NOTICE, "No one is answering queue '%s'\n", queue);
629 winner = ast_waitfor_n(watchers, pos, to);
632 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
634 if (option_verbose > 2)
635 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
637 *allowredir_in = o->allowredirect_in;
638 *allowredir_out = o->allowredirect_out;
639 *allowdisconnect = o->allowdisconnect;
641 } else if (o->chan && (o->chan == winner)) {
642 f = ast_read(winner);
644 if (f->frametype == AST_FRAME_CONTROL) {
645 switch(f->subclass) {
646 case AST_CONTROL_ANSWER:
647 /* This is our guy if someone answered. */
649 if (option_verbose > 2)
650 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
652 *allowredir_in = o->allowredirect_in;
653 *allowredir_out = o->allowredirect_out;
654 *allowdisconnect = o->allowdisconnect;
657 case AST_CONTROL_BUSY:
658 if (option_verbose > 2)
659 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
662 ast_cdr_busy(in->cdr);
665 if (qe->parent->strategy)
666 ring_one(qe, outgoing);
669 case AST_CONTROL_CONGESTION:
670 if (option_verbose > 2)
671 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
674 ast_cdr_busy(in->cdr);
677 if (qe->parent->strategy)
678 ring_one(qe, outgoing);
681 case AST_CONTROL_RINGING:
682 if (option_verbose > 2)
683 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
686 ast_indicate(in, AST_CONTROL_RINGING);
691 case AST_CONTROL_OFFHOOK:
692 /* Ignore going off hook */
695 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
703 if (qe->parent->strategy)
704 ring_one(qe, outgoing);
712 if (f && (f->frametype != AST_FRAME_VOICE))
713 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
714 else if (!f || (f->frametype != AST_FRAME_VOICE))
715 printf("Hangup received on %s\n", in->name);
717 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
722 if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect && (f->subclass == '*')) {
723 if (option_verbose > 3)
724 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
728 if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
729 if (option_verbose > 3)
730 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass);
736 if (!*to && (option_verbose > 2))
737 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
744 static int wait_our_turn(struct queue_ent *qe)
746 struct queue_ent *ch;
750 /* This is the holding pen for callers 2 through maxlen */
752 /* Atomically read the parent head -- does not need a lock */
753 ch = qe->parent->head;
755 /* If we are now at the top of the head, break out */
759 /* If we have timed out, break out */
760 if ( qe->queuetimeout ) {
762 if ( (now - qe->start) >= qe->queuetimeout )
766 /* Make a position announcement, if enabled */
767 if (qe->parent->announcefrequency)
771 /* Wait a second before checking again */
772 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
779 static int update_queue(struct ast_call_queue *q, struct localuser *user)
782 /* Since a reload could have taken place, we have to traverse the list to
783 be sure it's still valid */
784 ast_mutex_lock(&q->lock);
787 if (user->member == cur) {
788 time(&cur->lastcall);
795 ast_mutex_unlock(&q->lock);
799 static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp)
801 switch (q->strategy) {
802 case QUEUE_STRATEGY_RINGALL:
803 /* Everyone equal, except for penalty */
804 tmp->metric = mem->penalty * 1000000;
806 case QUEUE_STRATEGY_ROUNDROBIN:
809 /* No more channels, start over */
812 /* Prioritize next entry */
817 if (pos < q->rrpos) {
818 tmp->metric = 1000 + pos;
820 if (pos > q->rrpos) {
821 /* Indicate there is another priority */
826 tmp->metric += mem->penalty * 1000000;
828 case QUEUE_STRATEGY_RANDOM:
829 tmp->metric = rand() % 1000;
830 tmp->metric += mem->penalty * 1000000;
832 case QUEUE_STRATEGY_FEWESTCALLS:
833 tmp->metric = mem->calls;
834 tmp->metric += mem->penalty * 1000000;
836 case QUEUE_STRATEGY_LEASTRECENT:
840 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
841 tmp->metric += mem->penalty * 1000000;
844 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
850 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url, int *go_on)
853 struct localuser *outgoing=NULL, *tmp = NULL;
856 int allowredir_out=0;
857 int allowdisconnect=0;
858 char restofit[AST_MAX_EXTENSION];
859 char oldexten[AST_MAX_EXTENSION]="";
860 char oldcontext[AST_MAX_EXTENSION]="";
861 char queuename[256]="";
863 struct ast_channel *peer;
864 struct localuser *lpeer;
865 int res = 0, bridge = 0;
868 char *announce = NULL;
871 /* Hold the lock while we setup the outgoing calls */
872 ast_mutex_lock(&qe->parent->lock);
873 strncpy(queuename, qe->parent->name, sizeof(queuename) - 1);
874 cur = qe->parent->members;
875 if (strlen(qe->announce))
876 announce = qe->announce;
877 if (announceoverride && strlen(announceoverride))
878 announce = announceoverride;
880 /* Get a technology/[device:]number pair */
881 tmp = malloc(sizeof(struct localuser));
883 ast_mutex_unlock(&qe->parent->lock);
884 ast_log(LOG_WARNING, "Out of memory\n");
887 memset(tmp, 0, sizeof(struct localuser));
888 tmp->stillgoing = -1;
890 if (strchr(options, 't'))
891 tmp->allowredirect_in = 1;
892 if (strchr(options, 'T'))
893 tmp->allowredirect_out = 1;
894 if (strchr(options, 'r'))
895 tmp->ringbackonly = 1;
896 if (strchr(options, 'm'))
897 tmp->musiconhold = 1;
898 if (strchr(options, 'd'))
899 tmp->dataquality = 1;
900 if (strchr(options, 'H'))
901 tmp->allowdisconnect = 1;
902 if (strchr(options, 'n'))
906 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
908 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
910 tmp->member = cur; /* Never directly dereference! Could change on reload */
911 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
912 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
913 /* If we're dialing by extension, look at the extension to know what to dial */
914 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
915 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
916 snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
918 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
920 /* Special case: If we ring everyone, go ahead and ring them, otherwise
921 just calculate their metric for the appropriate strategy */
922 calc_metric(qe->parent, cur, x++, qe, tmp);
923 /* Put them in the list of outgoing thingies... We're ready now.
924 XXX If we're forcibly removed, these outgoing calls won't get
926 tmp->next = outgoing;
928 /* If this line is up, don't try anybody else */
929 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
934 if (qe->parent->timeout)
935 to = qe->parent->timeout * 1000;
938 ring_one(qe, outgoing);
939 ast_mutex_unlock(&qe->parent->lock);
940 lpeer = wait_for_answer(qe, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, &digit);
947 /* Musta gotten hung up */
948 record_abandoned(qe);
951 if (digit && valid_exit(qe, digit))
954 /* Nobody answered, next please? */
960 /* Ah ha! Someone answered within the desired timeframe. Of course after this
961 we will always return with -1 so that it is hung up properly after the
964 if (!strcmp(qe->chan->type,"Zap")) {
965 if (tmp->dataquality) zapx = 0;
966 ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
968 if (!strcmp(peer->type,"Zap")) {
969 if (tmp->dataquality) zapx = 0;
970 ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
972 /* Update parameters for the queue */
974 update_queue(qe->parent, lpeer);
975 hanguptree(outgoing, peer);
976 /* Stop music on hold */
977 ast_moh_stop(qe->chan);
981 res2 = ast_autoservice_start(qe->chan);
983 res2 = ast_streamfile(peer, announce, peer->language);
985 res2 = ast_waitstream(peer, "");
986 res2 |= ast_autoservice_stop(qe->chan);
988 /* Agent must have hung up */
989 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
990 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", "");
995 /* If appropriate, log that we have a destination channel */
997 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
998 /* Make sure channels are compatible */
999 res = ast_channel_make_compatible(qe->chan, peer);
1001 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", "");
1002 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
1006 /* Begin Monitoring */
1007 if (qe->parent->monfmt && *qe->parent->monfmt) {
1008 ast_monitor_start( peer, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
1010 /* Drop out of the queue at this point, to prepare for next caller */
1013 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
1014 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
1015 ast_channel_sendurl( peer, url );
1017 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start);
1018 strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1);
1019 strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1);
1021 bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
1022 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
1023 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context);
1024 } else if (qe->chan->_softhangup) {
1025 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1027 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart));
1030 if(bridge != AST_PBX_NO_HANGUP_PEER)
1033 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
1034 else res = bridge; /* bridge error, stay in the queue */
1037 hanguptree(outgoing, NULL);
1041 static int wait_a_bit(struct queue_ent *qe)
1043 /* Don't need to hold the lock while we setup the outgoing calls */
1044 int retrywait = qe->parent->retry * 1000;
1045 return ast_waitfordigit(qe->chan, retrywait);
1050 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
1052 struct member * ret = NULL ;
1060 while( mem != NULL ) {
1061 sprintf( buf, "%s/%s", mem->tech, mem->loc);
1063 if( strcmp( buf, interface ) == 0 ) {
1076 static struct member * create_queue_node( char * interface )
1078 struct member * cur ;
1081 /* Add a new member */
1083 cur = malloc(sizeof(struct member));
1086 memset(cur, 0, sizeof(struct member));
1087 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
1088 if ((tmp = strchr(cur->tech, '/')))
1090 if ((tmp = strchr(interface, '/'))) {
1092 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1094 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
1101 static int rqm_exec(struct ast_channel *chan, void *data)
1104 struct localuser *u;
1106 struct member * node ;
1107 struct member * look ;
1109 char tmpchan[256]="";
1110 char *interface=NULL;
1111 struct ast_call_queue *q;
1115 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
1119 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
1121 /* Parse our arguments XXX Check for failure XXX */
1122 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1125 interface = strchr(queuename, '|');
1131 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1132 interface = strrchr(tmpchan, '-');
1135 interface = tmpchan;
1139 if( ( q = queues) != NULL )
1141 while( q && ( res != 0 ) && (!found) )
1143 ast_mutex_lock(&q->lock);
1144 if( strcmp( q->name, queuename) == 0 )
1146 // found queue, try to remove interface
1149 if( ( node = interface_exists( q, interface ) ) != NULL )
1151 if( ( look = q->members ) == node )
1154 q->members = node->next;
1158 while( look != NULL )
1159 if( look->next == node )
1161 look->next = node->next ;
1170 ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n",
1171 interface, queuename);
1175 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
1176 "Not there\n", interface, queuename);
1179 ast_mutex_unlock(&q->lock);
1185 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
1187 LOCAL_USER_REMOVE(u);
1193 static int aqm_exec(struct ast_channel *chan, void *data)
1196 struct localuser *u;
1199 char tmpchan[512]="";
1200 char *interface=NULL;
1201 struct ast_call_queue *q;
1202 struct member *save;
1206 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
1210 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
1212 /* Parse our arguments XXX Check for failure XXX */
1213 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1216 interface = strchr(queuename, '|');
1222 strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1);
1223 interface = strrchr(tmpchan, '-');
1226 interface = tmpchan;
1230 if( ( q = queues) != NULL )
1232 while( q && ( res != 0 ) && (!found) )
1234 ast_mutex_lock(&q->lock);
1235 if( strcmp( q->name, queuename) == 0 )
1237 // found queue, try to enable interface
1240 if( interface_exists( q, interface ) == NULL )
1243 q->members = create_queue_node( interface ) ;
1245 if( q->members != NULL ) {
1246 q->members->dynamic = 1;
1247 q->members->next = save ;
1251 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
1255 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
1256 "Already there\n", interface, queuename);
1259 ast_mutex_unlock(&q->lock);
1265 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
1267 LOCAL_USER_REMOVE(u);
1272 static int queue_exec(struct ast_channel *chan, void *data)
1275 struct localuser *u;
1278 char *options = NULL;
1280 char *announceoverride = NULL;
1281 char *queuetimeoutstr = NULL;
1282 /* whether to exit Queue application after the timeout hits */
1287 /* Our queue entry */
1288 struct queue_ent qe;
1291 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
1297 /* Setup our queue entry */
1298 memset(&qe, 0, sizeof(qe));
1300 /* Parse our arguments XXX Check for failure XXX */
1301 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
1304 options = strchr(queuename, '|');
1308 url = strchr(options, '|');
1312 announceoverride = strchr(url, '|');
1313 if (announceoverride) {
1314 *announceoverride = '\0';
1316 queuetimeoutstr = strchr(announceoverride, '|');
1317 if (queuetimeoutstr) {
1318 *queuetimeoutstr = '\0';
1320 qe.queuetimeout = atoi(queuetimeoutstr);
1322 qe.queuetimeout = 0;
1329 printf("queue: %s, options: %s, url: %s, announce: %s, timeout: %d\n",
1330 queuename, options, url, announceoverride, qe.queuetimeout);
1334 qe.start = time(NULL);
1335 qe.last_pos_said = 0;
1337 if (!join_queue(queuename, &qe)) {
1338 ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", chan->callerid ? chan->callerid : "");
1339 /* Start music on hold */
1340 ast_moh_start(chan, qe.moh);
1342 /* This is the wait loop for callers 2 through maxlen */
1344 res = wait_our_turn(&qe);
1345 /* If they hungup, return immediately */
1347 /* Record this abandoned call */
1348 record_abandoned(&qe);
1349 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1350 if (option_verbose > 2) {
1351 ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
1358 if (valid_exit(&qe, res)) {
1359 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1365 /* This is the wait loop for the head caller*/
1366 /* To exit, they may get their call answered; */
1367 /* they may dial a digit from the queue context; */
1368 /* or, they may may timeout. */
1370 /* Leave if we have exceeded our queuetimeout */
1371 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1376 /* Make a position announcement, if enabled */
1377 if (qe.parent->announcefrequency)
1380 /* Try calling all queue members for 'timeout' seconds */
1381 res = try_calling(&qe, options, announceoverride, url, &go_on);
1385 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1387 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1391 /* Leave if we have exceeded our queuetimeout */
1392 if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
1397 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
1398 res = wait_a_bit(&qe);
1400 ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
1401 if (option_verbose > 2) {
1402 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
1407 if (res && valid_exit(&qe, res)) {
1408 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
1411 /* exit after 'timeout' cycle if 'n' option enabled */
1413 if (option_verbose > 2) {
1414 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
1417 ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
1424 /* Don't allow return code > 0 */
1425 if (res > 0 && res != AST_PBX_KEEPALIVE) {
1428 ast_stopstream(chan);
1432 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
1435 LOCAL_USER_REMOVE(u);
1439 static void reload_queues(void)
1441 struct ast_call_queue *q, *ql, *qn;
1442 struct ast_config *cfg;
1444 struct ast_variable *var;
1445 struct member *prev, *cur;
1447 cfg = ast_load("queues.conf");
1449 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
1452 ast_mutex_lock(&qlock);
1453 /* Mark all queues as dead for the moment */
1459 /* Chug through config file */
1460 cat = ast_category_browse(cfg, NULL);
1462 if (strcasecmp(cat, "general")) {
1463 /* Look for an existing one */
1466 if (!strcmp(q->name, cat))
1472 q = malloc(sizeof(struct ast_call_queue));
1475 memset(q, 0, sizeof(struct ast_call_queue));
1476 ast_mutex_init(&q->lock);
1477 strncpy(q->name, cat, sizeof(q->name));
1484 ast_mutex_lock(&q->lock);
1485 /* Re-initialize the queue */
1490 q->announcefrequency = 0;
1491 q->announceholdtime = 0;
1493 q->callscompleted = 0;
1494 q->callsabandoned = 0;
1495 q->callscompletedinsl = 0;
1496 q->servicelevel = 0;
1499 strcpy(q->announce, "");
1500 strcpy(q->context, "");
1501 strcpy(q->monfmt, "");
1502 strcpy(q->sound_next, "queue-youarenext");
1503 strcpy(q->sound_thereare, "queue-thereare");
1504 strcpy(q->sound_calls, "queue-callswaiting");
1505 strcpy(q->sound_holdtime, "queue-holdtime");
1506 strcpy(q->sound_minutes, "queue-minutes");
1507 strcpy(q->sound_thanks, "queue-thankyou");
1510 /* find the end of any dynamic members */
1514 var = ast_variable_browse(cfg, cat);
1516 if (!strcasecmp(var->name, "member")) {
1517 /* Add a new member */
1518 cur = malloc(sizeof(struct member));
1520 memset(cur, 0, sizeof(struct member));
1521 strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1522 if ((tmp = strchr(cur->tech, ','))) {
1525 cur->penalty = atoi(tmp);
1526 if (cur->penalty < 0)
1529 if ((tmp = strchr(cur->tech, '/')))
1531 if ((tmp = strchr(var->value, '/'))) {
1533 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1534 if ((tmp = strchr(cur->loc, ',')))
1537 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1544 } else if (!strcasecmp(var->name, "music")) {
1545 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1546 } else if (!strcasecmp(var->name, "announce")) {
1547 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1548 } else if (!strcasecmp(var->name, "context")) {
1549 strncpy(q->context, var->value, sizeof(q->context) - 1);
1550 } else if (!strcasecmp(var->name, "timeout")) {
1551 q->timeout = atoi(var->value);
1552 } else if (!strcasecmp(var->name, "monitor-format")) {
1553 strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1);
1554 } else if (!strcasecmp(var->name, "queue-youarenext")) {
1555 strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1);
1556 } else if (!strcasecmp(var->name, "queue-thereare")) {
1557 strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1);
1558 } else if (!strcasecmp(var->name, "queue-callswaiting")) {
1559 strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1);
1560 } else if (!strcasecmp(var->name, "queue-holdtime")) {
1561 strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1);
1562 } else if (!strcasecmp(var->name, "queue-minutes")) {
1563 strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1);
1564 } else if (!strcasecmp(var->name, "queue-thankyou")) {
1565 strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1);
1566 } else if (!strcasecmp(var->name, "announce-frequency")) {
1567 q->announcefrequency = atoi(var->value);
1568 } else if (!strcasecmp(var->name, "announce-holdtime")) {
1569 q->announceholdtime = (!strcasecmp(var->value,"once")) ? 1 : ast_true(var->value);
1570 } else if (!strcasecmp(var->name, "retry")) {
1571 q->retry = atoi(var->value);
1572 } else if (!strcasecmp(var->name, "maxlen")) {
1573 q->maxlen = atoi(var->value);
1574 } else if (!strcasecmp(var->name, "servicelevel")) {
1575 q->servicelevel= atoi(var->value);
1576 } else if (!strcasecmp(var->name, "strategy")) {
1577 q->strategy = strat2int(var->value);
1578 if (q->strategy < 0) {
1579 ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value);
1583 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1588 q->retry = DEFAULT_RETRY;
1590 q->timeout = DEFAULT_TIMEOUT;
1594 ast_mutex_unlock(&q->lock);
1601 cat = ast_category_browse(cfg, cat);
1616 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1621 ast_mutex_unlock(&qlock);
1624 static int __queues_show(int fd, int argc, char **argv, int queue_show)
1626 struct ast_call_queue *q;
1627 struct queue_ent *qe;
1636 if ((!queue_show && argc != 2) || (queue_show && argc != 3))
1637 return RESULT_SHOWUSAGE;
1638 ast_mutex_lock(&qlock);
1641 ast_mutex_unlock(&qlock);
1643 ast_cli(fd, "No such queue: %s.\n",argv[2]);
1645 ast_cli(fd, "No queues.\n");
1646 return RESULT_SUCCESS;
1649 ast_mutex_lock(&q->lock);
1651 if (strcasecmp(q->name, argv[2]) != 0) {
1652 ast_mutex_unlock(&q->lock);
1655 ast_cli(fd, "No such queue: %s.\n",argv[2]);
1662 snprintf(max, sizeof(max), "%d", q->maxlen);
1664 strcpy(max, "unlimited");
1666 if(q->callscompleted > 0)
1667 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
1668 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",
1669 q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->callscompleted, q->callsabandoned,sl,q->servicelevel);
1671 ast_cli(fd, " Members: \n");
1672 for (mem = q->members; mem; mem = mem->next) {
1674 snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty);
1678 strcat(max, " (dynamic)");
1680 snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
1681 mem->calls, (long)(time(NULL) - mem->lastcall));
1683 strcpy(calls, " has taken no calls yet");
1684 ast_cli(fd, " %s/%s%s%s\n", mem->tech, mem->loc, max, calls);
1687 ast_cli(fd, " No Members\n");
1690 ast_cli(fd, " Callers: \n");
1691 for (qe = q->head; qe; qe = qe->next)
1692 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld)\n", pos++, qe->chan->name,
1693 (long)(now - qe->start) / 60, (long)(now - qe->start) % 60);
1695 ast_cli(fd, " No Callers\n");
1697 ast_mutex_unlock(&q->lock);
1702 ast_mutex_unlock(&qlock);
1703 return RESULT_SUCCESS;
1706 static int queues_show(int fd, int argc, char **argv)
1708 return __queues_show(fd, argc, argv, 0);
1711 static int queue_show(int fd, int argc, char **argv)
1713 return __queues_show(fd, argc, argv, 1);
1716 static char *complete_queue(char *line, char *word, int pos, int state)
1718 struct ast_call_queue *q;
1721 ast_mutex_lock(&qlock);
1724 if (!strncasecmp(word, q->name, strlen(word))) {
1725 if (++which > state)
1730 ast_mutex_unlock(&qlock);
1731 return q ? strdup(q->name) : NULL;
1734 /* JDG: callback to display queues status in manager */
1735 static int manager_queues_show( struct mansession *s, struct message *m )
1737 char *a[] = { "show", "queues" };
1738 return queues_show( s->fd, 2, a );
1742 /* Dump queue status */
1743 static int manager_queues_status( struct mansession *s, struct message *m )
1747 char *id = astman_get_header(m,"ActionID");
1748 char idText[256] = "";
1749 struct ast_call_queue *q;
1750 struct queue_ent *qe;
1753 astman_send_ack(s, m, "Queue status will follow");
1755 ast_mutex_lock(&qlock);
1758 snprintf(idText,256,"ActionID: %s\r\n",id);
1761 ast_mutex_lock(&q->lock);
1763 /* List queue properties */
1764 if(q->callscompleted > 0)
1765 sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted);
1766 ast_cli(s->fd, "Event: QueueParams\r\n"
1773 "ServiceLevel: %d\r\n"
1774 "ServicelevelPerf: %2.1f\r\n"
1777 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
1778 q->callsabandoned, q->servicelevel, sl, idText);
1780 /* List Queue Members */
1781 for (mem = q->members; mem; mem = mem->next)
1782 ast_cli(s->fd, "Event: QueueMember\r\n"
1784 "Location: %s/%s\r\n"
1785 "Membership: %s\r\n"
1787 "CallsTaken: %d\r\n"
1791 q->name, mem->tech, mem->loc, mem->dynamic ? "dynamic" : "static",
1792 mem->penalty, mem->calls, mem->lastcall, idText);
1794 /* List Queue Entries */
1797 for (qe = q->head; qe; qe = qe->next)
1798 ast_cli(s->fd, "Event: QueueEntry\r\n"
1806 q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), (long)(now - qe->start), idText);
1807 ast_mutex_unlock(&q->lock);
1810 ast_mutex_unlock(&qlock);
1811 return RESULT_SUCCESS;
1814 static char show_queues_usage[] =
1815 "Usage: show queues\n"
1816 " Provides summary information on call queues.\n";
1818 static struct ast_cli_entry cli_show_queues = {
1819 { "show", "queues", NULL }, queues_show,
1820 "Show status of queues", show_queues_usage, NULL };
1822 static char show_queue_usage[] =
1823 "Usage: show queue\n"
1824 " Provides summary information on a specified queue.\n";
1826 static struct ast_cli_entry cli_show_queue = {
1827 { "show", "queue", NULL }, queue_show,
1828 "Show status of a specified queue", show_queue_usage, complete_queue };
1830 int unload_module(void)
1832 STANDARD_HANGUP_LOCALUSERS;
1833 ast_cli_unregister(&cli_show_queue);
1834 ast_cli_unregister(&cli_show_queues);
1835 ast_manager_unregister( "Queues" );
1836 ast_manager_unregister( "QueueStatus" );
1837 ast_unregister_application(app_aqm);
1838 ast_unregister_application(app_rqm);
1839 return ast_unregister_application(app);
1842 int load_module(void)
1845 res = ast_register_application(app, queue_exec, synopsis, descrip);
1847 ast_cli_register(&cli_show_queue);
1848 ast_cli_register(&cli_show_queues);
1849 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1850 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1853 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1854 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1867 char *description(void)
1875 STANDARD_USECOUNT(res);
1881 return ASTERISK_GPL_KEY;