Fix a memory leak in func_curl. Every thread that used this function leaked
[asterisk-bristuff.git] / apps / app_queue.c
blob1ddfc9fb5f13ac690d8732c93a94ce8e68784f32
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 /*** MODULEINFO
60 <depend>res_monitor</depend>
61 ***/
63 #include "asterisk.h"
65 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
67 #include <stdlib.h>
68 #include <errno.h>
69 #include <unistd.h>
70 #include <string.h>
71 #include <stdlib.h>
72 #include <stdio.h>
73 #include <sys/time.h>
74 #include <sys/signal.h>
75 #include <netinet/in.h>
77 #include "asterisk/lock.h"
78 #include "asterisk/file.h"
79 #include "asterisk/logger.h"
80 #include "asterisk/channel.h"
81 #include "asterisk/pbx.h"
82 #include "asterisk/options.h"
83 #include "asterisk/app.h"
84 #include "asterisk/linkedlists.h"
85 #include "asterisk/module.h"
86 #include "asterisk/translate.h"
87 #include "asterisk/say.h"
88 #include "asterisk/features.h"
89 #include "asterisk/musiconhold.h"
90 #include "asterisk/cli.h"
91 #include "asterisk/manager.h"
92 #include "asterisk/config.h"
93 #include "asterisk/monitor.h"
94 #include "asterisk/utils.h"
95 #include "asterisk/causes.h"
96 #include "asterisk/astdb.h"
97 #include "asterisk/devicestate.h"
98 #include "asterisk/stringfields.h"
99 #include "asterisk/astobj2.h"
100 #include "asterisk/global_datastores.h"
102 /* Please read before modifying this file.
103 * There are three locks which are regularly used
104 * throughout this file, the queue list lock, the lock
105 * for each individual queue, and the interface list lock.
106 * Please be extra careful to always lock in the following order
107 * 1) queue list lock
108 * 2) individual queue lock
109 * 3) interface list lock
110 * This order has sort of "evolved" over the lifetime of this
111 * application, but it is now in place this way, so please adhere
112 * to this order!
116 enum {
117 QUEUE_STRATEGY_RINGALL = 0,
118 QUEUE_STRATEGY_ROUNDROBIN,
119 QUEUE_STRATEGY_LEASTRECENT,
120 QUEUE_STRATEGY_FEWESTCALLS,
121 QUEUE_STRATEGY_RANDOM,
122 QUEUE_STRATEGY_RRMEMORY
125 static struct strategy {
126 int strategy;
127 char *name;
128 } strategies[] = {
129 { QUEUE_STRATEGY_RINGALL, "ringall" },
130 { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" },
131 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
132 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
133 { QUEUE_STRATEGY_RANDOM, "random" },
134 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
137 #define DEFAULT_RETRY 5
138 #define DEFAULT_TIMEOUT 15
139 #define RECHECK 1 /* Recheck every second to see we we're at the top yet */
140 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
142 #define RES_OKAY 0 /* Action completed */
143 #define RES_EXISTS (-1) /* Entry already exists */
144 #define RES_OUTOFMEMORY (-2) /* Out of memory */
145 #define RES_NOSUCHQUEUE (-3) /* No such queue */
146 #define RES_NOT_DYNAMIC (-4) /* Member is not dynamic */
148 static char *app = "Queue";
150 static char *synopsis = "Queue a call for a call queue";
152 static char *descrip =
153 " Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI]):\n"
154 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
155 "This application will return to the dialplan if the queue does not exist, or\n"
156 "any of the join options cause the caller to not enter the queue.\n"
157 "The option string may contain zero or more of the following characters:\n"
158 " 'd' -- data-quality (modem) call (minimum delay).\n"
159 " 'h' -- allow callee to hang up by hitting *.\n"
160 " 'H' -- allow caller to hang up by hitting *.\n"
161 " 'n' -- no retries on the timeout; will exit this application and \n"
162 " go to the next step.\n"
163 " 'i' -- ignore call forward requests from queue members and do nothing\n"
164 " when they are requested.\n"
165 " 'r' -- ring instead of playing MOH\n"
166 " 't' -- allow the called user transfer the calling user\n"
167 " 'T' -- to allow the calling user to transfer the call.\n"
168 " 'w' -- allow the called user to write the conversation to disk via Monitor\n"
169 " 'W' -- allow the calling user to write the conversation to disk via Monitor\n"
170 " In addition to transferring the call, a call may be parked and then picked\n"
171 "up by another user.\n"
172 " The optional URL will be sent to the called party if the channel supports\n"
173 "it.\n"
174 " The optional AGI parameter will setup an AGI script to be executed on the \n"
175 "calling party's channel once they are connected to a queue member.\n"
176 " The timeout will cause the queue to fail out after a specified number of\n"
177 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
178 " This application sets the following channel variable upon completion:\n"
179 " QUEUESTATUS The status of the call as a text string, one of\n"
180 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n";
182 static char *app_aqm = "AddQueueMember" ;
183 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
184 static char *app_aqm_descrip =
185 " AddQueueMember(queuename[|interface[|penalty[|options[|membername]]]]):\n"
186 "Dynamically adds interface to an existing queue.\n"
187 "If the interface is already in the queue and there exists an n+101 priority\n"
188 "then it will then jump to this priority. Otherwise it will return an error\n"
189 "The option string may contain zero or more of the following characters:\n"
190 " 'j' -- jump to +101 priority when appropriate.\n"
191 " This application sets the following channel variable upon completion:\n"
192 " AQMSTATUS The status of the attempt to add a queue member as a \n"
193 " text string, one of\n"
194 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
195 "Example: AddQueueMember(techsupport|SIP/3000)\n"
198 static char *app_rqm = "RemoveQueueMember" ;
199 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
200 static char *app_rqm_descrip =
201 " RemoveQueueMember(queuename[|interface[|options]]):\n"
202 "Dynamically removes interface to an existing queue\n"
203 "If the interface is NOT in the queue and there exists an n+101 priority\n"
204 "then it will then jump to this priority. Otherwise it will return an error\n"
205 "The option string may contain zero or more of the following characters:\n"
206 " 'j' -- jump to +101 priority when appropriate.\n"
207 " This application sets the following channel variable upon completion:\n"
208 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
209 " text string, one of\n"
210 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
211 "Example: RemoveQueueMember(techsupport|SIP/3000)\n"
214 static char *app_pqm = "PauseQueueMember" ;
215 static char *app_pqm_synopsis = "Pauses a queue member" ;
216 static char *app_pqm_descrip =
217 " PauseQueueMember([queuename]|interface[|options]):\n"
218 "Pauses (blocks calls for) a queue member.\n"
219 "The given interface will be paused in the given queue. This prevents\n"
220 "any calls from being sent from the queue to the interface until it is\n"
221 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
222 "queuename is given, the interface is paused in every queue it is a\n"
223 "member of. If the interface is not in the named queue, or if no queue\n"
224 "is given and the interface is not in any queue, it will jump to\n"
225 "priority n+101, if it exists and the appropriate options are set.\n"
226 "The application will fail if the interface is not found and no extension\n"
227 "to jump to exists.\n"
228 "The option string may contain zero or more of the following characters:\n"
229 " 'j' -- jump to +101 priority when appropriate.\n"
230 " This application sets the following channel variable upon completion:\n"
231 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
232 " text string, one of\n"
233 " PAUSED | NOTFOUND\n"
234 "Example: PauseQueueMember(|SIP/3000)\n";
236 static char *app_upqm = "UnpauseQueueMember" ;
237 static char *app_upqm_synopsis = "Unpauses a queue member" ;
238 static char *app_upqm_descrip =
239 " UnpauseQueueMember([queuename]|interface[|options]):\n"
240 "Unpauses (resumes calls to) a queue member.\n"
241 "This is the counterpart to PauseQueueMember and operates exactly the\n"
242 "same way, except it unpauses instead of pausing the given interface.\n"
243 "The option string may contain zero or more of the following characters:\n"
244 " 'j' -- jump to +101 priority when appropriate.\n"
245 " This application sets the following channel variable upon completion:\n"
246 " UPQMSTATUS The status of the attempt to unpause a queue \n"
247 " member as a text string, one of\n"
248 " UNPAUSED | NOTFOUND\n"
249 "Example: UnpauseQueueMember(|SIP/3000)\n";
251 static char *app_ql = "QueueLog" ;
252 static char *app_ql_synopsis = "Writes to the queue_log" ;
253 static char *app_ql_descrip =
254 " QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
255 "Allows you to write your own events into the queue log\n"
256 "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n";
258 /*! \brief Persistent Members astdb family */
259 static const char *pm_family = "Queue/PersistentMembers";
260 /* The maximum length of each persistent member queue database entry */
261 #define PM_MAX_LEN 8192
263 /*! \brief queues.conf [general] option */
264 static int queue_persistent_members = 0;
266 /*! \brief queues.conf per-queue weight option */
267 static int use_weight = 0;
269 /*! \brief queues.conf [general] option */
270 static int autofill_default = 0;
272 /*! \brief queues.conf [general] option */
273 static int montype_default = 0;
275 enum queue_result {
276 QUEUE_UNKNOWN = 0,
277 QUEUE_TIMEOUT = 1,
278 QUEUE_JOINEMPTY = 2,
279 QUEUE_LEAVEEMPTY = 3,
280 QUEUE_JOINUNAVAIL = 4,
281 QUEUE_LEAVEUNAVAIL = 5,
282 QUEUE_FULL = 6,
285 const struct {
286 enum queue_result id;
287 char *text;
288 } queue_results[] = {
289 { QUEUE_UNKNOWN, "UNKNOWN" },
290 { QUEUE_TIMEOUT, "TIMEOUT" },
291 { QUEUE_JOINEMPTY,"JOINEMPTY" },
292 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
293 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
294 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
295 { QUEUE_FULL, "FULL" },
298 /*! \brief We define a custom "local user" structure because we
299 use it not only for keeping track of what is in use but
300 also for keeping track of who we're dialing.
302 There are two "links" defined in this structure, q_next and call_next.
303 q_next links ALL defined callattempt structures into a linked list. call_next is
304 a link which allows for a subset of the callattempts to be traversed. This subset
305 is used in wait_for_answer so that irrelevant callattempts are not traversed. This
306 also is helpful so that queue logs are always accurate in the case where a call to
307 a member times out, especially if using the ringall strategy. */
309 struct callattempt {
310 struct callattempt *q_next;
311 struct callattempt *call_next;
312 struct ast_channel *chan;
313 char interface[256];
314 int stillgoing;
315 int metric;
316 int oldstatus;
317 time_t lastcall;
318 struct member *member;
322 struct queue_ent {
323 struct call_queue *parent; /*!< What queue is our parent */
324 char moh[80]; /*!< Name of musiconhold to be used */
325 char announce[80]; /*!< Announcement to play for member when call is answered */
326 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
327 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
328 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
329 int pos; /*!< Where we are in the queue */
330 int prio; /*!< Our priority */
331 int last_pos_said; /*!< Last position we told the user */
332 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
333 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
334 time_t last_pos; /*!< Last time we told the user their position */
335 int opos; /*!< Where we started in the queue */
336 int handled; /*!< Whether our call was handled */
337 int pending; /*!< Non-zero if we are attempting to call a member */
338 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
339 time_t start; /*!< When we started holding */
340 time_t expire; /*!< When this entry should expire (time out of queue) */
341 struct ast_channel *chan; /*!< Our channel */
342 struct queue_ent *next; /*!< The next queue entry */
345 struct member {
346 char interface[80]; /*!< Technology/Location */
347 char membername[80]; /*!< Member name to use in queue logs */
348 int penalty; /*!< Are we a last resort? */
349 int calls; /*!< Number of calls serviced by this member */
350 int dynamic; /*!< Are we dynamically added? */
351 int realtime; /*!< Is this member realtime? */
352 int status; /*!< Status of queue member */
353 int paused; /*!< Are we paused (not accepting calls)? */
354 time_t lastcall; /*!< When last successful call was hungup */
355 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
356 unsigned int delme:1; /*!< Flag to delete entry on reload */
359 struct member_interface {
360 char interface[80];
361 AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
364 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
366 /* values used in multi-bit flags in call_queue */
367 #define QUEUE_EMPTY_NORMAL 1
368 #define QUEUE_EMPTY_STRICT 2
369 #define ANNOUNCEHOLDTIME_ALWAYS 1
370 #define ANNOUNCEHOLDTIME_ONCE 2
371 #define QUEUE_EVENT_VARIABLES 3
373 struct call_queue {
374 ast_mutex_t lock;
375 char name[80]; /*!< Name */
376 char moh[80]; /*!< Music On Hold class to be used */
377 char announce[80]; /*!< Announcement to play when call is answered */
378 char context[AST_MAX_CONTEXT]; /*!< Exit context */
379 unsigned int monjoin:1;
380 unsigned int dead:1;
381 unsigned int joinempty:2;
382 unsigned int eventwhencalled:2;
383 unsigned int leavewhenempty:2;
384 unsigned int ringinuse:1;
385 unsigned int setinterfacevar:1;
386 unsigned int reportholdtime:1;
387 unsigned int wrapped:1;
388 unsigned int timeoutrestart:1;
389 unsigned int announceholdtime:2;
390 int strategy:4;
391 unsigned int maskmemberstatus:1;
392 unsigned int realtime:1;
393 unsigned int found:1;
394 int announcefrequency; /*!< How often to announce their position */
395 int periodicannouncefrequency; /*!< How often to play periodic announcement */
396 int roundingseconds; /*!< How many seconds do we round to? */
397 int holdtime; /*!< Current avg holdtime, based on an exponential average */
398 int callscompleted; /*!< Number of queue calls completed */
399 int callsabandoned; /*!< Number of queue calls abandoned */
400 int servicelevel; /*!< seconds setting for servicelevel*/
401 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
402 char monfmt[8]; /*!< Format to use when recording calls */
403 int montype; /*!< Monitor type Monitor vs. MixMonitor */
404 char sound_next[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
405 char sound_thereare[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */
406 char sound_calls[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
407 char sound_holdtime[80]; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
408 char sound_minutes[80]; /*!< Sound file: "minutes." (def. queue-minutes) */
409 char sound_lessthan[80]; /*!< Sound file: "less-than" (def. queue-lessthan) */
410 char sound_seconds[80]; /*!< Sound file: "seconds." (def. queue-seconds) */
411 char sound_thanks[80]; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
412 char sound_reporthold[80]; /*!< Sound file: "Hold time" (def. queue-reporthold) */
413 char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/*!< Sound files: Custom announce, no default */
415 int count; /*!< How many entries */
416 int maxlen; /*!< Max number of entries */
417 int wrapuptime; /*!< Wrapup Time */
419 int retry; /*!< Retry calling everyone after this amount of time */
420 int timeout; /*!< How long to wait for an answer */
421 int weight; /*!< Respective weight */
422 int autopause; /*!< Auto pause queue members if they fail to answer */
424 /* Queue strategy things */
425 int rrpos; /*!< Round Robin - position */
426 int memberdelay; /*!< Seconds to delay connecting member to caller */
427 int autofill; /*!< Ignore the head call status and ring an available agent */
429 struct ao2_container *members; /*!< Head of the list of members */
430 /*!
431 * \brief Number of members _logged in_
432 * \note There will be members in the members container that are not logged
433 * in, so this can not simply be replaced with ao2_container_count().
435 int membercount;
436 struct queue_ent *head; /*!< Head of the list of callers */
437 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
440 static AST_LIST_HEAD_STATIC(queues, call_queue);
442 static int set_member_paused(const char *queuename, const char *interface, int paused);
444 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
446 static void rr_dep_warning(void)
448 static unsigned int warned = 0;
450 if (!warned) {
451 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
452 warned = 1;
456 static void monjoin_dep_warning(void)
458 static unsigned int warned = 0;
459 if (!warned) {
460 ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
461 warned = 1;
464 /*! \brief sets the QUEUESTATUS channel variable */
465 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
467 int i;
469 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
470 if (queue_results[i].id == res) {
471 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
472 return;
477 static char *int2strat(int strategy)
479 int x;
481 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
482 if (strategy == strategies[x].strategy)
483 return strategies[x].name;
486 return "<unknown>";
489 static int strat2int(const char *strategy)
491 int x;
493 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
494 if (!strcasecmp(strategy, strategies[x].name))
495 return strategies[x].strategy;
498 return -1;
501 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
502 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
504 struct queue_ent *cur;
506 if (!q || !new)
507 return;
508 if (prev) {
509 cur = prev->next;
510 prev->next = new;
511 } else {
512 cur = q->head;
513 q->head = new;
515 new->next = cur;
516 new->parent = q;
517 new->pos = ++(*pos);
518 new->opos = *pos;
521 enum queue_member_status {
522 QUEUE_NO_MEMBERS,
523 QUEUE_NO_REACHABLE_MEMBERS,
524 QUEUE_NORMAL
527 /*! \brief Check if members are available
529 * This function checks to see if members are available to be called. If any member
530 * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
531 * the appropriate reason why is returned
533 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty)
535 struct member *member;
536 struct ao2_iterator mem_iter;
537 enum queue_member_status result = QUEUE_NO_MEMBERS;
539 ast_mutex_lock(&q->lock);
540 mem_iter = ao2_iterator_init(q->members, 0);
541 while ((member = ao2_iterator_next(&mem_iter))) {
542 if (max_penalty && (member->penalty > max_penalty)) {
543 ao2_ref(member, -1);
544 continue;
547 if (member->paused) {
548 ao2_ref(member, -1);
549 continue;
552 switch (member->status) {
553 case AST_DEVICE_INVALID:
554 /* nothing to do */
555 ao2_ref(member, -1);
556 break;
557 case AST_DEVICE_UNAVAILABLE:
558 result = QUEUE_NO_REACHABLE_MEMBERS;
559 ao2_ref(member, -1);
560 break;
561 default:
562 ast_mutex_unlock(&q->lock);
563 ao2_ref(member, -1);
564 return QUEUE_NORMAL;
568 ast_mutex_unlock(&q->lock);
569 return result;
572 struct statechange {
573 AST_LIST_ENTRY(statechange) entry;
574 int state;
575 char dev[0];
578 static int update_status(const char *interface, const int status)
580 struct member *cur;
581 struct ao2_iterator mem_iter;
582 struct call_queue *q;
584 AST_LIST_LOCK(&queues);
585 AST_LIST_TRAVERSE(&queues, q, list) {
586 ast_mutex_lock(&q->lock);
587 mem_iter = ao2_iterator_init(q->members, 0);
588 while ((cur = ao2_iterator_next(&mem_iter))) {
589 char *tmp_interface;
590 char *slash_pos;
591 tmp_interface = ast_strdupa(cur->interface);
592 if ((slash_pos = strchr(tmp_interface, '/')))
593 if ((slash_pos = strchr(slash_pos + 1, '/')))
594 *slash_pos = '\0';
596 if (strcasecmp(interface, tmp_interface)) {
597 ao2_ref(cur, -1);
598 continue;
601 if (cur->status != status) {
602 cur->status = status;
603 if (q->maskmemberstatus) {
604 ao2_ref(cur, -1);
605 continue;
608 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
609 "Queue: %s\r\n"
610 "Location: %s\r\n"
611 "MemberName: %s\r\n"
612 "Membership: %s\r\n"
613 "Penalty: %d\r\n"
614 "CallsTaken: %d\r\n"
615 "LastCall: %d\r\n"
616 "Status: %d\r\n"
617 "Paused: %d\r\n",
618 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
619 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
621 ao2_ref(cur, -1);
623 ast_mutex_unlock(&q->lock);
625 AST_LIST_UNLOCK(&queues);
627 return 0;
630 /*! \brief set a member's status based on device state of that member's interface*/
631 static void *handle_statechange(struct statechange *sc)
633 struct member_interface *curint;
634 char *loc;
635 char *technology;
637 technology = ast_strdupa(sc->dev);
638 loc = strchr(technology, '/');
639 if (loc) {
640 *loc++ = '\0';
641 } else {
642 return NULL;
645 AST_LIST_LOCK(&interfaces);
646 AST_LIST_TRAVERSE(&interfaces, curint, list) {
647 char *interface;
648 char *slash_pos;
649 interface = ast_strdupa(curint->interface);
650 if ((slash_pos = strchr(interface, '/')))
651 if ((slash_pos = strchr(slash_pos + 1, '/')))
652 *slash_pos = '\0';
654 if (!strcasecmp(interface, sc->dev))
655 break;
657 AST_LIST_UNLOCK(&interfaces);
659 if (!curint) {
660 if (option_debug > 2)
661 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));
662 return NULL;
665 if (option_debug)
666 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
668 update_status(sc->dev, sc->state);
670 return NULL;
674 * \brief Data used by the device state thread
676 static struct {
677 /*! Set to 1 to stop the thread */
678 unsigned int stop:1;
679 /*! The device state monitoring thread */
680 pthread_t thread;
681 /*! Lock for the state change queue */
682 ast_mutex_t lock;
683 /*! Condition for the state change queue */
684 ast_cond_t cond;
685 /*! Queue of state changes */
686 AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
687 } device_state = {
688 .thread = AST_PTHREADT_NULL,
691 /*! \brief Consumer of the statechange queue */
692 static void *device_state_thread(void *data)
694 struct statechange *sc = NULL;
696 while (!device_state.stop) {
697 ast_mutex_lock(&device_state.lock);
698 if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
699 ast_cond_wait(&device_state.cond, &device_state.lock);
700 sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
702 ast_mutex_unlock(&device_state.lock);
704 /* Check to see if we were woken up to see the request to stop */
705 if (device_state.stop)
706 break;
708 if (!sc)
709 continue;
711 handle_statechange(sc);
713 free(sc);
714 sc = NULL;
717 if (sc)
718 free(sc);
720 while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
721 free(sc);
723 return NULL;
725 /*! \brief Producer of the statechange queue */
726 static int statechange_queue(const char *dev, int state, void *ign)
728 struct statechange *sc;
730 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
731 return 0;
733 sc->state = state;
734 strcpy(sc->dev, dev);
736 ast_mutex_lock(&device_state.lock);
737 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
738 ast_cond_signal(&device_state.cond);
739 ast_mutex_unlock(&device_state.lock);
741 return 0;
743 /*! \brief allocate space for new queue member and set fields based on parameters passed */
744 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused)
746 struct member *cur;
748 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
749 cur->penalty = penalty;
750 cur->paused = paused;
751 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
752 if (!ast_strlen_zero(membername))
753 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
754 else
755 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
756 if (!strchr(cur->interface, '/'))
757 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
758 cur->status = ast_device_state(interface);
761 return cur;
764 static struct call_queue *alloc_queue(const char *queuename)
766 struct call_queue *q;
768 if ((q = ast_calloc(1, sizeof(*q)))) {
769 ast_mutex_init(&q->lock);
770 ast_copy_string(q->name, queuename, sizeof(q->name));
772 return q;
775 static int compress_char(const char c)
777 if (c < 32)
778 return 0;
779 else if (c > 96)
780 return c - 64;
781 else
782 return c - 32;
785 static int member_hash_fn(const void *obj, const int flags)
787 const struct member *mem = obj;
788 const char *chname = strchr(mem->interface, '/');
789 int ret = 0, i;
790 if (!chname)
791 chname = mem->interface;
792 for (i = 0; i < 5 && chname[i]; i++)
793 ret += compress_char(chname[i]) << (i * 6);
794 return ret;
797 static int member_cmp_fn(void *obj1, void *obj2, int flags)
799 struct member *mem1 = obj1, *mem2 = obj2;
800 return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
803 static void init_queue(struct call_queue *q)
805 int i;
807 q->dead = 0;
808 q->retry = DEFAULT_RETRY;
809 q->timeout = -1;
810 q->maxlen = 0;
811 q->announcefrequency = 0;
812 q->announceholdtime = 0;
813 q->roundingseconds = 0; /* Default - don't announce seconds */
814 q->servicelevel = 0;
815 q->ringinuse = 1;
816 q->setinterfacevar = 0;
817 q->autofill = autofill_default;
818 q->montype = montype_default;
819 q->moh[0] = '\0';
820 q->announce[0] = '\0';
821 q->context[0] = '\0';
822 q->monfmt[0] = '\0';
823 q->periodicannouncefrequency = 0;
824 q->reportholdtime = 0;
825 q->monjoin = 0;
826 q->wrapuptime = 0;
827 q->joinempty = 0;
828 q->leavewhenempty = 0;
829 q->memberdelay = 0;
830 q->maskmemberstatus = 0;
831 q->eventwhencalled = 0;
832 q->weight = 0;
833 q->timeoutrestart = 0;
834 if (!q->members)
835 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
836 q->membercount = 0;
837 q->found = 1;
838 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
839 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
840 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
841 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
842 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
843 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
844 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
845 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
846 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
847 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
848 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
849 q->sound_periodicannounce[i][0]='\0';
853 static void clear_queue(struct call_queue *q)
855 q->holdtime = 0;
856 q->callscompleted = 0;
857 q->callsabandoned = 0;
858 q->callscompletedinsl = 0;
859 q->wrapuptime = 0;
862 static int add_to_interfaces(const char *interface)
864 struct member_interface *curint;
866 AST_LIST_LOCK(&interfaces);
867 AST_LIST_TRAVERSE(&interfaces, curint, list) {
868 if (!strcasecmp(curint->interface, interface))
869 break;
872 if (curint) {
873 AST_LIST_UNLOCK(&interfaces);
874 return 0;
877 if (option_debug)
878 ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
880 if ((curint = ast_calloc(1, sizeof(*curint)))) {
881 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
882 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
884 AST_LIST_UNLOCK(&interfaces);
886 return 0;
889 static int interface_exists_global(const char *interface)
891 struct call_queue *q;
892 struct member *mem, tmpmem;
893 int ret = 0;
895 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
897 AST_LIST_LOCK(&queues);
898 AST_LIST_TRAVERSE(&queues, q, list) {
899 ast_mutex_lock(&q->lock);
900 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
901 ao2_ref(mem, -1);
902 ret = 1;
904 ast_mutex_unlock(&q->lock);
905 if (ret)
906 break;
908 AST_LIST_UNLOCK(&queues);
910 return ret;
913 static int remove_from_interfaces(const char *interface)
915 struct member_interface *curint;
917 if (interface_exists_global(interface))
918 return 0;
920 AST_LIST_LOCK(&interfaces);
921 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
922 if (!strcasecmp(curint->interface, interface)) {
923 if (option_debug)
924 ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
925 AST_LIST_REMOVE_CURRENT(&interfaces, list);
926 free(curint);
927 break;
930 AST_LIST_TRAVERSE_SAFE_END;
931 AST_LIST_UNLOCK(&interfaces);
933 return 0;
936 static void clear_and_free_interfaces(void)
938 struct member_interface *curint;
940 AST_LIST_LOCK(&interfaces);
941 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
942 free(curint);
943 AST_LIST_UNLOCK(&interfaces);
946 /*! \brief Configure a queue parameter.
947 \par
948 For error reporting, line number is passed for .conf static configuration.
949 For Realtime queues, linenum is -1.
950 The failunknown flag is set for config files (and static realtime) to show
951 errors for unknown parameters. It is cleared for dynamic realtime to allow
952 extra fields in the tables. */
953 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
955 if (!strcasecmp(param, "musicclass") ||
956 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
957 ast_copy_string(q->moh, val, sizeof(q->moh));
958 } else if (!strcasecmp(param, "announce")) {
959 ast_copy_string(q->announce, val, sizeof(q->announce));
960 } else if (!strcasecmp(param, "context")) {
961 ast_copy_string(q->context, val, sizeof(q->context));
962 } else if (!strcasecmp(param, "timeout")) {
963 q->timeout = atoi(val);
964 if (q->timeout < 0)
965 q->timeout = DEFAULT_TIMEOUT;
966 } else if (!strcasecmp(param, "ringinuse")) {
967 q->ringinuse = ast_true(val);
968 } else if (!strcasecmp(param, "setinterfacevar")) {
969 q->setinterfacevar = ast_true(val);
970 } else if (!strcasecmp(param, "monitor-join")) {
971 monjoin_dep_warning();
972 q->monjoin = ast_true(val);
973 } else if (!strcasecmp(param, "monitor-format")) {
974 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
975 } else if (!strcasecmp(param, "queue-youarenext")) {
976 ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
977 } else if (!strcasecmp(param, "queue-thereare")) {
978 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
979 } else if (!strcasecmp(param, "queue-callswaiting")) {
980 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
981 } else if (!strcasecmp(param, "queue-holdtime")) {
982 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
983 } else if (!strcasecmp(param, "queue-minutes")) {
984 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
985 } else if (!strcasecmp(param, "queue-seconds")) {
986 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
987 } else if (!strcasecmp(param, "queue-lessthan")) {
988 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
989 } else if (!strcasecmp(param, "queue-thankyou")) {
990 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
991 } else if (!strcasecmp(param, "queue-reporthold")) {
992 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
993 } else if (!strcasecmp(param, "announce-frequency")) {
994 q->announcefrequency = atoi(val);
995 } else if (!strcasecmp(param, "announce-round-seconds")) {
996 q->roundingseconds = atoi(val);
997 if (q->roundingseconds>60 || q->roundingseconds<0) {
998 if (linenum >= 0) {
999 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1000 "using 0 instead for queue '%s' at line %d of queues.conf\n",
1001 val, param, q->name, linenum);
1002 } else {
1003 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1004 "using 0 instead for queue '%s'\n", val, param, q->name);
1006 q->roundingseconds=0;
1008 } else if (!strcasecmp(param, "announce-holdtime")) {
1009 if (!strcasecmp(val, "once"))
1010 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
1011 else if (ast_true(val))
1012 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
1013 else
1014 q->announceholdtime = 0;
1015 } else if (!strcasecmp(param, "periodic-announce")) {
1016 if (strchr(val, '|')) {
1017 char *s, *buf = ast_strdupa(val);
1018 unsigned int i = 0;
1020 while ((s = strsep(&buf, "|"))) {
1021 ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
1022 i++;
1023 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
1024 break;
1026 } else {
1027 ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
1029 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
1030 q->periodicannouncefrequency = atoi(val);
1031 } else if (!strcasecmp(param, "retry")) {
1032 q->retry = atoi(val);
1033 if (q->retry <= 0)
1034 q->retry = DEFAULT_RETRY;
1035 } else if (!strcasecmp(param, "wrapuptime")) {
1036 q->wrapuptime = atoi(val);
1037 } else if (!strcasecmp(param, "autofill")) {
1038 q->autofill = ast_true(val);
1039 } else if (!strcasecmp(param, "monitor-type")) {
1040 if (!strcasecmp(val, "mixmonitor"))
1041 q->montype = 1;
1042 } else if (!strcasecmp(param, "autopause")) {
1043 q->autopause = ast_true(val);
1044 } else if (!strcasecmp(param, "maxlen")) {
1045 q->maxlen = atoi(val);
1046 if (q->maxlen < 0)
1047 q->maxlen = 0;
1048 } else if (!strcasecmp(param, "servicelevel")) {
1049 q->servicelevel= atoi(val);
1050 } else if (!strcasecmp(param, "strategy")) {
1051 q->strategy = strat2int(val);
1052 if (q->strategy < 0) {
1053 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1054 val, q->name);
1055 q->strategy = QUEUE_STRATEGY_RINGALL;
1057 } else if (!strcasecmp(param, "joinempty")) {
1058 if (!strcasecmp(val, "strict"))
1059 q->joinempty = QUEUE_EMPTY_STRICT;
1060 else if (ast_true(val))
1061 q->joinempty = QUEUE_EMPTY_NORMAL;
1062 else
1063 q->joinempty = 0;
1064 } else if (!strcasecmp(param, "leavewhenempty")) {
1065 if (!strcasecmp(val, "strict"))
1066 q->leavewhenempty = QUEUE_EMPTY_STRICT;
1067 else if (ast_true(val))
1068 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
1069 else
1070 q->leavewhenempty = 0;
1071 } else if (!strcasecmp(param, "eventmemberstatus")) {
1072 q->maskmemberstatus = !ast_true(val);
1073 } else if (!strcasecmp(param, "eventwhencalled")) {
1074 if (!strcasecmp(val, "vars")) {
1075 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
1076 } else {
1077 q->eventwhencalled = ast_true(val) ? 1 : 0;
1079 } else if (!strcasecmp(param, "reportholdtime")) {
1080 q->reportholdtime = ast_true(val);
1081 } else if (!strcasecmp(param, "memberdelay")) {
1082 q->memberdelay = atoi(val);
1083 } else if (!strcasecmp(param, "weight")) {
1084 q->weight = atoi(val);
1085 if (q->weight)
1086 use_weight++;
1087 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1088 we will not see any effect on use_weight until next reload. */
1089 } else if (!strcasecmp(param, "timeoutrestart")) {
1090 q->timeoutrestart = ast_true(val);
1091 } else if (failunknown) {
1092 if (linenum >= 0) {
1093 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1094 q->name, param, linenum);
1095 } else {
1096 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
1101 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str)
1103 struct member *m, tmpmem;
1104 int penalty = 0;
1105 int paused = 0;
1107 if (penalty_str) {
1108 penalty = atoi(penalty_str);
1109 if (penalty < 0)
1110 penalty = 0;
1113 if (paused_str) {
1114 paused = atoi(paused_str);
1115 if (paused < 0)
1116 paused = 0;
1119 /* Find the member, or the place to put a new one. */
1120 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
1121 m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
1123 /* Create a new one if not found, else update penalty */
1124 if (!m) {
1125 if ((m = create_queue_member(interface, membername, penalty, paused))) {
1126 m->dead = 0;
1127 m->realtime = 1;
1128 add_to_interfaces(interface);
1129 ao2_link(q->members, m);
1130 ao2_ref(m, -1);
1131 m = NULL;
1132 q->membercount++;
1134 } else {
1135 m->dead = 0; /* Do not delete this one. */
1136 if (paused_str)
1137 m->paused = paused;
1138 m->penalty = penalty;
1139 ao2_ref(m, -1);
1143 static void free_members(struct call_queue *q, int all)
1145 /* Free non-dynamic members */
1146 struct member *cur;
1147 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
1149 while ((cur = ao2_iterator_next(&mem_iter))) {
1150 if (all || !cur->dynamic) {
1151 ao2_unlink(q->members, cur);
1152 remove_from_interfaces(cur->interface);
1153 q->membercount--;
1155 ao2_ref(cur, -1);
1159 static void destroy_queue(struct call_queue *q)
1161 free_members(q, 1);
1162 ast_mutex_destroy(&q->lock);
1163 ao2_ref(q->members, -1);
1164 free(q);
1167 /*!\brief Reload a single queue via realtime.
1168 \return Return the queue, or NULL if it doesn't exist.
1169 \note Should be called with the global qlock locked. */
1170 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
1172 struct ast_variable *v;
1173 struct call_queue *q;
1174 struct member *m;
1175 struct ao2_iterator mem_iter;
1176 char *interface = NULL;
1177 char *tmp, *tmp_name;
1178 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
1180 /* Find the queue in the in-core list (we will create a new one if not found). */
1181 AST_LIST_TRAVERSE(&queues, q, list) {
1182 if (!strcasecmp(q->name, queuename))
1183 break;
1186 /* Static queues override realtime. */
1187 if (q) {
1188 ast_mutex_lock(&q->lock);
1189 if (!q->realtime) {
1190 if (q->dead) {
1191 ast_mutex_unlock(&q->lock);
1192 return NULL;
1193 } else {
1194 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
1195 ast_mutex_unlock(&q->lock);
1196 return q;
1199 } else if (!member_config)
1200 /* Not found in the list, and it's not realtime ... */
1201 return NULL;
1203 /* Check if queue is defined in realtime. */
1204 if (!queue_vars) {
1205 /* Delete queue from in-core list if it has been deleted in realtime. */
1206 if (q) {
1207 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1208 found condition... So we might delete an in-core queue
1209 in case of DB failure. */
1210 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
1212 q->dead = 1;
1213 /* Delete if unused (else will be deleted when last caller leaves). */
1214 if (!q->count) {
1215 /* Delete. */
1216 AST_LIST_REMOVE(&queues, q, list);
1217 ast_mutex_unlock(&q->lock);
1218 destroy_queue(q);
1219 } else
1220 ast_mutex_unlock(&q->lock);
1222 return NULL;
1225 /* Create a new queue if an in-core entry does not exist yet. */
1226 if (!q) {
1227 if (!(q = alloc_queue(queuename)))
1228 return NULL;
1229 ast_mutex_lock(&q->lock);
1230 clear_queue(q);
1231 q->realtime = 1;
1232 AST_LIST_INSERT_HEAD(&queues, q, list);
1234 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
1236 memset(tmpbuf, 0, sizeof(tmpbuf));
1237 for (v = queue_vars; v; v = v->next) {
1238 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1239 if ((tmp = strchr(v->name, '_'))) {
1240 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
1241 tmp_name = tmpbuf;
1242 tmp = tmp_name;
1243 while ((tmp = strchr(tmp, '_')))
1244 *tmp++ = '-';
1245 } else
1246 tmp_name = v->name;
1248 if (!ast_strlen_zero(v->value)) {
1249 /* Don't want to try to set the option if the value is empty */
1250 queue_set_param(q, tmp_name, v->value, -1, 0);
1254 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
1255 rr_dep_warning();
1257 /* Temporarily set realtime members dead so we can detect deleted ones.
1258 * Also set the membercount correctly for realtime*/
1259 mem_iter = ao2_iterator_init(q->members, 0);
1260 while ((m = ao2_iterator_next(&mem_iter))) {
1261 q->membercount++;
1262 if (m->realtime)
1263 m->dead = 1;
1264 ao2_ref(m, -1);
1267 while ((interface = ast_category_browse(member_config, interface))) {
1268 rt_handle_member_record(q, interface,
1269 ast_variable_retrieve(member_config, interface, "membername"),
1270 ast_variable_retrieve(member_config, interface, "penalty"),
1271 ast_variable_retrieve(member_config, interface, "paused"));
1274 /* Delete all realtime members that have been deleted in DB. */
1275 mem_iter = ao2_iterator_init(q->members, 0);
1276 while ((m = ao2_iterator_next(&mem_iter))) {
1277 if (m->dead) {
1278 ao2_unlink(q->members, m);
1279 ast_mutex_unlock(&q->lock);
1280 remove_from_interfaces(m->interface);
1281 ast_mutex_lock(&q->lock);
1282 q->membercount--;
1284 ao2_ref(m, -1);
1287 ast_mutex_unlock(&q->lock);
1289 return q;
1292 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
1294 struct ast_variable *var;
1295 int ret = -1;
1297 if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL)))
1298 return ret;
1299 while (var) {
1300 if (!strcmp(var->name, "uniqueid"))
1301 break;
1302 var = var->next;
1304 if (var && !ast_strlen_zero(var->value)) {
1305 if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
1306 ret = 0;
1308 return ret;
1311 static void update_realtime_members(struct call_queue *q)
1313 struct ast_config *member_config = NULL;
1314 struct member *m;
1315 char *interface = NULL;
1316 struct ao2_iterator mem_iter;
1318 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
1319 /*This queue doesn't have realtime members*/
1320 if (option_debug > 2)
1321 ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
1322 return;
1325 ast_mutex_lock(&q->lock);
1327 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1328 mem_iter = ao2_iterator_init(q->members, 0);
1329 while ((m = ao2_iterator_next(&mem_iter))) {
1330 if (m->realtime)
1331 m->dead = 1;
1332 ao2_ref(m, -1);
1335 while ((interface = ast_category_browse(member_config, interface))) {
1336 rt_handle_member_record(q, interface,
1337 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
1338 ast_variable_retrieve(member_config, interface, "penalty"),
1339 ast_variable_retrieve(member_config, interface, "paused"));
1342 /* Delete all realtime members that have been deleted in DB. */
1343 mem_iter = ao2_iterator_init(q->members, 0);
1344 while ((m = ao2_iterator_next(&mem_iter))) {
1345 if (m->dead) {
1346 ao2_unlink(q->members, m);
1347 ast_mutex_unlock(&q->lock);
1348 remove_from_interfaces(m->interface);
1349 ast_mutex_lock(&q->lock);
1350 q->membercount--;
1352 ao2_ref(m, -1);
1354 ast_mutex_unlock(&q->lock);
1355 ast_config_destroy(member_config);
1358 static struct call_queue *load_realtime_queue(const char *queuename)
1360 struct ast_variable *queue_vars;
1361 struct ast_config *member_config = NULL;
1362 struct call_queue *q;
1364 /* Find the queue in the in-core list first. */
1365 AST_LIST_LOCK(&queues);
1366 AST_LIST_TRAVERSE(&queues, q, list) {
1367 if (!strcasecmp(q->name, queuename)) {
1368 break;
1371 AST_LIST_UNLOCK(&queues);
1373 if (!q || q->realtime) {
1374 /*! \note Load from realtime before taking the global qlock, to avoid blocking all
1375 queue operations while waiting for the DB.
1377 This will be two separate database transactions, so we might
1378 see queue parameters as they were before another process
1379 changed the queue and member list as it was after the change.
1380 Thus we might see an empty member list when a queue is
1381 deleted. In practise, this is unlikely to cause a problem. */
1383 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
1384 if (queue_vars) {
1385 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
1386 if (!member_config) {
1387 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
1388 ast_variables_destroy(queue_vars);
1389 return NULL;
1393 AST_LIST_LOCK(&queues);
1395 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
1396 if (member_config)
1397 ast_config_destroy(member_config);
1398 if (queue_vars)
1399 ast_variables_destroy(queue_vars);
1401 AST_LIST_UNLOCK(&queues);
1402 } else {
1403 update_realtime_members(q);
1405 return q;
1408 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
1410 struct call_queue *q;
1411 struct queue_ent *cur, *prev = NULL;
1412 int res = -1;
1413 int pos = 0;
1414 int inserted = 0;
1415 enum queue_member_status stat;
1417 if (!(q = load_realtime_queue(queuename)))
1418 return res;
1420 AST_LIST_LOCK(&queues);
1421 ast_mutex_lock(&q->lock);
1423 /* This is our one */
1424 stat = get_member_status(q, qe->max_penalty);
1425 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
1426 *reason = QUEUE_JOINEMPTY;
1427 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
1428 *reason = QUEUE_JOINUNAVAIL;
1429 else if (q->maxlen && (q->count >= q->maxlen))
1430 *reason = QUEUE_FULL;
1431 else {
1432 /* There's space for us, put us at the right position inside
1433 * the queue.
1434 * Take into account the priority of the calling user */
1435 inserted = 0;
1436 prev = NULL;
1437 cur = q->head;
1438 while (cur) {
1439 /* We have higher priority than the current user, enter
1440 * before him, after all the other users with priority
1441 * higher or equal to our priority. */
1442 if ((!inserted) && (qe->prio > cur->prio)) {
1443 insert_entry(q, prev, qe, &pos);
1444 inserted = 1;
1446 cur->pos = ++pos;
1447 prev = cur;
1448 cur = cur->next;
1450 /* No luck, join at the end of the queue */
1451 if (!inserted)
1452 insert_entry(q, prev, qe, &pos);
1453 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
1454 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
1455 ast_copy_string(qe->context, q->context, sizeof(qe->context));
1456 q->count++;
1457 res = 0;
1458 manager_event(EVENT_FLAG_CALL, "Join",
1459 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1460 qe->chan->name,
1461 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
1462 S_OR(qe->chan->cid.cid_name, "unknown"),
1463 q->name, qe->pos, q->count, qe->chan->uniqueid );
1464 if (option_debug)
1465 ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
1467 ast_mutex_unlock(&q->lock);
1468 AST_LIST_UNLOCK(&queues);
1470 return res;
1473 static int play_file(struct ast_channel *chan, char *filename)
1475 int res;
1477 ast_stopstream(chan);
1479 res = ast_streamfile(chan, filename, chan->language);
1480 if (!res)
1481 res = ast_waitstream(chan, AST_DIGIT_ANY);
1483 ast_stopstream(chan);
1485 return res;
1488 static int valid_exit(struct queue_ent *qe, char digit)
1490 int digitlen = strlen(qe->digits);
1492 /* Prevent possible buffer overflow */
1493 if (digitlen < sizeof(qe->digits) - 2) {
1494 qe->digits[digitlen] = digit;
1495 qe->digits[digitlen + 1] = '\0';
1496 } else {
1497 qe->digits[0] = '\0';
1498 return 0;
1501 /* If there's no context to goto, short-circuit */
1502 if (ast_strlen_zero(qe->context))
1503 return 0;
1505 /* If the extension is bad, then reset the digits to blank */
1506 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
1507 qe->digits[0] = '\0';
1508 return 0;
1511 /* We have an exact match */
1512 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
1513 qe->valid_digits = 1;
1514 /* Return 1 on a successful goto */
1515 return 1;
1518 return 0;
1521 static int say_position(struct queue_ent *qe)
1523 int res = 0, avgholdmins, avgholdsecs;
1524 time_t now;
1526 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
1527 time(&now);
1528 if ((now - qe->last_pos) < 15)
1529 return 0;
1531 /* If either our position has changed, or we are over the freq timer, say position */
1532 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
1533 return 0;
1535 ast_moh_stop(qe->chan);
1536 /* Say we're next, if we are */
1537 if (qe->pos == 1) {
1538 res = play_file(qe->chan, qe->parent->sound_next);
1539 if (res)
1540 goto playout;
1541 else
1542 goto posout;
1543 } else {
1544 res = play_file(qe->chan, qe->parent->sound_thereare);
1545 if (res)
1546 goto playout;
1547 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
1548 if (res)
1549 goto playout;
1550 res = play_file(qe->chan, qe->parent->sound_calls);
1551 if (res)
1552 goto playout;
1554 /* Round hold time to nearest minute */
1555 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
1557 /* If they have specified a rounding then round the seconds as well */
1558 if (qe->parent->roundingseconds) {
1559 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
1560 avgholdsecs *= qe->parent->roundingseconds;
1561 } else {
1562 avgholdsecs = 0;
1565 if (option_verbose > 2)
1566 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
1568 /* If the hold time is >1 min, if it's enabled, and if it's not
1569 supposed to be only once and we have already said it, say it */
1570 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
1571 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
1572 res = play_file(qe->chan, qe->parent->sound_holdtime);
1573 if (res)
1574 goto playout;
1576 if (avgholdmins > 0) {
1577 if (avgholdmins < 2) {
1578 res = play_file(qe->chan, qe->parent->sound_lessthan);
1579 if (res)
1580 goto playout;
1582 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
1583 if (res)
1584 goto playout;
1585 } else {
1586 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
1587 if (res)
1588 goto playout;
1591 res = play_file(qe->chan, qe->parent->sound_minutes);
1592 if (res)
1593 goto playout;
1595 if (avgholdsecs>0) {
1596 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
1597 if (res)
1598 goto playout;
1600 res = play_file(qe->chan, qe->parent->sound_seconds);
1601 if (res)
1602 goto playout;
1607 posout:
1608 if (option_verbose > 2)
1609 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
1610 qe->chan->name, qe->parent->name, qe->pos);
1611 res = play_file(qe->chan, qe->parent->sound_thanks);
1613 playout:
1614 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
1615 res = 0;
1617 /* Set our last_pos indicators */
1618 qe->last_pos = now;
1619 qe->last_pos_said = qe->pos;
1621 /* Don't restart music on hold if we're about to exit the caller from the queue */
1622 if (!res)
1623 ast_moh_start(qe->chan, qe->moh, NULL);
1625 return res;
1628 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
1630 int oldvalue;
1632 /* Calculate holdtime using an exponential average */
1633 /* Thanks to SRT for this contribution */
1634 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1636 ast_mutex_lock(&qe->parent->lock);
1637 oldvalue = qe->parent->holdtime;
1638 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
1639 ast_mutex_unlock(&qe->parent->lock);
1643 static void leave_queue(struct queue_ent *qe)
1645 struct call_queue *q;
1646 struct queue_ent *cur, *prev = NULL;
1647 int pos = 0;
1649 if (!(q = qe->parent))
1650 return;
1651 ast_mutex_lock(&q->lock);
1653 prev = NULL;
1654 for (cur = q->head; cur; cur = cur->next) {
1655 if (cur == qe) {
1656 q->count--;
1658 /* Take us out of the queue */
1659 manager_event(EVENT_FLAG_CALL, "Leave",
1660 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1661 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
1662 if (option_debug)
1663 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
1664 /* Take us out of the queue */
1665 if (prev)
1666 prev->next = cur->next;
1667 else
1668 q->head = cur->next;
1669 } else {
1670 /* Renumber the people after us in the queue based on a new count */
1671 cur->pos = ++pos;
1672 prev = cur;
1675 ast_mutex_unlock(&q->lock);
1677 if (q->dead && !q->count) {
1678 /* It's dead and nobody is in it, so kill it */
1679 AST_LIST_LOCK(&queues);
1680 AST_LIST_REMOVE(&queues, q, list);
1681 AST_LIST_UNLOCK(&queues);
1682 destroy_queue(q);
1686 /* Hang up a list of outgoing calls */
1687 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
1689 struct callattempt *oo;
1691 while (outgoing) {
1692 /* Hangup any existing lines we have open */
1693 if (outgoing->chan && (outgoing->chan != exception))
1694 ast_hangup(outgoing->chan);
1695 oo = outgoing;
1696 outgoing = outgoing->q_next;
1697 if (oo->member)
1698 ao2_ref(oo->member, -1);
1699 free(oo);
1704 /* traverse all defined queues which have calls waiting and contain this member
1705 return 0 if no other queue has precedence (higher weight) or 1 if found */
1706 static int compare_weight(struct call_queue *rq, struct member *member)
1708 struct call_queue *q;
1709 struct member *mem;
1710 int found = 0;
1712 /* &qlock and &rq->lock already set by try_calling()
1713 * to solve deadlock */
1714 AST_LIST_TRAVERSE(&queues, q, list) {
1715 if (q == rq) /* don't check myself, could deadlock */
1716 continue;
1717 ast_mutex_lock(&q->lock);
1718 if (q->count && q->members) {
1719 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
1720 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
1721 if (q->weight > rq->weight) {
1722 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);
1723 found = 1;
1725 ao2_ref(mem, -1);
1728 ast_mutex_unlock(&q->lock);
1729 if (found)
1730 break;
1732 return found;
1735 /*! \brief common hangup actions */
1736 static void do_hang(struct callattempt *o)
1738 o->stillgoing = 0;
1739 ast_hangup(o->chan);
1740 o->chan = NULL;
1743 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
1745 char *tmp = alloca(len);
1747 if (pbx_builtin_serialize_variables(chan, tmp, len)) {
1748 int i, j;
1750 /* convert "\n" to "\nVariable: " */
1751 strcpy(vars, "Variable: ");
1753 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
1754 vars[j] = tmp[i];
1756 if (tmp[i + 1] == '\0')
1757 break;
1758 if (tmp[i] == '\n') {
1759 vars[j++] = '\r';
1760 vars[j++] = '\n';
1762 ast_copy_string(&(vars[j]), "Variable: ", len - j);
1763 j += 9;
1766 if (j > len - 3)
1767 j = len - 3;
1768 vars[j++] = '\r';
1769 vars[j++] = '\n';
1770 vars[j] = '\0';
1771 } else {
1772 /* there are no channel variables; leave it blank */
1773 *vars = '\0';
1775 return vars;
1778 /*! \brief Part 2 of ring_one
1780 * Does error checking before attempting to request a channel and call a member. This
1781 * function is only called from ring_one
1783 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
1785 int res;
1786 int status;
1787 char tech[256];
1788 char *location;
1789 const char *macrocontext, *macroexten;
1791 /* on entry here, we know that tmp->chan == NULL */
1792 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
1793 if (option_debug)
1794 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface);
1795 if (qe->chan->cdr)
1796 ast_cdr_busy(qe->chan->cdr);
1797 tmp->stillgoing = 0;
1798 (*busies)++;
1799 return 0;
1802 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
1803 if (option_debug)
1804 ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface);
1805 if (qe->chan->cdr)
1806 ast_cdr_busy(qe->chan->cdr);
1807 tmp->stillgoing = 0;
1808 return 0;
1811 if (tmp->member->paused) {
1812 if (option_debug)
1813 ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface);
1814 if (qe->chan->cdr)
1815 ast_cdr_busy(qe->chan->cdr);
1816 tmp->stillgoing = 0;
1817 return 0;
1819 if (use_weight && compare_weight(qe->parent,tmp->member)) {
1820 ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
1821 if (qe->chan->cdr)
1822 ast_cdr_busy(qe->chan->cdr);
1823 tmp->stillgoing = 0;
1824 (*busies)++;
1825 return 0;
1828 ast_copy_string(tech, tmp->interface, sizeof(tech));
1829 if ((location = strchr(tech, '/')))
1830 *location++ = '\0';
1831 else
1832 location = "";
1834 /* Request the peer */
1835 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
1836 if (!tmp->chan) { /* If we can't, just go on to the next call */
1837 if (qe->chan->cdr)
1838 ast_cdr_busy(qe->chan->cdr);
1839 tmp->stillgoing = 0;
1841 update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
1843 ast_mutex_lock(&qe->parent->lock);
1844 qe->parent->rrpos++;
1845 ast_mutex_unlock(&qe->parent->lock);
1847 (*busies)++;
1848 return 0;
1851 tmp->chan->appl = "AppQueue";
1852 tmp->chan->data = "(Outgoing Line)";
1853 tmp->chan->whentohangup = 0;
1854 if (tmp->chan->cid.cid_num)
1855 free(tmp->chan->cid.cid_num);
1856 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
1857 if (tmp->chan->cid.cid_name)
1858 free(tmp->chan->cid.cid_name);
1859 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
1860 if (tmp->chan->cid.cid_ani)
1861 free(tmp->chan->cid.cid_ani);
1862 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
1864 /* Inherit specially named variables from parent channel */
1865 ast_channel_inherit_variables(qe->chan, tmp->chan);
1867 /* Presense of ADSI CPE on outgoing channel follows ours */
1868 tmp->chan->adsicpe = qe->chan->adsicpe;
1870 /* Inherit context and extension */
1871 ast_channel_lock(qe->chan);
1872 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
1873 if (!ast_strlen_zero(macrocontext))
1874 ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
1875 else
1876 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
1877 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
1878 if (!ast_strlen_zero(macroexten))
1879 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
1880 else
1881 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
1882 ast_channel_unlock(qe->chan);
1884 /* Place the call, but don't wait on the answer */
1885 if ((res = ast_call(tmp->chan, location, 0))) {
1886 /* Again, keep going even if there's an error */
1887 if (option_debug)
1888 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
1889 if (option_verbose > 2)
1890 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
1891 do_hang(tmp);
1892 (*busies)++;
1893 return 0;
1894 } else if (qe->parent->eventwhencalled) {
1895 char vars[2048];
1897 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
1898 "AgentCalled: %s\r\n"
1899 "AgentName: %s\r\n"
1900 "ChannelCalling: %s\r\n"
1901 "CallerID: %s\r\n"
1902 "CallerIDName: %s\r\n"
1903 "Context: %s\r\n"
1904 "Extension: %s\r\n"
1905 "Priority: %d\r\n"
1906 "%s",
1907 tmp->interface, tmp->member->membername, qe->chan->name,
1908 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
1909 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
1910 qe->chan->context, qe->chan->exten, qe->chan->priority,
1911 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
1912 if (option_verbose > 2)
1913 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
1916 return 1;
1919 /*! \brief find the entry with the best metric, or NULL */
1920 static struct callattempt *find_best(struct callattempt *outgoing)
1922 struct callattempt *best = NULL, *cur;
1924 for (cur = outgoing; cur; cur = cur->q_next) {
1925 if (cur->stillgoing && /* Not already done */
1926 !cur->chan && /* Isn't already going */
1927 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
1928 best = cur;
1932 return best;
1935 /*! \brief Place a call to a queue member
1937 * Once metrics have been calculated for each member, this function is used
1938 * to place a call to the appropriate member (or members). The low-level
1939 * channel-handling and error detection is handled in ring_entry
1941 * Returns 1 if a member was called successfully, 0 otherwise
1943 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
1945 int ret = 0;
1947 while (ret == 0) {
1948 struct callattempt *best = find_best(outgoing);
1949 if (!best) {
1950 if (option_debug)
1951 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
1952 break;
1954 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
1955 struct callattempt *cur;
1956 /* Ring everyone who shares this best metric (for ringall) */
1957 for (cur = outgoing; cur; cur = cur->q_next) {
1958 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
1959 if (option_debug)
1960 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
1961 ret |= ring_entry(qe, cur, busies);
1964 } else {
1965 /* Ring just the best channel */
1966 if (option_debug)
1967 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
1968 ret = ring_entry(qe, best, busies);
1972 return ret;
1975 static int store_next(struct queue_ent *qe, struct callattempt *outgoing)
1977 struct callattempt *best = find_best(outgoing);
1979 if (best) {
1980 /* Ring just the best channel */
1981 if (option_debug)
1982 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
1983 qe->parent->rrpos = best->metric % 1000;
1984 } else {
1985 /* Just increment rrpos */
1986 if (qe->parent->wrapped) {
1987 /* No more channels, start over */
1988 qe->parent->rrpos = 0;
1989 } else {
1990 /* Prioritize next entry */
1991 qe->parent->rrpos++;
1994 qe->parent->wrapped = 0;
1996 return 0;
1999 static int say_periodic_announcement(struct queue_ent *qe)
2001 int res = 0;
2002 time_t now;
2004 /* Get the current time */
2005 time(&now);
2007 /* Check to see if it is time to announce */
2008 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
2009 return 0;
2011 /* Stop the music on hold so we can play our own file */
2012 ast_moh_stop(qe->chan);
2014 if (option_verbose > 2)
2015 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
2017 /* Check to make sure we have a sound file. If not, reset to the first sound file */
2018 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
2019 qe->last_periodic_announce_sound = 0;
2022 /* play the announcement */
2023 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
2025 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
2026 res = 0;
2028 /* Resume Music on Hold if the caller is going to stay in the queue */
2029 if (!res)
2030 ast_moh_start(qe->chan, qe->moh, NULL);
2032 /* update last_periodic_announce_time */
2033 qe->last_periodic_announce_time = now;
2035 /* Update the current periodic announcement to the next announcement */
2036 qe->last_periodic_announce_sound++;
2038 return res;
2041 static void record_abandoned(struct queue_ent *qe)
2043 ast_mutex_lock(&qe->parent->lock);
2044 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
2045 "Queue: %s\r\n"
2046 "Uniqueid: %s\r\n"
2047 "Position: %d\r\n"
2048 "OriginalPosition: %d\r\n"
2049 "HoldTime: %d\r\n",
2050 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
2052 qe->parent->callsabandoned++;
2053 ast_mutex_unlock(&qe->parent->lock);
2056 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2057 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
2059 if (option_verbose > 2)
2060 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
2061 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
2062 if (qe->parent->autopause) {
2063 if (!set_member_paused(qe->parent->name, interface, 1)) {
2064 if (option_verbose > 2)
2065 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
2066 } else {
2067 if (option_verbose > 2)
2068 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
2071 return;
2074 #define AST_MAX_WATCHERS 256
2075 /*! \brief Wait for a member to answer the call
2077 * \param[in] qe the queue_ent corresponding to the caller in the queue
2078 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
2079 * \param[in] to the amount of time (in milliseconds) to wait for a response
2080 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
2081 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
2082 * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
2083 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
2085 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2087 char *queue = qe->parent->name;
2088 struct callattempt *o, *start = NULL, *prev = NULL;
2089 int status;
2090 int numbusies = prebusies;
2091 int numnochan = 0;
2092 int stillgoing = 0;
2093 int orig = *to;
2094 struct ast_frame *f;
2095 struct callattempt *peer = NULL;
2096 struct ast_channel *winner;
2097 struct ast_channel *in = qe->chan;
2098 char on[80] = "";
2099 char membername[80] = "";
2100 long starttime = 0;
2101 long endtime = 0;
2103 starttime = (long) time(NULL);
2105 while (*to && !peer) {
2106 int numlines, retry, pos = 1;
2107 struct ast_channel *watchers[AST_MAX_WATCHERS];
2108 watchers[0] = in;
2109 start = NULL;
2111 for (retry = 0; retry < 2; retry++) {
2112 numlines = 0;
2113 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
2114 if (o->stillgoing) { /* Keep track of important channels */
2115 stillgoing = 1;
2116 if (o->chan) {
2117 watchers[pos++] = o->chan;
2118 if (!start)
2119 start = o;
2120 else
2121 prev->call_next = o;
2122 prev = o;
2125 numlines++;
2127 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
2128 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
2129 break;
2130 /* On "ringall" strategy we only move to the next penalty level
2131 when *all* ringing phones are done in the current penalty level */
2132 ring_one(qe, outgoing, &numbusies);
2133 /* and retry... */
2135 if (pos == 1 /* not found */) {
2136 if (numlines == (numbusies + numnochan)) {
2137 ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
2138 } else {
2139 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
2141 *to = 0;
2142 return NULL;
2144 winner = ast_waitfor_n(watchers, pos, to);
2145 for (o = start; o; o = o->call_next) {
2146 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
2147 if (!peer) {
2148 if (option_verbose > 2)
2149 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
2150 peer = o;
2152 } else if (o->chan && (o->chan == winner)) {
2154 ast_copy_string(on, o->member->interface, sizeof(on));
2155 ast_copy_string(membername, o->member->membername, sizeof(membername));
2157 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
2158 if (option_verbose > 2)
2159 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
2160 numnochan++;
2161 do_hang(o);
2162 winner = NULL;
2163 continue;
2164 } else if (!ast_strlen_zero(o->chan->call_forward)) {
2165 char tmpchan[256];
2166 char *stuff;
2167 char *tech;
2169 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
2170 if ((stuff = strchr(tmpchan, '/'))) {
2171 *stuff++ = '\0';
2172 tech = tmpchan;
2173 } else {
2174 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
2175 stuff = tmpchan;
2176 tech = "Local";
2178 /* Before processing channel, go ahead and check for forwarding */
2179 if (option_verbose > 2)
2180 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
2181 /* Setup parameters */
2182 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
2183 if (!o->chan) {
2184 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
2185 o->stillgoing = 0;
2186 numnochan++;
2187 } else {
2188 ast_channel_inherit_variables(in, o->chan);
2189 ast_channel_datastore_inherit(in, o->chan);
2190 if (o->chan->cid.cid_num)
2191 free(o->chan->cid.cid_num);
2192 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
2194 if (o->chan->cid.cid_name)
2195 free(o->chan->cid.cid_name);
2196 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
2198 ast_string_field_set(o->chan, accountcode, in->accountcode);
2199 o->chan->cdrflags = in->cdrflags;
2201 if (in->cid.cid_ani) {
2202 if (o->chan->cid.cid_ani)
2203 free(o->chan->cid.cid_ani);
2204 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
2206 if (o->chan->cid.cid_rdnis)
2207 free(o->chan->cid.cid_rdnis);
2208 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
2209 if (ast_call(o->chan, tmpchan, 0)) {
2210 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
2211 do_hang(o);
2212 numnochan++;
2215 /* Hangup the original channel now, in case we needed it */
2216 ast_hangup(winner);
2217 continue;
2219 f = ast_read(winner);
2220 if (f) {
2221 if (f->frametype == AST_FRAME_CONTROL) {
2222 switch (f->subclass) {
2223 case AST_CONTROL_ANSWER:
2224 /* This is our guy if someone answered. */
2225 if (!peer) {
2226 if (option_verbose > 2)
2227 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
2228 peer = o;
2230 break;
2231 case AST_CONTROL_BUSY:
2232 if (option_verbose > 2)
2233 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
2234 if (in->cdr)
2235 ast_cdr_busy(in->cdr);
2236 do_hang(o);
2237 endtime = (long)time(NULL);
2238 endtime -= starttime;
2239 rna(endtime*1000, qe, on, membername);
2240 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2241 if (qe->parent->timeoutrestart)
2242 *to = orig;
2243 ring_one(qe, outgoing, &numbusies);
2245 numbusies++;
2246 break;
2247 case AST_CONTROL_CONGESTION:
2248 if (option_verbose > 2)
2249 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
2250 if (in->cdr)
2251 ast_cdr_busy(in->cdr);
2252 endtime = (long)time(NULL);
2253 endtime -= starttime;
2254 rna(endtime*1000, qe, on, membername);
2255 do_hang(o);
2256 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2257 if (qe->parent->timeoutrestart)
2258 *to = orig;
2259 ring_one(qe, outgoing, &numbusies);
2261 numbusies++;
2262 break;
2263 case AST_CONTROL_RINGING:
2264 if (option_verbose > 2)
2265 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
2266 break;
2267 case AST_CONTROL_OFFHOOK:
2268 /* Ignore going off hook */
2269 break;
2270 default:
2271 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
2274 ast_frfree(f);
2275 } else {
2276 endtime = (long) time(NULL) - starttime;
2277 rna(endtime * 1000, qe, on, membername);
2278 do_hang(o);
2279 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2280 if (qe->parent->timeoutrestart)
2281 *to = orig;
2282 ring_one(qe, outgoing, &numbusies);
2287 if (winner == in) {
2288 f = ast_read(in);
2289 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
2290 /* Got hung up */
2291 *to = -1;
2292 if (f)
2293 ast_frfree(f);
2294 return NULL;
2296 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
2297 if (option_verbose > 3)
2298 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
2299 *to = 0;
2300 ast_frfree(f);
2301 return NULL;
2303 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
2304 if (option_verbose > 3)
2305 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
2306 *to = 0;
2307 *digit = f->subclass;
2308 ast_frfree(f);
2309 return NULL;
2311 ast_frfree(f);
2313 if (!*to) {
2314 for (o = start; o; o = o->call_next)
2315 rna(orig, qe, o->interface, o->member->membername);
2319 return peer;
2321 /*! \brief Check if we should start attempting to call queue members
2323 * The behavior of this function is dependent first on whether autofill is enabled
2324 * and second on whether the ring strategy is ringall. If autofill is not enabled,
2325 * then return true if we're the head of the queue. If autofill is enabled, then
2326 * we count the available members and see if the number of available members is enough
2327 * that given our position in the queue, we would theoretically be able to connect to
2328 * one of those available members
2330 static int is_our_turn(struct queue_ent *qe)
2332 struct queue_ent *ch;
2333 struct member *cur;
2334 int avl = 0;
2335 int idx = 0;
2336 int res;
2338 if (!qe->parent->autofill) {
2339 /* Atomically read the parent head -- does not need a lock */
2340 ch = qe->parent->head;
2341 /* If we are now at the top of the head, break out */
2342 if (ch == qe) {
2343 if (option_debug)
2344 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
2345 res = 1;
2346 } else {
2347 if (option_debug)
2348 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
2349 res = 0;
2352 } else {
2353 /* This needs a lock. How many members are available to be served? */
2354 ast_mutex_lock(&qe->parent->lock);
2356 ch = qe->parent->head;
2358 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2359 if (option_debug)
2360 ast_log(LOG_DEBUG, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
2361 avl = 1;
2362 } else {
2363 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
2364 while ((cur = ao2_iterator_next(&mem_iter))) {
2365 switch (cur->status) {
2366 case AST_DEVICE_INUSE:
2367 if (!qe->parent->ringinuse)
2368 break;
2369 /* else fall through */
2370 case AST_DEVICE_NOT_INUSE:
2371 case AST_DEVICE_UNKNOWN:
2372 if (!cur->paused)
2373 avl++;
2374 break;
2376 ao2_ref(cur, -1);
2380 if (option_debug)
2381 ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
2383 while ((idx < avl) && (ch) && (ch != qe)) {
2384 if (!ch->pending)
2385 idx++;
2386 ch = ch->next;
2389 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2390 if (ch && idx < avl) {
2391 if (option_debug)
2392 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
2393 res = 1;
2394 } else {
2395 if (option_debug)
2396 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
2397 res = 0;
2400 ast_mutex_unlock(&qe->parent->lock);
2403 return res;
2405 /*! \brief The waiting areas for callers who are not actively calling members
2407 * This function is one large loop. This function will return if a caller
2408 * either exits the queue or it becomes that caller's turn to attempt calling
2409 * queue members. Inside the loop, we service the caller with periodic announcements,
2410 * holdtime announcements, etc. as configured in queues.conf
2412 * \retval 0 if the caller's turn has arrived
2413 * \retval -1 if the caller should exit the queue.
2415 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
2417 int res = 0;
2419 /* This is the holding pen for callers 2 through maxlen */
2420 for (;;) {
2421 enum queue_member_status stat;
2423 if (is_our_turn(qe))
2424 break;
2426 /* If we have timed out, break out */
2427 if (qe->expire && (time(NULL) > qe->expire)) {
2428 *reason = QUEUE_TIMEOUT;
2429 break;
2432 stat = get_member_status(qe->parent, qe->max_penalty);
2434 /* leave the queue if no agents, if enabled */
2435 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
2436 *reason = QUEUE_LEAVEEMPTY;
2437 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2438 leave_queue(qe);
2439 break;
2442 /* leave the queue if no reachable agents, if enabled */
2443 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
2444 *reason = QUEUE_LEAVEUNAVAIL;
2445 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2446 leave_queue(qe);
2447 break;
2450 /* Make a position announcement, if enabled */
2451 if (qe->parent->announcefrequency && !ringing &&
2452 (res = say_position(qe)))
2453 break;
2455 /* Make a periodic announcement, if enabled */
2456 if (qe->parent->periodicannouncefrequency && !ringing &&
2457 (res = say_periodic_announcement(qe)))
2458 break;
2460 /* Wait a second before checking again */
2461 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
2462 if (res > 0 && !valid_exit(qe, res))
2463 res = 0;
2464 else
2465 break;
2469 return res;
2472 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
2474 ast_mutex_lock(&q->lock);
2475 time(&member->lastcall);
2476 member->calls++;
2477 q->callscompleted++;
2478 if (callcompletedinsl)
2479 q->callscompletedinsl++;
2480 ast_mutex_unlock(&q->lock);
2481 return 0;
2484 /*! \brief Calculate the metric of each member in the outgoing callattempts
2486 * A numeric metric is given to each member depending on the ring strategy used
2487 * by the queue. Members with lower metrics will be called before members with
2488 * higher metrics
2490 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
2492 if (qe->max_penalty && (mem->penalty > qe->max_penalty))
2493 return -1;
2495 switch (q->strategy) {
2496 case QUEUE_STRATEGY_RINGALL:
2497 /* Everyone equal, except for penalty */
2498 tmp->metric = mem->penalty * 1000000;
2499 break;
2500 case QUEUE_STRATEGY_ROUNDROBIN:
2501 if (!pos) {
2502 if (!q->wrapped) {
2503 /* No more channels, start over */
2504 q->rrpos = 0;
2505 } else {
2506 /* Prioritize next entry */
2507 q->rrpos++;
2509 q->wrapped = 0;
2511 /* Fall through */
2512 case QUEUE_STRATEGY_RRMEMORY:
2513 if (pos < q->rrpos) {
2514 tmp->metric = 1000 + pos;
2515 } else {
2516 if (pos > q->rrpos)
2517 /* Indicate there is another priority */
2518 q->wrapped = 1;
2519 tmp->metric = pos;
2521 tmp->metric += mem->penalty * 1000000;
2522 break;
2523 case QUEUE_STRATEGY_RANDOM:
2524 tmp->metric = ast_random() % 1000;
2525 tmp->metric += mem->penalty * 1000000;
2526 break;
2527 case QUEUE_STRATEGY_FEWESTCALLS:
2528 tmp->metric = mem->calls;
2529 tmp->metric += mem->penalty * 1000000;
2530 break;
2531 case QUEUE_STRATEGY_LEASTRECENT:
2532 if (!mem->lastcall)
2533 tmp->metric = 0;
2534 else
2535 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
2536 tmp->metric += mem->penalty * 1000000;
2537 break;
2538 default:
2539 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
2540 break;
2542 return 0;
2545 struct queue_transfer_ds {
2546 struct queue_ent *qe;
2547 struct member *member;
2548 int starttime;
2551 static void queue_transfer_destroy(void *data)
2553 struct queue_transfer_ds *qtds = data;
2554 ast_free(qtds);
2557 /*! \brief a datastore used to help correctly log attended transfers of queue callers
2559 static const struct ast_datastore_info queue_transfer_info = {
2560 .type = "queue_transfer",
2561 .chan_fixup = queue_transfer_fixup,
2562 .destroy = queue_transfer_destroy,
2565 /*! \brief Log an attended transfer when a queue caller channel is masqueraded
2567 * When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when
2568 * the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan
2569 * to new_chan. This is why new_chan is referenced for exten, context, and datastore information.
2571 * At the end of this, we want to remove the datastore so that this fixup function is not called on any
2572 * future masquerades of the caller during the current call.
2574 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
2576 struct queue_transfer_ds *qtds = data;
2577 struct queue_ent *qe = qtds->qe;
2578 struct member *member = qtds->member;
2579 int callstart = qtds->starttime;
2580 struct ast_datastore *datastore;
2582 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
2583 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
2584 (long) (time(NULL) - callstart));
2586 if (!(datastore = ast_channel_datastore_find(new_chan, &queue_transfer_info, NULL))) {
2587 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
2588 return;
2591 ast_channel_datastore_remove(new_chan, datastore);
2592 ast_channel_datastore_free(datastore);
2595 /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
2597 * When a caller is atxferred, then the queue_transfer_info datastore
2598 * is removed from the channel. If it's still there after the bridge is
2599 * broken, then the caller was not atxferred.
2601 static int attended_transfer_occurred(struct ast_channel *chan)
2603 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
2606 /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
2608 static void setup_transfer_datastore(struct queue_ent *qe, struct member *member, int starttime)
2610 struct ast_datastore *ds;
2611 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
2613 if (!qtds) {
2614 ast_log(LOG_WARNING, "Memory allocation error!\n");
2615 return;
2618 ast_channel_lock(qe->chan);
2619 if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
2620 ast_channel_unlock(qe->chan);
2621 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
2622 return;
2625 qtds->qe = qe;
2626 /* This member is refcounted in try_calling, so no need to add it here, too */
2627 qtds->member = member;
2628 qtds->starttime = starttime;
2629 ds->data = qtds;
2630 ast_channel_datastore_add(qe->chan, ds);
2631 ast_channel_unlock(qe->chan);
2635 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
2637 * Here is the process of this function
2638 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
2639 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
2640 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
2641 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
2642 * during each iteration, we call calc_metric to determine which members should be rung when.
2643 * 3. Call ring_one to place a call to the appropriate member(s)
2644 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
2645 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
2646 * 6. Start the monitor or mixmonitor if the option is set
2647 * 7. Remove the caller from the queue to allow other callers to advance
2648 * 8. Bridge the call.
2649 * 9. Do any post processing after the call has disconnected.
2651 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
2652 * \param[in] options the options passed as the third parameter to the Queue() application
2653 * \param[in] url the url passed as the fourth parameter to the Queue() application
2654 * \param[in,out] tries the number of times we have tried calling queue members
2655 * \param[out] noption set if the call to Queue() has the 'n' option set.
2656 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
2659 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
2661 struct member *cur;
2662 struct callattempt *outgoing = NULL; /* the list of calls we are building */
2663 int to;
2664 char oldexten[AST_MAX_EXTENSION]="";
2665 char oldcontext[AST_MAX_CONTEXT]="";
2666 char queuename[256]="";
2667 struct ast_channel *peer;
2668 struct ast_channel *which;
2669 struct callattempt *lpeer;
2670 struct member *member;
2671 struct ast_app *app;
2672 int res = 0, bridge = 0;
2673 int numbusies = 0;
2674 int x=0;
2675 char *announce = NULL;
2676 char digit = 0;
2677 time_t callstart;
2678 time_t now = time(NULL);
2679 struct ast_bridge_config bridge_config;
2680 char nondataquality = 1;
2681 char *agiexec = NULL;
2682 int ret = 0;
2683 const char *monitorfilename;
2684 const char *monitor_exec;
2685 const char *monitor_options;
2686 char tmpid[256], tmpid2[256];
2687 char meid[1024], meid2[1024];
2688 char mixmonargs[1512];
2689 struct ast_app *mixmonapp = NULL;
2690 char *p;
2691 char vars[2048];
2692 int forwardsallowed = 1;
2693 int callcompletedinsl;
2694 struct ao2_iterator memi;
2695 struct ast_datastore *datastore;
2697 ast_channel_lock(qe->chan);
2698 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
2699 ast_channel_unlock(qe->chan);
2701 memset(&bridge_config, 0, sizeof(bridge_config));
2702 time(&now);
2704 for (; options && *options; options++)
2705 switch (*options) {
2706 case 't':
2707 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
2708 break;
2709 case 'T':
2710 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
2711 break;
2712 case 'w':
2713 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
2714 break;
2715 case 'W':
2716 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
2717 break;
2718 case 'd':
2719 nondataquality = 0;
2720 break;
2721 case 'h':
2722 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
2723 break;
2724 case 'H':
2725 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
2726 break;
2727 case 'n':
2728 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
2729 (*tries)++;
2730 else
2731 *tries = qe->parent->membercount;
2732 *noption = 1;
2733 break;
2734 case 'i':
2735 forwardsallowed = 0;
2736 break;
2739 /* Hold the lock while we setup the outgoing calls */
2740 if (use_weight)
2741 AST_LIST_LOCK(&queues);
2742 ast_mutex_lock(&qe->parent->lock);
2743 if (option_debug)
2744 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
2745 qe->chan->name);
2746 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
2747 if (!ast_strlen_zero(qe->announce))
2748 announce = qe->announce;
2749 if (!ast_strlen_zero(announceoverride))
2750 announce = announceoverride;
2752 memi = ao2_iterator_init(qe->parent->members, 0);
2753 while ((cur = ao2_iterator_next(&memi))) {
2754 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
2755 struct ast_dialed_interface *di;
2756 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
2757 if (!tmp) {
2758 ao2_ref(cur, -1);
2759 ast_mutex_unlock(&qe->parent->lock);
2760 if (use_weight)
2761 AST_LIST_UNLOCK(&queues);
2762 goto out;
2764 if (!datastore) {
2765 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
2766 ao2_ref(cur, -1);
2767 ast_mutex_unlock(&qe->parent->lock);
2768 if (use_weight)
2769 AST_LIST_UNLOCK(&queues);
2770 free(tmp);
2771 goto out;
2773 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
2774 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
2775 ao2_ref(cur, -1);
2776 ast_mutex_unlock(&qe->parent->lock);
2777 if (use_weight)
2778 AST_LIST_UNLOCK(&queues);
2779 free(tmp);
2780 goto out;
2782 datastore->data = dialed_interfaces;
2783 AST_LIST_HEAD_INIT(dialed_interfaces);
2785 ast_channel_lock(qe->chan);
2786 ast_channel_datastore_add(qe->chan, datastore);
2787 ast_channel_unlock(qe->chan);
2788 } else
2789 dialed_interfaces = datastore->data;
2791 AST_LIST_LOCK(dialed_interfaces);
2792 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
2793 if (!strcasecmp(cur->interface, di->interface)) {
2794 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
2795 di->interface);
2796 break;
2799 AST_LIST_UNLOCK(dialed_interfaces);
2801 if (di) {
2802 free(tmp);
2803 continue;
2806 /* It is always ok to dial a Local interface. We only keep track of
2807 * which "real" interfaces have been dialed. The Local channel will
2808 * inherit this list so that if it ends up dialing a real interface,
2809 * it won't call one that has already been called. */
2810 if (strncasecmp(cur->interface, "Local/", 6)) {
2811 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
2812 ao2_ref(cur, -1);
2813 ast_mutex_unlock(&qe->parent->lock);
2814 if (use_weight)
2815 AST_LIST_UNLOCK(&queues);
2816 free(tmp);
2817 goto out;
2819 strcpy(di->interface, cur->interface);
2821 AST_LIST_LOCK(dialed_interfaces);
2822 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
2823 AST_LIST_UNLOCK(dialed_interfaces);
2826 tmp->stillgoing = -1;
2827 tmp->member = cur;
2828 tmp->oldstatus = cur->status;
2829 tmp->lastcall = cur->lastcall;
2830 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
2831 /* Special case: If we ring everyone, go ahead and ring them, otherwise
2832 just calculate their metric for the appropriate strategy */
2833 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
2834 /* Put them in the list of outgoing thingies... We're ready now.
2835 XXX If we're forcibly removed, these outgoing calls won't get
2836 hung up XXX */
2837 tmp->q_next = outgoing;
2838 outgoing = tmp;
2839 /* If this line is up, don't try anybody else */
2840 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
2841 break;
2842 } else {
2843 ao2_ref(cur, -1);
2844 free(tmp);
2847 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
2848 to = (qe->expire - now) * 1000;
2849 else
2850 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
2851 ++qe->pending;
2852 ast_mutex_unlock(&qe->parent->lock);
2853 ring_one(qe, outgoing, &numbusies);
2854 if (use_weight)
2855 AST_LIST_UNLOCK(&queues);
2856 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
2857 /* The ast_channel_datastore_remove() function could fail here if the
2858 * datastore was moved to another channel during a masquerade. If this is
2859 * the case, don't free the datastore here because later, when the channel
2860 * to which the datastore was moved hangs up, it will attempt to free this
2861 * datastore again, causing a crash
2863 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
2864 ast_channel_datastore_free(datastore);
2866 ast_mutex_lock(&qe->parent->lock);
2867 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
2868 store_next(qe, outgoing);
2870 ast_mutex_unlock(&qe->parent->lock);
2871 peer = lpeer ? lpeer->chan : NULL;
2872 if (!peer) {
2873 qe->pending = 0;
2874 if (to) {
2875 /* Must gotten hung up */
2876 res = -1;
2877 } else {
2878 /* User exited by pressing a digit */
2879 res = digit;
2881 if (option_debug && res == -1)
2882 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
2883 } else { /* peer is valid */
2884 /* Ah ha! Someone answered within the desired timeframe. Of course after this
2885 we will always return with -1 so that it is hung up properly after the
2886 conversation. */
2887 if (!strcmp(qe->chan->tech->type, "Zap"))
2888 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2889 if (!strcmp(peer->tech->type, "Zap"))
2890 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2891 /* Update parameters for the queue */
2892 time(&now);
2893 recalc_holdtime(qe, (now - qe->start));
2894 ast_mutex_lock(&qe->parent->lock);
2895 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
2896 ast_mutex_unlock(&qe->parent->lock);
2897 member = lpeer->member;
2898 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
2899 ao2_ref(member, 1);
2900 hangupcalls(outgoing, peer);
2901 outgoing = NULL;
2902 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
2903 int res2;
2905 res2 = ast_autoservice_start(qe->chan);
2906 if (!res2) {
2907 if (qe->parent->memberdelay) {
2908 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
2909 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
2911 if (!res2 && announce) {
2912 play_file(peer, announce);
2914 if (!res2 && qe->parent->reportholdtime) {
2915 if (!play_file(peer, qe->parent->sound_reporthold)) {
2916 int holdtime;
2918 time(&now);
2919 holdtime = abs((now - qe->start) / 60);
2920 if (holdtime < 2) {
2921 play_file(peer, qe->parent->sound_lessthan);
2922 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
2923 } else
2924 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
2925 play_file(peer, qe->parent->sound_minutes);
2929 res2 |= ast_autoservice_stop(qe->chan);
2930 if (peer->_softhangup) {
2931 /* Agent must have hung up */
2932 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
2933 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
2934 if (qe->parent->eventwhencalled)
2935 manager_event(EVENT_FLAG_AGENT, "AgentDump",
2936 "Queue: %s\r\n"
2937 "Uniqueid: %s\r\n"
2938 "Channel: %s\r\n"
2939 "Member: %s\r\n"
2940 "MemberName: %s\r\n"
2941 "%s",
2942 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2943 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2944 ast_hangup(peer);
2945 ao2_ref(member, -1);
2946 goto out;
2947 } else if (res2) {
2948 /* Caller must have hung up just before being connected*/
2949 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
2950 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2951 record_abandoned(qe);
2952 ast_hangup(peer);
2953 ao2_ref(member, -1);
2954 return -1;
2957 /* Stop music on hold */
2958 ast_moh_stop(qe->chan);
2959 /* If appropriate, log that we have a destination channel */
2960 if (qe->chan->cdr)
2961 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
2962 /* Make sure channels are compatible */
2963 res = ast_channel_make_compatible(qe->chan, peer);
2964 if (res < 0) {
2965 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
2966 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
2967 record_abandoned(qe);
2968 ast_hangup(peer);
2969 ao2_ref(member, -1);
2970 return -1;
2973 if (qe->parent->setinterfacevar)
2974 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
2976 /* Begin Monitoring */
2977 if (qe->parent->monfmt && *qe->parent->monfmt) {
2978 if (!qe->parent->montype) {
2979 if (option_debug)
2980 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
2981 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
2982 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
2983 which = qe->chan;
2984 else
2985 which = peer;
2986 if (monitorfilename)
2987 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
2988 else if (qe->chan->cdr)
2989 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
2990 else {
2991 /* Last ditch effort -- no CDR, make up something */
2992 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
2993 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
2995 if (qe->parent->monjoin)
2996 ast_monitor_setjoinfiles(which, 1);
2997 } else {
2998 if (option_debug)
2999 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
3000 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
3001 if (!monitorfilename) {
3002 if (qe->chan->cdr)
3003 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
3004 else
3005 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3006 } else {
3007 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
3008 for (p = tmpid2; *p ; p++) {
3009 if (*p == '^' && *(p+1) == '{') {
3010 *p = '$';
3014 memset(tmpid, 0, sizeof(tmpid));
3015 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
3018 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
3019 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
3021 if (monitor_exec) {
3022 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
3023 for (p = meid2; *p ; p++) {
3024 if (*p == '^' && *(p+1) == '{') {
3025 *p = '$';
3029 memset(meid, 0, sizeof(meid));
3030 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
3033 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
3035 mixmonapp = pbx_findapp("MixMonitor");
3037 if (strchr(tmpid2, '|')) {
3038 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
3039 mixmonapp = NULL;
3042 if (!monitor_options)
3043 monitor_options = "";
3045 if (strchr(monitor_options, '|')) {
3046 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
3047 mixmonapp = NULL;
3050 if (mixmonapp) {
3051 if (!ast_strlen_zero(monitor_exec))
3052 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
3053 else
3054 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
3056 if (option_debug)
3057 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
3058 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3059 if (qe->chan->cdr)
3060 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3061 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
3062 if (qe->chan->cdr)
3063 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3065 } else
3066 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3070 /* Drop out of the queue at this point, to prepare for next caller */
3071 leave_queue(qe);
3072 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
3073 if (option_debug)
3074 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
3075 ast_channel_sendurl(peer, url);
3077 if (!ast_strlen_zero(agi)) {
3078 if (option_debug)
3079 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
3080 app = pbx_findapp("agi");
3081 if (app) {
3082 agiexec = ast_strdupa(agi);
3083 ret = pbx_exec(qe->chan, app, agiexec);
3084 } else
3085 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3087 qe->handled++;
3088 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
3089 if (qe->parent->eventwhencalled)
3090 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
3091 "Queue: %s\r\n"
3092 "Uniqueid: %s\r\n"
3093 "Channel: %s\r\n"
3094 "Member: %s\r\n"
3095 "MemberName: %s\r\n"
3096 "Holdtime: %ld\r\n"
3097 "BridgedChannel: %s\r\n"
3098 "%s",
3099 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3100 (long)time(NULL) - qe->start, peer->uniqueid,
3101 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3102 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
3103 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
3104 time(&callstart);
3106 if (member->status == AST_DEVICE_NOT_INUSE)
3107 ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
3109 setup_transfer_datastore(qe, member, callstart);
3110 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3112 if (!attended_transfer_occurred(qe->chan)) {
3113 struct ast_datastore *transfer_ds;
3114 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3115 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
3116 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
3117 (long) (time(NULL) - callstart));
3118 } else if (qe->chan->_softhangup) {
3119 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
3120 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3121 if (qe->parent->eventwhencalled)
3122 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
3123 "Queue: %s\r\n"
3124 "Uniqueid: %s\r\n"
3125 "Channel: %s\r\n"
3126 "Member: %s\r\n"
3127 "MemberName: %s\r\n"
3128 "HoldTime: %ld\r\n"
3129 "TalkTime: %ld\r\n"
3130 "Reason: caller\r\n"
3131 "%s",
3132 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3133 (long)(callstart - qe->start), (long)(time(NULL) - callstart),
3134 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3135 } else {
3136 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
3137 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3138 if (qe->parent->eventwhencalled)
3139 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
3140 "Queue: %s\r\n"
3141 "Uniqueid: %s\r\n"
3142 "Channel: %s\r\n"
3143 "MemberName: %s\r\n"
3144 "HoldTime: %ld\r\n"
3145 "TalkTime: %ld\r\n"
3146 "Reason: agent\r\n"
3147 "%s",
3148 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
3149 (long)(time(NULL) - callstart),
3150 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3152 ast_channel_lock(qe->chan);
3153 transfer_ds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL);
3154 if (transfer_ds) {
3155 ast_channel_datastore_remove(qe->chan, transfer_ds);
3156 ast_channel_datastore_free(transfer_ds);
3158 ast_channel_unlock(qe->chan);
3161 if (bridge != AST_PBX_NO_HANGUP_PEER)
3162 ast_hangup(peer);
3163 update_queue(qe->parent, member, callcompletedinsl);
3164 res = bridge ? bridge : 1;
3165 ao2_ref(member, -1);
3167 out:
3168 hangupcalls(outgoing, NULL);
3170 return res;
3173 static int wait_a_bit(struct queue_ent *qe)
3175 /* Don't need to hold the lock while we setup the outgoing calls */
3176 int retrywait = qe->parent->retry * 1000;
3178 int res = ast_waitfordigit(qe->chan, retrywait);
3179 if (res > 0 && !valid_exit(qe, res))
3180 res = 0;
3182 return res;
3185 static struct member *interface_exists(struct call_queue *q, const char *interface)
3187 struct member *mem;
3188 struct ao2_iterator mem_iter;
3190 if (!q)
3191 return NULL;
3193 mem_iter = ao2_iterator_init(q->members, 0);
3194 while ((mem = ao2_iterator_next(&mem_iter))) {
3195 if (!strcasecmp(interface, mem->interface))
3196 return mem;
3197 ao2_ref(mem, -1);
3200 return NULL;
3204 /* Dump all members in a specific queue to the database
3206 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
3209 static void dump_queue_members(struct call_queue *pm_queue)
3211 struct member *cur_member;
3212 char value[PM_MAX_LEN];
3213 int value_len = 0;
3214 int res;
3215 struct ao2_iterator mem_iter;
3217 memset(value, 0, sizeof(value));
3219 if (!pm_queue)
3220 return;
3222 mem_iter = ao2_iterator_init(pm_queue->members, 0);
3223 while ((cur_member = ao2_iterator_next(&mem_iter))) {
3224 if (!cur_member->dynamic) {
3225 ao2_ref(cur_member, -1);
3226 continue;
3229 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
3230 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
3232 ao2_ref(cur_member, -1);
3234 if (res != strlen(value + value_len)) {
3235 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
3236 break;
3238 value_len += res;
3241 if (value_len && !cur_member) {
3242 if (ast_db_put(pm_family, pm_queue->name, value))
3243 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
3244 } else
3245 /* Delete the entry if the queue is empty or there is an error */
3246 ast_db_del(pm_family, pm_queue->name);
3249 static int remove_from_queue(const char *queuename, const char *interface)
3251 struct call_queue *q;
3252 struct member *mem, tmpmem;
3253 int res = RES_NOSUCHQUEUE;
3255 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
3257 AST_LIST_LOCK(&queues);
3258 AST_LIST_TRAVERSE(&queues, q, list) {
3259 ast_mutex_lock(&q->lock);
3260 if (strcmp(q->name, queuename)) {
3261 ast_mutex_unlock(&q->lock);
3262 continue;
3265 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
3266 /* XXX future changes should beware of this assumption!! */
3267 if (!mem->dynamic) {
3268 res = RES_NOT_DYNAMIC;
3269 ao2_ref(mem, -1);
3270 ast_mutex_unlock(&q->lock);
3271 break;
3273 q->membercount--;
3274 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
3275 "Queue: %s\r\n"
3276 "Location: %s\r\n"
3277 "MemberName: %s\r\n",
3278 q->name, mem->interface, mem->membername);
3279 ao2_unlink(q->members, mem);
3280 ao2_ref(mem, -1);
3282 if (queue_persistent_members)
3283 dump_queue_members(q);
3285 res = RES_OKAY;
3286 } else {
3287 res = RES_EXISTS;
3289 ast_mutex_unlock(&q->lock);
3290 break;
3293 if (res == RES_OKAY)
3294 remove_from_interfaces(interface);
3296 AST_LIST_UNLOCK(&queues);
3298 return res;
3302 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
3304 struct call_queue *q;
3305 struct member *new_member, *old_member;
3306 int res = RES_NOSUCHQUEUE;
3308 /* \note Ensure the appropriate realtime queue is loaded. Note that this
3309 * short-circuits if the queue is already in memory. */
3310 if (!(q = load_realtime_queue(queuename)))
3311 return res;
3313 AST_LIST_LOCK(&queues);
3315 ast_mutex_lock(&q->lock);
3316 if ((old_member = interface_exists(q, interface)) == NULL) {
3317 add_to_interfaces(interface);
3318 if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
3319 new_member->dynamic = 1;
3320 ao2_link(q->members, new_member);
3321 q->membercount++;
3322 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
3323 "Queue: %s\r\n"
3324 "Location: %s\r\n"
3325 "MemberName: %s\r\n"
3326 "Membership: %s\r\n"
3327 "Penalty: %d\r\n"
3328 "CallsTaken: %d\r\n"
3329 "LastCall: %d\r\n"
3330 "Status: %d\r\n"
3331 "Paused: %d\r\n",
3332 q->name, new_member->interface, new_member->membername,
3333 "dynamic",
3334 new_member->penalty, new_member->calls, (int) new_member->lastcall,
3335 new_member->status, new_member->paused);
3337 ao2_ref(new_member, -1);
3338 new_member = NULL;
3340 if (dump)
3341 dump_queue_members(q);
3343 res = RES_OKAY;
3344 } else {
3345 res = RES_OUTOFMEMORY;
3347 } else {
3348 ao2_ref(old_member, -1);
3349 res = RES_EXISTS;
3351 ast_mutex_unlock(&q->lock);
3352 AST_LIST_UNLOCK(&queues);
3354 return res;
3357 static int set_member_paused(const char *queuename, const char *interface, int paused)
3359 int found = 0;
3360 struct call_queue *q;
3361 struct member *mem;
3363 /* Special event for when all queues are paused - individual events still generated */
3364 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3365 if (ast_strlen_zero(queuename))
3366 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3368 AST_LIST_LOCK(&queues);
3369 AST_LIST_TRAVERSE(&queues, q, list) {
3370 ast_mutex_lock(&q->lock);
3371 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3372 if ((mem = interface_exists(q, interface))) {
3373 found++;
3374 if (mem->paused == paused)
3375 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
3376 mem->paused = paused;
3378 if (queue_persistent_members)
3379 dump_queue_members(q);
3381 if (mem->realtime)
3382 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
3384 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
3386 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3387 "Queue: %s\r\n"
3388 "Location: %s\r\n"
3389 "MemberName: %s\r\n"
3390 "Paused: %d\r\n",
3391 q->name, mem->interface, mem->membername, paused);
3392 ao2_ref(mem, -1);
3395 ast_mutex_unlock(&q->lock);
3397 AST_LIST_UNLOCK(&queues);
3399 return found ? RESULT_SUCCESS : RESULT_FAILURE;
3402 /* Reload dynamic queue members persisted into the astdb */
3403 static void reload_queue_members(void)
3405 char *cur_ptr;
3406 char *queue_name;
3407 char *member;
3408 char *interface;
3409 char *membername = NULL;
3410 char *penalty_tok;
3411 int penalty = 0;
3412 char *paused_tok;
3413 int paused = 0;
3414 struct ast_db_entry *db_tree;
3415 struct ast_db_entry *entry;
3416 struct call_queue *cur_queue;
3417 char queue_data[PM_MAX_LEN];
3419 AST_LIST_LOCK(&queues);
3421 /* Each key in 'pm_family' is the name of a queue */
3422 db_tree = ast_db_gettree(pm_family, NULL);
3423 for (entry = db_tree; entry; entry = entry->next) {
3425 queue_name = entry->key + strlen(pm_family) + 2;
3427 AST_LIST_TRAVERSE(&queues, cur_queue, list) {
3428 ast_mutex_lock(&cur_queue->lock);
3429 if (!strcmp(queue_name, cur_queue->name))
3430 break;
3431 ast_mutex_unlock(&cur_queue->lock);
3434 if (!cur_queue)
3435 cur_queue = load_realtime_queue(queue_name);
3437 if (!cur_queue) {
3438 /* If the queue no longer exists, remove it from the
3439 * database */
3440 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
3441 ast_db_del(pm_family, queue_name);
3442 continue;
3443 } else
3444 ast_mutex_unlock(&cur_queue->lock);
3446 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
3447 continue;
3449 cur_ptr = queue_data;
3450 while ((member = strsep(&cur_ptr, "|"))) {
3451 if (ast_strlen_zero(member))
3452 continue;
3454 interface = strsep(&member, ";");
3455 penalty_tok = strsep(&member, ";");
3456 paused_tok = strsep(&member, ";");
3457 membername = strsep(&member, ";");
3459 if (!penalty_tok) {
3460 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
3461 break;
3463 penalty = strtol(penalty_tok, NULL, 10);
3464 if (errno == ERANGE) {
3465 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
3466 break;
3469 if (!paused_tok) {
3470 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
3471 break;
3473 paused = strtol(paused_tok, NULL, 10);
3474 if ((errno == ERANGE) || paused < 0 || paused > 1) {
3475 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
3476 break;
3478 if (ast_strlen_zero(membername))
3479 membername = interface;
3481 if (option_debug)
3482 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
3484 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
3485 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
3486 break;
3491 AST_LIST_UNLOCK(&queues);
3492 if (db_tree) {
3493 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
3494 ast_db_freetree(db_tree);
3498 static int pqm_exec(struct ast_channel *chan, void *data)
3500 struct ast_module_user *lu;
3501 char *parse;
3502 int priority_jump = 0;
3503 AST_DECLARE_APP_ARGS(args,
3504 AST_APP_ARG(queuename);
3505 AST_APP_ARG(interface);
3506 AST_APP_ARG(options);
3509 if (ast_strlen_zero(data)) {
3510 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3511 return -1;
3514 parse = ast_strdupa(data);
3516 AST_STANDARD_APP_ARGS(args, parse);
3518 lu = ast_module_user_add(chan);
3520 if (args.options) {
3521 if (strchr(args.options, 'j'))
3522 priority_jump = 1;
3525 if (ast_strlen_zero(args.interface)) {
3526 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3527 ast_module_user_remove(lu);
3528 return -1;
3531 if (set_member_paused(args.queuename, args.interface, 1)) {
3532 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
3533 if (priority_jump || ast_opt_priority_jumping) {
3534 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3535 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3536 ast_module_user_remove(lu);
3537 return 0;
3540 ast_module_user_remove(lu);
3541 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3542 return 0;
3545 ast_module_user_remove(lu);
3546 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
3548 return 0;
3551 static int upqm_exec(struct ast_channel *chan, void *data)
3553 struct ast_module_user *lu;
3554 char *parse;
3555 int priority_jump = 0;
3556 AST_DECLARE_APP_ARGS(args,
3557 AST_APP_ARG(queuename);
3558 AST_APP_ARG(interface);
3559 AST_APP_ARG(options);
3562 if (ast_strlen_zero(data)) {
3563 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3564 return -1;
3567 parse = ast_strdupa(data);
3569 AST_STANDARD_APP_ARGS(args, parse);
3571 lu = ast_module_user_add(chan);
3573 if (args.options) {
3574 if (strchr(args.options, 'j'))
3575 priority_jump = 1;
3578 if (ast_strlen_zero(args.interface)) {
3579 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3580 ast_module_user_remove(lu);
3581 return -1;
3584 if (set_member_paused(args.queuename, args.interface, 0)) {
3585 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
3586 if (priority_jump || ast_opt_priority_jumping) {
3587 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3588 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3589 ast_module_user_remove(lu);
3590 return 0;
3593 ast_module_user_remove(lu);
3594 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3595 return 0;
3598 ast_module_user_remove(lu);
3599 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
3601 return 0;
3604 static int rqm_exec(struct ast_channel *chan, void *data)
3606 int res=-1;
3607 struct ast_module_user *lu;
3608 char *parse, *temppos = NULL;
3609 int priority_jump = 0;
3610 AST_DECLARE_APP_ARGS(args,
3611 AST_APP_ARG(queuename);
3612 AST_APP_ARG(interface);
3613 AST_APP_ARG(options);
3617 if (ast_strlen_zero(data)) {
3618 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
3619 return -1;
3622 parse = ast_strdupa(data);
3624 AST_STANDARD_APP_ARGS(args, parse);
3626 lu = ast_module_user_add(chan);
3628 if (ast_strlen_zero(args.interface)) {
3629 args.interface = ast_strdupa(chan->name);
3630 temppos = strrchr(args.interface, '-');
3631 if (temppos)
3632 *temppos = '\0';
3635 if (args.options) {
3636 if (strchr(args.options, 'j'))
3637 priority_jump = 1;
3640 switch (remove_from_queue(args.queuename, args.interface)) {
3641 case RES_OKAY:
3642 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
3643 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
3644 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
3645 res = 0;
3646 break;
3647 case RES_EXISTS:
3648 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
3649 if (priority_jump || ast_opt_priority_jumping)
3650 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3651 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
3652 res = 0;
3653 break;
3654 case RES_NOSUCHQUEUE:
3655 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
3656 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
3657 res = 0;
3658 break;
3659 case RES_NOT_DYNAMIC:
3660 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
3661 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
3662 res = 0;
3663 break;
3666 ast_module_user_remove(lu);
3668 return res;
3671 static int aqm_exec(struct ast_channel *chan, void *data)
3673 int res=-1;
3674 struct ast_module_user *lu;
3675 char *parse, *temppos = NULL;
3676 int priority_jump = 0;
3677 AST_DECLARE_APP_ARGS(args,
3678 AST_APP_ARG(queuename);
3679 AST_APP_ARG(interface);
3680 AST_APP_ARG(penalty);
3681 AST_APP_ARG(options);
3682 AST_APP_ARG(membername);
3684 int penalty = 0;
3686 if (ast_strlen_zero(data)) {
3687 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
3688 return -1;
3691 parse = ast_strdupa(data);
3693 AST_STANDARD_APP_ARGS(args, parse);
3695 lu = ast_module_user_add(chan);
3697 if (ast_strlen_zero(args.interface)) {
3698 args.interface = ast_strdupa(chan->name);
3699 temppos = strrchr(args.interface, '-');
3700 if (temppos)
3701 *temppos = '\0';
3704 if (!ast_strlen_zero(args.penalty)) {
3705 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
3706 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
3707 penalty = 0;
3711 if (args.options) {
3712 if (strchr(args.options, 'j'))
3713 priority_jump = 1;
3716 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
3717 case RES_OKAY:
3718 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
3719 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
3720 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
3721 res = 0;
3722 break;
3723 case RES_EXISTS:
3724 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
3725 if (priority_jump || ast_opt_priority_jumping)
3726 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3727 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
3728 res = 0;
3729 break;
3730 case RES_NOSUCHQUEUE:
3731 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
3732 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
3733 res = 0;
3734 break;
3735 case RES_OUTOFMEMORY:
3736 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
3737 break;
3740 ast_module_user_remove(lu);
3742 return res;
3745 static int ql_exec(struct ast_channel *chan, void *data)
3747 struct ast_module_user *u;
3748 char *parse;
3750 AST_DECLARE_APP_ARGS(args,
3751 AST_APP_ARG(queuename);
3752 AST_APP_ARG(uniqueid);
3753 AST_APP_ARG(membername);
3754 AST_APP_ARG(event);
3755 AST_APP_ARG(params);
3758 if (ast_strlen_zero(data)) {
3759 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
3760 return -1;
3763 u = ast_module_user_add(chan);
3765 parse = ast_strdupa(data);
3767 AST_STANDARD_APP_ARGS(args, parse);
3769 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
3770 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
3771 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
3772 ast_module_user_remove(u);
3773 return -1;
3776 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
3777 "%s", args.params ? args.params : "");
3779 ast_module_user_remove(u);
3781 return 0;
3784 /*!\brief The starting point for all queue calls
3786 * The process involved here is to
3787 * 1. Parse the options specified in the call to Queue()
3788 * 2. Join the queue
3789 * 3. Wait in a loop until it is our turn to try calling a queue member
3790 * 4. Attempt to call a queue member
3791 * 5. If 4. did not result in a bridged call, then check for between
3792 * call options such as periodic announcements etc.
3793 * 6. Try 4 again uless some condition (such as an expiration time) causes us to
3794 * exit the queue.
3796 static int queue_exec(struct ast_channel *chan, void *data)
3798 int res=-1;
3799 int ringing=0;
3800 struct ast_module_user *lu;
3801 const char *user_priority;
3802 const char *max_penalty_str;
3803 int prio;
3804 int max_penalty;
3805 enum queue_result reason = QUEUE_UNKNOWN;
3806 /* whether to exit Queue application after the timeout hits */
3807 int tries = 0;
3808 int noption = 0;
3809 char *parse;
3810 AST_DECLARE_APP_ARGS(args,
3811 AST_APP_ARG(queuename);
3812 AST_APP_ARG(options);
3813 AST_APP_ARG(url);
3814 AST_APP_ARG(announceoverride);
3815 AST_APP_ARG(queuetimeoutstr);
3816 AST_APP_ARG(agi);
3818 /* Our queue entry */
3819 struct queue_ent qe;
3821 if (ast_strlen_zero(data)) {
3822 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
3823 return -1;
3826 parse = ast_strdupa(data);
3827 AST_STANDARD_APP_ARGS(args, parse);
3829 lu = ast_module_user_add(chan);
3831 /* Setup our queue entry */
3832 memset(&qe, 0, sizeof(qe));
3833 qe.start = time(NULL);
3835 /* set the expire time based on the supplied timeout; */
3836 if (!ast_strlen_zero(args.queuetimeoutstr))
3837 qe.expire = qe.start + atoi(args.queuetimeoutstr);
3838 else
3839 qe.expire = 0;
3841 /* Get the priority from the variable ${QUEUE_PRIO} */
3842 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
3843 if (user_priority) {
3844 if (sscanf(user_priority, "%d", &prio) == 1) {
3845 if (option_debug)
3846 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
3847 chan->name, prio);
3848 } else {
3849 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
3850 user_priority, chan->name);
3851 prio = 0;
3853 } else {
3854 if (option_debug > 2)
3855 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
3856 prio = 0;
3859 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
3860 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
3861 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
3862 if (option_debug)
3863 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
3864 chan->name, max_penalty);
3865 } else {
3866 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
3867 max_penalty_str, chan->name);
3868 max_penalty = 0;
3870 } else {
3871 max_penalty = 0;
3874 if (args.options && (strchr(args.options, 'r')))
3875 ringing = 1;
3877 if (option_debug)
3878 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
3879 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
3881 qe.chan = chan;
3882 qe.prio = prio;
3883 qe.max_penalty = max_penalty;
3884 qe.last_pos_said = 0;
3885 qe.last_pos = 0;
3886 qe.last_periodic_announce_time = time(NULL);
3887 qe.last_periodic_announce_sound = 0;
3888 qe.valid_digits = 0;
3889 if (!join_queue(args.queuename, &qe, &reason)) {
3890 int makeannouncement = 0;
3892 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
3893 S_OR(chan->cid.cid_num, ""));
3894 check_turns:
3895 if (ringing) {
3896 ast_indicate(chan, AST_CONTROL_RINGING);
3897 } else {
3898 ast_moh_start(chan, qe.moh, NULL);
3901 /* This is the wait loop for callers 2 through maxlen */
3902 res = wait_our_turn(&qe, ringing, &reason);
3903 if (res)
3904 goto stop;
3906 for (;;) {
3907 /* This is the wait loop for the head caller*/
3908 /* To exit, they may get their call answered; */
3909 /* they may dial a digit from the queue context; */
3910 /* or, they may timeout. */
3912 enum queue_member_status stat;
3914 /* Leave if we have exceeded our queuetimeout */
3915 if (qe.expire && (time(NULL) > qe.expire)) {
3916 record_abandoned(&qe);
3917 reason = QUEUE_TIMEOUT;
3918 res = 0;
3919 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3920 break;
3923 if (makeannouncement) {
3924 /* Make a position announcement, if enabled */
3925 if (qe.parent->announcefrequency && !ringing)
3926 if ((res = say_position(&qe)))
3927 goto stop;
3930 makeannouncement = 1;
3932 /* Make a periodic announcement, if enabled */
3933 if (qe.parent->periodicannouncefrequency && !ringing)
3934 if ((res = say_periodic_announcement(&qe)))
3935 goto stop;
3937 /* Try calling all queue members for 'timeout' seconds */
3938 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
3939 if (res)
3940 goto stop;
3942 stat = get_member_status(qe.parent, qe.max_penalty);
3944 /* exit after 'timeout' cycle if 'n' option enabled */
3945 if (noption && tries >= qe.parent->membercount) {
3946 if (option_verbose > 2)
3947 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
3948 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3949 record_abandoned(&qe);
3950 reason = QUEUE_TIMEOUT;
3951 res = 0;
3952 break;
3955 /* leave the queue if no agents, if enabled */
3956 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
3957 record_abandoned(&qe);
3958 reason = QUEUE_LEAVEEMPTY;
3959 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
3960 res = 0;
3961 break;
3964 /* leave the queue if no reachable agents, if enabled */
3965 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
3966 record_abandoned(&qe);
3967 reason = QUEUE_LEAVEUNAVAIL;
3968 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
3969 res = 0;
3970 break;
3973 /* Leave if we have exceeded our queuetimeout */
3974 if (qe.expire && (time(NULL) > qe.expire)) {
3975 record_abandoned(&qe);
3976 reason = QUEUE_TIMEOUT;
3977 res = 0;
3978 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3979 break;
3982 /* If using dynamic realtime members, we should regenerate the member list for this queue */
3983 update_realtime_members(qe.parent);
3985 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
3986 res = wait_a_bit(&qe);
3987 if (res)
3988 goto stop;
3991 /* Since this is a priority queue and
3992 * it is not sure that we are still at the head
3993 * of the queue, go and check for our turn again.
3995 if (!is_our_turn(&qe)) {
3996 if (option_debug)
3997 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
3998 qe.chan->name);
3999 goto check_turns;
4003 stop:
4004 if (res) {
4005 if (res < 0) {
4006 if (!qe.handled) {
4007 record_abandoned(&qe);
4008 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
4009 "%d|%d|%ld", qe.pos, qe.opos,
4010 (long) time(NULL) - qe.start);
4012 res = -1;
4013 } else if (qe.valid_digits) {
4014 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
4015 "%s|%d", qe.digits, qe.pos);
4019 /* Don't allow return code > 0 */
4020 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
4021 res = 0;
4022 if (ringing) {
4023 ast_indicate(chan, -1);
4024 } else {
4025 ast_moh_stop(chan);
4027 ast_stopstream(chan);
4029 leave_queue(&qe);
4030 if (reason != QUEUE_UNKNOWN)
4031 set_queue_result(chan, reason);
4032 } else {
4033 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
4034 set_queue_result(chan, reason);
4035 res = 0;
4037 ast_module_user_remove(lu);
4039 return res;
4042 static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
4044 int count = 0;
4045 struct call_queue *q;
4046 struct ast_module_user *lu;
4047 struct member *m;
4048 struct ao2_iterator mem_iter;
4050 buf[0] = '\0';
4052 if (ast_strlen_zero(data)) {
4053 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4054 return -1;
4057 lu = ast_module_user_add(chan);
4059 if ((q = load_realtime_queue(data))) {
4060 ast_mutex_lock(&q->lock);
4061 mem_iter = ao2_iterator_init(q->members, 0);
4062 while ((m = ao2_iterator_next(&mem_iter))) {
4063 /* Count the agents who are logged in and presently answering calls */
4064 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4065 count++;
4067 ao2_ref(m, -1);
4069 ast_mutex_unlock(&q->lock);
4070 } else
4071 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4073 snprintf(buf, len, "%d", count);
4074 ast_module_user_remove(lu);
4076 return 0;
4079 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
4081 int count = 0;
4082 struct call_queue *q;
4083 struct ast_module_user *lu;
4084 struct ast_variable *var = NULL;
4086 buf[0] = '\0';
4088 if (ast_strlen_zero(data)) {
4089 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4090 return -1;
4093 lu = ast_module_user_add(chan);
4095 AST_LIST_LOCK(&queues);
4096 AST_LIST_TRAVERSE(&queues, q, list) {
4097 if (!strcasecmp(q->name, data)) {
4098 ast_mutex_lock(&q->lock);
4099 break;
4102 AST_LIST_UNLOCK(&queues);
4104 if (q) {
4105 count = q->count;
4106 ast_mutex_unlock(&q->lock);
4107 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
4108 /* if the queue is realtime but was not found in memory, this
4109 * means that the queue had been deleted from memory since it was
4110 * "dead." This means it has a 0 waiting count
4112 count = 0;
4113 ast_variables_destroy(var);
4114 } else
4115 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4117 snprintf(buf, len, "%d", count);
4118 ast_module_user_remove(lu);
4119 return 0;
4122 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
4124 struct ast_module_user *u;
4125 struct call_queue *q;
4126 struct member *m;
4128 /* Ensure an otherwise empty list doesn't return garbage */
4129 buf[0] = '\0';
4131 if (ast_strlen_zero(data)) {
4132 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
4133 return -1;
4136 u = ast_module_user_add(chan);
4138 AST_LIST_LOCK(&queues);
4139 AST_LIST_TRAVERSE(&queues, q, list) {
4140 if (!strcasecmp(q->name, data)) {
4141 ast_mutex_lock(&q->lock);
4142 break;
4145 AST_LIST_UNLOCK(&queues);
4147 if (q) {
4148 int buflen = 0, count = 0;
4149 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
4151 while ((m = ao2_iterator_next(&mem_iter))) {
4152 /* strcat() is always faster than printf() */
4153 if (count++) {
4154 strncat(buf + buflen, ",", len - buflen - 1);
4155 buflen++;
4157 strncat(buf + buflen, m->membername, len - buflen - 1);
4158 buflen += strlen(m->membername);
4159 /* Safeguard against overflow (negative length) */
4160 if (buflen >= len - 2) {
4161 ao2_ref(m, -1);
4162 ast_log(LOG_WARNING, "Truncating list\n");
4163 break;
4165 ao2_ref(m, -1);
4167 ast_mutex_unlock(&q->lock);
4168 } else
4169 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4171 /* We should already be terminated, but let's make sure. */
4172 buf[len - 1] = '\0';
4173 ast_module_user_remove(u);
4175 return 0;
4178 static struct ast_custom_function queueagentcount_function = {
4179 .name = "QUEUEAGENTCOUNT",
4180 .synopsis = "Count number of agents answering a queue",
4181 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
4182 .desc =
4183 "Returns the number of members currently associated with the specified queue.\n"
4184 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
4185 .read = queue_function_qac,
4188 static struct ast_custom_function queuemembercount_function = {
4189 .name = "QUEUE_MEMBER_COUNT",
4190 .synopsis = "Count number of members answering a queue",
4191 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
4192 .desc =
4193 "Returns the number of members currently associated with the specified queue.\n",
4194 .read = queue_function_qac,
4197 static struct ast_custom_function queuewaitingcount_function = {
4198 .name = "QUEUE_WAITING_COUNT",
4199 .synopsis = "Count number of calls currently waiting in a queue",
4200 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
4201 .desc =
4202 "Returns the number of callers currently waiting in the specified queue.\n",
4203 .read = queue_function_queuewaitingcount,
4206 static struct ast_custom_function queuememberlist_function = {
4207 .name = "QUEUE_MEMBER_LIST",
4208 .synopsis = "Returns a list of interfaces on a queue",
4209 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
4210 .desc =
4211 "Returns a comma-separated list of members associated with the specified queue.\n",
4212 .read = queue_function_queuememberlist,
4215 static int reload_queues(void)
4217 struct call_queue *q;
4218 struct ast_config *cfg;
4219 char *cat, *tmp;
4220 struct ast_variable *var;
4221 struct member *cur, *newm;
4222 struct ao2_iterator mem_iter;
4223 int new;
4224 const char *general_val = NULL;
4225 char parse[80];
4226 char *interface;
4227 char *membername = NULL;
4228 int penalty;
4229 AST_DECLARE_APP_ARGS(args,
4230 AST_APP_ARG(interface);
4231 AST_APP_ARG(penalty);
4232 AST_APP_ARG(membername);
4235 if (!(cfg = ast_config_load("queues.conf"))) {
4236 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
4237 return 0;
4239 AST_LIST_LOCK(&queues);
4240 use_weight=0;
4241 /* Mark all non-realtime queues as dead for the moment */
4242 AST_LIST_TRAVERSE(&queues, q, list) {
4243 if (!q->realtime) {
4244 q->dead = 1;
4245 q->found = 0;
4249 /* Chug through config file */
4250 cat = NULL;
4251 while ((cat = ast_category_browse(cfg, cat)) ) {
4252 if (!strcasecmp(cat, "general")) {
4253 /* Initialize global settings */
4254 queue_persistent_members = 0;
4255 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
4256 queue_persistent_members = ast_true(general_val);
4257 autofill_default = 0;
4258 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
4259 autofill_default = ast_true(general_val);
4260 montype_default = 0;
4261 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
4262 if (!strcasecmp(general_val, "mixmonitor"))
4263 montype_default = 1;
4264 } else { /* Define queue */
4265 /* Look for an existing one */
4266 AST_LIST_TRAVERSE(&queues, q, list) {
4267 if (!strcmp(q->name, cat))
4268 break;
4270 if (!q) {
4271 /* Make one then */
4272 if (!(q = alloc_queue(cat))) {
4273 /* TODO: Handle memory allocation failure */
4275 new = 1;
4276 } else
4277 new = 0;
4278 if (q) {
4279 if (!new)
4280 ast_mutex_lock(&q->lock);
4281 /* Check if a queue with this name already exists */
4282 if (q->found) {
4283 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
4284 if (!new)
4285 ast_mutex_unlock(&q->lock);
4286 continue;
4288 /* Re-initialize the queue, and clear statistics */
4289 init_queue(q);
4290 clear_queue(q);
4291 mem_iter = ao2_iterator_init(q->members, 0);
4292 while ((cur = ao2_iterator_next(&mem_iter))) {
4293 if (!cur->dynamic) {
4294 cur->delme = 1;
4296 ao2_ref(cur, -1);
4298 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4299 if (!strcasecmp(var->name, "member")) {
4300 struct member tmpmem;
4301 membername = NULL;
4303 /* Add a new member */
4304 ast_copy_string(parse, var->value, sizeof(parse));
4306 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
4308 interface = args.interface;
4309 if (!ast_strlen_zero(args.penalty)) {
4310 tmp = args.penalty;
4311 while (*tmp && *tmp < 33) tmp++;
4312 penalty = atoi(tmp);
4313 if (penalty < 0) {
4314 penalty = 0;
4316 } else
4317 penalty = 0;
4319 if (!ast_strlen_zero(args.membername)) {
4320 membername = args.membername;
4321 while (*membername && *membername < 33) membername++;
4324 /* Find the old position in the list */
4325 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
4326 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
4328 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
4329 ao2_link(q->members, newm);
4330 ao2_ref(newm, -1);
4331 newm = NULL;
4333 if (cur)
4334 ao2_ref(cur, -1);
4335 else {
4336 /* Add them to the master int list if necessary */
4337 add_to_interfaces(interface);
4338 q->membercount++;
4340 } else {
4341 queue_set_param(q, var->name, var->value, var->lineno, 1);
4345 /* Free remaining members marked as delme */
4346 mem_iter = ao2_iterator_init(q->members, 0);
4347 while ((cur = ao2_iterator_next(&mem_iter))) {
4348 if (! cur->delme) {
4349 ao2_ref(cur, -1);
4350 continue;
4353 q->membercount--;
4354 ao2_unlink(q->members, cur);
4355 remove_from_interfaces(cur->interface);
4356 ao2_ref(cur, -1);
4359 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
4360 rr_dep_warning();
4362 if (new) {
4363 AST_LIST_INSERT_HEAD(&queues, q, list);
4364 } else
4365 ast_mutex_unlock(&q->lock);
4369 ast_config_destroy(cfg);
4370 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
4371 if (q->dead) {
4372 AST_LIST_REMOVE_CURRENT(&queues, list);
4373 if (!q->count)
4374 destroy_queue(q);
4375 else
4376 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
4377 } else {
4378 ast_mutex_lock(&q->lock);
4379 mem_iter = ao2_iterator_init(q->members, 0);
4380 while ((cur = ao2_iterator_next(&mem_iter))) {
4381 if (cur->dynamic)
4382 q->membercount++;
4383 cur->status = ast_device_state(cur->interface);
4384 ao2_ref(cur, -1);
4386 ast_mutex_unlock(&q->lock);
4389 AST_LIST_TRAVERSE_SAFE_END;
4390 AST_LIST_UNLOCK(&queues);
4391 return 1;
4394 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
4396 struct call_queue *q;
4397 struct queue_ent *qe;
4398 struct member *mem;
4399 int pos, queue_show;
4400 time_t now;
4401 char max_buf[150];
4402 char *max;
4403 size_t max_left;
4404 float sl = 0;
4405 char *term = manager ? "\r\n" : "\n";
4406 struct ao2_iterator mem_iter;
4408 time(&now);
4409 if (argc == 2)
4410 queue_show = 0;
4411 else if (argc == 3)
4412 queue_show = 1;
4413 else
4414 return RESULT_SHOWUSAGE;
4416 /* We only want to load realtime queues when a specific queue is asked for. */
4417 if (queue_show) {
4418 load_realtime_queue(argv[2]);
4419 } else if (ast_check_realtime("queues")) {
4420 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
4421 char *queuename;
4422 if (cfg) {
4423 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
4424 load_realtime_queue(queuename);
4426 ast_config_destroy(cfg);
4430 AST_LIST_LOCK(&queues);
4431 if (AST_LIST_EMPTY(&queues)) {
4432 AST_LIST_UNLOCK(&queues);
4433 if (queue_show) {
4434 if (s)
4435 astman_append(s, "No such queue: %s.%s",argv[2], term);
4436 else
4437 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
4438 } else {
4439 if (s)
4440 astman_append(s, "No queues.%s", term);
4441 else
4442 ast_cli(fd, "No queues.%s", term);
4444 return RESULT_SUCCESS;
4446 AST_LIST_TRAVERSE(&queues, q, list) {
4447 ast_mutex_lock(&q->lock);
4448 if (queue_show) {
4449 if (strcasecmp(q->name, argv[2]) != 0) {
4450 ast_mutex_unlock(&q->lock);
4451 if (!AST_LIST_NEXT(q, list)) {
4452 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
4453 break;
4455 continue;
4458 max_buf[0] = '\0';
4459 max = max_buf;
4460 max_left = sizeof(max_buf);
4461 if (q->maxlen)
4462 ast_build_string(&max, &max_left, "%d", q->maxlen);
4463 else
4464 ast_build_string(&max, &max_left, "unlimited");
4465 sl = 0;
4466 if (q->callscompleted > 0)
4467 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4468 if (s)
4469 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",
4470 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
4471 q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
4472 else
4473 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",
4474 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
4475 if (ao2_container_count(q->members)) {
4476 if (s)
4477 astman_append(s, " Members: %s", term);
4478 else
4479 ast_cli(fd, " Members: %s", term);
4480 mem_iter = ao2_iterator_init(q->members, 0);
4481 while ((mem = ao2_iterator_next(&mem_iter))) {
4482 max_buf[0] = '\0';
4483 max = max_buf;
4484 max_left = sizeof(max_buf);
4485 if (strcasecmp(mem->membername, mem->interface)) {
4486 ast_build_string(&max, &max_left, " (%s)", mem->interface);
4488 if (mem->penalty)
4489 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
4490 if (mem->dynamic)
4491 ast_build_string(&max, &max_left, " (dynamic)");
4492 if (mem->realtime)
4493 ast_build_string(&max, &max_left, " (realtime)");
4494 if (mem->paused)
4495 ast_build_string(&max, &max_left, " (paused)");
4496 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
4497 if (mem->calls) {
4498 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
4499 mem->calls, (long) (time(NULL) - mem->lastcall));
4500 } else
4501 ast_build_string(&max, &max_left, " has taken no calls yet");
4502 if (s)
4503 astman_append(s, " %s%s%s", mem->membername, max_buf, term);
4504 else
4505 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term);
4506 ao2_ref(mem, -1);
4508 } else if (s)
4509 astman_append(s, " No Members%s", term);
4510 else
4511 ast_cli(fd, " No Members%s", term);
4512 if (q->head) {
4513 pos = 1;
4514 if (s)
4515 astman_append(s, " Callers: %s", term);
4516 else
4517 ast_cli(fd, " Callers: %s", term);
4518 for (qe = q->head; qe; qe = qe->next) {
4519 if (s)
4520 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
4521 pos++, qe->chan->name, (long) (now - qe->start) / 60,
4522 (long) (now - qe->start) % 60, qe->prio, term);
4523 else
4524 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
4525 qe->chan->name, (long) (now - qe->start) / 60,
4526 (long) (now - qe->start) % 60, qe->prio, term);
4528 } else if (s)
4529 astman_append(s, " No Callers%s", term);
4530 else
4531 ast_cli(fd, " No Callers%s", term);
4532 if (s)
4533 astman_append(s, "%s", term);
4534 else
4535 ast_cli(fd, "%s", term);
4536 ast_mutex_unlock(&q->lock);
4537 if (queue_show)
4538 break;
4540 AST_LIST_UNLOCK(&queues);
4541 return RESULT_SUCCESS;
4544 static int queue_show(int fd, int argc, char **argv)
4546 return __queues_show(NULL, 0, fd, argc, argv);
4549 static char *complete_queue(const char *line, const char *word, int pos, int state)
4551 struct call_queue *q;
4552 char *ret = NULL;
4553 int which = 0;
4554 int wordlen = strlen(word);
4556 AST_LIST_LOCK(&queues);
4557 AST_LIST_TRAVERSE(&queues, q, list) {
4558 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
4559 ret = ast_strdup(q->name);
4560 break;
4563 AST_LIST_UNLOCK(&queues);
4565 return ret;
4568 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
4570 if (pos == 2)
4571 return complete_queue(line, word, pos, state);
4572 return NULL;
4575 /*!\brief callback to display queues status in manager
4576 \addtogroup Group_AMI
4578 static int manager_queues_show(struct mansession *s, const struct message *m)
4580 char *a[] = { "queue", "show" };
4582 __queues_show(s, 1, -1, 2, a);
4583 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
4585 return RESULT_SUCCESS;
4588 /* Dump queue status */
4589 static int manager_queues_status(struct mansession *s, const struct message *m)
4591 time_t now;
4592 int pos;
4593 const char *id = astman_get_header(m,"ActionID");
4594 const char *queuefilter = astman_get_header(m,"Queue");
4595 const char *memberfilter = astman_get_header(m,"Member");
4596 char idText[256] = "";
4597 struct call_queue *q;
4598 struct queue_ent *qe;
4599 float sl = 0;
4600 struct member *mem;
4601 struct ao2_iterator mem_iter;
4603 astman_send_ack(s, m, "Queue status will follow");
4604 time(&now);
4605 AST_LIST_LOCK(&queues);
4606 if (!ast_strlen_zero(id))
4607 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
4609 AST_LIST_TRAVERSE(&queues, q, list) {
4610 ast_mutex_lock(&q->lock);
4612 /* List queue properties */
4613 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
4614 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
4615 astman_append(s, "Event: QueueParams\r\n"
4616 "Queue: %s\r\n"
4617 "Max: %d\r\n"
4618 "Calls: %d\r\n"
4619 "Holdtime: %d\r\n"
4620 "Completed: %d\r\n"
4621 "Abandoned: %d\r\n"
4622 "ServiceLevel: %d\r\n"
4623 "ServicelevelPerf: %2.1f\r\n"
4624 "Weight: %d\r\n"
4625 "%s"
4626 "\r\n",
4627 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
4628 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
4629 /* List Queue Members */
4630 mem_iter = ao2_iterator_init(q->members, 0);
4631 while ((mem = ao2_iterator_next(&mem_iter))) {
4632 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
4633 astman_append(s, "Event: QueueMember\r\n"
4634 "Queue: %s\r\n"
4635 "Name: %s\r\n"
4636 "Location: %s\r\n"
4637 "Membership: %s\r\n"
4638 "Penalty: %d\r\n"
4639 "CallsTaken: %d\r\n"
4640 "LastCall: %d\r\n"
4641 "Status: %d\r\n"
4642 "Paused: %d\r\n"
4643 "%s"
4644 "\r\n",
4645 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
4646 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
4648 ao2_ref(mem, -1);
4650 /* List Queue Entries */
4651 pos = 1;
4652 for (qe = q->head; qe; qe = qe->next) {
4653 astman_append(s, "Event: QueueEntry\r\n"
4654 "Queue: %s\r\n"
4655 "Position: %d\r\n"
4656 "Channel: %s\r\n"
4657 "CallerID: %s\r\n"
4658 "CallerIDName: %s\r\n"
4659 "Wait: %ld\r\n"
4660 "%s"
4661 "\r\n",
4662 q->name, pos++, qe->chan->name,
4663 S_OR(qe->chan->cid.cid_num, "unknown"),
4664 S_OR(qe->chan->cid.cid_name, "unknown"),
4665 (long) (now - qe->start), idText);
4668 ast_mutex_unlock(&q->lock);
4671 astman_append(s,
4672 "Event: QueueStatusComplete\r\n"
4673 "%s"
4674 "\r\n",idText);
4676 AST_LIST_UNLOCK(&queues);
4679 return RESULT_SUCCESS;
4682 static int manager_add_queue_member(struct mansession *s, const struct message *m)
4684 const char *queuename, *interface, *penalty_s, *paused_s, *membername;
4685 int paused, penalty = 0;
4687 queuename = astman_get_header(m, "Queue");
4688 interface = astman_get_header(m, "Interface");
4689 penalty_s = astman_get_header(m, "Penalty");
4690 paused_s = astman_get_header(m, "Paused");
4691 membername = astman_get_header(m, "MemberName");
4693 if (ast_strlen_zero(queuename)) {
4694 astman_send_error(s, m, "'Queue' not specified.");
4695 return 0;
4698 if (ast_strlen_zero(interface)) {
4699 astman_send_error(s, m, "'Interface' not specified.");
4700 return 0;
4703 if (ast_strlen_zero(penalty_s))
4704 penalty = 0;
4705 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
4706 penalty = 0;
4708 if (ast_strlen_zero(paused_s))
4709 paused = 0;
4710 else
4711 paused = abs(ast_true(paused_s));
4713 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
4714 case RES_OKAY:
4715 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
4716 astman_send_ack(s, m, "Added interface to queue");
4717 break;
4718 case RES_EXISTS:
4719 astman_send_error(s, m, "Unable to add interface: Already there");
4720 break;
4721 case RES_NOSUCHQUEUE:
4722 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
4723 break;
4724 case RES_OUTOFMEMORY:
4725 astman_send_error(s, m, "Out of memory");
4726 break;
4729 return 0;
4732 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
4734 const char *queuename, *interface;
4736 queuename = astman_get_header(m, "Queue");
4737 interface = astman_get_header(m, "Interface");
4739 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
4740 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
4741 return 0;
4744 switch (remove_from_queue(queuename, interface)) {
4745 case RES_OKAY:
4746 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
4747 astman_send_ack(s, m, "Removed interface from queue");
4748 break;
4749 case RES_EXISTS:
4750 astman_send_error(s, m, "Unable to remove interface: Not there");
4751 break;
4752 case RES_NOSUCHQUEUE:
4753 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
4754 break;
4755 case RES_OUTOFMEMORY:
4756 astman_send_error(s, m, "Out of memory");
4757 break;
4758 case RES_NOT_DYNAMIC:
4759 astman_send_error(s, m, "Member not dynamic");
4760 break;
4763 return 0;
4766 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
4768 const char *queuename, *interface, *paused_s;
4769 int paused;
4771 interface = astman_get_header(m, "Interface");
4772 paused_s = astman_get_header(m, "Paused");
4773 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
4775 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
4776 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
4777 return 0;
4780 paused = abs(ast_true(paused_s));
4782 if (set_member_paused(queuename, interface, paused))
4783 astman_send_error(s, m, "Interface not found");
4784 else
4785 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
4786 return 0;
4789 static int handle_queue_add_member(int fd, int argc, char *argv[])
4791 char *queuename, *interface, *membername = NULL;
4792 int penalty;
4794 if ((argc != 6) && (argc != 8) && (argc != 10)) {
4795 return RESULT_SHOWUSAGE;
4796 } else if (strcmp(argv[4], "to")) {
4797 return RESULT_SHOWUSAGE;
4798 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
4799 return RESULT_SHOWUSAGE;
4800 } else if ((argc == 10) && strcmp(argv[8], "as")) {
4801 return RESULT_SHOWUSAGE;
4804 queuename = argv[5];
4805 interface = argv[3];
4806 if (argc >= 8) {
4807 if (sscanf(argv[7], "%d", &penalty) == 1) {
4808 if (penalty < 0) {
4809 ast_cli(fd, "Penalty must be >= 0\n");
4810 penalty = 0;
4812 } else {
4813 ast_cli(fd, "Penalty must be an integer >= 0\n");
4814 penalty = 0;
4816 } else {
4817 penalty = 0;
4820 if (argc >= 10) {
4821 membername = argv[9];
4824 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
4825 case RES_OKAY:
4826 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
4827 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
4828 return RESULT_SUCCESS;
4829 case RES_EXISTS:
4830 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
4831 return RESULT_FAILURE;
4832 case RES_NOSUCHQUEUE:
4833 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
4834 return RESULT_FAILURE;
4835 case RES_OUTOFMEMORY:
4836 ast_cli(fd, "Out of memory\n");
4837 return RESULT_FAILURE;
4838 default:
4839 return RESULT_FAILURE;
4843 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
4845 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
4846 switch (pos) {
4847 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
4848 return NULL;
4849 case 4: /* only one possible match, "to" */
4850 return state == 0 ? ast_strdup("to") : NULL;
4851 case 5: /* <queue> */
4852 return complete_queue(line, word, pos, state);
4853 case 6: /* only one possible match, "penalty" */
4854 return state == 0 ? ast_strdup("penalty") : NULL;
4855 case 7:
4856 if (state < 100) { /* 0-99 */
4857 char *num;
4858 if ((num = ast_malloc(3))) {
4859 sprintf(num, "%d", state);
4861 return num;
4862 } else {
4863 return NULL;
4865 case 8: /* only one possible match, "as" */
4866 return state == 0 ? ast_strdup("as") : NULL;
4867 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
4868 return NULL;
4869 default:
4870 return NULL;
4874 static int handle_queue_remove_member(int fd, int argc, char *argv[])
4876 char *queuename, *interface;
4878 if (argc != 6) {
4879 return RESULT_SHOWUSAGE;
4880 } else if (strcmp(argv[4], "from")) {
4881 return RESULT_SHOWUSAGE;
4884 queuename = argv[5];
4885 interface = argv[3];
4887 switch (remove_from_queue(queuename, interface)) {
4888 case RES_OKAY:
4889 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
4890 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
4891 return RESULT_SUCCESS;
4892 case RES_EXISTS:
4893 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
4894 return RESULT_FAILURE;
4895 case RES_NOSUCHQUEUE:
4896 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
4897 return RESULT_FAILURE;
4898 case RES_OUTOFMEMORY:
4899 ast_cli(fd, "Out of memory\n");
4900 return RESULT_FAILURE;
4901 case RES_NOT_DYNAMIC:
4902 ast_cli(fd, "Member not dynamic\n");
4903 return RESULT_FAILURE;
4904 default:
4905 return RESULT_FAILURE;
4909 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
4911 int which = 0;
4912 struct call_queue *q;
4913 struct member *m;
4914 struct ao2_iterator mem_iter;
4916 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
4917 if (pos > 5 || pos < 3)
4918 return NULL;
4919 if (pos == 4) /* only one possible match, 'from' */
4920 return state == 0 ? ast_strdup("from") : NULL;
4922 if (pos == 5) /* No need to duplicate code */
4923 return complete_queue(line, word, pos, state);
4925 /* here is the case for 3, <member> */
4926 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
4927 AST_LIST_TRAVERSE(&queues, q, list) {
4928 ast_mutex_lock(&q->lock);
4929 mem_iter = ao2_iterator_init(q->members, 0);
4930 while ((m = ao2_iterator_next(&mem_iter))) {
4931 if (++which > state) {
4932 char *tmp;
4933 ast_mutex_unlock(&q->lock);
4934 tmp = ast_strdup(m->interface);
4935 ao2_ref(m, -1);
4936 return tmp;
4938 ao2_ref(m, -1);
4940 ast_mutex_unlock(&q->lock);
4944 return NULL;
4947 static char queue_show_usage[] =
4948 "Usage: queue show\n"
4949 " Provides summary information on a specified queue.\n";
4951 static char qam_cmd_usage[] =
4952 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
4954 static char qrm_cmd_usage[] =
4955 "Usage: queue remove member <channel> from <queue>\n";
4957 static struct ast_cli_entry cli_show_queue_deprecated = {
4958 { "show", "queue", NULL },
4959 queue_show, NULL,
4960 NULL, complete_queue_show };
4962 static struct ast_cli_entry cli_add_queue_member_deprecated = {
4963 { "add", "queue", "member", NULL },
4964 handle_queue_add_member, NULL,
4965 NULL, complete_queue_add_member };
4967 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
4968 { "remove", "queue", "member", NULL },
4969 handle_queue_remove_member, NULL,
4970 NULL, complete_queue_remove_member };
4972 static struct ast_cli_entry cli_queue[] = {
4973 /* Deprecated */
4974 { { "show", "queues", NULL },
4975 queue_show, NULL,
4976 NULL, NULL },
4978 { { "queue", "show", NULL },
4979 queue_show, "Show status of a specified queue",
4980 queue_show_usage, complete_queue_show, &cli_show_queue_deprecated },
4982 { { "queue", "add", "member", NULL },
4983 handle_queue_add_member, "Add a channel to a specified queue",
4984 qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
4986 { { "queue", "remove", "member", NULL },
4987 handle_queue_remove_member, "Removes a channel from a specified queue",
4988 qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
4991 static int unload_module(void)
4993 int res;
4995 if (device_state.thread != AST_PTHREADT_NULL) {
4996 device_state.stop = 1;
4997 ast_mutex_lock(&device_state.lock);
4998 ast_cond_signal(&device_state.cond);
4999 ast_mutex_unlock(&device_state.lock);
5000 pthread_join(device_state.thread, NULL);
5003 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
5004 res = ast_manager_unregister("QueueStatus");
5005 res |= ast_manager_unregister("Queues");
5006 res |= ast_manager_unregister("QueueStatus");
5007 res |= ast_manager_unregister("QueueAdd");
5008 res |= ast_manager_unregister("QueueRemove");
5009 res |= ast_manager_unregister("QueuePause");
5010 res |= ast_unregister_application(app_aqm);
5011 res |= ast_unregister_application(app_rqm);
5012 res |= ast_unregister_application(app_pqm);
5013 res |= ast_unregister_application(app_upqm);
5014 res |= ast_unregister_application(app_ql);
5015 res |= ast_unregister_application(app);
5016 res |= ast_custom_function_unregister(&queueagentcount_function);
5017 res |= ast_custom_function_unregister(&queuemembercount_function);
5018 res |= ast_custom_function_unregister(&queuememberlist_function);
5019 res |= ast_custom_function_unregister(&queuewaitingcount_function);
5020 ast_devstate_del(statechange_queue, NULL);
5022 ast_module_user_hangup_all();
5024 clear_and_free_interfaces();
5026 return res;
5029 static int load_module(void)
5031 int res;
5033 if (!reload_queues())
5034 return AST_MODULE_LOAD_DECLINE;
5036 if (queue_persistent_members)
5037 reload_queue_members();
5039 ast_mutex_init(&device_state.lock);
5040 ast_cond_init(&device_state.cond, NULL);
5041 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
5043 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
5044 res = ast_register_application(app, queue_exec, synopsis, descrip);
5045 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
5046 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
5047 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
5048 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
5049 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
5050 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
5051 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
5052 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
5053 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
5054 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
5055 res |= ast_custom_function_register(&queueagentcount_function);
5056 res |= ast_custom_function_register(&queuemembercount_function);
5057 res |= ast_custom_function_register(&queuememberlist_function);
5058 res |= ast_custom_function_register(&queuewaitingcount_function);
5059 res |= ast_devstate_add(statechange_queue, NULL);
5061 return res;
5064 static int reload(void)
5066 reload_queues();
5067 return 0;
5070 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
5071 .load = load_module,
5072 .unload = unload_module,
5073 .reload = reload,