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 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/module.h>
21 #include <asterisk/translate.h>
22 #include <asterisk/say.h>
23 #include <asterisk/parking.h>
24 #include <asterisk/musiconhold.h>
25 #include <asterisk/cli.h>
26 #include <asterisk/manager.h> /* JDG */
27 #include <asterisk/config.h>
35 #include <sys/signal.h>
36 #include <netinet/in.h>
40 #define QUEUE_STRATEGY_RINGALL 0
41 #define QUEUE_STRATEGY_ROUNDROBIN 1
42 #define QUEUE_STRATEGY_LEASTRECENT 2
43 #define QUEUE_STRATEGY_FEWESTCALLS 3
44 #define QUEUE_STRATEGY_RANDOM 4
46 #define DEFAULT_RETRY 5
47 #define DEFAULT_TIMEOUT 15
48 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
50 static char *tdesc = "True Call Queueing";
52 static char *app = "Queue";
54 static char *synopsis = "Queue a call for a call queue";
56 static char *descrip =
57 " Queue(queuename[|options[|URL][|announceoverride]]):\n"
58 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
59 " This application returns -1 if the originating channel hangs up, or if the\n"
60 "call is bridged and either of the parties in the bridge terminate the call.\n"
61 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
62 "The option string may contain zero or more of the following characters:\n"
63 " 't' -- allow the called user transfer the calling user\n"
64 " 'T' -- to allow the calling user to transfer the call.\n"
65 " 'd' -- data-quality (modem) call (minimum delay).\n"
66 " 'H' -- allow caller to hang up by hitting *.\n"
67 " In addition to transferring the call, a call may be parked and then picked\n"
68 "up by another user.\n"
69 " The optionnal URL will be sent to the called party if the channel supports\n"
73 static char *app_aqm = "AddQueueMember" ;
74 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
75 static char *app_aqm_descrip =
76 " AddQueueMember(queuename[|interface]):\n"
77 "Dynamically adds interface to an existing queue\n"
78 "Returns -1 if there is an error.\n"
79 "Example: AddQueueMember(techsupport|SIP/3000)\n"
82 static char *app_rqm = "RemoveQueueMember" ;
83 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
84 static char *app_rqm_descrip =
85 " RemoveQueueMember(queuename[|interface]):\n"
86 "Dynamically removes interface to an existing queue\n"
87 "Returns -1 if there is an error.\n"
88 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
94 /* We define a customer "local user" structure because we
95 use it not only for keeping track of what is in use but
96 also for keeping track of who we're dialing. */
99 struct ast_channel *chan;
104 int allowredirect_in;
105 int allowredirect_out;
110 struct localuser *next;
116 struct ast_call_queue *parent; /* What queue is our parent */
117 char moh[80]; /* Name of musiconhold to be used */
118 char announce[80]; /* Announcement to play */
119 char context[80]; /* Context when user exits queue */
120 int pos; /* Where we are in the queue */
121 time_t start; /* When we started holding */
122 struct ast_channel *chan; /* Our channel */
123 struct queue_ent *next; /* The next queue entry */
127 char tech[80]; /* Technology */
128 char loc[256]; /* Location */
129 struct timeval lastcall; /* When last successful call was hungup */
130 struct member *next; /* Next member */
133 struct ast_call_queue {
134 pthread_mutex_t lock;
135 char name[80]; /* Name of the queue */
136 char moh[80]; /* Name of musiconhold to be used */
137 char announce[80]; /* Announcement to play */
138 char context[80]; /* Announcement to play */
139 int strategy; /* Queueing strategy */
140 int announcetimeout; /* How often to announce their position */
141 int count; /* How many entries are in the queue */
142 int maxlen; /* Max number of entries in queue */
144 int dead; /* Whether this queue is dead or not */
145 int retry; /* Retry calling everyone after this amount of time */
146 int timeout; /* How long to wait for an answer */
148 struct member *members; /* Member channels to be tried */
149 struct queue_ent *head; /* Start of the actual queue */
150 struct ast_call_queue *next; /* Next call queue */
153 static struct ast_call_queue *queues = NULL;
154 static pthread_mutex_t qlock = AST_MUTEX_INITIALIZER;
157 static int join_queue(char *queuename, struct queue_ent *qe)
159 struct ast_call_queue *q;
160 struct queue_ent *cur, *prev = NULL;
163 ast_pthread_mutex_lock(&qlock);
166 if (!strcasecmp(q->name, queuename)) {
167 /* This is our one */
168 ast_pthread_mutex_lock(&q->lock);
169 if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
170 /* There's space for us, put us at the end */
182 /* Fix additional pointers and
187 strncpy(qe->moh, q->moh, sizeof(qe->moh));
188 strncpy(qe->announce, q->announce, sizeof(qe->announce));
189 strncpy(qe->context, q->context, sizeof(qe->context));
192 manager_event(EVENT_FLAG_CALL, "Join",
193 "Channel: %s\r\nCallerID: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
194 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), q->name, qe->pos, q->count );
196 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
199 ast_pthread_mutex_unlock(&q->lock);
204 ast_pthread_mutex_unlock(&qlock);
208 static void free_members(struct ast_call_queue *q)
210 struct member *curm, *next;
220 static void destroy_queue(struct ast_call_queue *q)
222 struct ast_call_queue *cur, *prev = NULL;
223 ast_pthread_mutex_lock(&qlock);
228 prev->next = cur->next;
236 ast_pthread_mutex_unlock(&qlock);
241 static void leave_queue(struct queue_ent *qe)
243 struct ast_call_queue *q;
244 struct queue_ent *cur, *prev = NULL;
249 ast_pthread_mutex_lock(&q->lock);
257 /* Take us out of the queue */
258 manager_event(EVENT_FLAG_CALL, "Leave",
259 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
260 qe->chan->name, q->name, q->count);
262 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
264 /* Take us out of the queue */
266 prev->next = cur->next;
275 ast_pthread_mutex_unlock(&q->lock);
276 if (q->dead && !q->count) {
277 /* It's dead and nobody is in it, so kill it */
282 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
284 /* Hang up a tree of stuff */
285 struct localuser *oo;
287 /* Hangup any existing lines we have open */
288 if (outgoing->chan && (outgoing->chan != exception))
289 ast_hangup(outgoing->chan);
291 outgoing=outgoing->next;
298 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *queue)
307 struct ast_channel *peer = NULL;
308 struct ast_channel *watchers[MAX];
310 struct ast_channel *winner;
312 while(*to && !peer) {
319 /* Keep track of important channels */
320 if (o->stillgoing && o->chan) {
321 watchers[pos++] = o->chan;
328 if (numlines == numbusies) {
329 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
331 ast_log(LOG_NOTICE, "No one is answered queue %s\n", queue);
336 winner = ast_waitfor_n(watchers, pos, to);
339 if (o->stillgoing && (o->chan->_state == AST_STATE_UP)) {
341 if (option_verbose > 2)
342 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
344 *allowredir_in = o->allowredirect_in;
345 *allowredir_out = o->allowredirect_out;
346 *allowdisconnect = o->allowdisconnect;
348 } else if (o->chan == winner) {
349 f = ast_read(winner);
351 if (f->frametype == AST_FRAME_CONTROL) {
352 switch(f->subclass) {
353 case AST_CONTROL_ANSWER:
354 /* This is our guy if someone answered. */
356 if (option_verbose > 2)
357 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
359 *allowredir_in = o->allowredirect_in;
360 *allowredir_out = o->allowredirect_out;
361 *allowdisconnect = o->allowdisconnect;
364 case AST_CONTROL_BUSY:
365 if (option_verbose > 2)
366 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
369 ast_cdr_busy(in->cdr);
372 case AST_CONTROL_CONGESTION:
373 if (option_verbose > 2)
374 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
377 ast_cdr_busy(in->cdr);
380 case AST_CONTROL_RINGING:
381 if (option_verbose > 2)
382 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
385 ast_indicate(in, AST_CONTROL_RINGING);
390 case AST_CONTROL_OFFHOOK:
391 /* Ignore going off hook */
394 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
407 if (f && (f->frametype != AST_FRAME_VOICE))
408 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
409 else if (!f || (f->frametype != AST_FRAME_VOICE))
410 printf("Hangup received on %s\n", in->name);
412 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
417 if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect &&
418 (f->subclass == '*')) {
419 if (option_verbose > 3)
420 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
425 if (!*to && (option_verbose > 2))
426 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
433 static int wait_our_turn(struct queue_ent *qe)
435 struct queue_ent *ch;
438 /* Atomically read the parent head */
439 pthread_mutex_lock(&qe->parent->lock);
440 ch = qe->parent->head;
441 pthread_mutex_unlock(&qe->parent->lock);
442 /* If we are now at the top of the head, break out */
443 if (qe->parent->head == qe)
445 /* Wait a second before checking again */
446 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
453 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
456 /* Request the peer */
457 tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
458 if (!tmp->chan) { /* If we can't, just go on to the next call */
460 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
463 ast_cdr_busy(qe->chan->cdr);
467 tmp->chan->appl = "AppQueue";
468 tmp->chan->data = "(Outgoing Line)";
469 tmp->chan->whentohangup = 0;
470 if (tmp->chan->callerid)
471 free(tmp->chan->callerid);
473 free(tmp->chan->ani);
474 if (qe->chan->callerid)
475 tmp->chan->callerid = strdup(qe->chan->callerid);
477 tmp->chan->callerid = NULL;
479 tmp->chan->ani = strdup(qe->chan->ani);
481 tmp->chan->ani = NULL;
482 /* Presense of ADSI CPE on outgoing channel follows ours */
483 tmp->chan->adsicpe = qe->chan->adsicpe;
484 /* Place the call, but don't wait on the answer */
485 res = ast_call(tmp->chan, tmp->numsubst, 0);
487 /* Again, keep going even if there's an error */
489 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
490 else if (option_verbose > 2)
491 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->numsubst);
492 ast_hangup(tmp->chan);
497 if (option_verbose > 2)
498 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->numsubst);
502 static int calc_metric(struct ast_call_queue *q, struct queue_ent *qe, struct localuser *tmp)
504 switch (q->strategy) {
505 case QUEUE_STRATEGY_RINGALL:
506 ast_log(LOG_WARNING, "Can't calculate metric for ringall strategy\n");
512 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url)
515 struct localuser *outgoing=NULL, *tmp = NULL;
518 int allowredir_out=0;
519 int allowdisconnect=0;
520 char restofit[AST_MAX_EXTENSION];
522 struct ast_channel *peer;
523 int res = 0, bridge = 0;
525 char *announce = NULL;
526 /* Hold the lock while we setup the outgoing calls */
527 ast_pthread_mutex_lock(&qe->parent->lock);
528 cur = qe->parent->members;
529 if (strlen(qe->announce))
530 announce = qe->announce;
531 if (announceoverride && strlen(announceoverride))
532 announce = announceoverride;
534 /* Get a technology/[device:]number pair */
535 tmp = malloc(sizeof(struct localuser));
537 ast_log(LOG_WARNING, "Out of memory\n");
540 memset(tmp, 0, sizeof(struct localuser));
541 tmp->stillgoing = -1;
543 if (strchr(options, 't'))
544 tmp->allowredirect_in = 1;
545 if (strchr(options, 'T'))
546 tmp->allowredirect_out = 1;
547 if (strchr(options, 'r'))
548 tmp->ringbackonly = 1;
549 if (strchr(options, 'm'))
550 tmp->musiconhold = 1;
551 if (strchr(options, 'd'))
552 tmp->dataquality = 1;
553 if (strchr(options, 'H'))
554 tmp->allowdisconnect = 1;
557 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
559 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
561 strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
562 strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
563 /* If we're dialing by extension, look at the extension to know what to dial */
564 if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
565 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
566 snprintf(newnum, sizeof(tmp->numsubst) - (newnum - tmp->numsubst), "%s%s", qe->chan->exten,restofit);
568 ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->numsubst);
570 /* Special case: If we ring everyone, go ahead and ring them, otherwise
571 just calculate their metric for the appropriate strategy */
572 if (!qe->parent->strategy)
575 calc_metric(qe->parent, qe, tmp);
576 /* Put them in the list of outgoing thingies... We're ready now.
577 XXX If we're forcibly removed, these outgoing calls won't get
579 tmp->next = outgoing;
581 /* If this line is up, don't try anybody else */
582 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
587 if (qe->parent->timeout)
588 to = qe->parent->timeout * 1000;
591 ast_pthread_mutex_unlock(&qe->parent->lock);
593 peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, qe->parent->name);
596 /* Musta gotten hung up */
599 /* Nobody answered, next please? */
605 /* Ah ha! Someone answered within the desired timeframe. Of course after this
606 we will always return with -1 so that it is hung up properly after the
608 if (!strcmp(qe->chan->type,"Zap")) {
609 if (tmp->dataquality) zapx = 0;
610 ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
612 if (!strcmp(peer->type,"Zap")) {
613 if (tmp->dataquality) zapx = 0;
614 ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
616 hanguptree(outgoing, peer);
617 /* Stop music on hold */
618 ast_moh_stop(qe->chan);
622 res2 = ast_streamfile(peer, announce, peer->language);
623 /* XXX Need a function to wait on *both* streams XXX */
625 res2 = ast_waitstream(peer, "");
629 /* Agent must have hung up */
630 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
635 /* If appropriate, log that we have a destination channel */
637 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
638 /* Make sure channels are compatible */
639 res = ast_channel_make_compatible(qe->chan, peer);
641 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
645 /* Drop out of the queue at this point, to prepare for next caller */
648 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
649 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
650 ast_channel_sendurl( peer, url );
652 bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
654 if(bridge != AST_PBX_NO_HANGUP_PEER)
657 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
658 else res = bridge; /* bridge error, stay in the queue */
661 hanguptree(outgoing, NULL);
665 static int wait_a_bit(struct queue_ent *qe)
668 /* Hold the lock while we setup the outgoing calls */
669 ast_pthread_mutex_lock(&qe->parent->lock);
670 retrywait = qe->parent->retry * 1000;
671 ast_pthread_mutex_unlock(&qe->parent->lock);
672 return ast_waitfordigit(qe->chan, retrywait);
675 static int valid_exit(struct queue_ent *qe, char digit)
678 if (!strlen(qe->context))
682 if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
683 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
684 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
685 qe->chan->priority = 0;
693 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
695 struct member * ret = NULL ;
703 while( mem != NULL ) {
704 sprintf( buf, "%s/%s", mem->tech, mem->loc);
706 if( strcmp( buf, interface ) == 0 ) {
719 static struct member * create_queue_node( char * interface )
721 struct member * cur ;
724 /* Add a new member */
726 cur = malloc(sizeof(struct member));
729 memset(cur, 0, sizeof(struct member));
730 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
731 if ((tmp = strchr(cur->tech, '/')))
733 if ((tmp = strchr(interface, '/'))) {
735 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
737 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
744 static int rqm_exec(struct ast_channel *chan, void *data)
749 struct member * node ;
750 struct member * look ;
752 char *interface=NULL;
753 struct ast_call_queue *q;
757 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
761 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
763 /* Parse our arguments XXX Check for failure XXX */
764 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
767 interface = strchr(queuename, '|');
773 interface = chan->name ;
776 if( ( q = queues) != NULL )
778 while( q && ( res != 0 ) && (!found) )
780 ast_pthread_mutex_lock(&q->lock);
781 if( strcmp( q->name, queuename) == 0 )
783 // found queue, try to remove interface
786 if( ( node = interface_exists( q, interface ) ) != NULL )
788 if( ( look = q->members ) == node )
791 q->members = node->next;
795 while( look != NULL )
796 if( look->next == node )
798 look->next = node->next ;
807 ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n",
808 interface, queuename);
812 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
813 "Not there\n", interface, queuename);
816 ast_pthread_mutex_unlock(&q->lock);
822 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
824 LOCAL_USER_REMOVE(u);
830 static int aqm_exec(struct ast_channel *chan, void *data)
836 char *interface=NULL;
837 struct ast_call_queue *q;
842 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
846 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
848 /* Parse our arguments XXX Check for failure XXX */
849 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
852 interface = strchr(queuename, '|');
858 interface = chan->name ;
861 if( ( q = queues) != NULL )
863 while( q && ( res != 0 ) && (!found) )
865 ast_pthread_mutex_lock(&q->lock);
866 if( strcmp( q->name, queuename) == 0 )
868 // found queue, try to enable interface
871 if( interface_exists( q, interface ) == NULL )
874 q->members = create_queue_node( interface ) ;
876 if( q->members != NULL )
877 q->members->next = save ;
881 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
885 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
886 "Already there\n", interface, queuename);
889 ast_pthread_mutex_unlock(&q->lock);
895 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
897 LOCAL_USER_REMOVE(u);
902 static int queue_exec(struct ast_channel *chan, void *data)
908 char *options = NULL;
910 char *announceoverride = NULL;
912 /* Our queue entry */
916 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
922 /* Parse our arguments XXX Check for failure XXX */
923 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
926 options = strchr(queuename, '|');
930 url = strchr(options, '|');
934 announceoverride = strchr(url, '|');
935 if (announceoverride) {
936 *announceoverride = '\0';
942 printf("queue: %s, options: %s, url: %s, announce: %s\n",
943 queuename, options, url, announceoverride);
944 /* Setup our queue entry */
945 memset(&qe, 0, sizeof(qe));
947 qe.start = time(NULL);
948 if (!join_queue(queuename, &qe)) {
949 /* Start music on hold */
950 ast_moh_start(chan, qe.moh);
952 res = wait_our_turn(&qe);
953 /* If they hungup, return immediately */
955 if (option_verbose > 2) {
956 ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
963 if (valid_exit(&qe, res))
968 res = try_calling(&qe, options, announceoverride, url);
971 res = wait_a_bit(&qe);
973 if (option_verbose > 2) {
974 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
979 if (res && valid_exit(&qe, res))
983 /* Don't allow return code > 0 */
984 if (res > 0 && res != AST_PBX_KEEPALIVE) {
990 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
993 LOCAL_USER_REMOVE(u);
997 static void reload_queues(void)
999 struct ast_call_queue *q, *ql, *qn;
1000 struct ast_config *cfg;
1002 struct ast_variable *var;
1003 struct member *prev, *cur;
1005 cfg = ast_load("queues.conf");
1007 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
1010 ast_pthread_mutex_lock(&qlock);
1011 /* Mark all queues as dead for the moment */
1017 /* Chug through config file */
1018 cat = ast_category_browse(cfg, NULL);
1020 if (strcasecmp(cat, "general")) {
1021 /* Look for an existing one */
1024 if (!strcmp(q->name, cat))
1030 q = malloc(sizeof(struct ast_call_queue));
1033 memset(q, 0, sizeof(struct ast_call_queue));
1034 ast_pthread_mutex_init(&q->lock);
1035 strncpy(q->name, cat, sizeof(q->name));
1042 ast_pthread_mutex_lock(&q->lock);
1043 /* Re-initialize the queue */
1050 strcpy(q->announce, "");
1051 strcpy(q->context, "");
1053 var = ast_variable_browse(cfg, cat);
1055 if (!strcasecmp(var->name, "member")) {
1056 /* Add a new member */
1057 cur = malloc(sizeof(struct member));
1059 memset(cur, 0, sizeof(struct member));
1060 strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1061 if ((tmp = strchr(cur->tech, '/')))
1063 if ((tmp = strchr(var->value, '/'))) {
1065 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1067 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1074 } else if (!strcasecmp(var->name, "music")) {
1075 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1076 } else if (!strcasecmp(var->name, "announce")) {
1077 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1078 } else if (!strcasecmp(var->name, "context")) {
1079 strncpy(q->context, var->value, sizeof(q->context) - 1);
1080 } else if (!strcasecmp(var->name, "timeout")) {
1081 q->timeout = atoi(var->value);
1082 } else if (!strcasecmp(var->name, "retry")) {
1083 q->retry = atoi(var->value);
1084 } else if (!strcasecmp(var->name, "maxlen")) {
1085 q->maxlen = atoi(var->value);
1087 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1092 q->retry = DEFAULT_RETRY;
1094 q->timeout = DEFAULT_TIMEOUT;
1098 ast_pthread_mutex_unlock(&q->lock);
1105 cat = ast_category_browse(cfg, cat);
1120 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1125 ast_pthread_mutex_unlock(&qlock);
1128 static int queues_show(int fd, int argc, char **argv)
1130 struct ast_call_queue *q;
1131 struct queue_ent *qe;
1139 return RESULT_SHOWUSAGE;
1142 ast_cli(fd, "No queues.\n");
1143 return RESULT_SUCCESS;
1146 ast_pthread_mutex_lock(&q->lock);
1148 snprintf(max, sizeof(max), "%d", q->maxlen);
1150 strcpy(max, "unlimited");
1151 ast_cli(fd, "%-12.12s has %d calls (max %s)\n", q->name, q->count, max);
1153 ast_cli(fd, " Members: \n");
1154 for (mem = q->members; mem; mem = mem->next)
1155 ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
1157 ast_cli(fd, " No Members\n");
1160 ast_cli(fd, " Callers: \n");
1161 for (qe = q->head; qe; qe = qe->next)
1162 ast_cli(fd, " %d. %s (wait: %d:%02.2d)\n", pos++, qe->chan->name,
1163 (now - qe->start) / 60, (now - qe->start) % 60);
1165 ast_cli(fd, " No Callers\n");
1167 ast_pthread_mutex_unlock(&q->lock);
1170 return RESULT_SUCCESS;
1173 /* JDG: callback to display queues status in manager */
1174 static int manager_queues_show( struct mansession *s, struct message *m )
1176 char *a[] = { "show", "queues" };
1177 return queues_show( s->fd, 2, a );
1181 /* Dump queue status */
1182 static int manager_queues_status( struct mansession *s, struct message *m )
1186 struct ast_call_queue *q;
1187 struct queue_ent *qe;
1188 astman_send_ack(s, "Queue status will follow");
1192 ast_pthread_mutex_lock(&q->lock);
1193 ast_cli(s->fd, "Event: QueueParams\r\n"
1198 q->name, q->maxlen, q->count);
1200 /* Do we care about queue members? */
1201 for (mem = q->members; mem; mem = mem->next)
1202 ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
1205 for (qe = q->head; qe; qe = qe->next)
1206 ast_cli(s->fd, "Event: QueueMember\r\n"
1213 q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), now - qe->start);
1214 ast_pthread_mutex_unlock(&q->lock);
1217 return RESULT_SUCCESS;
1220 static char show_queues_usage[] =
1221 "Usage: show queues\n"
1222 " Provides summary information on call queues.\n";
1224 static struct ast_cli_entry cli_show_queues = {
1225 { "show", "queues", NULL }, queues_show,
1226 "Show status of queues", show_queues_usage, NULL };
1228 int unload_module(void)
1230 STANDARD_HANGUP_LOCALUSERS;
1231 ast_cli_unregister(&cli_show_queues);
1232 ast_manager_unregister( "Queues" );
1233 ast_manager_unregister( "QueueStatus" );
1234 return ast_unregister_application(app);
1237 int load_module(void)
1240 res = ast_register_application(app, queue_exec, synopsis, descrip);
1242 ast_cli_register(&cli_show_queues);
1243 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1244 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1247 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1248 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1261 char *description(void)
1269 STANDARD_USECOUNT(res);
1275 return ASTERISK_GPL_KEY;