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;
100 struct localuser *next;
106 struct ast_call_queue *parent; /* What queue is our parent */
107 char moh[80]; /* Name of musiconhold to be used */
108 char announce[80]; /* Announcement to play */
109 char context[80]; /* Context when user exits queue */
110 int pos; /* Where we are in the queue */
111 time_t start; /* When we started holding */
112 struct ast_channel *chan; /* Our channel */
113 struct queue_ent *next; /* The next queue entry */
117 char tech[80]; /* Technology */
118 char loc[256]; /* Location */
119 struct member *next; /* Next member */
122 struct ast_call_queue {
123 pthread_mutex_t lock;
124 char name[80]; /* Name of the queue */
125 char moh[80]; /* Name of musiconhold to be used */
126 char announce[80]; /* Announcement to play */
127 char context[80]; /* Announcement to play */
128 int announcetimeout; /* How often to announce their position */
129 int count; /* How many entries are in the queue */
130 int maxlen; /* Max number of entries in queue */
132 int dead; /* Whether this queue is dead or not */
133 int retry; /* Retry calling everyone after this amount of time */
134 int timeout; /* How long to wait for an answer */
136 struct member *members; /* Member channels to be tried */
137 struct queue_ent *head; /* Start of the actual queue */
138 struct ast_call_queue *next; /* Next call queue */
141 static struct ast_call_queue *queues = NULL;
142 static pthread_mutex_t qlock = AST_MUTEX_INITIALIZER;
145 static int join_queue(char *queuename, struct queue_ent *qe)
147 struct ast_call_queue *q;
148 struct queue_ent *cur, *prev = NULL;
151 ast_pthread_mutex_lock(&qlock);
154 if (!strcasecmp(q->name, queuename)) {
155 /* This is our one */
156 ast_pthread_mutex_lock(&q->lock);
157 if (q->members && (!q->maxlen || (q->count < q->maxlen))) {
158 /* There's space for us, put us at the end */
170 /* Fix additional pointers and
175 strncpy(qe->moh, q->moh, sizeof(qe->moh));
176 strncpy(qe->announce, q->announce, sizeof(qe->announce));
177 strncpy(qe->context, q->context, sizeof(qe->context));
180 manager_event(EVENT_FLAG_CALL, "Join",
181 "Channel: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n",
182 qe->chan->name, q->name, qe->pos, q->count );
184 ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
187 ast_pthread_mutex_unlock(&q->lock);
192 ast_pthread_mutex_unlock(&qlock);
196 static void free_members(struct ast_call_queue *q)
198 struct member *curm, *next;
208 static void destroy_queue(struct ast_call_queue *q)
210 struct ast_call_queue *cur, *prev = NULL;
211 ast_pthread_mutex_lock(&qlock);
216 prev->next = cur->next;
224 ast_pthread_mutex_unlock(&qlock);
229 static void leave_queue(struct queue_ent *qe)
231 struct ast_call_queue *q;
232 struct queue_ent *cur, *prev = NULL;
237 ast_pthread_mutex_lock(&q->lock);
245 /* Take us out of the queue */
246 manager_event(EVENT_FLAG_CALL, "Leave",
247 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n",
248 qe->chan->name, q->name, q->count);
250 ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
252 /* Take us out of the queue */
254 prev->next = cur->next;
263 ast_pthread_mutex_unlock(&q->lock);
264 if (q->dead && !q->count) {
265 /* It's dead and nobody is in it, so kill it */
270 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
272 /* Hang up a tree of stuff */
273 struct localuser *oo;
275 /* Hangup any existing lines we have open */
276 if (outgoing->chan != exception)
277 ast_hangup(outgoing->chan);
279 outgoing=outgoing->next;
286 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir, int *allowdisconnect, char *queue)
295 struct ast_channel *peer = NULL;
296 struct ast_channel *watchers[MAX];
298 struct ast_channel *winner;
300 while(*to && !peer) {
307 /* Keep track of important channels */
309 watchers[pos++] = o->chan;
316 if (numlines == numbusies) {
317 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
319 ast_log(LOG_NOTICE, "No one is answered queue %s\n", queue);
324 winner = ast_waitfor_n(watchers, pos, to);
327 if (o->stillgoing && (o->chan->_state == AST_STATE_UP)) {
329 if (option_verbose > 2)
330 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
332 *allowredir = o->allowredirect;
333 *allowdisconnect = o->allowdisconnect;
335 } else if (o->chan == winner) {
336 f = ast_read(winner);
338 if (f->frametype == AST_FRAME_CONTROL) {
339 switch(f->subclass) {
340 case AST_CONTROL_ANSWER:
341 /* This is our guy if someone answered. */
343 if (option_verbose > 2)
344 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
346 *allowredir = o->allowredirect;
347 *allowdisconnect = o->allowdisconnect;
350 case AST_CONTROL_BUSY:
351 if (option_verbose > 2)
352 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
355 ast_cdr_busy(in->cdr);
358 case AST_CONTROL_CONGESTION:
359 if (option_verbose > 2)
360 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
363 ast_cdr_busy(in->cdr);
366 case AST_CONTROL_RINGING:
367 if (option_verbose > 2)
368 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
371 ast_indicate(in, AST_CONTROL_RINGING);
376 case AST_CONTROL_OFFHOOK:
377 /* Ignore going off hook */
380 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
393 if (f && (f->frametype != AST_FRAME_VOICE))
394 printf("Frame type: %d, %d\n", f->frametype, f->subclass);
395 else if (!f || (f->frametype != AST_FRAME_VOICE))
396 printf("Hangup received on %s\n", in->name);
398 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
403 if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect &&
404 (f->subclass == '*')) {
405 if (option_verbose > 3)
406 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
411 if (!*to && (option_verbose > 2))
412 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
419 static int wait_our_turn(struct queue_ent *qe)
421 struct queue_ent *ch;
424 /* Atomically read the parent head */
425 pthread_mutex_lock(&qe->parent->lock);
426 ch = qe->parent->head;
427 pthread_mutex_unlock(&qe->parent->lock);
428 /* If we are now at the top of the head, break out */
429 if (qe->parent->head == qe)
431 /* Wait a second before checking again */
432 res = ast_waitfordigit(qe->chan, RECHECK * 1000);
439 static int try_calling(struct queue_ent *qe, char *options, char *announceoverride, char *url)
442 struct localuser *outgoing=NULL, *tmp = NULL;
445 int allowdisconnect=0;
446 char numsubst[AST_MAX_EXTENSION];
447 char restofit[AST_MAX_EXTENSION];
449 struct ast_channel *peer;
450 int res = 0, bridge = 0;
451 char *announce = NULL;
452 /* Hold the lock while we setup the outgoing calls */
453 ast_pthread_mutex_lock(&qe->parent->lock);
454 cur = qe->parent->members;
455 if (strlen(qe->announce))
456 announce = qe->announce;
457 if (announceoverride && strlen(announceoverride))
458 announce = announceoverride;
460 /* Get a technology/[device:]number pair */
461 tmp = malloc(sizeof(struct localuser));
463 ast_log(LOG_WARNING, "Out of memory\n");
466 memset(tmp, 0, sizeof(struct localuser));
468 if (strchr(options, 't'))
469 tmp->allowredirect = 1;
470 if (strchr(options, 'r'))
471 tmp->ringbackonly = 1;
472 if (strchr(options, 'm'))
473 tmp->musiconhold = 1;
474 if (strchr(options, 'd'))
475 tmp->dataquality = 1;
476 if (strchr(options, 'H'))
477 tmp->allowdisconnect = 1;
480 ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url);
482 ast_log(LOG_DEBUG, "Simple queue (no URL)\n");
484 strncpy(numsubst, cur->loc, sizeof(numsubst)-1);
485 /* If we're dialing by extension, look at the extension to know what to dial */
486 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
487 strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
488 snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", qe->chan->exten,restofit);
490 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
492 /* Request the peer */
493 tmp->chan = ast_request(cur->tech, qe->chan->nativeformats, numsubst);
495 /* If we can't, just go on to the next call */
497 ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
500 ast_cdr_busy(qe->chan->cdr);
506 /* Don't honor call forwarding on a queue! */
507 if (strlen(tmp->chan->call_forward)) {
508 if (option_verbose > 2)
509 ast_verbose(VERBOSE_PREFIX_3 "Forwarding call to '%s@%s'\n", tmp->chan->call_forward, tmp->chan->context);
510 /* Setup parameters */
511 strncpy(chan->exten, tmp->chan->call_forward, sizeof(chan->exten));
512 strncpy(chan->context, tmp->chan->context, sizeof(chan->context));
515 ast_hangup(tmp->chan);
521 tmp->chan->appl = "AppQueue";
522 tmp->chan->data = "(Outgoing Line)";
523 tmp->chan->whentohangup = 0;
524 if (tmp->chan->callerid)
525 free(tmp->chan->callerid);
527 free(tmp->chan->ani);
528 if (qe->chan->callerid)
529 tmp->chan->callerid = strdup(qe->chan->callerid);
531 tmp->chan->callerid = NULL;
533 tmp->chan->ani = strdup(qe->chan->ani);
535 tmp->chan->ani = NULL;
536 /* Presense of ADSI CPE on outgoing channel follows ours */
537 tmp->chan->adsicpe = qe->chan->adsicpe;
538 /* Place the call, but don't wait on the answer */
539 res = ast_call(tmp->chan, numsubst, 0);
541 /* Again, keep going even if there's an error */
543 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
544 else if (option_verbose > 2)
545 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
546 ast_hangup(tmp->chan);
551 if (option_verbose > 2)
552 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
553 /* Put them in the list of outgoing thingies... We're ready now.
554 XXX If we're forcibly removed, these outgoing calls won't get
556 tmp->stillgoing = -1;
557 tmp->next = outgoing;
559 /* If this line is up, don't try anybody else */
560 if (outgoing->chan->_state == AST_STATE_UP)
565 if (qe->parent->timeout)
566 to = qe->parent->timeout * 1000;
569 ast_pthread_mutex_unlock(&qe->parent->lock);
571 peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir, &allowdisconnect, qe->parent->name);
574 /* Musta gotten hung up */
577 /* Nobody answered, next please? */
583 /* Ah ha! Someone answered within the desired timeframe. Of course after this
584 we will always return with -1 so that it is hung up properly after the
586 hanguptree(outgoing, peer);
587 /* Stop music on hold */
588 ast_moh_stop(qe->chan);
592 res2 = ast_streamfile(peer, announce, peer->language);
593 /* XXX Need a function to wait on *both* streams XXX */
595 res2 = ast_waitstream(peer, "");
599 /* Agent must have hung up */
600 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
605 /* If appropriate, log that we have a destination channel */
607 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
608 /* Make sure channels are compatible */
609 res = ast_channel_make_compatible(qe->chan, peer);
611 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
615 if (!strcmp(qe->chan->type,"Zap")) {
617 if (tmp->dataquality) x = 0;
618 ast_channel_setoption(qe->chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
620 if (!strcmp(peer->type,"Zap")) {
622 if (tmp->dataquality) x = 0;
623 ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
625 /* Drop out of the queue at this point, to prepare for next caller */
628 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
629 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
630 ast_channel_sendurl( peer, url );
632 bridge = ast_bridge_call(qe->chan, peer, allowredir, allowdisconnect);
634 if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
635 else res = bridge; /* bridge error, stay in the queue */
638 hanguptree(outgoing, NULL);
642 static int wait_a_bit(struct queue_ent *qe)
645 /* Hold the lock while we setup the outgoing calls */
646 ast_pthread_mutex_lock(&qe->parent->lock);
647 retrywait = qe->parent->retry * 1000;
648 ast_pthread_mutex_unlock(&qe->parent->lock);
649 return ast_waitfordigit(qe->chan, retrywait);
652 static int valid_exit(struct queue_ent *qe, char digit)
655 if (!strlen(qe->context))
659 if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->callerid)) {
660 strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1);
661 strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1);
662 qe->chan->priority = 0;
670 static struct member * interface_exists( struct ast_call_queue * q, char * interface )
672 struct member * ret = NULL ;
680 while( mem != NULL ) {
681 sprintf( buf, "%s/%s", mem->tech, mem->loc);
683 if( strcmp( buf, interface ) == 0 ) {
696 static struct member * create_queue_node( char * interface )
698 struct member * cur ;
701 /* Add a new member */
703 cur = malloc(sizeof(struct member));
706 memset(cur, 0, sizeof(struct member));
707 strncpy(cur->tech, interface, sizeof(cur->tech) - 1);
708 if ((tmp = strchr(cur->tech, '/')))
710 if ((tmp = strchr(interface, '/'))) {
712 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
714 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
721 static int rqm_exec(struct ast_channel *chan, void *data)
726 struct member * node ;
727 struct member * look ;
729 char *interface=NULL;
730 struct ast_call_queue *q;
734 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename|optional interface)\n");
738 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
740 /* Parse our arguments XXX Check for failure XXX */
741 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
744 interface = strchr(queuename, '|');
750 interface = chan->name ;
753 if( ( q = queues) != NULL )
755 while( q && ( res != 0 ) && (!found) )
757 ast_pthread_mutex_lock(&q->lock);
758 if( strcmp( q->name, queuename) == 0 )
760 // found queue, try to remove interface
763 if( ( node = interface_exists( q, interface ) ) != NULL )
765 if( ( look = q->members ) == node )
768 q->members = node->next;
772 while( look != NULL )
773 if( look->next == node )
775 look->next = node->next ;
784 ast_log(LOG_NOTICE, "Removed interface '%s' to queue '%s'\n",
785 interface, queuename);
789 ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': "
790 "Not there\n", interface, queuename);
793 ast_pthread_mutex_unlock(&q->lock);
799 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename);
801 LOCAL_USER_REMOVE(u);
807 static int aqm_exec(struct ast_channel *chan, void *data)
813 char *interface=NULL;
814 struct ast_call_queue *q;
819 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename|optional interface)\n");
823 LOCAL_USER_ADD(u); // not sure if we need this, but better be safe than sorry ;-)
825 /* Parse our arguments XXX Check for failure XXX */
826 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
829 interface = strchr(queuename, '|');
835 interface = chan->name ;
838 if( ( q = queues) != NULL )
840 while( q && ( res != 0 ) && (!found) )
842 ast_pthread_mutex_lock(&q->lock);
843 if( strcmp( q->name, queuename) == 0 )
845 // found queue, try to enable interface
848 if( interface_exists( q, interface ) == NULL )
851 q->members = create_queue_node( interface ) ;
853 if( q->members != NULL )
854 q->members->next = save ;
858 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename);
862 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': "
863 "Already there\n", interface, queuename);
866 ast_pthread_mutex_unlock(&q->lock);
872 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename);
874 LOCAL_USER_REMOVE(u);
879 static int queue_exec(struct ast_channel *chan, void *data)
885 char *options = NULL;
887 char *announceoverride = NULL;
889 /* Our queue entry */
893 ast_log(LOG_WARNING, "Queue requires an argument (queuename|optional timeout|optional URL)\n");
899 /* Parse our arguments XXX Check for failure XXX */
900 strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
903 options = strchr(queuename, '|');
907 url = strchr(options, '|');
911 announceoverride = strchr(url, '|');
912 if (announceoverride) {
913 *announceoverride = '\0';
919 printf("queue: %s, options: %s, url: %s, announce: %s\n",
920 queuename, options, url, announceoverride);
921 /* Setup our queue entry */
922 memset(&qe, 0, sizeof(qe));
924 qe.start = time(NULL);
925 if (!join_queue(queuename, &qe)) {
926 /* Start music on hold */
927 ast_moh_start(chan, qe.moh);
929 res = wait_our_turn(&qe);
930 /* If they hungup, return immediately */
932 if (option_verbose > 2) {
933 ast_verbose(VERBOSE_PREFIX_3 "User disconnected while waiting their turn\n");
940 if (valid_exit(&qe, res))
945 res = try_calling(&qe, options, announceoverride, url);
948 res = wait_a_bit(&qe);
950 if (option_verbose > 2) {
951 ast_verbose(VERBOSE_PREFIX_3 "User disconnected when they almost made it\n");
956 if (res && valid_exit(&qe, res))
960 /* Don't allow return code > 0 */
961 if (res > 0 && res != AST_PBX_KEEPALIVE) {
967 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename);
970 LOCAL_USER_REMOVE(u);
974 static void reload_queues(void)
976 struct ast_call_queue *q, *ql, *qn;
977 struct ast_config *cfg;
979 struct ast_variable *var;
980 struct member *prev, *cur;
982 cfg = ast_load("queues.conf");
984 ast_log(LOG_NOTICE, "No call queueing config file, so no call queues\n");
987 ast_pthread_mutex_lock(&qlock);
988 /* Mark all queues as dead for the moment */
994 /* Chug through config file */
995 cat = ast_category_browse(cfg, NULL);
997 if (strcasecmp(cat, "general")) {
998 /* Look for an existing one */
1001 if (!strcmp(q->name, cat))
1007 q = malloc(sizeof(struct ast_call_queue));
1010 memset(q, 0, sizeof(struct ast_call_queue));
1011 ast_pthread_mutex_init(&q->lock);
1012 strncpy(q->name, cat, sizeof(q->name));
1019 ast_pthread_mutex_lock(&q->lock);
1020 /* Re-initialize the queue */
1027 strcpy(q->announce, "");
1028 strcpy(q->context, "");
1030 var = ast_variable_browse(cfg, cat);
1032 if (!strcasecmp(var->name, "member")) {
1033 /* Add a new member */
1034 cur = malloc(sizeof(struct member));
1036 memset(cur, 0, sizeof(struct member));
1037 strncpy(cur->tech, var->value, sizeof(cur->tech) - 1);
1038 if ((tmp = strchr(cur->tech, '/')))
1040 if ((tmp = strchr(var->value, '/'))) {
1042 strncpy(cur->loc, tmp, sizeof(cur->loc) - 1);
1044 ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno);
1051 } else if (!strcasecmp(var->name, "music")) {
1052 strncpy(q->moh, var->value, sizeof(q->moh) - 1);
1053 } else if (!strcasecmp(var->name, "announce")) {
1054 strncpy(q->announce, var->value, sizeof(q->announce) - 1);
1055 } else if (!strcasecmp(var->name, "context")) {
1056 strncpy(q->context, var->value, sizeof(q->context) - 1);
1057 } else if (!strcasecmp(var->name, "timeout")) {
1058 q->timeout = atoi(var->value);
1059 } else if (!strcasecmp(var->name, "retry")) {
1060 q->retry = atoi(var->value);
1061 } else if (!strcasecmp(var->name, "maxlen")) {
1062 q->maxlen = atoi(var->value);
1064 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno);
1069 q->retry = DEFAULT_RETRY;
1071 q->timeout = DEFAULT_TIMEOUT;
1075 ast_pthread_mutex_unlock(&q->lock);
1082 cat = ast_category_browse(cfg, cat);
1097 ast_log(LOG_WARNING, "XXX Leaking a litttle memory :( XXX\n");
1102 ast_pthread_mutex_unlock(&qlock);
1105 static int queues_show(int fd, int argc, char **argv)
1107 struct ast_call_queue *q;
1108 struct queue_ent *qe;
1116 return RESULT_SHOWUSAGE;
1119 ast_cli(fd, "No queues.\n");
1120 return RESULT_SUCCESS;
1123 ast_pthread_mutex_lock(&q->lock);
1125 snprintf(max, sizeof(max), "%d", q->maxlen);
1127 strcpy(max, "unlimited");
1128 ast_cli(fd, "%-12.12s has %d calls (max %s)\n", q->name, q->count, max);
1130 ast_cli(fd, " Members: \n");
1131 for (mem = q->members; mem; mem = mem->next)
1132 ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
1134 ast_cli(fd, " No Members\n");
1137 ast_cli(fd, " Callers: \n");
1138 for (qe = q->head; qe; qe = qe->next)
1139 ast_cli(fd, " %d. %s (wait: %d:%02.2d)\n", pos++, qe->chan->name,
1140 (now - qe->start) / 60, (now - qe->start) % 60);
1142 ast_cli(fd, " No Callers\n");
1144 ast_pthread_mutex_unlock(&q->lock);
1147 return RESULT_SUCCESS;
1150 /* JDG: callback to display queues status in manager */
1151 static int manager_queues_show( struct mansession *s, struct message *m )
1153 char *a[] = { "show", "queues" };
1154 return queues_show( s->fd, 2, a );
1158 /* Dump queue status */
1159 static int manager_queues_status( struct mansession *s, struct message *m )
1163 struct ast_call_queue *q;
1164 struct queue_ent *qe;
1165 astman_send_ack(s, "Queue status will follow");
1169 ast_pthread_mutex_lock(&q->lock);
1170 ast_cli(s->fd, "Event: QueueParams\r\n"
1175 q->name, q->maxlen, q->count);
1177 /* Do we care about queue members? */
1178 for (mem = q->members; mem; mem = mem->next)
1179 ast_cli(fd, " %s/%s\n", mem->tech, mem->loc);
1182 for (qe = q->head; qe; qe = qe->next)
1183 ast_cli(s->fd, "Event: QueueMember\r\n"
1190 q->name, pos++, qe->chan->name, qe->chan->callerid ? qe->chan->callerid : "", now - qe->start);
1191 ast_pthread_mutex_unlock(&q->lock);
1194 return RESULT_SUCCESS;
1197 static char show_queues_usage[] =
1198 "Usage: show queues\n"
1199 " Provides summary information on call queues.\n";
1201 static struct ast_cli_entry cli_show_queues = {
1202 { "show", "queues", NULL }, queues_show,
1203 "Show status of queues", show_queues_usage, NULL };
1205 int unload_module(void)
1207 STANDARD_HANGUP_LOCALUSERS;
1208 ast_cli_unregister(&cli_show_queues);
1209 ast_manager_unregister( "Queues" );
1210 ast_manager_unregister( "QueueStatus" );
1211 return ast_unregister_application(app);
1214 int load_module(void)
1217 res = ast_register_application(app, queue_exec, synopsis, descrip);
1219 ast_cli_register(&cli_show_queues);
1220 ast_manager_register( "Queues", 0, manager_queues_show, "Queues" );
1221 ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" );
1224 ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ;
1225 ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ;
1238 char *description(void)
1246 STANDARD_USECOUNT(res);
1252 return ASTERISK_GPL_KEY;