don't mark these allocations as 'cache' allocations when caching has been disabled
[asterisk-bristuff.git] / apps / app_queue.c
blob0a41d2b9dbd5e6571b5e5653f821ca7c3e24fd79
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief True call queues with optional send URL on answer
23 * \author Mark Spencer <markster@digium.com>
25 * \arg Config in \ref Config_qu queues.conf
27 * \par Development notes
28 * \note 2004-11-25: Persistent Dynamic Members added by:
29 * NetNation Communications (www.netnation.com)
30 * Kevin Lindsay <kevinl@netnation.com>
32 * Each dynamic agent in each queue is now stored in the astdb.
33 * When asterisk is restarted, each agent will be automatically
34 * readded into their recorded queues. This feature can be
35 * configured with the 'persistent_members=<1|0>' setting in the
36 * '[general]' category in queues.conf. The default is on.
38 * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
40 * \note These features added by David C. Troy <dave@toad.net>:
41 * - Per-queue holdtime calculation
42 * - Estimated holdtime announcement
43 * - Position announcement
44 * - Abandoned/completed call counters
45 * - Failout timer passed as optional app parameter
46 * - Optional monitoring of calls, started when call is answered
48 * Patch Version 1.07 2003-12-24 01
50 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
51 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
53 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
54 * by Matthew Enger <m.enger@xi.com.au>
56 * \ingroup applications
59 #include "asterisk.h"
61 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
63 #include <stdlib.h>
64 #include <errno.h>
65 #include <unistd.h>
66 #include <string.h>
67 #include <stdlib.h>
68 #include <stdio.h>
69 #include <sys/time.h>
70 #include <sys/signal.h>
71 #include <netinet/in.h>
73 #include "asterisk/lock.h"
74 #include "asterisk/file.h"
75 #include "asterisk/logger.h"
76 #include "asterisk/channel.h"
77 #include "asterisk/pbx.h"
78 #include "asterisk/options.h"
79 #include "asterisk/app.h"
80 #include "asterisk/linkedlists.h"
81 #include "asterisk/module.h"
82 #include "asterisk/translate.h"
83 #include "asterisk/say.h"
84 #include "asterisk/features.h"
85 #include "asterisk/musiconhold.h"
86 #include "asterisk/cli.h"
87 #include "asterisk/manager.h"
88 #include "asterisk/config.h"
89 #include "asterisk/monitor.h"
90 #include "asterisk/utils.h"
91 #include "asterisk/causes.h"
92 #include "asterisk/astdb.h"
93 #include "asterisk/devicestate.h"
94 #include "asterisk/stringfields.h"
96 enum {
97 QUEUE_STRATEGY_RINGALL = 0,
98 QUEUE_STRATEGY_ROUNDROBIN,
99 QUEUE_STRATEGY_LEASTRECENT,
100 QUEUE_STRATEGY_FEWESTCALLS,
101 QUEUE_STRATEGY_RANDOM,
102 QUEUE_STRATEGY_RRMEMORY
105 static struct strategy {
106 int strategy;
107 char *name;
108 } strategies[] = {
109 { QUEUE_STRATEGY_RINGALL, "ringall" },
110 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
111 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
112 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
113 { QUEUE_STRATEGY_RANDOM, "random" },
114 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
117 #define DEFAULT_RETRY 5
118 #define DEFAULT_TIMEOUT 15
119 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
120 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
122 #define RES_OKAY 0 /* Action completed */
123 #define RES_EXISTS (-1) /* Entry already exists */
124 #define RES_OUTOFMEMORY (-2) /* Out of memory */
125 #define RES_NOSUCHQUEUE (-3) /* No such queue */
127 static char *app = "Queue";
129 static char *synopsis = "Queue a call for a call queue";
131 static char *descrip =
132 " Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
133 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
134 "This application will return to the dialplan if the queue does not exist, or\n"
135 "any of the join options cause the caller to not enter the queue.\n"
136 "The option string may contain zero or more of the following characters:\n"
137 " 'd' -- data-quality (modem) call (minimum delay).\n"
138 " 'h' -- allow callee to hang up by hitting *.\n"
139 " 'H' -- allow caller to hang up by hitting *.\n"
140 " 'n' -- no retries on the timeout; will exit this application and \n"
141 " go to the next step.\n"
142 " 'i' -- ignore call forward requests from queue members and do nothing\n"
143 " when they are requested.\n"
144 " 'r' -- ring instead of playing MOH\n"
145 " 't' -- allow the called user transfer the calling user\n"
146 " 'T' -- to allow the calling user to transfer the call.\n"
147 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
148 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
149 " In addition to transferring the call, a call may be parked and then picked\n"
150 "up by another user.\n"
151 " The optional URL will be sent to the called party if the channel supports\n"
152 "it.\n"
153 " The optional AGI parameter will setup an AGI script to be executed on the \n"
154 "calling party's channel once they are connected to a queue member.\n"
155 " The timeout will cause the queue to fail out after a specified number of\n"
156 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
157 " This application sets the following channel variable upon completion:\n"
158 " QUEUESTATUS The status of the call as a text string, one of\n"
159 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
161 static char *app_aqm = "AddQueueMember" ;
162 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
163 static char *app_aqm_descrip =
164 " AddQueueMember(queuename[|interface[|penalty[|options]]]):\n"
165 "Dynamically adds interface to an existing queue.\n"
166 "If the interface is already in the queue and there exists an n+101 priority\n"
167 "then it will then jump to this priority. Otherwise it will return an error\n"
168 "The option string may contain zero or more of the following characters:\n"
169 " 'j' -- jump to +101 priority when appropriate.\n"
170 " This application sets the following channel variable upon completion:\n"
171 " AQMSTATUS The status of the attempt to add a queue member as a \n"
172 " text string, one of\n"
173 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
174 "Example: AddQueueMember(techsupport|SIP/3000)\n"
177 static char *app_rqm = "RemoveQueueMember" ;
178 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
179 static char *app_rqm_descrip =
180 " RemoveQueueMember(queuename[|interface[|options]]):\n"
181 "Dynamically removes interface to an existing queue\n"
182 "If the interface is NOT in the queue and there exists an n+101 priority\n"
183 "then it will then jump to this priority. Otherwise it will return an error\n"
184 "The option string may contain zero or more of the following characters:\n"
185 " 'j' -- jump to +101 priority when appropriate.\n"
186 " This application sets the following channel variable upon completion:\n"
187 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
188 " text string, one of\n"
189 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
190 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
193 static char *app_pqm = "PauseQueueMember" ;
194 static char *app_pqm_synopsis = "Pauses a queue member" ;
195 static char *app_pqm_descrip =
196 " PauseQueueMember([queuename]|interface[|options]):\n"
197 "Pauses (blocks calls for) a queue member.\n"
198 "The given interface will be paused in the given queue. This prevents\n"
199 "any calls from being sent from the queue to the interface until it is\n"
200 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
201 "queuename is given, the interface is paused in every queue it is a\n"
202 "member of. If the interface is not in the named queue, or if no queue\n"
203 "is given and the interface is not in any queue, it will jump to\n"
204 "priority n+101, if it exists and the appropriate options are set.\n"
205 "The application will fail if the interface is not found and no extension\n"
206 "to jump to exists.\n"
207 "The option string may contain zero or more of the following characters:\n"
208 " 'j' -- jump to +101 priority when appropriate.\n"
209 " This application sets the following channel variable upon completion:\n"
210 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
211 " text string, one of\n"
212 " PAUSED | NOTFOUND\n"
213 "Example: PauseQueueMember(|SIP/3000)\n";
215 static char *app_upqm = "UnpauseQueueMember" ;
216 static char *app_upqm_synopsis = "Unpauses a queue member" ;
217 static char *app_upqm_descrip =
218 " UnpauseQueueMember([queuename]|interface[|options]):\n"
219 "Unpauses (resumes calls to) a queue member.\n"
220 "This is the counterpart to PauseQueueMember and operates exactly the\n"
221 "same way, except it unpauses instead of pausing the given interface.\n"
222 "The option string may contain zero or more of the following characters:\n"
223 " 'j' -- jump to +101 priority when appropriate.\n"
224 " This application sets the following channel variable upon completion:\n"
225 " UPQMSTATUS The status of the attempt to unpause a queue \n"
226 " member as a text string, one of\n"
227 " UNPAUSED | NOTFOUND\n"
228 "Example: UnpauseQueueMember(|SIP/3000)\n";
230 static char *app_ql = "QueueLog" ;
231 static char *app_ql_synopsis = "Writes to the queue_log" ;
232 static char *app_ql_descrip =
233 " QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
234 "Allows you to write your own events into the queue log\n"
235 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
237 /*! \brief Persistent Members astdb family */
238 static const char *pm_family = "/Queue/PersistentMembers";
239 /* The maximum length of each persistent member queue database entry */
240 #define PM_MAX_LEN 8192
242 /*! \brief queues.conf [general] option */
243 static int queue_persistent_members = 0;
245 /*! \brief queues.conf per-queue weight option */
246 static int use_weight = 0;
248 /*! \brief queues.conf [general] option */
249 static int autofill_default = 0;
251 /*! \brief queues.conf [general] option */
252 static int montype_default = 0;
254 enum queue_result {
255 QUEUE_UNKNOWN = 0,
256 QUEUE_TIMEOUT = 1,
257 QUEUE_JOINEMPTY = 2,
258 QUEUE_LEAVEEMPTY = 3,
259 QUEUE_JOINUNAVAIL = 4,
260 QUEUE_LEAVEUNAVAIL = 5,
261 QUEUE_FULL = 6,
264 const struct {
265 enum queue_result id;
266 char *text;
267 } queue_results[] = {
268 { QUEUE_UNKNOWN, "UNKNOWN" },
269 { QUEUE_TIMEOUT, "TIMEOUT" },
270 { QUEUE_JOINEMPTY,"JOINEMPTY" },
271 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
272 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
273 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
274 { QUEUE_FULL, "FULL" },
277 /*! \brief We define a custom "local user" structure because we
278 use it not only for keeping track of what is in use but
279 also for keeping track of who we're dialing. */
281 struct callattempt {
282 struct callattempt *q_next;
283 struct ast_channel *chan;
284 char interface[256];
285 int stillgoing;
286 int metric;
287 int oldstatus;
288 time_t lastcall;
289 struct member *member;
293 struct queue_ent {
294 struct call_queue *parent; /*!< What queue is our parent */
295 char moh[80]; /*!< Name of musiconhold to be used */
296 char announce[80]; /*!< Announcement to play for member when call is answered */
297 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
298 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
299 int pos; /*!< Where we are in the queue */
300 int prio; /*!< Our priority */
301 int last_pos_said; /*!< Last position we told the user */
302 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
303 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
304 time_t last_pos; /*!< Last time we told the user their position */
305 int opos; /*!< Where we started in the queue */
306 int handled; /*!< Whether our call was handled */
307 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
308 time_t start; /*!< When we started holding */
309 time_t expire; /*!< When this entry should expire (time out of queue) */
310 struct ast_channel *chan; /*!< Our channel */
311 struct queue_ent *next; /*!< The next queue entry */
314 struct member {
315 char interface[80]; /*!< Technology/Location */
316 char membername[80]; /*!< Member name to use in queue logs */
317 int penalty; /*!< Are we a last resort? */
318 int calls; /*!< Number of calls serviced by this member */
319 int dynamic; /*!< Are we dynamically added? */
320 int status; /*!< Status of queue member */
321 int paused; /*!< Are we paused (not accepting calls)? */
322 time_t lastcall; /*!< When last successful call was hungup */
323 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
324 unsigned int delme:1; /*!< Flag to delete entry on reload */
325 struct member *next; /*!< Next member */
328 struct member_interface {
329 char interface[80];
330 AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
333 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
335 /* values used in multi-bit flags in call_queue */
336 #define QUEUE_EMPTY_NORMAL 1
337 #define QUEUE_EMPTY_STRICT 2
338 #define ANNOUNCEHOLDTIME_ALWAYS 1
339 #define ANNOUNCEHOLDTIME_ONCE 2
340 #define QUEUE_EVENT_VARIABLES 3
342 struct call_queue {
343 ast_mutex_t lock;
344 char name[80]; /*!< Name */
345 char moh[80]; /*!< Music On Hold class to be used */
346 char announce[80]; /*!< Announcement to play when call is answered */
347 char context[AST_MAX_CONTEXT]; /*!< Exit context */
348 unsigned int monjoin:1;
349 unsigned int dead:1;
350 unsigned int joinempty:2;
351 unsigned int eventwhencalled:2;
352 unsigned int leavewhenempty:2;
353 unsigned int ringinuse:1;
354 unsigned int setinterfacevar:1;
355 unsigned int reportholdtime:1;
356 unsigned int wrapped:1;
357 unsigned int timeoutrestart:1;
358 unsigned int announceholdtime:2;
359 unsigned int strategy:3;
360 unsigned int maskmemberstatus:1;
361 unsigned int realtime:1;
362 int announcefrequency; /*!< How often to announce their position */
363 int periodicannouncefrequency; /*!< How often to play periodic announcement */
364 int roundingseconds; /*!< How many seconds do we round to? */
365 int holdtime; /*!< Current avg holdtime, based on recursive boxcar filter */
366 int callscompleted; /*!< Number of queue calls completed */
367 int callsabandoned; /*!< Number of queue calls abandoned */
368 int servicelevel; /*!< seconds setting for servicelevel*/
369 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
370 char monfmt[8]; /*!< Format to use when recording calls */
371 int montype; /*!< Monitor type Monitor vs. MixMonitor */
372 char sound_next[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
373 char sound_thereare[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */
374 char sound_calls[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
375 char sound_holdtime[80]; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
376 char sound_minutes[80]; /*!< Sound file: "minutes." (def. queue-minutes) */
377 char sound_lessthan[80]; /*!< Sound file: "less-than" (def. queue-lessthan) */
378 char sound_seconds[80]; /*!< Sound file: "seconds." (def. queue-seconds) */
379 char sound_thanks[80]; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
380 char sound_reporthold[80]; /*!< Sound file: "Hold time" (def. queue-reporthold) */
381 char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/*!< Sound files: Custom announce, no default */
383 int count; /*!< How many entries */
384 int maxlen; /*!< Max number of entries */
385 int wrapuptime; /*!< Wrapup Time */
387 int retry; /*!< Retry calling everyone after this amount of time */
388 int timeout; /*!< How long to wait for an answer */
389 int weight; /*!< Respective weight */
390 int autopause; /*!< Auto pause queue members if they fail to answer */
392 /* Queue strategy things */
393 int rrpos; /*!< Round Robin - position */
394 int memberdelay; /*!< Seconds to delay connecting member to caller */
395 int autofill; /*!< Ignore the head call status and ring an available agent */
397 struct member *members; /*!< Head of the list of members */
398 struct queue_ent *head; /*!< Head of the list of callers */
399 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
402 static AST_LIST_HEAD_STATIC(queues, call_queue);
404 static int set_member_paused(char *queuename, char *interface, int paused);
406 static void rr_dep_warning(void)
408 static unsigned int warned = 0;
410 if (!warned) {
411 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
412 warned = 1;
416 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
418 int i;
420 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
421 if (queue_results[i].id == res) {
422 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
423 return;
428 static char *int2strat(int strategy)
430 int x;
432 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
433 if (strategy == strategies[x].strategy)
434 return strategies[x].name;
437 return "<unknown>";
440 static int strat2int(const char *strategy)
442 int x;
444 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
445 if (!strcasecmp(strategy, strategies[x].name))
446 return strategies[x].strategy;
449 return -1;
452 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
453 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
455 struct queue_ent *cur;
457 if (!q || !new)
458 return;
459 if (prev) {
460 cur = prev->next;
461 prev->next = new;
462 } else {
463 cur = q->head;
464 q->head = new;
466 new->next = cur;
467 new->parent = q;
468 new->pos = ++(*pos);
469 new->opos = *pos;
472 enum queue_member_status {
473 QUEUE_NO_MEMBERS,
474 QUEUE_NO_REACHABLE_MEMBERS,
475 QUEUE_NORMAL
478 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
480 struct member *member;
481 enum queue_member_status result = QUEUE_NO_MEMBERS;
483 ast_mutex_lock(&q->lock);
484 for (member = q->members; member; member = member->next) {
485 if (max_penalty && (member->penalty > max_penalty))
486 continue;
488 if (member->paused) continue;
490 switch (member->status) {
491 case AST_DEVICE_INVALID:
492 /* nothing to do */
493 break;
494 case AST_DEVICE_UNAVAILABLE:
495 result = QUEUE_NO_REACHABLE_MEMBERS;
496 break;
497 default:
498 ast_mutex_unlock(&q->lock);
499 return QUEUE_NORMAL;
503 ast_mutex_unlock(&q->lock);
504 return result;
507 struct statechange {
508 int state;
509 char dev[0];
512 static void *changethread(void *data)
514 struct call_queue *q;
515 struct statechange *sc = data;
516 struct member *cur;
517 struct member_interface *curint;
518 char *loc;
519 char *technology;
521 technology = ast_strdupa(sc->dev);
522 loc = strchr(technology, '/');
523 if (loc) {
524 *loc++ = '\0';
525 } else {
526 free(sc);
527 return NULL;
530 AST_LIST_LOCK(&interfaces);
531 AST_LIST_TRAVERSE(&interfaces, curint, list) {
532 char *interface;
533 char *slash_pos;
534 interface = ast_strdupa(curint->interface);
535 if ((slash_pos = strchr(interface, '/')))
536 if ((slash_pos = strchr(slash_pos + 1, '/')))
537 *slash_pos = '\0';
539 if (!strcasecmp(interface, sc->dev))
540 break;
542 AST_LIST_UNLOCK(&interfaces);
544 if (!curint) {
545 if (option_debug)
546 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
547 free(sc);
548 return NULL;
551 if (option_debug)
552 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
553 AST_LIST_LOCK(&queues);
554 AST_LIST_TRAVERSE(&queues, q, list) {
555 ast_mutex_lock(&q->lock);
556 for (cur = q->members; cur; cur = cur->next) {
557 char *interface;
558 char *slash_pos;
559 interface = ast_strdupa(cur->interface);
560 if ((slash_pos = strchr(interface, '/')))
561 if ((slash_pos = strchr(slash_pos + 1, '/')))
562 *slash_pos = '\0';
564 if (strcasecmp(sc->dev, interface))
565 continue;
567 if (cur->status != sc->state) {
568 cur->status = sc->state;
569 if (q->maskmemberstatus)
570 continue;
572 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
573 "Queue: %s\r\n"
574 "Location: %s\r\n"
575 "MemberName: %s\r\n"
576 "Membership: %s\r\n"
577 "Penalty: %d\r\n"
578 "CallsTaken: %d\r\n"
579 "LastCall: %d\r\n"
580 "Status: %d\r\n"
581 "Paused: %d\r\n",
582 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : "static",
583 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
586 ast_mutex_unlock(&q->lock);
588 AST_LIST_UNLOCK(&queues);
590 return NULL;
593 static int statechange_queue(const char *dev, int state, void *ign)
595 /* Avoid potential for deadlocks by spawning a new thread to handle
596 the event */
597 struct statechange *sc;
598 pthread_t t;
599 pthread_attr_t attr;
601 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
602 return 0;
604 sc->state = state;
605 strcpy(sc->dev, dev);
606 pthread_attr_init(&attr);
607 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
608 if (ast_pthread_create_background(&t, &attr, changethread, sc)) {
609 ast_log(LOG_WARNING, "Failed to create update thread!\n");
610 free(sc);
613 return 0;
616 static struct member *create_queue_member(char *interface, const char *membername, int penalty, int paused)
618 struct member *cur;
620 if ((cur = ast_calloc(1, sizeof(*cur)))) {
621 cur->penalty = penalty;
622 cur->paused = paused;
623 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
624 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
625 if (!strchr(cur->interface, '/'))
626 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
627 cur->status = ast_device_state(interface);
630 return cur;
633 static struct call_queue *alloc_queue(const char *queuename)
635 struct call_queue *q;
637 if ((q = ast_calloc(1, sizeof(*q)))) {
638 ast_mutex_init(&q->lock);
639 ast_copy_string(q->name, queuename, sizeof(q->name));
641 return q;
644 static void init_queue(struct call_queue *q)
646 int i;
648 q->dead = 0;
649 q->retry = DEFAULT_RETRY;
650 q->timeout = -1;
651 q->maxlen = 0;
652 q->announcefrequency = 0;
653 q->announceholdtime = 0;
654 q->roundingseconds = 0; /* Default - don't announce seconds */
655 q->servicelevel = 0;
656 q->ringinuse = 1;
657 q->setinterfacevar = 0;
658 q->autofill = autofill_default;
659 q->montype = montype_default;
660 q->moh[0] = '\0';
661 q->announce[0] = '\0';
662 q->context[0] = '\0';
663 q->monfmt[0] = '\0';
664 q->periodicannouncefrequency = 0;
665 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
666 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
667 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
668 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
669 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
670 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
671 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
672 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
673 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
674 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
675 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
676 q->sound_periodicannounce[i][0]='\0';
680 static void clear_queue(struct call_queue *q)
682 q->holdtime = 0;
683 q->callscompleted = 0;
684 q->callsabandoned = 0;
685 q->callscompletedinsl = 0;
686 q->wrapuptime = 0;
689 static int add_to_interfaces(char *interface)
691 struct member_interface *curint;
693 AST_LIST_LOCK(&interfaces);
694 AST_LIST_TRAVERSE(&interfaces, curint, list) {
695 if (!strcasecmp(curint->interface, interface))
696 break;
699 if (curint) {
700 AST_LIST_UNLOCK(&interfaces);
701 return 0;
704 if (option_debug)
705 ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
707 if ((curint = ast_calloc(1, sizeof(*curint)))) {
708 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
709 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
711 AST_LIST_UNLOCK(&interfaces);
713 return 0;
716 static int interface_exists_global(char *interface)
718 struct call_queue *q;
719 struct member *mem;
720 int ret = 0;
722 AST_LIST_LOCK(&queues);
723 AST_LIST_TRAVERSE(&queues, q, list) {
724 ast_mutex_lock(&q->lock);
725 for (mem = q->members; mem && !ret; mem = mem->next) {
726 if (!strcasecmp(interface, mem->interface))
727 ret = 1;
729 ast_mutex_unlock(&q->lock);
730 if (ret)
731 break;
733 AST_LIST_UNLOCK(&queues);
735 return ret;
738 static int remove_from_interfaces(char *interface)
740 struct member_interface *curint;
742 AST_LIST_LOCK(&interfaces);
743 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
744 if (!strcasecmp(curint->interface, interface)) {
745 if (!interface_exists_global(interface)) {
746 if (option_debug)
747 ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
748 AST_LIST_REMOVE_CURRENT(&interfaces, list);
749 free(curint);
751 break;
754 AST_LIST_TRAVERSE_SAFE_END;
755 AST_LIST_UNLOCK(&interfaces);
757 return 0;
760 static void clear_and_free_interfaces(void)
762 struct member_interface *curint;
764 AST_LIST_LOCK(&interfaces);
765 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
766 free(curint);
767 AST_LIST_UNLOCK(&interfaces);
770 /*! \brief Configure a queue parameter.
771 \par
772 For error reporting, line number is passed for .conf static configuration.
773 For Realtime queues, linenum is -1.
774 The failunknown flag is set for config files (and static realtime) to show
775 errors for unknown parameters. It is cleared for dynamic realtime to allow
776 extra fields in the tables. */
777 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
779 if (!strcasecmp(param, "musicclass") ||
780 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
781 ast_copy_string(q->moh, val, sizeof(q->moh));
782 } else if (!strcasecmp(param, "announce")) {
783 ast_copy_string(q->announce, val, sizeof(q->announce));
784 } else if (!strcasecmp(param, "context")) {
785 ast_copy_string(q->context, val, sizeof(q->context));
786 } else if (!strcasecmp(param, "timeout")) {
787 q->timeout = atoi(val);
788 if (q->timeout < 0)
789 q->timeout = DEFAULT_TIMEOUT;
790 } else if (!strcasecmp(param, "ringinuse")) {
791 q->ringinuse = ast_true(val);
792 } else if (!strcasecmp(param, "setinterfacevar")) {
793 q->setinterfacevar = ast_true(val);
794 } else if (!strcasecmp(param, "monitor-join")) {
795 q->monjoin = ast_true(val);
796 } else if (!strcasecmp(param, "monitor-format")) {
797 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
798 } else if (!strcasecmp(param, "queue-youarenext")) {
799 ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
800 } else if (!strcasecmp(param, "queue-thereare")) {
801 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
802 } else if (!strcasecmp(param, "queue-callswaiting")) {
803 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
804 } else if (!strcasecmp(param, "queue-holdtime")) {
805 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
806 } else if (!strcasecmp(param, "queue-minutes")) {
807 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
808 } else if (!strcasecmp(param, "queue-seconds")) {
809 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
810 } else if (!strcasecmp(param, "queue-lessthan")) {
811 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
812 } else if (!strcasecmp(param, "queue-thankyou")) {
813 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
814 } else if (!strcasecmp(param, "queue-reporthold")) {
815 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
816 } else if (!strcasecmp(param, "announce-frequency")) {
817 q->announcefrequency = atoi(val);
818 } else if (!strcasecmp(param, "announce-round-seconds")) {
819 q->roundingseconds = atoi(val);
820 if (q->roundingseconds>60 || q->roundingseconds<0) {
821 if (linenum >= 0) {
822 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
823 "using 0 instead for queue '%s' at line %d of queues.conf\n",
824 val, param, q->name, linenum);
825 } else {
826 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
827 "using 0 instead for queue '%s'\n", val, param, q->name);
829 q->roundingseconds=0;
831 } else if (!strcasecmp(param, "announce-holdtime")) {
832 if (!strcasecmp(val, "once"))
833 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
834 else if (ast_true(val))
835 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
836 else
837 q->announceholdtime = 0;
838 } else if (!strcasecmp(param, "periodic-announce")) {
839 if (strchr(val, '|')) {
840 char *s, *buf = ast_strdupa(val);
841 unsigned int i = 0;
843 while ((s = strsep(&buf, "|"))) {
844 ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
845 i++;
846 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
847 break;
849 } else {
850 ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
852 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
853 q->periodicannouncefrequency = atoi(val);
854 } else if (!strcasecmp(param, "retry")) {
855 q->retry = atoi(val);
856 if (q->retry <= 0)
857 q->retry = DEFAULT_RETRY;
858 } else if (!strcasecmp(param, "wrapuptime")) {
859 q->wrapuptime = atoi(val);
860 } else if (!strcasecmp(param, "autofill")) {
861 q->autofill = ast_true(val);
862 } else if (!strcasecmp(param, "monitor-type")) {
863 if (!strcasecmp(val, "mixmonitor"))
864 q->montype = 1;
865 } else if (!strcasecmp(param, "autopause")) {
866 q->autopause = ast_true(val);
867 } else if (!strcasecmp(param, "maxlen")) {
868 q->maxlen = atoi(val);
869 if (q->maxlen < 0)
870 q->maxlen = 0;
871 } else if (!strcasecmp(param, "servicelevel")) {
872 q->servicelevel= atoi(val);
873 } else if (!strcasecmp(param, "strategy")) {
874 q->strategy = strat2int(val);
875 if (q->strategy < 0) {
876 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
877 val, q->name);
878 q->strategy = QUEUE_STRATEGY_RINGALL;
880 } else if (!strcasecmp(param, "joinempty")) {
881 if (!strcasecmp(val, "strict"))
882 q->joinempty = QUEUE_EMPTY_STRICT;
883 else if (ast_true(val))
884 q->joinempty = QUEUE_EMPTY_NORMAL;
885 else
886 q->joinempty = 0;
887 } else if (!strcasecmp(param, "leavewhenempty")) {
888 if (!strcasecmp(val, "strict"))
889 q->leavewhenempty = QUEUE_EMPTY_STRICT;
890 else if (ast_true(val))
891 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
892 else
893 q->leavewhenempty = 0;
894 } else if (!strcasecmp(param, "eventmemberstatus")) {
895 q->maskmemberstatus = !ast_true(val);
896 } else if (!strcasecmp(param, "eventwhencalled")) {
897 if (!strcasecmp(val, "vars")) {
898 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
899 } else {
900 q->eventwhencalled = ast_true(val);
902 } else if (!strcasecmp(param, "reportholdtime")) {
903 q->reportholdtime = ast_true(val);
904 } else if (!strcasecmp(param, "memberdelay")) {
905 q->memberdelay = atoi(val);
906 } else if (!strcasecmp(param, "weight")) {
907 q->weight = atoi(val);
908 if (q->weight)
909 use_weight++;
910 /* With Realtime queues, if the last queue using weights is deleted in realtime,
911 we will not see any effect on use_weight until next reload. */
912 } else if (!strcasecmp(param, "timeoutrestart")) {
913 q->timeoutrestart = ast_true(val);
914 } else if (failunknown) {
915 if (linenum >= 0) {
916 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
917 q->name, param, linenum);
918 } else {
919 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
924 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str)
926 struct member *m, *prev_m;
927 int penalty = 0;
929 if (penalty_str) {
930 penalty = atoi(penalty_str);
931 if (penalty < 0)
932 penalty = 0;
935 /* Find the member, or the place to put a new one. */
936 for (m = q->members, prev_m = NULL;
937 m && strcmp(m->interface, interface);
938 prev_m = m, m = m->next);
940 /* Create a new one if not found, else update penalty */
941 if (!m) {
942 if ((m = create_queue_member(interface, membername, penalty, 0))) {
943 m->dead = 0;
944 add_to_interfaces(interface);
945 if (prev_m) {
946 prev_m->next = m;
947 } else {
948 q->members = m;
951 } else {
952 m->dead = 0; /* Do not delete this one. */
953 m->penalty = penalty;
957 static void free_members(struct call_queue *q, int all)
959 /* Free non-dynamic members */
960 struct member *curm, *next, *prev = NULL;
962 for (curm = q->members; curm; curm = next) {
963 next = curm->next;
964 if (all || !curm->dynamic) {
965 if (prev)
966 prev->next = next;
967 else
968 q->members = next;
969 remove_from_interfaces(curm->interface);
970 free(curm);
971 } else
972 prev = curm;
976 static void destroy_queue(struct call_queue *q)
978 free_members(q, 1);
979 ast_mutex_destroy(&q->lock);
980 free(q);
983 /*!\brief Reload a single queue via realtime.
984 \return Return the queue, or NULL if it doesn't exist.
985 \note Should be called with the global qlock locked. */
986 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
988 struct ast_variable *v;
989 struct call_queue *q;
990 struct member *m, *prev_m, *next_m;
991 char *interface = NULL;
992 char *tmp, *tmp_name;
993 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
995 /* Find the queue in the in-core list (we will create a new one if not found). */
996 AST_LIST_TRAVERSE(&queues, q, list) {
997 if (!strcasecmp(q->name, queuename))
998 break;
1001 /* Static queues override realtime. */
1002 if (q) {
1003 ast_mutex_lock(&q->lock);
1004 if (!q->realtime) {
1005 if (q->dead) {
1006 ast_mutex_unlock(&q->lock);
1007 return NULL;
1008 } else {
1009 ast_mutex_unlock(&q->lock);
1010 return q;
1013 } else if (!member_config)
1014 /* Not found in the list, and it's not realtime ... */
1015 return NULL;
1017 /* Check if queue is defined in realtime. */
1018 if (!queue_vars) {
1019 /* Delete queue from in-core list if it has been deleted in realtime. */
1020 if (q) {
1021 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1022 found condition... So we might delete an in-core queue
1023 in case of DB failure. */
1024 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
1026 q->dead = 1;
1027 /* Delete if unused (else will be deleted when last caller leaves). */
1028 if (!q->count) {
1029 /* Delete. */
1030 AST_LIST_REMOVE(&queues, q, list);
1031 ast_mutex_unlock(&q->lock);
1032 destroy_queue(q);
1033 } else
1034 ast_mutex_unlock(&q->lock);
1036 return NULL;
1039 /* Create a new queue if an in-core entry does not exist yet. */
1040 if (!q) {
1041 if (!(q = alloc_queue(queuename)))
1042 return NULL;
1043 ast_mutex_lock(&q->lock);
1044 clear_queue(q);
1045 q->realtime = 1;
1046 AST_LIST_INSERT_HEAD(&queues, q, list);
1048 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
1050 memset(tmpbuf, 0, sizeof(tmpbuf));
1051 for (v = queue_vars; v; v = v->next) {
1052 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1053 if ((tmp = strchr(v->name, '_'))) {
1054 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
1055 tmp_name = tmpbuf;
1056 tmp = tmp_name;
1057 while ((tmp = strchr(tmp, '_')))
1058 *tmp++ = '-';
1059 } else
1060 tmp_name = v->name;
1061 queue_set_param(q, tmp_name, v->value, -1, 0);
1064 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
1065 rr_dep_warning();
1067 /* Temporarily set non-dynamic members dead so we can detect deleted ones. */
1068 for (m = q->members; m; m = m->next) {
1069 if (!m->dynamic)
1070 m->dead = 1;
1073 while ((interface = ast_category_browse(member_config, interface))) {
1074 rt_handle_member_record(q, interface,
1075 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
1076 ast_variable_retrieve(member_config, interface, "penalty"));
1079 /* Delete all realtime members that have been deleted in DB. */
1080 m = q->members;
1081 prev_m = NULL;
1082 while (m) {
1083 next_m = m->next;
1084 if (m->dead) {
1085 if (prev_m) {
1086 prev_m->next = next_m;
1087 } else {
1088 q->members = next_m;
1090 remove_from_interfaces(m->interface);
1091 free(m);
1092 } else {
1093 prev_m = m;
1095 m = next_m;
1098 ast_mutex_unlock(&q->lock);
1100 return q;
1103 static struct call_queue *load_realtime_queue(char *queuename)
1105 struct ast_variable *queue_vars;
1106 struct ast_config *member_config = NULL;
1107 struct call_queue *q;
1109 /* Find the queue in the in-core list first. */
1110 AST_LIST_LOCK(&queues);
1111 AST_LIST_TRAVERSE(&queues, q, list) {
1112 if (!strcasecmp(q->name, queuename)) {
1113 break;
1116 AST_LIST_UNLOCK(&queues);
1118 if (!q || q->realtime) {
1119 /*! \note Load from realtime before taking the global qlock, to avoid blocking all
1120 queue operations while waiting for the DB.
1122 This will be two separate database transactions, so we might
1123 see queue parameters as they were before another process
1124 changed the queue and member list as it was after the change.
1125 Thus we might see an empty member list when a queue is
1126 deleted. In practise, this is unlikely to cause a problem. */
1128 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
1129 if (queue_vars) {
1130 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
1131 if (!member_config) {
1132 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
1133 return NULL;
1137 AST_LIST_LOCK(&queues);
1139 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
1140 if (member_config)
1141 ast_config_destroy(member_config);
1142 if (queue_vars)
1143 ast_variables_destroy(queue_vars);
1145 AST_LIST_UNLOCK(&queues);
1147 return q;
1150 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
1152 struct call_queue *q;
1153 struct queue_ent *cur, *prev = NULL;
1154 int res = -1;
1155 int pos = 0;
1156 int inserted = 0;
1157 enum queue_member_status stat;
1159 if (!(q = load_realtime_queue(queuename)))
1160 return res;
1162 AST_LIST_LOCK(&queues);
1163 ast_mutex_lock(&q->lock);
1165 /* This is our one */
1166 stat = get_member_status(q, qe->max_penalty);
1167 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
1168 *reason = QUEUE_JOINEMPTY;
1169 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
1170 *reason = QUEUE_JOINUNAVAIL;
1171 else if (q->maxlen && (q->count >= q->maxlen))
1172 *reason = QUEUE_FULL;
1173 else {
1174 /* There's space for us, put us at the right position inside
1175 * the queue.
1176 * Take into account the priority of the calling user */
1177 inserted = 0;
1178 prev = NULL;
1179 cur = q->head;
1180 while (cur) {
1181 /* We have higher priority than the current user, enter
1182 * before him, after all the other users with priority
1183 * higher or equal to our priority. */
1184 if ((!inserted) && (qe->prio > cur->prio)) {
1185 insert_entry(q, prev, qe, &pos);
1186 inserted = 1;
1188 cur->pos = ++pos;
1189 prev = cur;
1190 cur = cur->next;
1192 /* No luck, join at the end of the queue */
1193 if (!inserted)
1194 insert_entry(q, prev, qe, &pos);
1195 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
1196 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
1197 ast_copy_string(qe->context, q->context, sizeof(qe->context));
1198 q->count++;
1199 res = 0;
1200 manager_event(EVENT_FLAG_CALL, "Join",
1201 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1202 qe->chan->name,
1203 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
1204 S_OR(qe->chan->cid.cid_name, "unknown"),
1205 q->name, qe->pos, q->count, qe->chan->uniqueid );
1206 if (option_debug)
1207 ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
1209 ast_mutex_unlock(&q->lock);
1210 AST_LIST_UNLOCK(&queues);
1212 return res;
1215 static int play_file(struct ast_channel *chan, char *filename)
1217 int res;
1219 ast_stopstream(chan);
1220 res = ast_streamfile(chan, filename, chan->language);
1221 if (!res)
1222 res = ast_waitstream(chan, AST_DIGIT_ANY);
1223 ast_stopstream(chan);
1225 return res;
1228 static int valid_exit(struct queue_ent *qe, char digit)
1230 int digitlen = strlen(qe->digits);
1232 /* Prevent possible buffer overflow */
1233 if (digitlen < sizeof(qe->digits) - 2) {
1234 qe->digits[digitlen] = digit;
1235 qe->digits[digitlen + 1] = '\0';
1236 } else {
1237 qe->digits[0] = '\0';
1238 return 0;
1241 /* If there's no context to goto, short-circuit */
1242 if (ast_strlen_zero(qe->context))
1243 return 0;
1245 /* If the extension is bad, then reset the digits to blank */
1246 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
1247 qe->digits[0] = '\0';
1248 return 0;
1251 /* We have an exact match */
1252 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
1253 /* Return 1 on a successful goto */
1254 return 1;
1257 return 0;
1260 static int say_position(struct queue_ent *qe)
1262 int res = 0, avgholdmins, avgholdsecs;
1263 time_t now;
1265 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
1266 time(&now);
1267 if ((now - qe->last_pos) < 15)
1268 return 0;
1270 /* If either our position has changed, or we are over the freq timer, say position */
1271 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
1272 return 0;
1274 ast_moh_stop(qe->chan);
1275 /* Say we're next, if we are */
1276 if (qe->pos == 1) {
1277 res = play_file(qe->chan, qe->parent->sound_next);
1278 if (res && valid_exit(qe, res))
1279 goto playout;
1280 else
1281 goto posout;
1282 } else {
1283 res = play_file(qe->chan, qe->parent->sound_thereare);
1284 if (res && valid_exit(qe, res))
1285 goto playout;
1286 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
1287 if (res && valid_exit(qe, res))
1288 goto playout;
1289 res = play_file(qe->chan, qe->parent->sound_calls);
1290 if (res && valid_exit(qe, res))
1291 goto playout;
1293 /* Round hold time to nearest minute */
1294 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
1296 /* If they have specified a rounding then round the seconds as well */
1297 if (qe->parent->roundingseconds) {
1298 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
1299 avgholdsecs *= qe->parent->roundingseconds;
1300 } else {
1301 avgholdsecs = 0;
1304 if (option_verbose > 2)
1305 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
1307 /* If the hold time is >1 min, if it's enabled, and if it's not
1308 supposed to be only once and we have already said it, say it */
1309 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
1310 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
1311 res = play_file(qe->chan, qe->parent->sound_holdtime);
1312 if (res && valid_exit(qe, res))
1313 goto playout;
1315 if (avgholdmins > 0) {
1316 if (avgholdmins < 2) {
1317 res = play_file(qe->chan, qe->parent->sound_lessthan);
1318 if (res && valid_exit(qe, res))
1319 goto playout;
1321 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
1322 if (res && valid_exit(qe, res))
1323 goto playout;
1324 } else {
1325 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
1326 if (res && valid_exit(qe, res))
1327 goto playout;
1330 res = play_file(qe->chan, qe->parent->sound_minutes);
1331 if (res && valid_exit(qe, res))
1332 goto playout;
1334 if (avgholdsecs>0) {
1335 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
1336 if (res && valid_exit(qe, res))
1337 goto playout;
1339 res = play_file(qe->chan, qe->parent->sound_seconds);
1340 if (res && valid_exit(qe, res))
1341 goto playout;
1346 posout:
1347 if (option_verbose > 2)
1348 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
1349 qe->chan->name, qe->parent->name, qe->pos);
1350 res = play_file(qe->chan, qe->parent->sound_thanks);
1351 if (res && !valid_exit(qe, res))
1352 res = 0;
1354 playout:
1355 /* Set our last_pos indicators */
1356 qe->last_pos = now;
1357 qe->last_pos_said = qe->pos;
1359 /* Don't restart music on hold if we're about to exit the caller from the queue */
1360 if (!res)
1361 ast_moh_start(qe->chan, qe->moh, NULL);
1363 return res;
1366 static void recalc_holdtime(struct queue_ent *qe)
1368 int oldvalue, newvalue;
1370 /* Calculate holdtime using a recursive boxcar filter */
1371 /* Thanks to SRT for this contribution */
1372 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1374 newvalue = time(NULL) - qe->start;
1376 ast_mutex_lock(&qe->parent->lock);
1377 if (newvalue <= qe->parent->servicelevel)
1378 qe->parent->callscompletedinsl++;
1379 oldvalue = qe->parent->holdtime;
1380 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2;
1381 ast_mutex_unlock(&qe->parent->lock);
1385 static void leave_queue(struct queue_ent *qe)
1387 struct call_queue *q;
1388 struct queue_ent *cur, *prev = NULL;
1389 int pos = 0;
1391 if (!(q = qe->parent))
1392 return;
1393 ast_mutex_lock(&q->lock);
1395 prev = NULL;
1396 for (cur = q->head; cur; cur = cur->next) {
1397 if (cur == qe) {
1398 q->count--;
1400 /* Take us out of the queue */
1401 manager_event(EVENT_FLAG_CALL, "Leave",
1402 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1403 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
1404 if (option_debug)
1405 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
1406 /* Take us out of the queue */
1407 if (prev)
1408 prev->next = cur->next;
1409 else
1410 q->head = cur->next;
1411 } else {
1412 /* Renumber the people after us in the queue based on a new count */
1413 cur->pos = ++pos;
1414 prev = cur;
1417 ast_mutex_unlock(&q->lock);
1419 if (q->dead && !q->count) {
1420 /* It's dead and nobody is in it, so kill it */
1421 AST_LIST_LOCK(&queues);
1422 AST_LIST_REMOVE(&queues, q, list);
1423 AST_LIST_UNLOCK(&queues);
1424 destroy_queue(q);
1428 /* Hang up a list of outgoing calls */
1429 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
1431 struct callattempt *oo;
1433 while (outgoing) {
1434 /* Hangup any existing lines we have open */
1435 if (outgoing->chan && (outgoing->chan != exception))
1436 ast_hangup(outgoing->chan);
1437 oo = outgoing;
1438 outgoing = outgoing->q_next;
1439 free(oo);
1443 static int update_status(struct call_queue *q, struct member *member, int status)
1445 struct member *cur;
1447 /* Since a reload could have taken place, we have to traverse the list to
1448 be sure it's still valid */
1449 ast_mutex_lock(&q->lock);
1450 for (cur = q->members; cur; cur = cur->next) {
1451 if (member != cur)
1452 continue;
1454 cur->status = status;
1455 if (!q->maskmemberstatus) {
1456 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
1457 "Queue: %s\r\n"
1458 "Location: %s\r\n"
1459 "MemberName: %s\r\n"
1460 "Membership: %s\r\n"
1461 "Penalty: %d\r\n"
1462 "CallsTaken: %d\r\n"
1463 "LastCall: %d\r\n"
1464 "Status: %d\r\n"
1465 "Paused: %d\r\n",
1466 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : "static",
1467 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
1470 ast_mutex_unlock(&q->lock);
1471 return 0;
1474 static int update_dial_status(struct call_queue *q, struct member *member, int status)
1476 if (status == AST_CAUSE_BUSY)
1477 status = AST_DEVICE_BUSY;
1478 else if (status == AST_CAUSE_UNREGISTERED)
1479 status = AST_DEVICE_UNAVAILABLE;
1480 else if (status == AST_CAUSE_NOSUCHDRIVER)
1481 status = AST_DEVICE_INVALID;
1482 else
1483 status = AST_DEVICE_UNKNOWN;
1484 return update_status(q, member, status);
1487 /* traverse all defined queues which have calls waiting and contain this member
1488 return 0 if no other queue has precedence (higher weight) or 1 if found */
1489 static int compare_weight(struct call_queue *rq, struct member *member)
1491 struct call_queue *q;
1492 struct member *mem;
1493 int found = 0;
1495 /* &qlock and &rq->lock already set by try_calling()
1496 * to solve deadlock */
1497 AST_LIST_TRAVERSE(&queues, q, list) {
1498 if (q == rq) /* don't check myself, could deadlock */
1499 continue;
1500 ast_mutex_lock(&q->lock);
1501 if (q->count && q->members) {
1502 for (mem = q->members; mem; mem = mem->next) {
1503 if (strcmp(mem->interface, member->interface))
1504 continue;
1506 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
1507 if (q->weight > rq->weight) {
1508 ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
1509 found = 1;
1510 break;
1514 ast_mutex_unlock(&q->lock);
1515 if (found)
1516 break;
1518 return found;
1521 /*! \brief common hangup actions */
1522 static void do_hang(struct callattempt *o)
1524 o->stillgoing = 0;
1525 ast_hangup(o->chan);
1526 o->chan = NULL;
1529 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
1531 char *tmp = alloca(len);
1533 if (pbx_builtin_serialize_variables(chan, tmp, len)) {
1534 int i, j;
1536 /* convert "\n" to "\nVariable: " */
1537 strcpy(vars, "Variable: ");
1539 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
1540 vars[j] = tmp[i];
1542 if (tmp[i + 1] == '\0')
1543 break;
1544 if (tmp[i] == '\n') {
1545 vars[j] = '\r';
1546 vars[++j] = '\n';
1548 ast_copy_string(&(vars[j]), "Variable: ", len - j);
1549 j += 9;
1552 if (j > len - 1)
1553 j = len - 1;
1554 vars[j - 2] = '\r';
1555 vars[j - 1] = '\n';
1556 vars[j] = '\0';
1557 } else {
1558 /* there are no channel variables; leave it blank */
1559 *vars = '\0';
1561 return vars;
1564 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
1566 int res;
1567 int status;
1568 char tech[256];
1569 char *location;
1571 /* on entry here, we know that tmp->chan == NULL */
1572 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
1573 if (option_debug)
1574 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
1575 if (qe->chan->cdr)
1576 ast_cdr_busy(qe->chan->cdr);
1577 tmp->stillgoing = 0;
1578 (*busies)++;
1579 return 0;
1582 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
1583 if (option_debug)
1584 ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
1585 if (qe->chan->cdr)
1586 ast_cdr_busy(qe->chan->cdr);
1587 tmp->stillgoing = 0;
1588 return 0;
1591 if (tmp->member->paused) {
1592 if (option_debug)
1593 ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
1594 if (qe->chan->cdr)
1595 ast_cdr_busy(qe->chan->cdr);
1596 tmp->stillgoing = 0;
1597 return 0;
1599 if (use_weight && compare_weight(qe->parent,tmp->member)) {
1600 ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
1601 if (qe->chan->cdr)
1602 ast_cdr_busy(qe->chan->cdr);
1603 tmp->stillgoing = 0;
1604 (*busies)++;
1605 return 0;
1608 ast_copy_string(tech, tmp->interface, sizeof(tech));
1609 if ((location = strchr(tech, '/')))
1610 *location++ = '\0';
1611 else
1612 location = "";
1614 /* Request the peer */
1615 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
1616 if (!tmp->chan) { /* If we can't, just go on to the next call */
1617 if (qe->chan->cdr)
1618 ast_cdr_busy(qe->chan->cdr);
1619 tmp->stillgoing = 0;
1620 update_dial_status(qe->parent, tmp->member, status);
1622 ast_mutex_lock(&qe->parent->lock);
1623 qe->parent->rrpos++;
1624 ast_mutex_unlock(&qe->parent->lock);
1626 (*busies)++;
1627 return 0;
1628 } else if (status != tmp->oldstatus)
1629 update_dial_status(qe->parent, tmp->member, status);
1631 tmp->chan->appl = "AppQueue";
1632 tmp->chan->data = "(Outgoing Line)";
1633 tmp->chan->whentohangup = 0;
1634 if (tmp->chan->cid.cid_num)
1635 free(tmp->chan->cid.cid_num);
1636 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
1637 if (tmp->chan->cid.cid_name)
1638 free(tmp->chan->cid.cid_name);
1639 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
1640 if (tmp->chan->cid.cid_ani)
1641 free(tmp->chan->cid.cid_ani);
1642 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
1644 /* Inherit specially named variables from parent channel */
1645 ast_channel_inherit_variables(qe->chan, tmp->chan);
1647 /* Presense of ADSI CPE on outgoing channel follows ours */
1648 tmp->chan->adsicpe = qe->chan->adsicpe;
1650 /* Place the call, but don't wait on the answer */
1651 if ((res = ast_call(tmp->chan, location, 0))) {
1652 /* Again, keep going even if there's an error */
1653 if (option_debug)
1654 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
1655 if (option_verbose > 2)
1656 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
1657 do_hang(tmp);
1658 (*busies)++;
1659 return 0;
1660 } else if (qe->parent->eventwhencalled) {
1661 char vars[2048];
1663 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
1664 "AgentCalled: %s\r\n"
1665 "ChannelCalling: %s\r\n"
1666 "CallerID: %s\r\n"
1667 "CallerIDName: %s\r\n"
1668 "Context: %s\r\n"
1669 "Extension: %s\r\n"
1670 "Priority: %d\r\n"
1671 "%s",
1672 tmp->interface, qe->chan->name,
1673 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
1674 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
1675 qe->chan->context, qe->chan->exten, qe->chan->priority,
1676 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
1677 if (option_verbose > 2)
1678 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
1681 return 1;
1684 /*! \brief find the entry with the best metric, or NULL */
1685 static struct callattempt *find_best(struct callattempt *outgoing)
1687 struct callattempt *best = NULL, *cur;
1689 for (cur = outgoing; cur; cur = cur->q_next) {
1690 if (cur->stillgoing && /* Not already done */
1691 !cur->chan && /* Isn't already going */
1692 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
1693 best = cur;
1697 return best;
1700 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
1702 int ret = 0;
1704 while (ret == 0) {
1705 struct callattempt *best = find_best(outgoing);
1706 if (!best) {
1707 if (option_debug)
1708 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
1709 break;
1711 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
1712 struct callattempt *cur;
1713 /* Ring everyone who shares this best metric (for ringall) */
1714 for (cur = outgoing; cur; cur = cur->q_next) {
1715 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
1716 if (option_debug)
1717 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
1718 ring_entry(qe, cur, busies);
1721 } else {
1722 /* Ring just the best channel */
1723 if (option_debug)
1724 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
1725 ring_entry(qe, best, busies);
1727 if (best->chan) /* break out with result = 1 */
1728 ret = 1;
1731 return ret;
1734 static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
1736 struct callattempt *best = find_best(outgoing);
1738 if (best) {
1739 /* Ring just the best channel */
1740 if (option_debug)
1741 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
1742 qe->parent->rrpos = best->metric % 1000;
1743 } else {
1744 /* Just increment rrpos */
1745 if (qe->parent->wrapped) {
1746 /* No more channels, start over */
1747 qe->parent->rrpos = 0;
1748 } else {
1749 /* Prioritize next entry */
1750 qe->parent->rrpos++;
1753 qe->parent->wrapped = 0;
1755 return 0;
1758 static int background_file(struct queue_ent *qe, struct ast_channel *chan, char *filename)
1760 int res;
1762 ast_stopstream(chan);
1763 res = ast_streamfile(chan, filename, chan->language);
1765 if (!res) {
1766 /* Wait for a keypress */
1767 res = ast_waitstream(chan, AST_DIGIT_ANY);
1768 if (res < 0 || !valid_exit(qe, res))
1769 res = 0;
1771 /* Stop playback */
1772 ast_stopstream(chan);
1775 return res;
1778 static int say_periodic_announcement(struct queue_ent *qe)
1780 int res = 0;
1781 time_t now;
1783 /* Get the current time */
1784 time(&now);
1786 /* Check to see if it is time to announce */
1787 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
1788 return 0;
1790 /* Stop the music on hold so we can play our own file */
1791 ast_moh_stop(qe->chan);
1793 if (option_verbose > 2)
1794 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
1796 /* Check to make sure we have a sound file. If not, reset to the first sound file */
1797 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
1798 qe->last_periodic_announce_sound = 0;
1801 /* play the announcement */
1802 res = background_file(qe, qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
1804 /* Resume Music on Hold if the caller is going to stay in the queue */
1805 if (!res)
1806 ast_moh_start(qe->chan, qe->moh, NULL);
1808 /* update last_periodic_announce_time */
1809 qe->last_periodic_announce_time = now;
1811 /* Update the current periodic announcement to the next announcement */
1812 qe->last_periodic_announce_sound++;
1814 return res;
1817 static void record_abandoned(struct queue_ent *qe)
1819 ast_mutex_lock(&qe->parent->lock);
1820 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
1821 "Queue: %s\r\n"
1822 "Uniqueid: %s\r\n"
1823 "Position: %d\r\n"
1824 "OriginalPosition: %d\r\n"
1825 "HoldTime: %d\r\n",
1826 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
1828 qe->parent->callsabandoned++;
1829 ast_mutex_unlock(&qe->parent->lock);
1832 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
1833 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
1835 if (option_verbose > 2)
1836 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
1837 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
1838 if (qe->parent->autopause) {
1839 if (!set_member_paused(qe->parent->name, interface, 1)) {
1840 if (option_verbose > 2)
1841 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
1842 } else {
1843 if (option_verbose > 2)
1844 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
1847 return;
1850 #define AST_MAX_WATCHERS 256
1852 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
1854 char *queue = qe->parent->name;
1855 struct callattempt *o;
1856 int status;
1857 int sentringing = 0;
1858 int numbusies = prebusies;
1859 int numnochan = 0;
1860 int stillgoing = 0;
1861 int orig = *to;
1862 struct ast_frame *f;
1863 struct callattempt *peer = NULL;
1864 struct ast_channel *winner;
1865 struct ast_channel *in = qe->chan;
1866 char on[80] = "";
1867 char membername[80] = "";
1868 long starttime = 0;
1869 long endtime = 0;
1871 starttime = (long) time(NULL);
1873 while (*to && !peer) {
1874 int numlines, retry, pos = 1;
1875 struct ast_channel *watchers[AST_MAX_WATCHERS];
1876 watchers[0] = in;
1878 for (retry = 0; retry < 2; retry++) {
1879 numlines = 0;
1880 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
1881 if (o->stillgoing) { /* Keep track of important channels */
1882 stillgoing = 1;
1883 if (o->chan)
1884 watchers[pos++] = o->chan;
1886 numlines++;
1888 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
1889 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
1890 break;
1891 /* On "ringall" strategy we only move to the next penalty level
1892 when *all* ringing phones are done in the current penalty level */
1893 ring_one(qe, outgoing, &numbusies);
1894 /* and retry... */
1896 if (pos == 1 /* not found */) {
1897 if (numlines == (numbusies + numnochan)) {
1898 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
1899 } else {
1900 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
1902 *to = 0;
1903 return NULL;
1905 winner = ast_waitfor_n(watchers, pos, to);
1906 for (o = outgoing; o; o = o->q_next) {
1907 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
1908 if (!peer) {
1909 if (option_verbose > 2)
1910 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
1911 peer = o;
1913 } else if (o->chan && (o->chan == winner)) {
1915 ast_copy_string(on, o->member->interface, sizeof(on));
1916 ast_copy_string(membername, o->member->membername, sizeof(membername));
1918 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
1919 if (option_verbose > 2)
1920 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
1921 winner = o->chan = NULL;
1922 } else if (!ast_strlen_zero(o->chan->call_forward)) {
1923 char tmpchan[256];
1924 char *stuff;
1925 char *tech;
1927 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
1928 if ((stuff = strchr(tmpchan, '/'))) {
1929 *stuff++ = '\0';
1930 tech = tmpchan;
1931 } else {
1932 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
1933 stuff = tmpchan;
1934 tech = "Local";
1936 /* Before processing channel, go ahead and check for forwarding */
1937 if (option_verbose > 2)
1938 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
1939 /* Setup parameters */
1940 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
1941 if (status != o->oldstatus)
1942 update_dial_status(qe->parent, o->member, status);
1943 if (!o->chan) {
1944 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
1945 o->stillgoing = 0;
1946 numnochan++;
1947 } else {
1948 ast_channel_inherit_variables(in, o->chan);
1949 if (o->chan->cid.cid_num)
1950 free(o->chan->cid.cid_num);
1951 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
1953 if (o->chan->cid.cid_name)
1954 free(o->chan->cid.cid_name);
1955 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
1957 ast_string_field_set(o->chan, accountcode, in->accountcode);
1958 o->chan->cdrflags = in->cdrflags;
1960 if (in->cid.cid_ani) {
1961 if (o->chan->cid.cid_ani)
1962 free(o->chan->cid.cid_ani);
1963 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
1965 if (o->chan->cid.cid_rdnis)
1966 free(o->chan->cid.cid_rdnis);
1967 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
1968 if (ast_call(o->chan, tmpchan, 0)) {
1969 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
1970 do_hang(o);
1971 numnochan++;
1974 /* Hangup the original channel now, in case we needed it */
1975 ast_hangup(winner);
1976 continue;
1978 f = ast_read(winner);
1979 if (f) {
1980 if (f->frametype == AST_FRAME_CONTROL) {
1981 switch (f->subclass) {
1982 case AST_CONTROL_ANSWER:
1983 /* This is our guy if someone answered. */
1984 if (!peer) {
1985 if (option_verbose > 2)
1986 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
1987 peer = o;
1989 break;
1990 case AST_CONTROL_BUSY:
1991 if (option_verbose > 2)
1992 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
1993 if (in->cdr)
1994 ast_cdr_busy(in->cdr);
1995 do_hang(o);
1996 endtime = (long)time(NULL);
1997 endtime -= starttime;
1998 rna(endtime*1000, qe, on, membername);
1999 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2000 if (qe->parent->timeoutrestart)
2001 *to = orig;
2002 ring_one(qe, outgoing, &numbusies);
2004 numbusies++;
2005 break;
2006 case AST_CONTROL_CONGESTION:
2007 if (option_verbose > 2)
2008 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
2009 if (in->cdr)
2010 ast_cdr_busy(in->cdr);
2011 endtime = (long)time(NULL);
2012 endtime -= starttime;
2013 rna(endtime*1000, qe, on, membername);
2014 do_hang(o);
2015 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2016 if (qe->parent->timeoutrestart)
2017 *to = orig;
2018 ring_one(qe, outgoing, &numbusies);
2020 numbusies++;
2021 break;
2022 case AST_CONTROL_RINGING:
2023 if (option_verbose > 2)
2024 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
2025 if (!sentringing) {
2026 #if 0
2027 ast_indicate(in, AST_CONTROL_RINGING);
2028 #endif
2029 sentringing++;
2031 break;
2032 case AST_CONTROL_OFFHOOK:
2033 /* Ignore going off hook */
2034 break;
2035 default:
2036 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
2039 ast_frfree(f);
2040 } else {
2041 endtime = (long) time(NULL) - starttime;
2042 rna(endtime * 1000, qe, on, membername);
2043 do_hang(o);
2044 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2045 if (qe->parent->timeoutrestart)
2046 *to = orig;
2047 ring_one(qe, outgoing, &numbusies);
2052 if (winner == in) {
2053 f = ast_read(in);
2054 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
2055 /* Got hung up */
2056 *to = -1;
2057 if (f)
2058 ast_frfree(f);
2059 return NULL;
2061 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
2062 if (option_verbose > 3)
2063 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
2064 *to = 0;
2065 ast_frfree(f);
2066 return NULL;
2068 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) {
2069 if (option_verbose > 3)
2070 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
2071 *to = 0;
2072 *digit = f->subclass;
2073 ast_frfree(f);
2074 return NULL;
2076 ast_frfree(f);
2078 if (!*to)
2079 rna(orig, qe, on, membername);
2082 return peer;
2085 static int is_our_turn(struct queue_ent *qe)
2087 struct queue_ent *ch;
2088 struct member *cur;
2089 int avl = 0;
2090 int idx = 0;
2091 int res;
2093 if (!qe->parent->autofill) {
2094 /* Atomically read the parent head -- does not need a lock */
2095 ch = qe->parent->head;
2096 /* If we are now at the top of the head, break out */
2097 if (ch == qe) {
2098 if (option_debug)
2099 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
2100 res = 1;
2101 } else {
2102 if (option_debug)
2103 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
2104 res = 0;
2107 } else {
2108 /* This needs a lock. How many members are available to be served? */
2109 ast_mutex_lock(&qe->parent->lock);
2111 ch = qe->parent->head;
2113 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2114 if (option_debug)
2115 ast_log(LOG_DEBUG, "Even though there are %d available members, the strategy is ringall so only the head call is allowed in\n", avl);
2116 avl = 1;
2117 } else {
2118 for (cur = qe->parent->members; cur; cur = cur->next) {
2119 switch (cur->status) {
2120 case AST_DEVICE_NOT_INUSE:
2121 case AST_DEVICE_UNKNOWN:
2122 avl++;
2123 break;
2128 if (option_debug)
2129 ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
2131 while ((idx < avl) && (ch) && (ch != qe)) {
2132 idx++;
2133 ch = ch->next;
2136 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2137 if (ch && idx < avl) {
2138 if (option_debug)
2139 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
2140 res = 1;
2141 } else {
2142 if (option_debug)
2143 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
2144 res = 0;
2147 ast_mutex_unlock(&qe->parent->lock);
2150 return res;
2153 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
2155 int res = 0;
2157 /* This is the holding pen for callers 2 through maxlen */
2158 for (;;) {
2159 enum queue_member_status stat;
2161 if (is_our_turn(qe))
2162 break;
2164 /* If we have timed out, break out */
2165 if (qe->expire && (time(NULL) > qe->expire)) {
2166 *reason = QUEUE_TIMEOUT;
2167 break;
2170 stat = get_member_status(qe->parent, qe->max_penalty);
2172 /* leave the queue if no agents, if enabled */
2173 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
2174 *reason = QUEUE_LEAVEEMPTY;
2175 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2176 leave_queue(qe);
2177 break;
2180 /* leave the queue if no reachable agents, if enabled */
2181 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
2182 *reason = QUEUE_LEAVEUNAVAIL;
2183 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2184 leave_queue(qe);
2185 break;
2188 /* Make a position announcement, if enabled */
2189 if (qe->parent->announcefrequency && !ringing &&
2190 (res = say_position(qe)))
2191 break;
2193 /* Make a periodic announcement, if enabled */
2194 if (qe->parent->periodicannouncefrequency && !ringing &&
2195 (res = say_periodic_announcement(qe)))
2196 break;
2198 /* Wait a second before checking again */
2199 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000)))
2200 break;
2203 return res;
2206 static int update_queue(struct call_queue *q, struct member *member)
2208 struct member *cur;
2210 /* Since a reload could have taken place, we have to traverse the list to
2211 be sure it's still valid */
2212 ast_mutex_lock(&q->lock);
2213 cur = q->members;
2214 while (cur) {
2215 if (member == cur) {
2216 time(&cur->lastcall);
2217 cur->calls++;
2218 break;
2220 cur = cur->next;
2222 q->callscompleted++;
2223 ast_mutex_unlock(&q->lock);
2224 return 0;
2227 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
2229 if (qe->max_penalty && (mem->penalty > qe->max_penalty))
2230 return -1;
2232 switch (q->strategy) {
2233 case QUEUE_STRATEGY_RINGALL:
2234 /* Everyone equal, except for penalty */
2235 tmp->metric = mem->penalty * 1000000;
2236 break;
2237 case QUEUE_STRATEGY_ROUNDROBIN:
2238 if (!pos) {
2239 if (!q->wrapped) {
2240 /* No more channels, start over */
2241 q->rrpos = 0;
2242 } else {
2243 /* Prioritize next entry */
2244 q->rrpos++;
2246 q->wrapped = 0;
2248 /* Fall through */
2249 case QUEUE_STRATEGY_RRMEMORY:
2250 if (pos < q->rrpos) {
2251 tmp->metric = 1000 + pos;
2252 } else {
2253 if (pos > q->rrpos)
2254 /* Indicate there is another priority */
2255 q->wrapped = 1;
2256 tmp->metric = pos;
2258 tmp->metric += mem->penalty * 1000000;
2259 break;
2260 case QUEUE_STRATEGY_RANDOM:
2261 tmp->metric = ast_random() % 1000;
2262 tmp->metric += mem->penalty * 1000000;
2263 break;
2264 case QUEUE_STRATEGY_FEWESTCALLS:
2265 tmp->metric = mem->calls;
2266 tmp->metric += mem->penalty * 1000000;
2267 break;
2268 case QUEUE_STRATEGY_LEASTRECENT:
2269 if (!mem->lastcall)
2270 tmp->metric = 0;
2271 else
2272 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
2273 tmp->metric += mem->penalty * 1000000;
2274 break;
2275 default:
2276 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
2277 break;
2279 return 0;
2282 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on, const char *agi)
2284 struct member *cur;
2285 struct callattempt *outgoing = NULL; /* the list of calls we are building */
2286 int to;
2287 char oldexten[AST_MAX_EXTENSION]="";
2288 char oldcontext[AST_MAX_CONTEXT]="";
2289 char queuename[256]="";
2290 struct ast_channel *peer;
2291 struct ast_channel *which;
2292 struct callattempt *lpeer;
2293 struct member *member;
2294 struct ast_app *app;
2295 int res = 0, bridge = 0;
2296 int numbusies = 0;
2297 int x=0;
2298 char *announce = NULL;
2299 char digit = 0;
2300 time_t callstart;
2301 time_t now = time(NULL);
2302 struct ast_bridge_config bridge_config;
2303 char nondataquality = 1;
2304 char *agiexec = NULL;
2305 int ret = 0;
2306 const char *monitorfilename;
2307 const char *monitor_exec;
2308 const char *monitor_options;
2309 char tmpid[256], tmpid2[256];
2310 char meid[1024], meid2[1024];
2311 char mixmonargs[1512];
2312 struct ast_app *mixmonapp = NULL;
2313 char *p;
2314 char vars[2048];
2315 int forwardsallowed = 1;
2317 memset(&bridge_config, 0, sizeof(bridge_config));
2318 time(&now);
2320 for (; options && *options; options++)
2321 switch (*options) {
2322 case 't':
2323 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
2324 break;
2325 case 'T':
2326 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
2327 break;
2328 case 'w':
2329 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
2330 break;
2331 case 'W':
2332 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
2333 break;
2334 case 'd':
2335 nondataquality = 0;
2336 break;
2337 case 'h':
2338 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
2339 break;
2340 case 'H':
2341 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
2342 break;
2343 case 'n':
2344 if ((now - qe->start >= qe->parent->timeout))
2345 *go_on = 1;
2346 break;
2347 case 'i':
2348 forwardsallowed = 0;
2349 break;
2352 /* Hold the lock while we setup the outgoing calls */
2353 if (use_weight)
2354 AST_LIST_LOCK(&queues);
2355 ast_mutex_lock(&qe->parent->lock);
2356 if (option_debug)
2357 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
2358 qe->chan->name);
2359 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
2360 cur = qe->parent->members;
2361 if (!ast_strlen_zero(qe->announce))
2362 announce = qe->announce;
2363 if (!ast_strlen_zero(announceoverride))
2364 announce = announceoverride;
2366 for (; cur; cur = cur->next) {
2367 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
2369 if (!tmp) {
2370 ast_mutex_unlock(&qe->parent->lock);
2371 if (use_weight)
2372 AST_LIST_UNLOCK(&queues);
2373 goto out;
2375 tmp->stillgoing = -1;
2376 tmp->member = cur; /* Never directly dereference! Could change on reload */
2377 tmp->oldstatus = cur->status;
2378 tmp->lastcall = cur->lastcall;
2379 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
2380 /* Special case: If we ring everyone, go ahead and ring them, otherwise
2381 just calculate their metric for the appropriate strategy */
2382 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
2383 /* Put them in the list of outgoing thingies... We're ready now.
2384 XXX If we're forcibly removed, these outgoing calls won't get
2385 hung up XXX */
2386 tmp->q_next = outgoing;
2387 outgoing = tmp;
2388 /* If this line is up, don't try anybody else */
2389 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
2390 break;
2391 } else {
2392 free(tmp);
2395 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
2396 to = (qe->expire - now) * 1000;
2397 else
2398 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
2399 ring_one(qe, outgoing, &numbusies);
2400 ast_mutex_unlock(&qe->parent->lock);
2401 if (use_weight)
2402 AST_LIST_UNLOCK(&queues);
2403 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
2404 ast_mutex_lock(&qe->parent->lock);
2405 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
2406 store_next(qe, outgoing);
2408 ast_mutex_unlock(&qe->parent->lock);
2409 peer = lpeer ? lpeer->chan : NULL;
2410 if (!peer) {
2411 if (to) {
2412 /* Must gotten hung up */
2413 res = -1;
2414 } else {
2415 res = digit;
2417 if (option_debug)
2418 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
2419 } else { /* peer is valid */
2420 /* Ah ha! Someone answered within the desired timeframe. Of course after this
2421 we will always return with -1 so that it is hung up properly after the
2422 conversation. */
2423 qe->handled++;
2424 if (!strcmp(qe->chan->tech->type, "Zap"))
2425 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2426 if (!strcmp(peer->tech->type, "Zap"))
2427 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2428 /* Update parameters for the queue */
2429 recalc_holdtime(qe);
2430 member = lpeer->member;
2431 hangupcalls(outgoing, peer);
2432 outgoing = NULL;
2433 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
2434 int res2;
2436 res2 = ast_autoservice_start(qe->chan);
2437 if (!res2) {
2438 if (qe->parent->memberdelay) {
2439 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
2440 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
2442 if (!res2 && announce) {
2443 if (play_file(peer, announce))
2444 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce);
2446 if (!res2 && qe->parent->reportholdtime) {
2447 if (!play_file(peer, qe->parent->sound_reporthold)) {
2448 int holdtime;
2450 time(&now);
2451 holdtime = abs((now - qe->start) / 60);
2452 if (holdtime < 2) {
2453 play_file(peer, qe->parent->sound_lessthan);
2454 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
2455 } else
2456 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
2457 play_file(peer, qe->parent->sound_minutes);
2461 res2 |= ast_autoservice_stop(qe->chan);
2462 if (peer->_softhangup) {
2463 /* Agent must have hung up */
2464 ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name);
2465 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
2466 record_abandoned(qe);
2467 if (qe->parent->eventwhencalled)
2468 manager_event(EVENT_FLAG_AGENT, "AgentDump",
2469 "Queue: %s\r\n"
2470 "Uniqueid: %s\r\n"
2471 "Channel: %s\r\n"
2472 "Member: %s\r\n"
2473 "MemberName: %s\r\n"
2474 "%s",
2475 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2476 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2477 ast_hangup(peer);
2478 goto out;
2479 } else if (res2) {
2480 /* Caller must have hung up just before being connected*/
2481 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
2482 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2483 record_abandoned(qe);
2484 ast_hangup(peer);
2485 return -1;
2488 /* Stop music on hold */
2489 ast_moh_stop(qe->chan);
2490 /* If appropriate, log that we have a destination channel */
2491 if (qe->chan->cdr)
2492 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
2493 /* Make sure channels are compatible */
2494 res = ast_channel_make_compatible(qe->chan, peer);
2495 if (res < 0) {
2496 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
2497 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
2498 record_abandoned(qe);
2499 ast_hangup(peer);
2500 return -1;
2502 /* Begin Monitoring */
2503 if (qe->parent->monfmt && *qe->parent->monfmt) {
2504 if (!qe->parent->montype) {
2505 if (option_debug)
2506 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
2507 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
2508 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
2509 which = qe->chan;
2510 else
2511 which = peer;
2512 if (monitorfilename)
2513 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
2514 else if (qe->chan->cdr)
2515 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
2516 else {
2517 /* Last ditch effort -- no CDR, make up something */
2518 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
2519 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
2521 if (qe->parent->monjoin)
2522 ast_monitor_setjoinfiles(which, 1);
2523 } else {
2524 if (option_debug)
2525 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
2526 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
2527 if (!monitorfilename) {
2528 if (qe->chan->cdr)
2529 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
2530 else
2531 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
2532 } else {
2533 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
2534 for (p = tmpid2; *p ; p++) {
2535 if (*p == '^' && *(p+1) == '{') {
2536 *p = '$';
2540 memset(tmpid, 0, sizeof(tmpid));
2541 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
2544 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
2545 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
2547 if (monitor_exec) {
2548 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
2549 for (p = meid2; *p ; p++) {
2550 if (*p == '^' && *(p+1) == '{') {
2551 *p = '$';
2555 memset(meid, 0, sizeof(meid));
2556 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
2559 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
2561 mixmonapp = pbx_findapp("MixMonitor");
2563 if (strchr(tmpid2, '|')) {
2564 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
2565 mixmonapp = NULL;
2568 if (!monitor_options)
2569 monitor_options = "";
2571 if (strchr(monitor_options, '|')) {
2572 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
2573 mixmonapp = NULL;
2576 if (mixmonapp) {
2577 if (!ast_strlen_zero(monitor_exec) && !ast_strlen_zero(monitor_options))
2578 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
2579 else
2580 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
2582 if (option_debug)
2583 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
2585 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
2587 } else
2588 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
2592 /* Drop out of the queue at this point, to prepare for next caller */
2593 leave_queue(qe);
2594 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
2595 if (option_debug)
2596 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
2597 ast_channel_sendurl(peer, url);
2599 if (qe->parent->setinterfacevar)
2600 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
2601 if (!ast_strlen_zero(agi)) {
2602 if (option_debug)
2603 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
2604 app = pbx_findapp("agi");
2605 if (app) {
2606 agiexec = ast_strdupa(agi);
2607 ret = pbx_exec(qe->chan, app, agiexec);
2608 } else
2609 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
2611 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
2612 if (qe->parent->eventwhencalled)
2613 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
2614 "Queue: %s\r\n"
2615 "Uniqueid: %s\r\n"
2616 "Channel: %s\r\n"
2617 "Member: %s\r\n"
2618 "MemberName: %s\r\n"
2619 "Holdtime: %ld\r\n"
2620 "BridgedChannel: %s\r\n"
2621 "%s",
2622 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2623 (long)time(NULL) - qe->start, peer->uniqueid,
2624 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2625 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
2626 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
2627 time(&callstart);
2629 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
2631 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
2632 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
2633 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
2634 (long) (time(NULL) - callstart));
2635 } else if (qe->chan->_softhangup) {
2636 ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld",
2637 (long) (callstart - qe->start), (long) (time(NULL) - callstart));
2638 if (qe->parent->eventwhencalled)
2639 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
2640 "Queue: %s\r\n"
2641 "Uniqueid: %s\r\n"
2642 "Channel: %s\r\n"
2643 "Member: %s\r\n"
2644 "MemberName: %s\r\n"
2645 "HoldTime: %ld\r\n"
2646 "TalkTime: %ld\r\n"
2647 "Reason: caller\r\n"
2648 "%s",
2649 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2650 (long)(callstart - qe->start), (long)(time(NULL) - callstart),
2651 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2652 } else {
2653 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld",
2654 (long) (callstart - qe->start), (long) (time(NULL) - callstart));
2655 if (qe->parent->eventwhencalled)
2656 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
2657 "Queue: %s\r\n"
2658 "Uniqueid: %s\r\n"
2659 "Channel: %s\r\n"
2660 "MemberName: %s\r\n"
2661 "HoldTime: %ld\r\n"
2662 "TalkTime: %ld\r\n"
2663 "Reason: agent\r\n"
2664 "%s",
2665 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
2666 (long)(time(NULL) - callstart),
2667 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2670 if (bridge != AST_PBX_NO_HANGUP_PEER)
2671 ast_hangup(peer);
2672 update_queue(qe->parent, member);
2673 res = bridge ? bridge : 1;
2675 out:
2676 hangupcalls(outgoing, NULL);
2678 return res;
2681 static int wait_a_bit(struct queue_ent *qe)
2683 /* Don't need to hold the lock while we setup the outgoing calls */
2684 int retrywait = qe->parent->retry * 1000;
2686 return ast_waitfordigit(qe->chan, retrywait);
2689 static struct member *interface_exists(struct call_queue *q, char *interface)
2691 struct member *mem;
2693 if (!q)
2694 return NULL;
2696 for (mem = q->members; mem; mem = mem->next) {
2697 if (!strcasecmp(interface, mem->interface))
2698 return mem;
2701 return NULL;
2705 /* Dump all members in a specific queue to the database
2707 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
2710 static void dump_queue_members(struct call_queue *pm_queue)
2712 struct member *cur_member;
2713 char value[PM_MAX_LEN];
2714 int value_len = 0;
2715 int res;
2717 memset(value, 0, sizeof(value));
2719 if (!pm_queue)
2720 return;
2722 for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) {
2723 if (!cur_member->dynamic)
2724 continue;
2726 res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d;%s%s",
2727 cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername,
2728 cur_member->next ? "|" : "");
2729 if (res != strlen(value + value_len)) {
2730 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
2731 break;
2733 value_len += res;
2736 if (value_len && !cur_member) {
2737 if (ast_db_put(pm_family, pm_queue->name, value))
2738 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
2739 } else
2740 /* Delete the entry if the queue is empty or there is an error */
2741 ast_db_del(pm_family, pm_queue->name);
2744 static int remove_from_queue(char *queuename, char *interface)
2746 struct call_queue *q;
2747 struct member *last_member, *look;
2748 int res = RES_NOSUCHQUEUE;
2750 AST_LIST_LOCK(&queues);
2751 AST_LIST_TRAVERSE(&queues, q, list) {
2752 ast_mutex_lock(&q->lock);
2753 if (strcmp(q->name, queuename)) {
2754 ast_mutex_unlock(&q->lock);
2755 continue;
2758 if ((last_member = interface_exists(q, interface))) {
2759 if ((look = q->members) == last_member) {
2760 q->members = last_member->next;
2761 } else {
2762 while (look != NULL) {
2763 if (look->next == last_member) {
2764 look->next = last_member->next;
2765 break;
2766 } else {
2767 look = look->next;
2771 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
2772 "Queue: %s\r\n"
2773 "Location: %s\r\n"
2774 "MemberName: %s\r\n",
2775 q->name, last_member->interface, last_member->membername);
2776 free(last_member);
2778 if (queue_persistent_members)
2779 dump_queue_members(q);
2781 res = RES_OKAY;
2782 } else {
2783 res = RES_EXISTS;
2785 ast_mutex_unlock(&q->lock);
2786 break;
2789 if (res == RES_OKAY)
2790 remove_from_interfaces(interface);
2792 AST_LIST_UNLOCK(&queues);
2794 return res;
2798 static int add_to_queue(char *queuename, char *interface, char *membername, int penalty, int paused, int dump)
2800 struct call_queue *q;
2801 struct member *new_member;
2802 int res = RES_NOSUCHQUEUE;
2804 /* \note Ensure the appropriate realtime queue is loaded. Note that this
2805 * short-circuits if the queue is already in memory. */
2806 if (!(q = load_realtime_queue(queuename)))
2807 return res;
2809 AST_LIST_LOCK(&queues);
2811 ast_mutex_lock(&q->lock);
2812 if (interface_exists(q, interface) == NULL) {
2813 add_to_interfaces(interface);
2814 if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
2815 new_member->dynamic = 1;
2816 new_member->next = q->members;
2817 q->members = new_member;
2818 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
2819 "Queue: %s\r\n"
2820 "Location: %s\r\n"
2821 "MemberName: %s\r\n"
2822 "Membership: %s\r\n"
2823 "Penalty: %d\r\n"
2824 "CallsTaken: %d\r\n"
2825 "LastCall: %d\r\n"
2826 "Status: %d\r\n"
2827 "Paused: %d\r\n",
2828 q->name, new_member->interface, new_member->membername,
2829 new_member->dynamic ? "dynamic" : "static",
2830 new_member->penalty, new_member->calls, (int) new_member->lastcall,
2831 new_member->status, new_member->paused);
2833 if (dump)
2834 dump_queue_members(q);
2836 res = RES_OKAY;
2837 } else {
2838 res = RES_OUTOFMEMORY;
2840 } else {
2841 res = RES_EXISTS;
2843 ast_mutex_unlock(&q->lock);
2844 AST_LIST_UNLOCK(&queues);
2846 return res;
2849 static int set_member_paused(char *queuename, char *interface, int paused)
2851 int found = 0;
2852 struct call_queue *q;
2853 struct member *mem;
2855 /* Special event for when all queues are paused - individual events still generated */
2856 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
2857 if (ast_strlen_zero(queuename))
2858 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
2860 AST_LIST_LOCK(&queues);
2861 AST_LIST_TRAVERSE(&queues, q, list) {
2862 ast_mutex_lock(&q->lock);
2863 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
2864 if ((mem = interface_exists(q, interface))) {
2865 found++;
2866 if (mem->paused == paused)
2867 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
2868 mem->paused = paused;
2870 if (queue_persistent_members)
2871 dump_queue_members(q);
2873 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
2875 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
2876 "Queue: %s\r\n"
2877 "Location: %s\r\n"
2878 "MemberName: %s\r\n"
2879 "Paused: %d\r\n",
2880 q->name, mem->interface, mem->membername, paused);
2883 ast_mutex_unlock(&q->lock);
2885 AST_LIST_UNLOCK(&queues);
2887 return found ? RESULT_SUCCESS : RESULT_FAILURE;
2890 /* Reload dynamic queue members persisted into the astdb */
2891 static void reload_queue_members(void)
2893 char *cur_ptr;
2894 char *queue_name;
2895 char *member;
2896 char *interface;
2897 char *membername;
2898 char *penalty_tok;
2899 int penalty = 0;
2900 char *paused_tok;
2901 int paused = 0;
2902 struct ast_db_entry *db_tree;
2903 struct ast_db_entry *entry;
2904 struct call_queue *cur_queue;
2905 char queue_data[PM_MAX_LEN];
2907 AST_LIST_LOCK(&queues);
2909 /* Each key in 'pm_family' is the name of a queue */
2910 db_tree = ast_db_gettree(pm_family, NULL);
2911 for (entry = db_tree; entry; entry = entry->next) {
2913 queue_name = entry->key + strlen(pm_family) + 2;
2915 AST_LIST_TRAVERSE(&queues, cur_queue, list) {
2916 ast_mutex_lock(&cur_queue->lock);
2917 if (!strcmp(queue_name, cur_queue->name))
2918 break;
2919 ast_mutex_unlock(&cur_queue->lock);
2922 if (!cur_queue) {
2923 /* If the queue no longer exists, remove it from the
2924 * database */
2925 ast_db_del(pm_family, queue_name);
2926 continue;
2927 } else
2928 ast_mutex_unlock(&cur_queue->lock);
2930 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
2931 continue;
2933 cur_ptr = queue_data;
2934 while ((member = strsep(&cur_ptr, "|"))) {
2935 if (ast_strlen_zero(member))
2936 continue;
2938 interface = strsep(&member, ";");
2939 penalty_tok = strsep(&member, ";");
2940 paused_tok = strsep(&member, ";");
2941 membername = strsep(&member, ";");
2943 if (!penalty_tok) {
2944 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
2945 break;
2947 penalty = strtol(penalty_tok, NULL, 10);
2948 if (errno == ERANGE) {
2949 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
2950 break;
2953 if (!paused_tok) {
2954 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
2955 break;
2957 paused = strtol(paused_tok, NULL, 10);
2958 if ((errno == ERANGE) || paused < 0 || paused > 1) {
2959 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
2960 break;
2962 if (ast_strlen_zero(membername))
2963 membername = interface;
2965 if (option_debug)
2966 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
2968 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
2969 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
2970 break;
2975 AST_LIST_UNLOCK(&queues);
2976 if (db_tree) {
2977 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
2978 ast_db_freetree(db_tree);
2982 static int pqm_exec(struct ast_channel *chan, void *data)
2984 struct ast_module_user *lu;
2985 char *parse;
2986 int priority_jump = 0;
2987 AST_DECLARE_APP_ARGS(args,
2988 AST_APP_ARG(queuename);
2989 AST_APP_ARG(interface);
2990 AST_APP_ARG(options);
2993 if (ast_strlen_zero(data)) {
2994 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
2995 return -1;
2998 parse = ast_strdupa(data);
3000 AST_STANDARD_APP_ARGS(args, parse);
3002 lu = ast_module_user_add(chan);
3004 if (args.options) {
3005 if (strchr(args.options, 'j'))
3006 priority_jump = 1;
3009 if (ast_strlen_zero(args.interface)) {
3010 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3011 ast_module_user_remove(lu);
3012 return -1;
3015 if (set_member_paused(args.queuename, args.interface, 1)) {
3016 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
3017 if (priority_jump || ast_opt_priority_jumping) {
3018 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3019 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3020 ast_module_user_remove(lu);
3021 return 0;
3024 ast_module_user_remove(lu);
3025 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3026 return -1;
3029 ast_module_user_remove(lu);
3030 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
3032 return 0;
3035 static int upqm_exec(struct ast_channel *chan, void *data)
3037 struct ast_module_user *lu;
3038 char *parse;
3039 int priority_jump = 0;
3040 AST_DECLARE_APP_ARGS(args,
3041 AST_APP_ARG(queuename);
3042 AST_APP_ARG(interface);
3043 AST_APP_ARG(options);
3046 if (ast_strlen_zero(data)) {
3047 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3048 return -1;
3051 parse = ast_strdupa(data);
3053 AST_STANDARD_APP_ARGS(args, parse);
3055 lu = ast_module_user_add(chan);
3057 if (args.options) {
3058 if (strchr(args.options, 'j'))
3059 priority_jump = 1;
3062 if (ast_strlen_zero(args.interface)) {
3063 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3064 ast_module_user_remove(lu);
3065 return -1;
3068 if (set_member_paused(args.queuename, args.interface, 0)) {
3069 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
3070 if (priority_jump || ast_opt_priority_jumping) {
3071 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3072 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3073 ast_module_user_remove(lu);
3074 return 0;
3077 ast_module_user_remove(lu);
3078 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3079 return -1;
3082 ast_module_user_remove(lu);
3083 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
3085 return 0;
3088 static int rqm_exec(struct ast_channel *chan, void *data)
3090 int res=-1;
3091 struct ast_module_user *lu;
3092 char *parse, *temppos = NULL;
3093 int priority_jump = 0;
3094 AST_DECLARE_APP_ARGS(args,
3095 AST_APP_ARG(queuename);
3096 AST_APP_ARG(interface);
3097 AST_APP_ARG(options);
3101 if (ast_strlen_zero(data)) {
3102 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
3103 return -1;
3106 parse = ast_strdupa(data);
3108 AST_STANDARD_APP_ARGS(args, parse);
3110 lu = ast_module_user_add(chan);
3112 if (ast_strlen_zero(args.interface)) {
3113 args.interface = ast_strdupa(chan->name);
3114 temppos = strrchr(args.interface, '-');
3115 if (temppos)
3116 *temppos = '\0';
3119 if (args.options) {
3120 if (strchr(args.options, 'j'))
3121 priority_jump = 1;
3124 switch (remove_from_queue(args.queuename, args.interface)) {
3125 case RES_OKAY:
3126 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
3127 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
3128 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
3129 res = 0;
3130 break;
3131 case RES_EXISTS:
3132 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
3133 if (priority_jump || ast_opt_priority_jumping)
3134 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3135 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
3136 res = 0;
3137 break;
3138 case RES_NOSUCHQUEUE:
3139 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
3140 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
3141 res = 0;
3142 break;
3145 ast_module_user_remove(lu);
3147 return res;
3150 static int aqm_exec(struct ast_channel *chan, void *data)
3152 int res=-1;
3153 struct ast_module_user *lu;
3154 char *parse, *temppos = NULL;
3155 int priority_jump = 0;
3156 AST_DECLARE_APP_ARGS(args,
3157 AST_APP_ARG(queuename);
3158 AST_APP_ARG(interface);
3159 AST_APP_ARG(penalty);
3160 AST_APP_ARG(options);
3161 AST_APP_ARG(membername);
3163 int penalty = 0;
3165 if (ast_strlen_zero(data)) {
3166 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
3167 return -1;
3170 parse = ast_strdupa(data);
3172 AST_STANDARD_APP_ARGS(args, parse);
3174 lu = ast_module_user_add(chan);
3176 if (ast_strlen_zero(args.interface)) {
3177 args.interface = ast_strdupa(chan->name);
3178 temppos = strrchr(args.interface, '-');
3179 if (temppos)
3180 *temppos = '\0';
3183 if (!ast_strlen_zero(args.penalty)) {
3184 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
3185 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
3186 penalty = 0;
3190 if (args.options) {
3191 if (strchr(args.options, 'j'))
3192 priority_jump = 1;
3195 if (ast_strlen_zero(args.membername))
3196 args.membername = args.interface;
3199 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
3200 case RES_OKAY:
3201 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
3202 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
3203 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
3204 res = 0;
3205 break;
3206 case RES_EXISTS:
3207 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
3208 if (priority_jump || ast_opt_priority_jumping)
3209 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3210 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
3211 res = 0;
3212 break;
3213 case RES_NOSUCHQUEUE:
3214 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
3215 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
3216 res = 0;
3217 break;
3218 case RES_OUTOFMEMORY:
3219 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
3220 break;
3223 ast_module_user_remove(lu);
3225 return res;
3228 static int ql_exec(struct ast_channel *chan, void *data)
3230 struct ast_module_user *u;
3231 char *parse;
3233 AST_DECLARE_APP_ARGS(args,
3234 AST_APP_ARG(queuename);
3235 AST_APP_ARG(uniqueid);
3236 AST_APP_ARG(membername);
3237 AST_APP_ARG(event);
3238 AST_APP_ARG(params);
3241 if (ast_strlen_zero(data)) {
3242 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
3243 return -1;
3246 u = ast_module_user_add(chan);
3248 parse = ast_strdupa(data);
3250 AST_STANDARD_APP_ARGS(args, parse);
3252 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
3253 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
3254 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
3255 ast_module_user_remove(u);
3256 return -1;
3259 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
3260 "%s", args.params ? args.params : "");
3262 ast_module_user_remove(u);
3264 return 0;
3267 static int queue_exec(struct ast_channel *chan, void *data)
3269 int res=-1;
3270 int ringing=0;
3271 struct ast_module_user *lu;
3272 const char *user_priority;
3273 const char *max_penalty_str;
3274 int prio;
3275 int max_penalty;
3276 enum queue_result reason = QUEUE_UNKNOWN;
3277 /* whether to exit Queue application after the timeout hits */
3278 int go_on = 0;
3279 char *parse;
3280 AST_DECLARE_APP_ARGS(args,
3281 AST_APP_ARG(queuename);
3282 AST_APP_ARG(options);
3283 AST_APP_ARG(url);
3284 AST_APP_ARG(announceoverride);
3285 AST_APP_ARG(queuetimeoutstr);
3286 AST_APP_ARG(agi);
3288 /* Our queue entry */
3289 struct queue_ent qe;
3291 if (ast_strlen_zero(data)) {
3292 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
3293 return -1;
3296 parse = ast_strdupa(data);
3297 AST_STANDARD_APP_ARGS(args, parse);
3299 lu = ast_module_user_add(chan);
3301 /* Setup our queue entry */
3302 memset(&qe, 0, sizeof(qe));
3303 qe.start = time(NULL);
3305 /* set the expire time based on the supplied timeout; */
3306 if (args.queuetimeoutstr)
3307 qe.expire = qe.start + atoi(args.queuetimeoutstr);
3308 else
3309 qe.expire = 0;
3311 /* Get the priority from the variable ${QUEUE_PRIO} */
3312 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
3313 if (user_priority) {
3314 if (sscanf(user_priority, "%d", &prio) == 1) {
3315 if (option_debug)
3316 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
3317 chan->name, prio);
3318 } else {
3319 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
3320 user_priority, chan->name);
3321 prio = 0;
3323 } else {
3324 if (option_debug > 2)
3325 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
3326 prio = 0;
3329 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
3330 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
3331 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
3332 if (option_debug)
3333 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
3334 chan->name, max_penalty);
3335 } else {
3336 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
3337 max_penalty_str, chan->name);
3338 max_penalty = 0;
3340 } else {
3341 max_penalty = 0;
3344 if (args.options && (strchr(args.options, 'r')))
3345 ringing = 1;
3347 if (option_debug)
3348 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
3349 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
3351 qe.chan = chan;
3352 qe.prio = prio;
3353 qe.max_penalty = max_penalty;
3354 qe.last_pos_said = 0;
3355 qe.last_pos = 0;
3356 qe.last_periodic_announce_time = time(NULL);
3357 qe.last_periodic_announce_sound = 0;
3358 if (!join_queue(args.queuename, &qe, &reason)) {
3359 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
3360 S_OR(chan->cid.cid_num, ""));
3361 check_turns:
3362 if (ringing) {
3363 ast_indicate(chan, AST_CONTROL_RINGING);
3364 } else {
3365 ast_moh_start(chan, qe.moh, NULL);
3367 for (;;) {
3368 /* This is the wait loop for callers 2 through maxlen */
3370 res = wait_our_turn(&qe, ringing, &reason);
3371 /* If they hungup, return immediately */
3372 if (res < 0) {
3373 /* Record this abandoned call */
3374 record_abandoned(&qe);
3375 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld",
3376 qe.pos, qe.opos, (long) time(NULL) - qe.start);
3377 if (option_verbose > 2) {
3378 ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", args.queuename);
3380 res = -1;
3381 break;
3383 if (!res)
3384 break;
3385 if (valid_exit(&qe, res)) {
3386 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
3387 break;
3390 if (!res) {
3391 int makeannouncement = 0;
3393 for (;;) {
3394 /* This is the wait loop for the head caller*/
3395 /* To exit, they may get their call answered; */
3396 /* they may dial a digit from the queue context; */
3397 /* or, they may timeout. */
3399 enum queue_member_status stat;
3401 /* Leave if we have exceeded our queuetimeout */
3402 if (qe.expire && (time(NULL) > qe.expire)) {
3403 record_abandoned(&qe);
3404 reason = QUEUE_TIMEOUT;
3405 res = 0;
3406 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3407 break;
3410 if (makeannouncement) {
3411 /* Make a position announcement, if enabled */
3412 if (qe.parent->announcefrequency && !ringing &&
3413 (res = say_position(&qe))) {
3414 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
3415 break;
3419 makeannouncement = 1;
3421 /* Make a periodic announcement, if enabled */
3422 if (qe.parent->periodicannouncefrequency && !ringing &&
3423 (res = say_periodic_announcement(&qe))) {
3424 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos);
3425 break;
3428 /* Try calling all queue members for 'timeout' seconds */
3429 res = try_calling(&qe, args.options, args.announceoverride, args.url, &go_on, args.agi);
3430 if (res) {
3431 if (res < 0) {
3432 if (!qe.handled) {
3433 record_abandoned(&qe);
3434 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
3435 "%d|%d|%ld", qe.pos, qe.opos,
3436 (long) time(NULL) - qe.start);
3438 } else if (valid_exit(&qe, res)) {
3439 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
3440 "%s|%d", qe.digits, qe.pos);
3442 break;
3445 stat = get_member_status(qe.parent, qe.max_penalty);
3447 /* leave the queue if no agents, if enabled */
3448 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
3449 record_abandoned(&qe);
3450 reason = QUEUE_LEAVEEMPTY;
3451 res = 0;
3452 break;
3455 /* leave the queue if no reachable agents, if enabled */
3456 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
3457 record_abandoned(&qe);
3458 reason = QUEUE_LEAVEUNAVAIL;
3459 res = 0;
3460 break;
3463 /* Leave if we have exceeded our queuetimeout */
3464 if (qe.expire && (time(NULL) > qe.expire)) {
3465 record_abandoned(&qe);
3466 reason = QUEUE_TIMEOUT;
3467 res = 0;
3468 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3469 break;
3472 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
3473 res = wait_a_bit(&qe);
3474 if (res < 0) {
3475 record_abandoned(&qe);
3476 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start);
3477 if (option_verbose > 2) {
3478 ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", args.queuename);
3480 res = -1;
3481 break;
3483 if (res && valid_exit(&qe, res)) {
3484 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%s|%d", qe.digits, qe.pos);
3485 break;
3487 /* exit after 'timeout' cycle if 'n' option enabled */
3488 if (go_on) {
3489 if (option_verbose > 2)
3490 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
3491 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3492 record_abandoned(&qe);
3493 reason = QUEUE_TIMEOUT;
3494 res = 0;
3495 break;
3497 /* Since this is a priority queue and
3498 * it is not sure that we are still at the head
3499 * of the queue, go and check for our turn again.
3501 if (!is_our_turn(&qe)) {
3502 if (option_debug)
3503 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
3504 qe.chan->name);
3505 goto check_turns;
3509 /* Don't allow return code > 0 */
3510 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
3511 res = 0;
3512 if (ringing) {
3513 ast_indicate(chan, -1);
3514 } else {
3515 ast_moh_stop(chan);
3517 ast_stopstream(chan);
3519 leave_queue(&qe);
3520 if (reason != QUEUE_UNKNOWN)
3521 set_queue_result(chan, reason);
3522 } else {
3523 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
3524 set_queue_result(chan, reason);
3525 res = 0;
3527 ast_module_user_remove(lu);
3529 return res;
3532 static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
3534 int count = 0;
3535 struct call_queue *q;
3536 struct ast_module_user *lu;
3537 struct member *m;
3539 buf[0] = '\0';
3541 if (ast_strlen_zero(data)) {
3542 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
3543 return -1;
3546 lu = ast_module_user_add(chan);
3548 AST_LIST_LOCK(&queues);
3549 AST_LIST_TRAVERSE(&queues, q, list) {
3550 if (!strcasecmp(q->name, data)) {
3551 ast_mutex_lock(&q->lock);
3552 break;
3555 AST_LIST_UNLOCK(&queues);
3557 if (q) {
3558 for (m = q->members; m; m = m->next) {
3559 /* Count the agents who are logged in and presently answering calls */
3560 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
3561 count++;
3564 ast_mutex_unlock(&q->lock);
3565 } else
3566 ast_log(LOG_WARNING, "queue %s was not found\n", data);
3568 snprintf(buf, len, "%d", count);
3569 ast_module_user_remove(lu);
3571 return 0;
3574 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
3576 int count = 0;
3577 struct call_queue *q;
3578 struct ast_module_user *lu;
3580 buf[0] = '\0';
3582 if (ast_strlen_zero(data)) {
3583 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
3584 return -1;
3587 lu = ast_module_user_add(chan);
3589 AST_LIST_LOCK(&queues);
3590 AST_LIST_TRAVERSE(&queues, q, list) {
3591 if (!strcasecmp(q->name, data)) {
3592 ast_mutex_lock(&q->lock);
3593 break;
3596 AST_LIST_UNLOCK(&queues);
3598 if (q) {
3599 count = q->count;
3600 ast_mutex_unlock(&q->lock);
3601 } else
3602 ast_log(LOG_WARNING, "queue %s was not found\n", data);
3604 snprintf(buf, len, "%d", count);
3605 ast_module_user_remove(lu);
3606 return 0;
3609 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
3611 struct ast_module_user *u;
3612 struct call_queue *q;
3613 struct member *m;
3615 /* Ensure an otherwise empty list doesn't return garbage */
3616 buf[0] = '\0';
3618 if (ast_strlen_zero(data)) {
3619 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
3620 return -1;
3623 u = ast_module_user_add(chan);
3625 AST_LIST_LOCK(&queues);
3626 AST_LIST_TRAVERSE(&queues, q, list) {
3627 if (!strcasecmp(q->name, data)) {
3628 ast_mutex_lock(&q->lock);
3629 break;
3632 AST_LIST_UNLOCK(&queues);
3634 if (q) {
3635 int buflen = 0, count = 0;
3637 for (m = q->members; m; m = m->next) {
3638 /* strcat() is always faster than printf() */
3639 if (count++) {
3640 strncat(buf + buflen, ",", len - buflen - 1);
3641 buflen++;
3643 strncat(buf + buflen, m->interface, len - buflen - 1);
3644 buflen += strlen(m->interface);
3645 /* Safeguard against overflow (negative length) */
3646 if (buflen >= len - 2) {
3647 ast_log(LOG_WARNING, "Truncating list\n");
3648 break;
3651 ast_mutex_unlock(&q->lock);
3652 } else
3653 ast_log(LOG_WARNING, "queue %s was not found\n", data);
3655 /* We should already be terminated, but let's make sure. */
3656 buf[len - 1] = '\0';
3657 ast_module_user_remove(u);
3659 return 0;
3662 static struct ast_custom_function queueagentcount_function = {
3663 .name = "QUEUEAGENTCOUNT",
3664 .synopsis = "Count number of agents answering a queue",
3665 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
3666 .desc =
3667 "Returns the number of members currently associated with the specified queue.\n"
3668 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
3669 .read = queue_function_qac,
3672 static struct ast_custom_function queuemembercount_function = {
3673 .name = "QUEUE_MEMBER_COUNT",
3674 .synopsis = "Count number of members answering a queue",
3675 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
3676 .desc =
3677 "Returns the number of members currently associated with the specified queue.\n",
3678 .read = queue_function_qac,
3681 static struct ast_custom_function queuewaitingcount_function = {
3682 .name = "QUEUE_WAITING_COUNT",
3683 .synopsis = "Count number of calls currently waiting in a queue",
3684 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
3685 .desc =
3686 "Returns the number of callers currently waiting in the specified queue.\n",
3687 .read = queue_function_queuewaitingcount,
3690 static struct ast_custom_function queuememberlist_function = {
3691 .name = "QUEUE_MEMBER_LIST",
3692 .synopsis = "Returns a list of interfaces on a queue",
3693 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
3694 .desc =
3695 "Returns a comma-separated list of members associated with the specified queue.\n",
3696 .read = queue_function_queuememberlist,
3699 static int reload_queues(void)
3701 struct call_queue *q;
3702 struct ast_config *cfg;
3703 char *cat, *tmp;
3704 struct ast_variable *var;
3705 struct member *prev, *cur, *newm, *next;
3706 int new;
3707 const char *general_val = NULL;
3708 char parse[80];
3709 char *interface;
3710 char *membername;
3711 int penalty;
3712 AST_DECLARE_APP_ARGS(args,
3713 AST_APP_ARG(interface);
3714 AST_APP_ARG(penalty);
3715 AST_APP_ARG(membername);
3718 if (!(cfg = ast_config_load("queues.conf"))) {
3719 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
3720 return 0;
3722 AST_LIST_LOCK(&queues);
3723 use_weight=0;
3724 /* Mark all queues as dead for the moment */
3725 AST_LIST_TRAVERSE(&queues, q, list)
3726 q->dead = 1;
3728 /* Chug through config file */
3729 cat = NULL;
3730 while ((cat = ast_category_browse(cfg, cat)) ) {
3731 if (!strcasecmp(cat, "general")) {
3732 /* Initialize global settings */
3733 queue_persistent_members = 0;
3734 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
3735 queue_persistent_members = ast_true(general_val);
3736 autofill_default = 0;
3737 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
3738 autofill_default = ast_true(general_val);
3739 montype_default = 0;
3740 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
3741 if (!strcasecmp(general_val, "mixmonitor"))
3742 montype_default = 1;
3743 } else { /* Define queue */
3744 /* Look for an existing one */
3745 AST_LIST_TRAVERSE(&queues, q, list) {
3746 if (!strcmp(q->name, cat))
3747 break;
3749 if (!q) {
3750 /* Make one then */
3751 if (!(q = alloc_queue(cat))) {
3752 /* TODO: Handle memory allocation failure */
3754 new = 1;
3755 } else
3756 new = 0;
3757 if (q) {
3758 if (!new)
3759 ast_mutex_lock(&q->lock);
3760 /* Re-initialize the queue, and clear statistics */
3761 init_queue(q);
3762 clear_queue(q);
3763 for (cur = q->members; cur; cur = cur->next) {
3764 if (!cur->dynamic) {
3765 cur->delme = 1;
3768 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
3769 if (!strcasecmp(var->name, "member")) {
3770 /* Add a new member */
3771 ast_copy_string(parse, var->value, sizeof(parse));
3773 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
3775 interface = args.interface;
3776 if(!ast_strlen_zero(args.penalty)) {
3777 tmp = args.penalty;
3778 while (*tmp && *tmp < 33) tmp++;
3779 penalty = atoi(tmp);
3780 if (penalty < 0) {
3781 penalty = 0;
3783 } else
3784 penalty = 0;
3786 if (!ast_strlen_zero(args.membername)) {
3787 membername = args.membername;
3788 while (*membername && *membername < 33) membername++;
3789 } else
3790 membername = interface;
3792 /* Find the old position in the list */
3793 for (prev = NULL, cur = q->members; cur; prev = cur, cur = cur->next) {
3794 if (!strcmp(cur->interface, interface)) {
3795 break;
3799 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
3801 if (cur) {
3802 /* Delete it now */
3803 newm->next = cur->next;
3804 if (prev) {
3805 prev->next = newm;
3806 } else {
3807 q->members = newm;
3809 free(cur);
3810 } else {
3811 /* Add them to the master int list if necessary */
3812 add_to_interfaces(interface);
3813 newm->next = q->members;
3814 q->members = newm;
3816 } else {
3817 queue_set_param(q, var->name, var->value, var->lineno, 1);
3821 /* Free remaining members marked as delme */
3822 for (prev = NULL, cur = q->members;
3823 cur;
3824 cur = next) {
3825 next = cur->next;
3827 if (!cur->delme) {
3828 prev = cur;
3829 continue;
3832 if (prev)
3833 prev->next = next;
3834 else
3835 q->members = next;
3837 remove_from_interfaces(cur->interface);
3838 free(cur);
3841 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
3842 rr_dep_warning();
3844 if (new) {
3845 AST_LIST_INSERT_HEAD(&queues, q, list);
3846 } else
3847 ast_mutex_unlock(&q->lock);
3851 ast_config_destroy(cfg);
3852 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
3853 if (q->dead) {
3854 AST_LIST_REMOVE_CURRENT(&queues, list);
3855 if (!q->count)
3856 destroy_queue(q);
3857 else
3858 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
3859 } else {
3860 ast_mutex_lock(&q->lock);
3861 for (cur = q->members; cur; cur = cur->next)
3862 cur->status = ast_device_state(cur->interface);
3863 ast_mutex_unlock(&q->lock);
3866 AST_LIST_TRAVERSE_SAFE_END;
3867 AST_LIST_UNLOCK(&queues);
3868 return 1;
3871 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
3873 struct call_queue *q;
3874 struct queue_ent *qe;
3875 struct member *mem;
3876 int pos, queue_show;
3877 time_t now;
3878 char max_buf[80];
3879 char *max;
3880 size_t max_left;
3881 float sl = 0;
3882 char *term = manager ? "\r\n" : "\n";
3884 time(&now);
3885 if (argc == 2)
3886 queue_show = 0;
3887 else if (argc == 3)
3888 queue_show = 1;
3889 else
3890 return RESULT_SHOWUSAGE;
3892 /* We only want to load realtime queues when a specific queue is asked for. */
3893 if (queue_show)
3894 load_realtime_queue(argv[2]);
3896 AST_LIST_LOCK(&queues);
3897 if (AST_LIST_EMPTY(&queues)) {
3898 AST_LIST_UNLOCK(&queues);
3899 if (queue_show) {
3900 if (s)
3901 astman_append(s, "No such queue: %s.%s",argv[2], term);
3902 else
3903 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
3904 } else {
3905 if (s)
3906 astman_append(s, "No queues.%s", term);
3907 else
3908 ast_cli(fd, "No queues.%s", term);
3910 return RESULT_SUCCESS;
3912 AST_LIST_TRAVERSE(&queues, q, list) {
3913 ast_mutex_lock(&q->lock);
3914 if (queue_show) {
3915 if (strcasecmp(q->name, argv[2]) != 0) {
3916 ast_mutex_unlock(&q->lock);
3917 if (!AST_LIST_NEXT(q, list)) {
3918 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
3919 break;
3921 continue;
3924 max_buf[0] = '\0';
3925 max = max_buf;
3926 max_left = sizeof(max_buf);
3927 if (q->maxlen)
3928 ast_build_string(&max, &max_left, "%d", q->maxlen);
3929 else
3930 ast_build_string(&max, &max_left, "unlimited");
3931 sl = 0;
3932 if (q->callscompleted > 0)
3933 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
3934 if (s)
3935 astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
3936 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
3937 q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
3938 else
3939 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
3940 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
3941 if (q->members) {
3942 if (s)
3943 astman_append(s, " Members: %s", term);
3944 else
3945 ast_cli(fd, " Members: %s", term);
3946 for (mem = q->members; mem; mem = mem->next) {
3947 max_buf[0] = '\0';
3948 max = max_buf;
3949 max_left = sizeof(max_buf);
3950 if (mem->penalty)
3951 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
3952 if (mem->dynamic)
3953 ast_build_string(&max, &max_left, " (dynamic)");
3954 if (mem->paused)
3955 ast_build_string(&max, &max_left, " (paused)");
3956 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
3957 if (mem->calls) {
3958 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
3959 mem->calls, (long) (time(NULL) - mem->lastcall));
3960 } else
3961 ast_build_string(&max, &max_left, " has taken no calls yet");
3962 if (s)
3963 astman_append(s, " %s%s%s", mem->interface, max_buf, term);
3964 else
3965 ast_cli(fd, " %s%s%s", mem->interface, max_buf, term);
3967 } else if (s)
3968 astman_append(s, " No Members%s", term);
3969 else
3970 ast_cli(fd, " No Members%s", term);
3971 if (q->head) {
3972 pos = 1;
3973 if (s)
3974 astman_append(s, " Callers: %s", term);
3975 else
3976 ast_cli(fd, " Callers: %s", term);
3977 for (qe = q->head; qe; qe = qe->next) {
3978 if (s)
3979 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
3980 pos++, qe->chan->name, (long) (now - qe->start) / 60,
3981 (long) (now - qe->start) % 60, qe->prio, term);
3982 else
3983 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
3984 qe->chan->name, (long) (now - qe->start) / 60,
3985 (long) (now - qe->start) % 60, qe->prio, term);
3987 } else if (s)
3988 astman_append(s, " No Callers%s", term);
3989 else
3990 ast_cli(fd, " No Callers%s", term);
3991 if (s)
3992 astman_append(s, "%s", term);
3993 else
3994 ast_cli(fd, "%s", term);
3995 ast_mutex_unlock(&q->lock);
3996 if (queue_show)
3997 break;
3999 AST_LIST_UNLOCK(&queues);
4000 return RESULT_SUCCESS;
4003 static int queue_show(int fd, int argc, char **argv)
4005 return __queues_show(NULL, 0, fd, argc, argv);
4008 static char *complete_queue(const char *line, const char *word, int pos, int state)
4010 struct call_queue *q;
4011 char *ret = NULL;
4012 int which = 0;
4013 int wordlen = strlen(word);
4015 AST_LIST_LOCK(&queues);
4016 AST_LIST_TRAVERSE(&queues, q, list) {
4017 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
4018 ret = ast_strdup(q->name);
4019 break;
4022 AST_LIST_UNLOCK(&queues);
4024 return ret;
4027 /*!\brief callback to display queues status in manager
4028 \addtogroup Group_AMI
4030 static int manager_queues_show( struct mansession *s, struct message *m )
4032 char *a[] = { "queue", "show" };
4034 __queues_show(s, 1, -1, 2, a);
4035 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
4037 return RESULT_SUCCESS;
4040 /* Dump queue status */
4041 static int manager_queues_status( struct mansession *s, struct message *m )
4043 time_t now;
4044 int pos;
4045 char *id = astman_get_header(m,"ActionID");
4046 char *queuefilter = astman_get_header(m,"Queue");
4047 char *memberfilter = astman_get_header(m,"Member");
4048 char idText[256] = "";
4049 struct call_queue *q;
4050 struct queue_ent *qe;
4051 float sl = 0;
4052 struct member *mem;
4054 astman_send_ack(s, m, "Queue status will follow");
4055 time(&now);
4056 AST_LIST_LOCK(&queues);
4057 if (!ast_strlen_zero(id))
4058 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
4060 AST_LIST_TRAVERSE(&queues, q, list) {
4061 ast_mutex_lock(&q->lock);
4063 /* List queue properties */
4064 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
4065 if (q->callscompleted > 0)
4066 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4067 astman_append(s, "Event: QueueParams\r\n"
4068 "Queue: %s\r\n"
4069 "Max: %d\r\n"
4070 "Calls: %d\r\n"
4071 "Holdtime: %d\r\n"
4072 "Completed: %d\r\n"
4073 "Abandoned: %d\r\n"
4074 "ServiceLevel: %d\r\n"
4075 "ServicelevelPerf: %2.1f\r\n"
4076 "Weight: %d\r\n"
4077 "%s"
4078 "\r\n",
4079 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
4080 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
4081 /* List Queue Members */
4082 for (mem = q->members; mem; mem = mem->next) {
4083 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
4084 astman_append(s, "Event: QueueMember\r\n"
4085 "Queue: %s\r\n"
4086 "Location: %s\r\n"
4087 "Membership: %s\r\n"
4088 "Penalty: %d\r\n"
4089 "CallsTaken: %d\r\n"
4090 "LastCall: %d\r\n"
4091 "Status: %d\r\n"
4092 "Paused: %d\r\n"
4093 "%s"
4094 "\r\n",
4095 q->name, mem->interface, mem->dynamic ? "dynamic" : "static",
4096 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
4099 /* List Queue Entries */
4100 pos = 1;
4101 for (qe = q->head; qe; qe = qe->next) {
4102 astman_append(s, "Event: QueueEntry\r\n"
4103 "Queue: %s\r\n"
4104 "Position: %d\r\n"
4105 "Channel: %s\r\n"
4106 "CallerID: %s\r\n"
4107 "CallerIDName: %s\r\n"
4108 "Wait: %ld\r\n"
4109 "%s"
4110 "\r\n",
4111 q->name, pos++, qe->chan->name,
4112 S_OR(qe->chan->cid.cid_num, "unknown"),
4113 S_OR(qe->chan->cid.cid_name, "unknown"),
4114 (long) (now - qe->start), idText);
4117 ast_mutex_unlock(&q->lock);
4120 astman_append(s,
4121 "Event: QueueStatusComplete\r\n"
4122 "%s"
4123 "\r\n",idText);
4125 AST_LIST_UNLOCK(&queues);
4128 return RESULT_SUCCESS;
4131 static int manager_add_queue_member(struct mansession *s, struct message *m)
4133 char *queuename, *interface, *penalty_s, *paused_s, *membername;
4134 int paused, penalty = 0;
4136 queuename = astman_get_header(m, "Queue");
4137 interface = astman_get_header(m, "Interface");
4138 penalty_s = astman_get_header(m, "Penalty");
4139 paused_s = astman_get_header(m, "Paused");
4140 membername = astman_get_header(m, "MemberName");
4142 if (ast_strlen_zero(queuename)) {
4143 astman_send_error(s, m, "'Queue' not specified.");
4144 return 0;
4147 if (ast_strlen_zero(interface)) {
4148 astman_send_error(s, m, "'Interface' not specified.");
4149 return 0;
4152 if (ast_strlen_zero(penalty_s))
4153 penalty = 0;
4154 else if (sscanf(penalty_s, "%d", &penalty) != 1)
4155 penalty = 0;
4157 if (ast_strlen_zero(paused_s))
4158 paused = 0;
4159 else
4160 paused = abs(ast_true(paused_s));
4162 if (ast_strlen_zero(membername))
4163 membername = interface;
4165 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
4166 case RES_OKAY:
4167 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
4168 astman_send_ack(s, m, "Added interface to queue");
4169 break;
4170 case RES_EXISTS:
4171 astman_send_error(s, m, "Unable to add interface: Already there");
4172 break;
4173 case RES_NOSUCHQUEUE:
4174 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
4175 break;
4176 case RES_OUTOFMEMORY:
4177 astman_send_error(s, m, "Out of memory");
4178 break;
4181 return 0;
4184 static int manager_remove_queue_member(struct mansession *s, struct message *m)
4186 char *queuename, *interface;
4188 queuename = astman_get_header(m, "Queue");
4189 interface = astman_get_header(m, "Interface");
4191 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
4192 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
4193 return 0;
4196 switch (remove_from_queue(queuename, interface)) {
4197 case RES_OKAY:
4198 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
4199 astman_send_ack(s, m, "Removed interface from queue");
4200 break;
4201 case RES_EXISTS:
4202 astman_send_error(s, m, "Unable to remove interface: Not there");
4203 break;
4204 case RES_NOSUCHQUEUE:
4205 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
4206 break;
4207 case RES_OUTOFMEMORY:
4208 astman_send_error(s, m, "Out of memory");
4209 break;
4212 return 0;
4215 static int manager_pause_queue_member(struct mansession *s, struct message *m)
4217 char *queuename, *interface, *paused_s;
4218 int paused;
4220 interface = astman_get_header(m, "Interface");
4221 paused_s = astman_get_header(m, "Paused");
4222 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
4224 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
4225 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
4226 return 0;
4229 paused = abs(ast_true(paused_s));
4231 if (set_member_paused(queuename, interface, paused))
4232 astman_send_error(s, m, "Interface not found");
4233 else
4234 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
4235 return 0;
4238 static int handle_queue_add_member(int fd, int argc, char *argv[])
4240 char *queuename, *interface, *membername;
4241 int penalty;
4243 if ((argc != 6) && (argc != 8) && (argc != 10)) {
4244 return RESULT_SHOWUSAGE;
4245 } else if (strcmp(argv[4], "to")) {
4246 return RESULT_SHOWUSAGE;
4247 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
4248 return RESULT_SHOWUSAGE;
4249 } else if ((argc == 10) && strcmp(argv[8], "as")) {
4250 return RESULT_SHOWUSAGE;
4253 queuename = argv[5];
4254 interface = argv[3];
4255 if (argc >= 8) {
4256 if (sscanf(argv[7], "%d", &penalty) == 1) {
4257 if (penalty < 0) {
4258 ast_cli(fd, "Penalty must be >= 0\n");
4259 penalty = 0;
4261 } else {
4262 ast_cli(fd, "Penalty must be an integer >= 0\n");
4263 penalty = 0;
4265 } else {
4266 penalty = 0;
4269 if (argc >= 10) {
4270 membername = argv[9];
4271 } else {
4272 membername = interface;
4275 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
4276 case RES_OKAY:
4277 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
4278 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
4279 return RESULT_SUCCESS;
4280 case RES_EXISTS:
4281 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
4282 return RESULT_FAILURE;
4283 case RES_NOSUCHQUEUE:
4284 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
4285 return RESULT_FAILURE;
4286 case RES_OUTOFMEMORY:
4287 ast_cli(fd, "Out of memory\n");
4288 return RESULT_FAILURE;
4289 default:
4290 return RESULT_FAILURE;
4294 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
4296 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
4297 switch (pos) {
4298 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
4299 return NULL;
4300 case 4: /* only one possible match, "to" */
4301 return state == 0 ? ast_strdup("to") : NULL;
4302 case 5: /* <queue> */
4303 return complete_queue(line, word, pos, state);
4304 case 6: /* only one possible match, "penalty" */
4305 return state == 0 ? ast_strdup("penalty") : NULL;
4306 case 7:
4307 if (state < 100) { /* 0-99 */
4308 char *num;
4309 if ((num = ast_malloc(3))) {
4310 sprintf(num, "%d", state);
4312 return num;
4313 } else {
4314 return NULL;
4316 case 8: /* only one possible match, "as" */
4317 return state == 0 ? ast_strdup("as") : NULL;
4318 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
4319 return NULL;
4320 default:
4321 return NULL;
4325 static int handle_queue_remove_member(int fd, int argc, char *argv[])
4327 char *queuename, *interface;
4329 if (argc != 6) {
4330 return RESULT_SHOWUSAGE;
4331 } else if (strcmp(argv[4], "from")) {
4332 return RESULT_SHOWUSAGE;
4335 queuename = argv[5];
4336 interface = argv[3];
4338 switch (remove_from_queue(queuename, interface)) {
4339 case RES_OKAY:
4340 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
4341 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
4342 return RESULT_SUCCESS;
4343 case RES_EXISTS:
4344 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
4345 return RESULT_FAILURE;
4346 case RES_NOSUCHQUEUE:
4347 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
4348 return RESULT_FAILURE;
4349 case RES_OUTOFMEMORY:
4350 ast_cli(fd, "Out of memory\n");
4351 return RESULT_FAILURE;
4352 default:
4353 return RESULT_FAILURE;
4357 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
4359 int which = 0;
4360 struct call_queue *q;
4361 struct member *m;
4363 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
4364 if (pos > 5 || pos < 3)
4365 return NULL;
4366 if (pos == 4) /* only one possible match, 'from' */
4367 return state == 0 ? ast_strdup("from") : NULL;
4369 if (pos == 5) /* No need to duplicate code */
4370 return complete_queue(line, word, pos, state);
4372 /* here is the case for 3, <member> */
4373 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
4374 AST_LIST_TRAVERSE(&queues, q, list) {
4375 ast_mutex_lock(&q->lock);
4376 for (m = q->members ; m ; m = m->next) {
4377 if (++which > state) {
4378 ast_mutex_unlock(&q->lock);
4379 return ast_strdup(m->interface);
4382 ast_mutex_unlock(&q->lock);
4386 return NULL;
4389 static char queue_show_usage[] =
4390 "Usage: queue show\n"
4391 " Provides summary information on a specified queue.\n";
4393 static char qam_cmd_usage[] =
4394 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
4396 static char qrm_cmd_usage[] =
4397 "Usage: queue remove member <channel> from <queue>\n";
4399 static struct ast_cli_entry cli_show_queue_deprecated = {
4400 { "show", "queue", NULL },
4401 queue_show, NULL,
4402 NULL, complete_queue };
4404 static struct ast_cli_entry cli_add_queue_member_deprecated = {
4405 { "add", "queue", "member", NULL },
4406 handle_queue_add_member, NULL,
4407 NULL, complete_queue_add_member };
4409 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
4410 { "remove", "queue", "member", NULL },
4411 handle_queue_remove_member, NULL,
4412 NULL, complete_queue_remove_member };
4414 static struct ast_cli_entry cli_queue[] = {
4415 /* Deprecated */
4416 { { "show", "queues", NULL },
4417 queue_show, NULL,
4418 NULL, NULL },
4420 { { "queue", "show", NULL },
4421 queue_show, "Show status of a specified queue",
4422 queue_show_usage, complete_queue, &cli_show_queue_deprecated },
4424 { { "queue", "add", "member", NULL },
4425 handle_queue_add_member, "Add a channel to a specified queue",
4426 qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
4428 { { "queue", "remove", "member", NULL },
4429 handle_queue_remove_member, "Removes a channel from a specified queue",
4430 qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
4433 static int unload_module(void)
4435 int res;
4437 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
4438 res = ast_manager_unregister("QueueStatus");
4439 res |= ast_manager_unregister("Queues");
4440 res |= ast_manager_unregister("QueueStatus");
4441 res |= ast_manager_unregister("QueueAdd");
4442 res |= ast_manager_unregister("QueueRemove");
4443 res |= ast_manager_unregister("QueuePause");
4444 res |= ast_unregister_application(app_aqm);
4445 res |= ast_unregister_application(app_rqm);
4446 res |= ast_unregister_application(app_pqm);
4447 res |= ast_unregister_application(app_upqm);
4448 res |= ast_unregister_application(app_ql);
4449 res |= ast_unregister_application(app);
4450 res |= ast_custom_function_unregister(&queueagentcount_function);
4451 res |= ast_custom_function_unregister(&queuemembercount_function);
4452 res |= ast_custom_function_unregister(&queuememberlist_function);
4453 res |= ast_custom_function_unregister(&queuewaitingcount_function);
4455 ast_module_user_hangup_all();
4457 clear_and_free_interfaces();
4459 return res;
4462 static int load_module(void)
4464 int res;
4465 if(!reload_queues())
4466 return AST_MODULE_LOAD_DECLINE;
4467 if (queue_persistent_members)
4468 reload_queue_members();
4469 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
4470 res = ast_register_application(app, queue_exec, synopsis, descrip);
4471 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
4472 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
4473 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
4474 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
4475 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
4476 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
4477 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
4478 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
4479 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
4480 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
4481 res |= ast_custom_function_register(&queueagentcount_function);
4482 res |= ast_custom_function_register(&queuemembercount_function);
4483 res |= ast_custom_function_register(&queuememberlist_function);
4484 res |= ast_custom_function_register(&queuewaitingcount_function);
4485 res |= ast_devstate_add(statechange_queue, NULL);
4487 return res;
4490 static int reload(void)
4492 reload_queues();
4493 return 0;
4496 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
4497 .load = load_module,
4498 .unload = unload_module,
4499 .reload = reload,