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 DEFAULT_RETRY 5
41 #define DEFAULT_TIMEOUT 15
42 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
44 static char *tdesc = "True Call Queueing";
46 static char *app = "Queue";
48 static char *synopsis = "Queue a call for a call queue";
50 static char *descrip =
51 " Queue(queuename[|options[|URL][|announceoverride]]):\n"
52 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
53 " This application returns -1 if the originating channel hangs up, or if the\n"
54 "call is bridged and either of the parties in the bridge terminate the call.\n"
55 "Returns 0 if the queue is full, nonexistant, or has no members.\n"
56 "The option string may contain zero or more of the following characters:\n"
57 " 't' -- allow the called user transfer the calling user\n"
58 " 'T' -- to allow the calling user to transfer the call.\n"
59 " 'd' -- data-quality (modem) call (minimum delay).\n"
60 " 'H' -- allow caller to hang up by hitting *.\n"
61 " In addition to transferring the call, a call may be parked and then picked\n"
62 "up by another user.\n"
63 " The optionnal URL will be sent to the called party if the channel supports\n"
67 static char *app_aqm = "AddQueueMember" ;
68 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
69 static char *app_aqm_descrip =
70 " AddQueueMember(queuename[|interface]):\n"
71 "Dynamically adds interface to an existing queue\n"
72 "Returns -1 if there is an error.\n"
73 "Example: AddQueueMember(techsupport|SIP/3000)\n"
76 static char *app_rqm = "RemoveQueueMember" ;
77 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
78 static char *app_rqm_descrip =
79 " RemoveQueueMember(queuename[|interface]):\n"
80 "Dynamically removes interface to an existing queue\n"
81 "Returns -1 if there is an error.\n"
82 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
88 /* We define a customer "local user" structure because we
89 use it not only for keeping track of what is in use but
90 also for keeping track of who we're dialing. */
93 struct ast_channel *chan;
96 int allowredirect_out;
101 struct localuser *next;
107 struct ast_call_queue *parent; /* What queue is our parent */
108 char moh[80]; /* Name of musiconhold to be used */
109 char announce[80]; /* Announcement to play */
110 char context[80]; /* Context when user exits queue */
111 int pos; /* Where we are in the queue */
112 time_t start; /* When we started holding */
113 struct ast_channel *chan; /* Our channel */
114 struct queue_ent *next; /* The next queue entry */
118 char tech[80]; /* Technology */
119 char loc[256]; /* Location */
120 struct member *next; /* Next member */
123 struct ast_call_queue {
124 pthread_mutex_t lock;
125 char name[80]; /* Name of the queue */
126 char moh[80]; /* Name of musiconhold to be used */
127 char announce[80]; /* Announcement to play */
128 char context[80]; /* Announcement to play */
129 int announcetimeout; /* How often to announce their position */
130 int count; /* How many entries are in the queue */
131 int maxlen; /* Max number of entries in queue */
133 int dead; /* Whether this queue is dead or not */
134 int retry; /* Retry calling everyone after this amount of time */
135 int timeout; /* How long to wait for an answer */
137 struct member *members; /* Member channels to be tried */
138 struct queue_ent *head; /* Start of the actual queue */
139 struct ast_call_queue *next; /* Next call queue */
142 static struct ast_call_queue *queues = NULL;
143 static pthread_mutex_t qlock = AST_MUTEX_INITIALIZER;
146 static int join_queue(char *queuename, struct queue_ent *qe)
148 struct ast_call_queue *q;
149 struct queue_ent *cur, *prev = NULL;
152 ast_pthread_mutex_lock(&qlock);
155 if (!strcasecmp(q->name, queuename)) {
156 /* This is our one */
157 ast_pthread_mutex_lock(&q->lock);
158 if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
159 /* There's space for us, put us at the end */
171 /* Fix additional pointers and
176 strncpy(qe->moh, q->moh, sizeof(qe->moh));
177 strncpy(qe->announce, q->announce, sizeof(qe->announce));
178 strncpy(qe->context, q->context, sizeof(qe->context));
181 manager_event(EVENT_FLAG_CALL, "Join",
182 "Channel: %s\r\nCallerID:%s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
183 qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), q->name, qe->pos, q->count );
185 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
188 ast_pthread_mutex_unlock(&q->lock);
193 ast_pthread_mutex_unlock(&qlock);
197 static void free_members(struct ast_call_queue *q)
199 struct member *curm, *next;
209 static void destroy_queue(struct ast_call_queue *q)
211 struct ast_call_queue *cur, *prev = NULL;
212 ast_pthread_mutex_lock(&qlock);
217 prev->next = cur->next;
225 ast_pthread_mutex_unlock(&qlock);
230 static void leave_queue(struct queue_ent *qe)
232 struct ast_call_queue *q;
233 struct queue_ent *cur, *prev = NULL;
238 ast_pthread_mutex_lock(&q->lock);
246 /* Take us out of the queue */
247 manager_event(EVENT_FLAG_CALL, "Leave",
248 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
249 qe->chan->name, q->name, q->count);
251 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
253 /* Take us out of the queue */
255 prev->next = cur->next;
264 ast_pthread_mutex_unlock(&q->lock);
265 if (q->dead && !q->count) {
266 /* It's dead and nobody is in it, so kill it */
271 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
273 /* Hang up a tree of stuff */
274 struct localuser *oo;
276 /* Hangup any existing lines we have open */
277 if (outgoing->chan != exception)
278 ast_hangup(outgoing->chan);
280 outgoing=outgoing->next;
287 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)
296 struct ast_channel *peer = NULL;
297 struct ast_channel *watchers[MAX];
299 struct ast_channel *winner;
301 while(*to && !peer) {
308 /* Keep track of important channels */
310 watchers[pos++] = o->chan;
317 if (numlines == numbusies) {
318 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
320 ast_log(LOG_NOTICE, "No one is answered queue %s\n", queue);
325 winner = ast_waitfor_n(watchers, pos, to);
328 if (o->stillgoing && (o->chan->_state == AST_STATE_UP)) {
330 if (option_verbose > 2)
331 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
333 *allowredir_in = o->allowredirect_in;
334 *allowredir_out = o->allowredirect_out;
335 *allowdisconnect = o->allowdisconnect;
337 } else if (o->chan == winner) {
338 f = ast_read(winner);
340 if (f->frametype == AST_FRAME_CONTROL) {
341 switch(f->subclass) {
342 case AST_CONTROL_ANSWER:
343 /* This is our guy if someone answered. */
345 if (option_verbose > 2)
346 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
348 *allowredir_in = o->allowredirect_in;
349 *allowredir_out = o->allowredirect_out;
350 *allowdisconnect = o->allowdisconnect;
353 case AST_CONTROL_BUSY:
354 if (option_verbose > 2)
355 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
358 ast_cdr_busy(in->cdr);
361 case AST_CONTROL_CONGESTION:
362 if (option_verbose > 2)
363 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
366 ast_cdr_busy(in->cdr);
369 case AST_CONTROL_RINGING:
370 if (option_verbose > 2)
371 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
374 ast_indicate(in, AST_CONTROL_RINGING);
379 case AST_CONTROL_OFFHOOK:
380 /* Ignore going off hook */
383 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
396 if (f && (f->frametype != AST_FRAME_VOICE))
397 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
398 else if (!f || (f->frametype != AST_FRAME_VOICE))
399 printf("Hangup received on %s\n", in->name);
401 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
406 if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect &&
407 (f->subclass == '*')) {
408 if (option_verbose > 3)
409 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
414 if (!*to && (option_verbose > 2))
415 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
422 static int wait_our_turn(struct queue_ent *qe)
424 struct queue_ent *ch;
427 /* Atomically read the parent head */
428 pthread_mutex_lock(&qe->parent->lock);
429 ch = qe->parent->head;
430 pthread_mutex_unlock(&qe->parent->lock);
431 /* If we are now at the top of the head, break out */
432 if (qe->parent->head == qe)
434 /* Wait a second before checking again */
435 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
442 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url)
445 struct localuser *outgoing=NULL, *tmp = NULL;
448 int allowredir_out=0;
449 int allowdisconnect=0;
450 char numsubst[AST_MAX_EXTENSION];
451 char restofit[AST_MAX_EXTENSION];
453 struct ast_channel *peer;
454 int res = 0, bridge = 0;
456 char *announce = NULL;
457 /* Hold the lock while we setup the outgoing calls */
458 ast_pthread_mutex_lock(&qe->parent->lock);
459 cur = qe->parent->members;
460 if (strlen(qe->announce))
461 announce = qe->announce;
462 if (announceoverride && strlen(announceoverride))
463 announce = announceoverride;
465 /* Get a technology/[device:]number pair */
466 tmp = malloc(sizeof(struct localuser));
468 ast_log(LOG_WARNING, "Out of memory\n");
471 memset(tmp, 0, sizeof(struct localuser));
473 if (strchr(options, 't'))
474 tmp->allowredirect_in = 1;
475 if (strchr(options, 'T'))
476 tmp->allowredirect_out = 1;
477 if (strchr(options, 'r'))
478 tmp->ringbackonly = 1;
479 if (strchr(options, 'm'))
480 tmp->musiconhold = 1;
481 if (strchr(options, 'd'))
482 tmp->dataquality = 1;
483 if (strchr(options, 'H'))
484 tmp->allowdisconnect = 1;
487 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
489 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
491 strncpy(numsubst, cur->loc, sizeof(numsubst)-1);
492 /* If we're dialing by extension, look at the extension to know what to dial */
493 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
494 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
495 snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", qe->chan->exten,restofit);
497 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
499 /* Request the peer */
500 tmp->chan = ast_request(cur->tech, qe->chan->nativeformats, numsubst);
502 /* If we can't, just go on to the next call */
504 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
507 ast_cdr_busy(qe->chan->cdr);
513 /* Don't honor call forwarding on a queue! */
514 if (strlen(tmp->chan->call_forward)) {
515 if (option_verbose > 2)
516 ast_verbose(VERBOSE_PREFIX_3 "Forwarding call to '%s@%s'\n", tmp->chan->call_forward, tmp->chan->context);
517 /* Setup parameters */
518 strncpy(chan->exten, tmp->chan->call_forward, sizeof(chan->exten));
519 strncpy(chan->context, tmp->chan->context, sizeof(chan->context));
522 ast_hangup(tmp->chan);
528 tmp->chan->appl = "AppQueue";
529 tmp->chan->data = "(Outgoing Line)";
530 tmp->chan->whentohangup = 0;
531 if (tmp->chan->callerid)
532 free(tmp->chan->callerid);
534 free(tmp->chan->ani);
535 if (qe->chan->callerid)
536 tmp->chan->callerid = strdup(qe->chan->callerid);
538 tmp->chan->callerid = NULL;
540 tmp->chan->ani = strdup(qe->chan->ani);
542 tmp->chan->ani = NULL;
543 /* Presense of ADSI CPE on outgoing channel follows ours */
544 tmp->chan->adsicpe = qe->chan->adsicpe;
545 /* Place the call, but don't wait on the answer */
546 res = ast_call(tmp->chan, numsubst, 0);
548 /* Again, keep going even if there's an error */
550 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
551 else if (option_verbose > 2)
552 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
553 ast_hangup(tmp->chan);
558 if (option_verbose > 2)
559 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
560 /* Put them in the list of outgoing thingies... We're ready now.
561 XXX If we're forcibly removed, these outgoing calls won't get
563 tmp->stillgoing = -1;
564 tmp->next = outgoing;
566 /* If this line is up, don't try anybody else */
567 if (outgoing->chan->_state == AST_STATE_UP)
572 if (qe->parent->timeout)
573 to = qe->parent->timeout * 1000;
576 ast_pthread_mutex_unlock(&qe->parent->lock);
578 peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, qe->parent->name);
581 /* Musta gotten hung up */
584 /* Nobody answered, next please? */
590 /* Ah ha! Someone answered within the desired timeframe. Of course after this
591 we will always return with -1 so that it is hung up properly after the
593 if (!strcmp(qe->chan->type,"Zap")) {
594 if (tmp->dataquality) zapx = 0;
595 ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
597 if (!strcmp(peer->type,"Zap")) {
598 if (tmp->dataquality) zapx = 0;
599 ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&zapx,sizeof(char),0);
601 hanguptree(outgoing, peer);
602 /* Stop music on hold */
603 ast_moh_stop(qe->chan);
607 res2 = ast_streamfile(peer, announce, peer->language);
608 /* XXX Need a function to wait on *both* streams XXX */
610 res2 = ast_waitstream(peer, "");
614 /* Agent must have hung up */
615 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
620 /* If appropriate, log that we have a destination channel */
622 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
623 /* Make sure channels are compatible */
624 res = ast_channel_make_compatible(qe->chan, peer);
626 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
630 /* Drop out of the queue at this point, to prepare for next caller */
633 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
634 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
635 ast_channel_sendurl( peer, url );
637 bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
639 if(bridge != AST_PBX_NO_HANGUP_PEER)
642 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
643 else res = bridge; /* bridge error, stay in the queue */
646 hanguptree(outgoing, NULL);
650 static int wait_a_bit(struct queue_ent *qe)
653 /* Hold the lock while we setup the outgoing calls */
654 ast_pthread_mutex_lock(&qe->parent->lock);
655 retrywait = qe->parent->retry * 1000;
656 ast_pthread_mutex_unlock(&qe->parent->lock);
657 return ast_waitfordigit(qe->chan, retrywait);
660 static int valid_exit(struct queue_ent *qe, char digit)
663 if (!strlen(qe->context))
667 if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
668 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
669 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
670 qe->chan->priority = 0;
678 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
680 struct member * ret = NULL ;
688 while( mem != NULL ) {
689 sprintf( buf, "%s/%s", mem->tech, mem->loc);
691 if( strcmp( buf, interface ) == 0 ) {
704 static struct member * create_queue_node( char * interface )
706 struct member * cur ;
709 /* Add a new member */
711 cur = malloc(sizeof(struct member));
714 memset(cur, 0, sizeof(struct member));
715 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
716 if ((tmp = strchr(cur->tech, '/')))
718 if ((tmp = strchr(interface, '/'))) {
720 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
722 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
729 static int rqm_exec(struct ast_channel *chan, void *data)
734 struct member * node ;
735 struct member * look ;
737 char *interface=NULL;
738 struct ast_call_queue *q;
742 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
746 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
748 /* Parse our arguments XXX Check for failure XXX */
749 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
752 interface = strchr(queuename, '|');
758 interface = chan->name ;
761 if( ( q = queues) != NULL )
763 while( q && ( res != 0 ) && (!found) )
765 ast_pthread_mutex_lock(&q->lock);
766 if( strcmp( q->name, queuename) == 0 )
768 // found queue, try to remove interface
771 if( ( node = interface_exists( q, interface ) ) != NULL )
773 if( ( look = q->members ) == node )
776 q->members = node->next;
780 while( look != NULL )
781 if( look->next == node )
783 look->next = node->next ;
792 ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n",
793 interface, queuename);
797 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
798 "Not there\n", interface, queuename);
801 ast_pthread_mutex_unlock(&q->lock);
807 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
809 LOCAL_USER_REMOVE(u);
815 static int aqm_exec(struct ast_channel *chan, void *data)
821 char *interface=NULL;
822 struct ast_call_queue *q;
827 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
831 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
833 /* Parse our arguments XXX Check for failure XXX */
834 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
837 interface = strchr(queuename, '|');
843 interface = chan->name ;
846 if( ( q = queues) != NULL )
848 while( q && ( res != 0 ) && (!found) )
850 ast_pthread_mutex_lock(&q->lock);
851 if( strcmp( q->name, queuename) == 0 )
853 // found queue, try to enable interface
856 if( interface_exists( q, interface ) == NULL )
859 q->members = create_queue_node( interface ) ;
861 if( q->members != NULL )
862 q->members->next = save ;
866 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
870 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
871 "Already there\n", interface, queuename);
874 ast_pthread_mutex_unlock(&q->lock);
880 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
882 LOCAL_USER_REMOVE(u);
887 static int queue_exec(struct ast_channel *chan, void *data)
893 char *options = NULL;
895 char *announceoverride = NULL;
897 /* Our queue entry */
901 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
907 /* Parse our arguments XXX Check for failure XXX */
908 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
911 options = strchr(queuename, '|');
915 url = strchr(options, '|');
919 announceoverride = strchr(url, '|');
920 if (announceoverride) {
921 *announceoverride = '\0';
927 printf("queue: %s, options: %s, url: %s, announce: %s\n",
928 queuename, options, url, announceoverride);
929 /* Setup our queue entry */
930 memset(&qe, 0, sizeof(qe));
932 qe.start = time(NULL);
933 if (!join_queue(queuename, &qe)) {
934 /* Start music on hold */
935 ast_moh_start(chan, qe.moh);
937 res = wait_our_turn(&qe);
938 /* If they hungup, return immediately */
940 if (option_verbose > 2) {
941 ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
948 if (valid_exit(&qe, res))
953 res = try_calling(&qe, options, announceoverride, url);
956 res = wait_a_bit(&qe);
958 if (option_verbose > 2) {
959 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
964 if (res && valid_exit(&qe, res))
968 /* Don't allow return code > 0 */
969 if (res > 0 && res != AST_PBX_KEEPALIVE) {
975 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
978 LOCAL_USER_REMOVE(u);
982 static void reload_queues(void)
984 struct ast_call_queue *q, *ql, *qn;
985 struct ast_config *cfg;
987 struct ast_variable *var;
988 struct member *prev, *cur;
990 cfg = ast_load("queues.conf");
992 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
995 ast_pthread_mutex_lock(&qlock);
996 /* Mark all queues as dead for the moment */
1002 /* Chug through config file */
1003 cat = ast_category_browse(cfg, NULL);
1005 if (strcasecmp(cat, "general")) {
1006 /* Look for an existing one */
1009 if (!strcmp(q->name, cat))
1015 q = malloc(sizeof(struct ast_call_queue));
1018 memset(q, 0, sizeof(struct ast_call_queue));
1019 ast_pthread_mutex_init(&q->lock);
1020 strncpy(q->name, cat, sizeof(q->name));
1027 ast_pthread_mutex_lock(&q->lock);
1028 /* Re-initialize the queue */
1035 strcpy(q->announce, "");
1036 strcpy(q->context, "");
1038 var = ast_variable_browse(cfg, cat);
1040 if (!strcasecmp(var->name, "member")) {
1041 /* Add a new member */
1042 cur = malloc(sizeof(struct member));
1044 memset(cur, 0, sizeof(struct member));
1045 strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1046 if ((tmp = strchr(cur->tech, '/')))
1048 if ((tmp = strchr(var->value, '/'))) {
1050 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1052 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1059 } else if (!strcasecmp(var->name, "music")) {
1060 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1061 } else if (!strcasecmp(var->name, "announce")) {
1062 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1063 } else if (!strcasecmp(var->name, "context")) {
1064 strncpy(q->context, var->value, sizeof(q->context) - 1);
1065 } else if (!strcasecmp(var->name, "timeout")) {
1066 q->timeout = atoi(var->value);
1067 } else if (!strcasecmp(var->name, "retry")) {
1068 q->retry = atoi(var->value);
1069 } else if (!strcasecmp(var->name, "maxlen")) {
1070 q->maxlen = atoi(var->value);
1072 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1077 q->retry = DEFAULT_RETRY;
1079 q->timeout = DEFAULT_TIMEOUT;
1083 ast_pthread_mutex_unlock(&q->lock);
1090 cat = ast_category_browse(cfg, cat);
1105 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1110 ast_pthread_mutex_unlock(&qlock);
1113 static int queues_show(int fd, int argc, char **argv)
1115 struct ast_call_queue *q;
1116 struct queue_ent *qe;
1124 return RESULT_SHOWUSAGE;
1127 ast_cli(fd, "No queues.\n");
1128 return RESULT_SUCCESS;
1131 ast_pthread_mutex_lock(&q->lock);
1133 snprintf(max, sizeof(max), "%d", q->maxlen);
1135 strcpy(max, "unlimited");
1136 ast_cli(fd, "%-12.12s has %d calls (max %s)\n", q->name, q->count, max);
1138 ast_cli(fd, " Members: \n");
1139 for (mem = q->members; mem; mem = mem->next)
1140 ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
1142 ast_cli(fd, " No Members\n");
1145 ast_cli(fd, " Callers: \n");
1146 for (qe = q->head; qe; qe = qe->next)
1147 ast_cli(fd, " %d. %s (wait: %d:%02.2d)\n", pos++, qe->chan->name,
1148 (now - qe->start) / 60, (now - qe->start) % 60);
1150 ast_cli(fd, " No Callers\n");
1152 ast_pthread_mutex_unlock(&q->lock);
1155 return RESULT_SUCCESS;
1158 /* JDG: callback to display queues status in manager */
1159 static int manager_queues_show( struct mansession *s, struct message *m )
1161 char *a[] = { "show", "queues" };
1162 return queues_show( s->fd, 2, a );
1166 /* Dump queue status */
1167 static int manager_queues_status( struct mansession *s, struct message *m )
1171 struct ast_call_queue *q;
1172 struct queue_ent *qe;
1173 astman_send_ack(s, "Queue status will follow");
1177 ast_pthread_mutex_lock(&q->lock);
1178 ast_cli(s->fd, "Event: QueueParams\r\n"
1183 q->name, q->maxlen, q->count);
1185 /* Do we care about queue members? */
1186 for (mem = q->members; mem; mem = mem->next)
1187 ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
1190 for (qe = q->head; qe; qe = qe->next)
1191 ast_cli(s->fd, "Event: QueueMember\r\n"
1198 q->name, pos++, qe->chan->name, (qe->chan->callerid ? qe->chan->callerid : ""), now - qe->start);
1199 ast_pthread_mutex_unlock(&q->lock);
1202 return RESULT_SUCCESS;
1205 static char show_queues_usage[] =
1206 "Usage: show queues\n"
1207 " Provides summary information on call queues.\n";
1209 static struct ast_cli_entry cli_show_queues = {
1210 { "show", "queues", NULL }, queues_show,
1211 "Show status of queues", show_queues_usage, NULL };
1213 int unload_module(void)
1215 STANDARD_HANGUP_LOCALUSERS;
1216 ast_cli_unregister(&cli_show_queues);
1217 ast_manager_unregister( "Queues" );
1218 ast_manager_unregister( "QueueStatus" );
1219 return ast_unregister_application(app);
1222 int load_module(void)
1225 res = ast_register_application(app, queue_exec, synopsis, descrip);
1227 ast_cli_register(&cli_show_queues);
1228 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1229 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1232 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1233 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1246 char *description(void)
1254 STANDARD_USECOUNT(res);
1260 return ASTERISK_GPL_KEY;