Fix a typo in the non-DEBUG_THREADS version of the recently added DEADLOCK_AVOIDANCE()
[asterisk-bristuff.git] / apps / app_queue.c
blob587e2585b9e4f1bc823e90499bcd70ac24ba6924
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 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
1233 AST_LIST_INSERT_HEAD(&queues, q, list);
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);
2594 /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
2596 * When a caller is atxferred, then the queue_transfer_info datastore
2597 * is removed from the channel. If it's still there after the bridge is
2598 * broken, then the caller was not atxferred.
2600 static int attended_transfer_occurred(struct ast_channel *chan)
2602 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
2605 /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
2607 static void setup_transfer_datastore(struct queue_ent *qe, struct member *member, int starttime)
2609 struct ast_datastore *ds;
2610 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
2612 if (!qtds) {
2613 ast_log(LOG_WARNING, "Memory allocation error!\n");
2614 return;
2617 ast_channel_lock(qe->chan);
2618 if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
2619 ast_channel_unlock(qe->chan);
2620 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
2621 return;
2624 qtds->qe = qe;
2625 /* This member is refcounted in try_calling, so no need to add it here, too */
2626 qtds->member = member;
2627 qtds->starttime = starttime;
2628 ds->data = qtds;
2629 ast_channel_datastore_add(qe->chan, ds);
2630 ast_channel_unlock(qe->chan);
2634 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
2636 * Here is the process of this function
2637 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
2638 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
2639 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
2640 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
2641 * during each iteration, we call calc_metric to determine which members should be rung when.
2642 * 3. Call ring_one to place a call to the appropriate member(s)
2643 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
2644 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
2645 * 6. Start the monitor or mixmonitor if the option is set
2646 * 7. Remove the caller from the queue to allow other callers to advance
2647 * 8. Bridge the call.
2648 * 9. Do any post processing after the call has disconnected.
2650 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
2651 * \param[in] options the options passed as the third parameter to the Queue() application
2652 * \param[in] url the url passed as the fourth parameter to the Queue() application
2653 * \param[in,out] tries the number of times we have tried calling queue members
2654 * \param[out] noption set if the call to Queue() has the 'n' option set.
2655 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
2658 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
2660 struct member *cur;
2661 struct callattempt *outgoing = NULL; /* the list of calls we are building */
2662 int to;
2663 char oldexten[AST_MAX_EXTENSION]="";
2664 char oldcontext[AST_MAX_CONTEXT]="";
2665 char queuename[256]="";
2666 struct ast_channel *peer;
2667 struct ast_channel *which;
2668 struct callattempt *lpeer;
2669 struct member *member;
2670 struct ast_app *app;
2671 int res = 0, bridge = 0;
2672 int numbusies = 0;
2673 int x=0;
2674 char *announce = NULL;
2675 char digit = 0;
2676 time_t callstart;
2677 time_t now = time(NULL);
2678 struct ast_bridge_config bridge_config;
2679 char nondataquality = 1;
2680 char *agiexec = NULL;
2681 int ret = 0;
2682 const char *monitorfilename;
2683 const char *monitor_exec;
2684 const char *monitor_options;
2685 char tmpid[256], tmpid2[256];
2686 char meid[1024], meid2[1024];
2687 char mixmonargs[1512];
2688 struct ast_app *mixmonapp = NULL;
2689 char *p;
2690 char vars[2048];
2691 int forwardsallowed = 1;
2692 int callcompletedinsl;
2693 struct ao2_iterator memi;
2694 struct ast_datastore *datastore;
2696 ast_channel_lock(qe->chan);
2697 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
2698 ast_channel_unlock(qe->chan);
2700 memset(&bridge_config, 0, sizeof(bridge_config));
2701 time(&now);
2703 for (; options && *options; options++)
2704 switch (*options) {
2705 case 't':
2706 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
2707 break;
2708 case 'T':
2709 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
2710 break;
2711 case 'w':
2712 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
2713 break;
2714 case 'W':
2715 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
2716 break;
2717 case 'd':
2718 nondataquality = 0;
2719 break;
2720 case 'h':
2721 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
2722 break;
2723 case 'H':
2724 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
2725 break;
2726 case 'n':
2727 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
2728 (*tries)++;
2729 else
2730 *tries = qe->parent->membercount;
2731 *noption = 1;
2732 break;
2733 case 'i':
2734 forwardsallowed = 0;
2735 break;
2738 /* Hold the lock while we setup the outgoing calls */
2739 if (use_weight)
2740 AST_LIST_LOCK(&queues);
2741 ast_mutex_lock(&qe->parent->lock);
2742 if (option_debug)
2743 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
2744 qe->chan->name);
2745 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
2746 if (!ast_strlen_zero(qe->announce))
2747 announce = qe->announce;
2748 if (!ast_strlen_zero(announceoverride))
2749 announce = announceoverride;
2751 memi = ao2_iterator_init(qe->parent->members, 0);
2752 while ((cur = ao2_iterator_next(&memi))) {
2753 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
2754 struct ast_dialed_interface *di;
2755 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
2756 if (!tmp) {
2757 ao2_ref(cur, -1);
2758 ast_mutex_unlock(&qe->parent->lock);
2759 if (use_weight)
2760 AST_LIST_UNLOCK(&queues);
2761 goto out;
2763 if (!datastore) {
2764 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
2765 ao2_ref(cur, -1);
2766 ast_mutex_unlock(&qe->parent->lock);
2767 if (use_weight)
2768 AST_LIST_UNLOCK(&queues);
2769 free(tmp);
2770 goto out;
2772 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
2773 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
2774 ao2_ref(cur, -1);
2775 ast_mutex_unlock(&qe->parent->lock);
2776 if (use_weight)
2777 AST_LIST_UNLOCK(&queues);
2778 free(tmp);
2779 goto out;
2781 datastore->data = dialed_interfaces;
2782 AST_LIST_HEAD_INIT(dialed_interfaces);
2784 ast_channel_lock(qe->chan);
2785 ast_channel_datastore_add(qe->chan, datastore);
2786 ast_channel_unlock(qe->chan);
2787 } else
2788 dialed_interfaces = datastore->data;
2790 AST_LIST_LOCK(dialed_interfaces);
2791 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
2792 if (!strcasecmp(cur->interface, di->interface)) {
2793 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
2794 di->interface);
2795 break;
2798 AST_LIST_UNLOCK(dialed_interfaces);
2800 if (di) {
2801 free(tmp);
2802 continue;
2805 /* It is always ok to dial a Local interface. We only keep track of
2806 * which "real" interfaces have been dialed. The Local channel will
2807 * inherit this list so that if it ends up dialing a real interface,
2808 * it won't call one that has already been called. */
2809 if (strncasecmp(cur->interface, "Local/", 6)) {
2810 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
2811 ao2_ref(cur, -1);
2812 ast_mutex_unlock(&qe->parent->lock);
2813 if (use_weight)
2814 AST_LIST_UNLOCK(&queues);
2815 free(tmp);
2816 goto out;
2818 strcpy(di->interface, cur->interface);
2820 AST_LIST_LOCK(dialed_interfaces);
2821 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
2822 AST_LIST_UNLOCK(dialed_interfaces);
2825 tmp->stillgoing = -1;
2826 tmp->member = cur;
2827 tmp->oldstatus = cur->status;
2828 tmp->lastcall = cur->lastcall;
2829 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
2830 /* Special case: If we ring everyone, go ahead and ring them, otherwise
2831 just calculate their metric for the appropriate strategy */
2832 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
2833 /* Put them in the list of outgoing thingies... We're ready now.
2834 XXX If we're forcibly removed, these outgoing calls won't get
2835 hung up XXX */
2836 tmp->q_next = outgoing;
2837 outgoing = tmp;
2838 /* If this line is up, don't try anybody else */
2839 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
2840 break;
2841 } else {
2842 ao2_ref(cur, -1);
2843 free(tmp);
2846 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
2847 to = (qe->expire - now) * 1000;
2848 else
2849 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
2850 ++qe->pending;
2851 ast_mutex_unlock(&qe->parent->lock);
2852 ring_one(qe, outgoing, &numbusies);
2853 if (use_weight)
2854 AST_LIST_UNLOCK(&queues);
2855 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
2856 /* The ast_channel_datastore_remove() function could fail here if the
2857 * datastore was moved to another channel during a masquerade. If this is
2858 * the case, don't free the datastore here because later, when the channel
2859 * to which the datastore was moved hangs up, it will attempt to free this
2860 * datastore again, causing a crash
2862 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
2863 ast_channel_datastore_free(datastore);
2865 ast_mutex_lock(&qe->parent->lock);
2866 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
2867 store_next(qe, outgoing);
2869 ast_mutex_unlock(&qe->parent->lock);
2870 peer = lpeer ? lpeer->chan : NULL;
2871 if (!peer) {
2872 qe->pending = 0;
2873 if (to) {
2874 /* Must gotten hung up */
2875 res = -1;
2876 } else {
2877 /* User exited by pressing a digit */
2878 res = digit;
2880 if (option_debug && res == -1)
2881 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name);
2882 } else { /* peer is valid */
2883 /* Ah ha! Someone answered within the desired timeframe. Of course after this
2884 we will always return with -1 so that it is hung up properly after the
2885 conversation. */
2886 if (!strcmp(qe->chan->tech->type, "Zap"))
2887 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2888 if (!strcmp(peer->tech->type, "Zap"))
2889 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
2890 /* Update parameters for the queue */
2891 time(&now);
2892 recalc_holdtime(qe, (now - qe->start));
2893 ast_mutex_lock(&qe->parent->lock);
2894 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
2895 ast_mutex_unlock(&qe->parent->lock);
2896 member = lpeer->member;
2897 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
2898 ao2_ref(member, 1);
2899 hangupcalls(outgoing, peer);
2900 outgoing = NULL;
2901 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
2902 int res2;
2904 res2 = ast_autoservice_start(qe->chan);
2905 if (!res2) {
2906 if (qe->parent->memberdelay) {
2907 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
2908 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
2910 if (!res2 && announce) {
2911 play_file(peer, announce);
2913 if (!res2 && qe->parent->reportholdtime) {
2914 if (!play_file(peer, qe->parent->sound_reporthold)) {
2915 int holdtime;
2917 time(&now);
2918 holdtime = abs((now - qe->start) / 60);
2919 if (holdtime < 2) {
2920 play_file(peer, qe->parent->sound_lessthan);
2921 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
2922 } else
2923 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
2924 play_file(peer, qe->parent->sound_minutes);
2928 res2 |= ast_autoservice_stop(qe->chan);
2929 if (peer->_softhangup) {
2930 /* Agent must have hung up */
2931 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
2932 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
2933 if (qe->parent->eventwhencalled)
2934 manager_event(EVENT_FLAG_AGENT, "AgentDump",
2935 "Queue: %s\r\n"
2936 "Uniqueid: %s\r\n"
2937 "Channel: %s\r\n"
2938 "Member: %s\r\n"
2939 "MemberName: %s\r\n"
2940 "%s",
2941 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
2942 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2943 ast_hangup(peer);
2944 ao2_ref(member, -1);
2945 goto out;
2946 } else if (res2) {
2947 /* Caller must have hung up just before being connected*/
2948 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
2949 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
2950 record_abandoned(qe);
2951 ast_hangup(peer);
2952 ao2_ref(member, -1);
2953 return -1;
2956 /* Stop music on hold */
2957 ast_moh_stop(qe->chan);
2958 /* If appropriate, log that we have a destination channel */
2959 if (qe->chan->cdr)
2960 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
2961 /* Make sure channels are compatible */
2962 res = ast_channel_make_compatible(qe->chan, peer);
2963 if (res < 0) {
2964 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
2965 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
2966 record_abandoned(qe);
2967 ast_hangup(peer);
2968 ao2_ref(member, -1);
2969 return -1;
2972 if (qe->parent->setinterfacevar)
2973 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
2975 /* Begin Monitoring */
2976 if (qe->parent->monfmt && *qe->parent->monfmt) {
2977 if (!qe->parent->montype) {
2978 if (option_debug)
2979 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
2980 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
2981 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
2982 which = qe->chan;
2983 else
2984 which = peer;
2985 if (monitorfilename)
2986 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
2987 else if (qe->chan->cdr)
2988 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
2989 else {
2990 /* Last ditch effort -- no CDR, make up something */
2991 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
2992 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
2994 if (qe->parent->monjoin)
2995 ast_monitor_setjoinfiles(which, 1);
2996 } else {
2997 if (option_debug)
2998 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
2999 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
3000 if (!monitorfilename) {
3001 if (qe->chan->cdr)
3002 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
3003 else
3004 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3005 } else {
3006 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
3007 for (p = tmpid2; *p ; p++) {
3008 if (*p == '^' && *(p+1) == '{') {
3009 *p = '$';
3013 memset(tmpid, 0, sizeof(tmpid));
3014 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
3017 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
3018 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
3020 if (monitor_exec) {
3021 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
3022 for (p = meid2; *p ; p++) {
3023 if (*p == '^' && *(p+1) == '{') {
3024 *p = '$';
3028 memset(meid, 0, sizeof(meid));
3029 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
3032 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
3034 mixmonapp = pbx_findapp("MixMonitor");
3036 if (strchr(tmpid2, '|')) {
3037 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
3038 mixmonapp = NULL;
3041 if (!monitor_options)
3042 monitor_options = "";
3044 if (strchr(monitor_options, '|')) {
3045 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
3046 mixmonapp = NULL;
3049 if (mixmonapp) {
3050 if (!ast_strlen_zero(monitor_exec))
3051 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
3052 else
3053 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
3055 if (option_debug)
3056 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
3057 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3058 if (qe->chan->cdr)
3059 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3060 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
3061 if (qe->chan->cdr)
3062 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3064 } else
3065 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3069 /* Drop out of the queue at this point, to prepare for next caller */
3070 leave_queue(qe);
3071 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
3072 if (option_debug)
3073 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
3074 ast_channel_sendurl(peer, url);
3076 if (!ast_strlen_zero(agi)) {
3077 if (option_debug)
3078 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
3079 app = pbx_findapp("agi");
3080 if (app) {
3081 agiexec = ast_strdupa(agi);
3082 ret = pbx_exec(qe->chan, app, agiexec);
3083 } else
3084 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3086 qe->handled++;
3087 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
3088 if (qe->parent->eventwhencalled)
3089 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
3090 "Queue: %s\r\n"
3091 "Uniqueid: %s\r\n"
3092 "Channel: %s\r\n"
3093 "Member: %s\r\n"
3094 "MemberName: %s\r\n"
3095 "Holdtime: %ld\r\n"
3096 "BridgedChannel: %s\r\n"
3097 "%s",
3098 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3099 (long)time(NULL) - qe->start, peer->uniqueid,
3100 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3101 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
3102 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
3103 time(&callstart);
3105 if (member->status == AST_DEVICE_NOT_INUSE)
3106 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);
3108 setup_transfer_datastore(qe, member, callstart);
3109 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3111 if (!attended_transfer_occurred(qe->chan)) {
3112 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3113 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
3114 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
3115 (long) (time(NULL) - callstart));
3116 } else if (qe->chan->_softhangup) {
3117 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
3118 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3119 if (qe->parent->eventwhencalled)
3120 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
3121 "Queue: %s\r\n"
3122 "Uniqueid: %s\r\n"
3123 "Channel: %s\r\n"
3124 "Member: %s\r\n"
3125 "MemberName: %s\r\n"
3126 "HoldTime: %ld\r\n"
3127 "TalkTime: %ld\r\n"
3128 "Reason: caller\r\n"
3129 "%s",
3130 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3131 (long)(callstart - qe->start), (long)(time(NULL) - callstart),
3132 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3133 } else {
3134 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
3135 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3136 if (qe->parent->eventwhencalled)
3137 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
3138 "Queue: %s\r\n"
3139 "Uniqueid: %s\r\n"
3140 "Channel: %s\r\n"
3141 "MemberName: %s\r\n"
3142 "HoldTime: %ld\r\n"
3143 "TalkTime: %ld\r\n"
3144 "Reason: agent\r\n"
3145 "%s",
3146 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
3147 (long)(time(NULL) - callstart),
3148 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3152 if (bridge != AST_PBX_NO_HANGUP_PEER)
3153 ast_hangup(peer);
3154 update_queue(qe->parent, member, callcompletedinsl);
3155 res = bridge ? bridge : 1;
3156 ao2_ref(member, -1);
3158 out:
3159 hangupcalls(outgoing, NULL);
3161 return res;
3164 static int wait_a_bit(struct queue_ent *qe)
3166 /* Don't need to hold the lock while we setup the outgoing calls */
3167 int retrywait = qe->parent->retry * 1000;
3169 int res = ast_waitfordigit(qe->chan, retrywait);
3170 if (res > 0 && !valid_exit(qe, res))
3171 res = 0;
3173 return res;
3176 static struct member *interface_exists(struct call_queue *q, const char *interface)
3178 struct member *mem;
3179 struct ao2_iterator mem_iter;
3181 if (!q)
3182 return NULL;
3184 mem_iter = ao2_iterator_init(q->members, 0);
3185 while ((mem = ao2_iterator_next(&mem_iter))) {
3186 if (!strcasecmp(interface, mem->interface))
3187 return mem;
3188 ao2_ref(mem, -1);
3191 return NULL;
3195 /* Dump all members in a specific queue to the database
3197 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
3200 static void dump_queue_members(struct call_queue *pm_queue)
3202 struct member *cur_member;
3203 char value[PM_MAX_LEN];
3204 int value_len = 0;
3205 int res;
3206 struct ao2_iterator mem_iter;
3208 memset(value, 0, sizeof(value));
3210 if (!pm_queue)
3211 return;
3213 mem_iter = ao2_iterator_init(pm_queue->members, 0);
3214 while ((cur_member = ao2_iterator_next(&mem_iter))) {
3215 if (!cur_member->dynamic) {
3216 ao2_ref(cur_member, -1);
3217 continue;
3220 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
3221 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
3223 ao2_ref(cur_member, -1);
3225 if (res != strlen(value + value_len)) {
3226 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
3227 break;
3229 value_len += res;
3232 if (value_len && !cur_member) {
3233 if (ast_db_put(pm_family, pm_queue->name, value))
3234 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
3235 } else
3236 /* Delete the entry if the queue is empty or there is an error */
3237 ast_db_del(pm_family, pm_queue->name);
3240 static int remove_from_queue(const char *queuename, const char *interface)
3242 struct call_queue *q;
3243 struct member *mem, tmpmem;
3244 int res = RES_NOSUCHQUEUE;
3246 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
3248 AST_LIST_LOCK(&queues);
3249 AST_LIST_TRAVERSE(&queues, q, list) {
3250 ast_mutex_lock(&q->lock);
3251 if (strcmp(q->name, queuename)) {
3252 ast_mutex_unlock(&q->lock);
3253 continue;
3256 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
3257 /* XXX future changes should beware of this assumption!! */
3258 if (!mem->dynamic) {
3259 res = RES_NOT_DYNAMIC;
3260 ao2_ref(mem, -1);
3261 ast_mutex_unlock(&q->lock);
3262 break;
3264 q->membercount--;
3265 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
3266 "Queue: %s\r\n"
3267 "Location: %s\r\n"
3268 "MemberName: %s\r\n",
3269 q->name, mem->interface, mem->membername);
3270 ao2_unlink(q->members, mem);
3271 ao2_ref(mem, -1);
3273 if (queue_persistent_members)
3274 dump_queue_members(q);
3276 res = RES_OKAY;
3277 } else {
3278 res = RES_EXISTS;
3280 ast_mutex_unlock(&q->lock);
3281 break;
3284 if (res == RES_OKAY)
3285 remove_from_interfaces(interface);
3287 AST_LIST_UNLOCK(&queues);
3289 return res;
3293 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump)
3295 struct call_queue *q;
3296 struct member *new_member, *old_member;
3297 int res = RES_NOSUCHQUEUE;
3299 /* \note Ensure the appropriate realtime queue is loaded. Note that this
3300 * short-circuits if the queue is already in memory. */
3301 if (!(q = load_realtime_queue(queuename)))
3302 return res;
3304 AST_LIST_LOCK(&queues);
3306 ast_mutex_lock(&q->lock);
3307 if ((old_member = interface_exists(q, interface)) == NULL) {
3308 add_to_interfaces(interface);
3309 if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
3310 new_member->dynamic = 1;
3311 ao2_link(q->members, new_member);
3312 q->membercount++;
3313 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
3314 "Queue: %s\r\n"
3315 "Location: %s\r\n"
3316 "MemberName: %s\r\n"
3317 "Membership: %s\r\n"
3318 "Penalty: %d\r\n"
3319 "CallsTaken: %d\r\n"
3320 "LastCall: %d\r\n"
3321 "Status: %d\r\n"
3322 "Paused: %d\r\n",
3323 q->name, new_member->interface, new_member->membername,
3324 "dynamic",
3325 new_member->penalty, new_member->calls, (int) new_member->lastcall,
3326 new_member->status, new_member->paused);
3328 ao2_ref(new_member, -1);
3329 new_member = NULL;
3331 if (dump)
3332 dump_queue_members(q);
3334 res = RES_OKAY;
3335 } else {
3336 res = RES_OUTOFMEMORY;
3338 } else {
3339 ao2_ref(old_member, -1);
3340 res = RES_EXISTS;
3342 ast_mutex_unlock(&q->lock);
3343 AST_LIST_UNLOCK(&queues);
3345 return res;
3348 static int set_member_paused(const char *queuename, const char *interface, int paused)
3350 int found = 0;
3351 struct call_queue *q;
3352 struct member *mem;
3354 /* Special event for when all queues are paused - individual events still generated */
3355 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3356 if (ast_strlen_zero(queuename))
3357 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3359 AST_LIST_LOCK(&queues);
3360 AST_LIST_TRAVERSE(&queues, q, list) {
3361 ast_mutex_lock(&q->lock);
3362 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3363 if ((mem = interface_exists(q, interface))) {
3364 found++;
3365 if (mem->paused == paused)
3366 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
3367 mem->paused = paused;
3369 if (queue_persistent_members)
3370 dump_queue_members(q);
3372 if (mem->realtime)
3373 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
3375 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
3377 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3378 "Queue: %s\r\n"
3379 "Location: %s\r\n"
3380 "MemberName: %s\r\n"
3381 "Paused: %d\r\n",
3382 q->name, mem->interface, mem->membername, paused);
3383 ao2_ref(mem, -1);
3386 ast_mutex_unlock(&q->lock);
3388 AST_LIST_UNLOCK(&queues);
3390 return found ? RESULT_SUCCESS : RESULT_FAILURE;
3393 /* Reload dynamic queue members persisted into the astdb */
3394 static void reload_queue_members(void)
3396 char *cur_ptr;
3397 char *queue_name;
3398 char *member;
3399 char *interface;
3400 char *membername = NULL;
3401 char *penalty_tok;
3402 int penalty = 0;
3403 char *paused_tok;
3404 int paused = 0;
3405 struct ast_db_entry *db_tree;
3406 struct ast_db_entry *entry;
3407 struct call_queue *cur_queue;
3408 char queue_data[PM_MAX_LEN];
3410 AST_LIST_LOCK(&queues);
3412 /* Each key in 'pm_family' is the name of a queue */
3413 db_tree = ast_db_gettree(pm_family, NULL);
3414 for (entry = db_tree; entry; entry = entry->next) {
3416 queue_name = entry->key + strlen(pm_family) + 2;
3418 AST_LIST_TRAVERSE(&queues, cur_queue, list) {
3419 ast_mutex_lock(&cur_queue->lock);
3420 if (!strcmp(queue_name, cur_queue->name))
3421 break;
3422 ast_mutex_unlock(&cur_queue->lock);
3425 if (!cur_queue)
3426 cur_queue = load_realtime_queue(queue_name);
3428 if (!cur_queue) {
3429 /* If the queue no longer exists, remove it from the
3430 * database */
3431 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
3432 ast_db_del(pm_family, queue_name);
3433 continue;
3434 } else
3435 ast_mutex_unlock(&cur_queue->lock);
3437 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
3438 continue;
3440 cur_ptr = queue_data;
3441 while ((member = strsep(&cur_ptr, "|"))) {
3442 if (ast_strlen_zero(member))
3443 continue;
3445 interface = strsep(&member, ";");
3446 penalty_tok = strsep(&member, ";");
3447 paused_tok = strsep(&member, ";");
3448 membername = strsep(&member, ";");
3450 if (!penalty_tok) {
3451 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
3452 break;
3454 penalty = strtol(penalty_tok, NULL, 10);
3455 if (errno == ERANGE) {
3456 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
3457 break;
3460 if (!paused_tok) {
3461 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
3462 break;
3464 paused = strtol(paused_tok, NULL, 10);
3465 if ((errno == ERANGE) || paused < 0 || paused > 1) {
3466 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
3467 break;
3469 if (ast_strlen_zero(membername))
3470 membername = interface;
3472 if (option_debug)
3473 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
3475 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
3476 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
3477 break;
3482 AST_LIST_UNLOCK(&queues);
3483 if (db_tree) {
3484 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
3485 ast_db_freetree(db_tree);
3489 static int pqm_exec(struct ast_channel *chan, void *data)
3491 struct ast_module_user *lu;
3492 char *parse;
3493 int priority_jump = 0;
3494 AST_DECLARE_APP_ARGS(args,
3495 AST_APP_ARG(queuename);
3496 AST_APP_ARG(interface);
3497 AST_APP_ARG(options);
3500 if (ast_strlen_zero(data)) {
3501 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3502 return -1;
3505 parse = ast_strdupa(data);
3507 AST_STANDARD_APP_ARGS(args, parse);
3509 lu = ast_module_user_add(chan);
3511 if (args.options) {
3512 if (strchr(args.options, 'j'))
3513 priority_jump = 1;
3516 if (ast_strlen_zero(args.interface)) {
3517 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3518 ast_module_user_remove(lu);
3519 return -1;
3522 if (set_member_paused(args.queuename, args.interface, 1)) {
3523 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
3524 if (priority_jump || ast_opt_priority_jumping) {
3525 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3526 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3527 ast_module_user_remove(lu);
3528 return 0;
3531 ast_module_user_remove(lu);
3532 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
3533 return 0;
3536 ast_module_user_remove(lu);
3537 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
3539 return 0;
3542 static int upqm_exec(struct ast_channel *chan, void *data)
3544 struct ast_module_user *lu;
3545 char *parse;
3546 int priority_jump = 0;
3547 AST_DECLARE_APP_ARGS(args,
3548 AST_APP_ARG(queuename);
3549 AST_APP_ARG(interface);
3550 AST_APP_ARG(options);
3553 if (ast_strlen_zero(data)) {
3554 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
3555 return -1;
3558 parse = ast_strdupa(data);
3560 AST_STANDARD_APP_ARGS(args, parse);
3562 lu = ast_module_user_add(chan);
3564 if (args.options) {
3565 if (strchr(args.options, 'j'))
3566 priority_jump = 1;
3569 if (ast_strlen_zero(args.interface)) {
3570 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
3571 ast_module_user_remove(lu);
3572 return -1;
3575 if (set_member_paused(args.queuename, args.interface, 0)) {
3576 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
3577 if (priority_jump || ast_opt_priority_jumping) {
3578 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
3579 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3580 ast_module_user_remove(lu);
3581 return 0;
3584 ast_module_user_remove(lu);
3585 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
3586 return 0;
3589 ast_module_user_remove(lu);
3590 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
3592 return 0;
3595 static int rqm_exec(struct ast_channel *chan, void *data)
3597 int res=-1;
3598 struct ast_module_user *lu;
3599 char *parse, *temppos = NULL;
3600 int priority_jump = 0;
3601 AST_DECLARE_APP_ARGS(args,
3602 AST_APP_ARG(queuename);
3603 AST_APP_ARG(interface);
3604 AST_APP_ARG(options);
3608 if (ast_strlen_zero(data)) {
3609 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
3610 return -1;
3613 parse = ast_strdupa(data);
3615 AST_STANDARD_APP_ARGS(args, parse);
3617 lu = ast_module_user_add(chan);
3619 if (ast_strlen_zero(args.interface)) {
3620 args.interface = ast_strdupa(chan->name);
3621 temppos = strrchr(args.interface, '-');
3622 if (temppos)
3623 *temppos = '\0';
3626 if (args.options) {
3627 if (strchr(args.options, 'j'))
3628 priority_jump = 1;
3631 switch (remove_from_queue(args.queuename, args.interface)) {
3632 case RES_OKAY:
3633 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
3634 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
3635 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
3636 res = 0;
3637 break;
3638 case RES_EXISTS:
3639 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
3640 if (priority_jump || ast_opt_priority_jumping)
3641 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3642 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
3643 res = 0;
3644 break;
3645 case RES_NOSUCHQUEUE:
3646 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
3647 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
3648 res = 0;
3649 break;
3650 case RES_NOT_DYNAMIC:
3651 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
3652 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
3653 res = 0;
3654 break;
3657 ast_module_user_remove(lu);
3659 return res;
3662 static int aqm_exec(struct ast_channel *chan, void *data)
3664 int res=-1;
3665 struct ast_module_user *lu;
3666 char *parse, *temppos = NULL;
3667 int priority_jump = 0;
3668 AST_DECLARE_APP_ARGS(args,
3669 AST_APP_ARG(queuename);
3670 AST_APP_ARG(interface);
3671 AST_APP_ARG(penalty);
3672 AST_APP_ARG(options);
3673 AST_APP_ARG(membername);
3675 int penalty = 0;
3677 if (ast_strlen_zero(data)) {
3678 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
3679 return -1;
3682 parse = ast_strdupa(data);
3684 AST_STANDARD_APP_ARGS(args, parse);
3686 lu = ast_module_user_add(chan);
3688 if (ast_strlen_zero(args.interface)) {
3689 args.interface = ast_strdupa(chan->name);
3690 temppos = strrchr(args.interface, '-');
3691 if (temppos)
3692 *temppos = '\0';
3695 if (!ast_strlen_zero(args.penalty)) {
3696 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
3697 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
3698 penalty = 0;
3702 if (args.options) {
3703 if (strchr(args.options, 'j'))
3704 priority_jump = 1;
3707 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
3708 case RES_OKAY:
3709 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
3710 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
3711 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
3712 res = 0;
3713 break;
3714 case RES_EXISTS:
3715 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
3716 if (priority_jump || ast_opt_priority_jumping)
3717 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3718 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
3719 res = 0;
3720 break;
3721 case RES_NOSUCHQUEUE:
3722 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
3723 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
3724 res = 0;
3725 break;
3726 case RES_OUTOFMEMORY:
3727 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
3728 break;
3731 ast_module_user_remove(lu);
3733 return res;
3736 static int ql_exec(struct ast_channel *chan, void *data)
3738 struct ast_module_user *u;
3739 char *parse;
3741 AST_DECLARE_APP_ARGS(args,
3742 AST_APP_ARG(queuename);
3743 AST_APP_ARG(uniqueid);
3744 AST_APP_ARG(membername);
3745 AST_APP_ARG(event);
3746 AST_APP_ARG(params);
3749 if (ast_strlen_zero(data)) {
3750 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
3751 return -1;
3754 u = ast_module_user_add(chan);
3756 parse = ast_strdupa(data);
3758 AST_STANDARD_APP_ARGS(args, parse);
3760 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
3761 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
3762 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
3763 ast_module_user_remove(u);
3764 return -1;
3767 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
3768 "%s", args.params ? args.params : "");
3770 ast_module_user_remove(u);
3772 return 0;
3775 /*!\brief The starting point for all queue calls
3777 * The process involved here is to
3778 * 1. Parse the options specified in the call to Queue()
3779 * 2. Join the queue
3780 * 3. Wait in a loop until it is our turn to try calling a queue member
3781 * 4. Attempt to call a queue member
3782 * 5. If 4. did not result in a bridged call, then check for between
3783 * call options such as periodic announcements etc.
3784 * 6. Try 4 again uless some condition (such as an expiration time) causes us to
3785 * exit the queue.
3787 static int queue_exec(struct ast_channel *chan, void *data)
3789 int res=-1;
3790 int ringing=0;
3791 struct ast_module_user *lu;
3792 const char *user_priority;
3793 const char *max_penalty_str;
3794 int prio;
3795 int max_penalty;
3796 enum queue_result reason = QUEUE_UNKNOWN;
3797 /* whether to exit Queue application after the timeout hits */
3798 int tries = 0;
3799 int noption = 0;
3800 char *parse;
3801 AST_DECLARE_APP_ARGS(args,
3802 AST_APP_ARG(queuename);
3803 AST_APP_ARG(options);
3804 AST_APP_ARG(url);
3805 AST_APP_ARG(announceoverride);
3806 AST_APP_ARG(queuetimeoutstr);
3807 AST_APP_ARG(agi);
3809 /* Our queue entry */
3810 struct queue_ent qe;
3812 if (ast_strlen_zero(data)) {
3813 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
3814 return -1;
3817 parse = ast_strdupa(data);
3818 AST_STANDARD_APP_ARGS(args, parse);
3820 lu = ast_module_user_add(chan);
3822 /* Setup our queue entry */
3823 memset(&qe, 0, sizeof(qe));
3824 qe.start = time(NULL);
3826 /* set the expire time based on the supplied timeout; */
3827 if (!ast_strlen_zero(args.queuetimeoutstr))
3828 qe.expire = qe.start + atoi(args.queuetimeoutstr);
3829 else
3830 qe.expire = 0;
3832 /* Get the priority from the variable ${QUEUE_PRIO} */
3833 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
3834 if (user_priority) {
3835 if (sscanf(user_priority, "%d", &prio) == 1) {
3836 if (option_debug)
3837 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
3838 chan->name, prio);
3839 } else {
3840 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
3841 user_priority, chan->name);
3842 prio = 0;
3844 } else {
3845 if (option_debug > 2)
3846 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
3847 prio = 0;
3850 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
3851 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
3852 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
3853 if (option_debug)
3854 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
3855 chan->name, max_penalty);
3856 } else {
3857 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
3858 max_penalty_str, chan->name);
3859 max_penalty = 0;
3861 } else {
3862 max_penalty = 0;
3865 if (args.options && (strchr(args.options, 'r')))
3866 ringing = 1;
3868 if (option_debug)
3869 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
3870 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
3872 qe.chan = chan;
3873 qe.prio = prio;
3874 qe.max_penalty = max_penalty;
3875 qe.last_pos_said = 0;
3876 qe.last_pos = 0;
3877 qe.last_periodic_announce_time = time(NULL);
3878 qe.last_periodic_announce_sound = 0;
3879 qe.valid_digits = 0;
3880 if (!join_queue(args.queuename, &qe, &reason)) {
3881 int makeannouncement = 0;
3883 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
3884 S_OR(chan->cid.cid_num, ""));
3885 check_turns:
3886 if (ringing) {
3887 ast_indicate(chan, AST_CONTROL_RINGING);
3888 } else {
3889 ast_moh_start(chan, qe.moh, NULL);
3892 /* This is the wait loop for callers 2 through maxlen */
3893 res = wait_our_turn(&qe, ringing, &reason);
3894 if (res)
3895 goto stop;
3897 for (;;) {
3898 /* This is the wait loop for the head caller*/
3899 /* To exit, they may get their call answered; */
3900 /* they may dial a digit from the queue context; */
3901 /* or, they may timeout. */
3903 enum queue_member_status stat;
3905 /* Leave if we have exceeded our queuetimeout */
3906 if (qe.expire && (time(NULL) > qe.expire)) {
3907 record_abandoned(&qe);
3908 reason = QUEUE_TIMEOUT;
3909 res = 0;
3910 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3911 break;
3914 if (makeannouncement) {
3915 /* Make a position announcement, if enabled */
3916 if (qe.parent->announcefrequency && !ringing)
3917 if ((res = say_position(&qe)))
3918 goto stop;
3921 makeannouncement = 1;
3923 /* Make a periodic announcement, if enabled */
3924 if (qe.parent->periodicannouncefrequency && !ringing)
3925 if ((res = say_periodic_announcement(&qe)))
3926 goto stop;
3928 /* Try calling all queue members for 'timeout' seconds */
3929 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
3930 if (res)
3931 goto stop;
3933 stat = get_member_status(qe.parent, qe.max_penalty);
3935 /* exit after 'timeout' cycle if 'n' option enabled */
3936 if (noption && tries >= qe.parent->membercount) {
3937 if (option_verbose > 2)
3938 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
3939 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3940 record_abandoned(&qe);
3941 reason = QUEUE_TIMEOUT;
3942 res = 0;
3943 break;
3946 /* leave the queue if no agents, if enabled */
3947 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
3948 record_abandoned(&qe);
3949 reason = QUEUE_LEAVEEMPTY;
3950 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
3951 res = 0;
3952 break;
3955 /* leave the queue if no reachable agents, if enabled */
3956 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
3957 record_abandoned(&qe);
3958 reason = QUEUE_LEAVEUNAVAIL;
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 if we have exceeded our queuetimeout */
3965 if (qe.expire && (time(NULL) > qe.expire)) {
3966 record_abandoned(&qe);
3967 reason = QUEUE_TIMEOUT;
3968 res = 0;
3969 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
3970 break;
3973 /* If using dynamic realtime members, we should regenerate the member list for this queue */
3974 update_realtime_members(qe.parent);
3976 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
3977 res = wait_a_bit(&qe);
3978 if (res)
3979 goto stop;
3982 /* Since this is a priority queue and
3983 * it is not sure that we are still at the head
3984 * of the queue, go and check for our turn again.
3986 if (!is_our_turn(&qe)) {
3987 if (option_debug)
3988 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
3989 qe.chan->name);
3990 goto check_turns;
3994 stop:
3995 if (res) {
3996 if (res < 0) {
3997 if (!qe.handled) {
3998 record_abandoned(&qe);
3999 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
4000 "%d|%d|%ld", qe.pos, qe.opos,
4001 (long) time(NULL) - qe.start);
4003 res = -1;
4004 } else if (qe.valid_digits) {
4005 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
4006 "%s|%d", qe.digits, qe.pos);
4010 /* Don't allow return code > 0 */
4011 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
4012 res = 0;
4013 if (ringing) {
4014 ast_indicate(chan, -1);
4015 } else {
4016 ast_moh_stop(chan);
4018 ast_stopstream(chan);
4020 leave_queue(&qe);
4021 if (reason != QUEUE_UNKNOWN)
4022 set_queue_result(chan, reason);
4023 } else {
4024 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
4025 set_queue_result(chan, reason);
4026 res = 0;
4028 ast_module_user_remove(lu);
4030 return res;
4033 static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
4035 int count = 0;
4036 struct call_queue *q;
4037 struct ast_module_user *lu;
4038 struct member *m;
4039 struct ao2_iterator mem_iter;
4041 buf[0] = '\0';
4043 if (ast_strlen_zero(data)) {
4044 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4045 return -1;
4048 lu = ast_module_user_add(chan);
4050 if ((q = load_realtime_queue(data))) {
4051 ast_mutex_lock(&q->lock);
4052 mem_iter = ao2_iterator_init(q->members, 0);
4053 while ((m = ao2_iterator_next(&mem_iter))) {
4054 /* Count the agents who are logged in and presently answering calls */
4055 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4056 count++;
4058 ao2_ref(m, -1);
4060 ast_mutex_unlock(&q->lock);
4061 } else
4062 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4064 snprintf(buf, len, "%d", count);
4065 ast_module_user_remove(lu);
4067 return 0;
4070 static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
4072 int count = 0;
4073 struct call_queue *q;
4074 struct ast_module_user *lu;
4075 struct ast_variable *var = NULL;
4077 buf[0] = '\0';
4079 if (ast_strlen_zero(data)) {
4080 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4081 return -1;
4084 lu = ast_module_user_add(chan);
4086 AST_LIST_LOCK(&queues);
4087 AST_LIST_TRAVERSE(&queues, q, list) {
4088 if (!strcasecmp(q->name, data)) {
4089 ast_mutex_lock(&q->lock);
4090 break;
4093 AST_LIST_UNLOCK(&queues);
4095 if (q) {
4096 count = q->count;
4097 ast_mutex_unlock(&q->lock);
4098 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
4099 /* if the queue is realtime but was not found in memory, this
4100 * means that the queue had been deleted from memory since it was
4101 * "dead." This means it has a 0 waiting count
4103 count = 0;
4104 ast_variables_destroy(var);
4105 } else
4106 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4108 snprintf(buf, len, "%d", count);
4109 ast_module_user_remove(lu);
4110 return 0;
4113 static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
4115 struct ast_module_user *u;
4116 struct call_queue *q;
4117 struct member *m;
4119 /* Ensure an otherwise empty list doesn't return garbage */
4120 buf[0] = '\0';
4122 if (ast_strlen_zero(data)) {
4123 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
4124 return -1;
4127 u = ast_module_user_add(chan);
4129 AST_LIST_LOCK(&queues);
4130 AST_LIST_TRAVERSE(&queues, q, list) {
4131 if (!strcasecmp(q->name, data)) {
4132 ast_mutex_lock(&q->lock);
4133 break;
4136 AST_LIST_UNLOCK(&queues);
4138 if (q) {
4139 int buflen = 0, count = 0;
4140 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
4142 while ((m = ao2_iterator_next(&mem_iter))) {
4143 /* strcat() is always faster than printf() */
4144 if (count++) {
4145 strncat(buf + buflen, ",", len - buflen - 1);
4146 buflen++;
4148 strncat(buf + buflen, m->membername, len - buflen - 1);
4149 buflen += strlen(m->membername);
4150 /* Safeguard against overflow (negative length) */
4151 if (buflen >= len - 2) {
4152 ao2_ref(m, -1);
4153 ast_log(LOG_WARNING, "Truncating list\n");
4154 break;
4156 ao2_ref(m, -1);
4158 ast_mutex_unlock(&q->lock);
4159 } else
4160 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4162 /* We should already be terminated, but let's make sure. */
4163 buf[len - 1] = '\0';
4164 ast_module_user_remove(u);
4166 return 0;
4169 static struct ast_custom_function queueagentcount_function = {
4170 .name = "QUEUEAGENTCOUNT",
4171 .synopsis = "Count number of agents answering a queue",
4172 .syntax = "QUEUEAGENTCOUNT(<queuename>)",
4173 .desc =
4174 "Returns the number of members currently associated with the specified queue.\n"
4175 "This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n",
4176 .read = queue_function_qac,
4179 static struct ast_custom_function queuemembercount_function = {
4180 .name = "QUEUE_MEMBER_COUNT",
4181 .synopsis = "Count number of members answering a queue",
4182 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
4183 .desc =
4184 "Returns the number of members currently associated with the specified queue.\n",
4185 .read = queue_function_qac,
4188 static struct ast_custom_function queuewaitingcount_function = {
4189 .name = "QUEUE_WAITING_COUNT",
4190 .synopsis = "Count number of calls currently waiting in a queue",
4191 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
4192 .desc =
4193 "Returns the number of callers currently waiting in the specified queue.\n",
4194 .read = queue_function_queuewaitingcount,
4197 static struct ast_custom_function queuememberlist_function = {
4198 .name = "QUEUE_MEMBER_LIST",
4199 .synopsis = "Returns a list of interfaces on a queue",
4200 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
4201 .desc =
4202 "Returns a comma-separated list of members associated with the specified queue.\n",
4203 .read = queue_function_queuememberlist,
4206 static int reload_queues(void)
4208 struct call_queue *q;
4209 struct ast_config *cfg;
4210 char *cat, *tmp;
4211 struct ast_variable *var;
4212 struct member *cur, *newm;
4213 struct ao2_iterator mem_iter;
4214 int new;
4215 const char *general_val = NULL;
4216 char parse[80];
4217 char *interface;
4218 char *membername = NULL;
4219 int penalty;
4220 AST_DECLARE_APP_ARGS(args,
4221 AST_APP_ARG(interface);
4222 AST_APP_ARG(penalty);
4223 AST_APP_ARG(membername);
4226 if (!(cfg = ast_config_load("queues.conf"))) {
4227 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
4228 return 0;
4230 AST_LIST_LOCK(&queues);
4231 use_weight=0;
4232 /* Mark all non-realtime queues as dead for the moment */
4233 AST_LIST_TRAVERSE(&queues, q, list) {
4234 if (!q->realtime) {
4235 q->dead = 1;
4236 q->found = 0;
4240 /* Chug through config file */
4241 cat = NULL;
4242 while ((cat = ast_category_browse(cfg, cat)) ) {
4243 if (!strcasecmp(cat, "general")) {
4244 /* Initialize global settings */
4245 queue_persistent_members = 0;
4246 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
4247 queue_persistent_members = ast_true(general_val);
4248 autofill_default = 0;
4249 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
4250 autofill_default = ast_true(general_val);
4251 montype_default = 0;
4252 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
4253 if (!strcasecmp(general_val, "mixmonitor"))
4254 montype_default = 1;
4255 } else { /* Define queue */
4256 /* Look for an existing one */
4257 AST_LIST_TRAVERSE(&queues, q, list) {
4258 if (!strcmp(q->name, cat))
4259 break;
4261 if (!q) {
4262 /* Make one then */
4263 if (!(q = alloc_queue(cat))) {
4264 /* TODO: Handle memory allocation failure */
4266 new = 1;
4267 } else
4268 new = 0;
4269 if (q) {
4270 if (!new)
4271 ast_mutex_lock(&q->lock);
4272 /* Check if a queue with this name already exists */
4273 if (q->found) {
4274 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
4275 if (!new)
4276 ast_mutex_unlock(&q->lock);
4277 continue;
4279 /* Re-initialize the queue, and clear statistics */
4280 init_queue(q);
4281 clear_queue(q);
4282 mem_iter = ao2_iterator_init(q->members, 0);
4283 while ((cur = ao2_iterator_next(&mem_iter))) {
4284 if (!cur->dynamic) {
4285 cur->delme = 1;
4287 ao2_ref(cur, -1);
4289 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
4290 if (!strcasecmp(var->name, "member")) {
4291 struct member tmpmem;
4292 membername = NULL;
4294 /* Add a new member */
4295 ast_copy_string(parse, var->value, sizeof(parse));
4297 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
4299 interface = args.interface;
4300 if (!ast_strlen_zero(args.penalty)) {
4301 tmp = args.penalty;
4302 while (*tmp && *tmp < 33) tmp++;
4303 penalty = atoi(tmp);
4304 if (penalty < 0) {
4305 penalty = 0;
4307 } else
4308 penalty = 0;
4310 if (!ast_strlen_zero(args.membername)) {
4311 membername = args.membername;
4312 while (*membername && *membername < 33) membername++;
4315 /* Find the old position in the list */
4316 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
4317 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
4319 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
4320 ao2_link(q->members, newm);
4321 ao2_ref(newm, -1);
4322 newm = NULL;
4324 if (cur)
4325 ao2_ref(cur, -1);
4326 else {
4327 /* Add them to the master int list if necessary */
4328 add_to_interfaces(interface);
4329 q->membercount++;
4331 } else {
4332 queue_set_param(q, var->name, var->value, var->lineno, 1);
4336 /* Free remaining members marked as delme */
4337 mem_iter = ao2_iterator_init(q->members, 0);
4338 while ((cur = ao2_iterator_next(&mem_iter))) {
4339 if (! cur->delme) {
4340 ao2_ref(cur, -1);
4341 continue;
4344 q->membercount--;
4345 ao2_unlink(q->members, cur);
4346 remove_from_interfaces(cur->interface);
4347 ao2_ref(cur, -1);
4350 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
4351 rr_dep_warning();
4353 if (new) {
4354 AST_LIST_INSERT_HEAD(&queues, q, list);
4355 } else
4356 ast_mutex_unlock(&q->lock);
4360 ast_config_destroy(cfg);
4361 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
4362 if (q->dead) {
4363 AST_LIST_REMOVE_CURRENT(&queues, list);
4364 if (!q->count)
4365 destroy_queue(q);
4366 else
4367 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
4368 } else {
4369 ast_mutex_lock(&q->lock);
4370 mem_iter = ao2_iterator_init(q->members, 0);
4371 while ((cur = ao2_iterator_next(&mem_iter))) {
4372 if (cur->dynamic)
4373 q->membercount++;
4374 cur->status = ast_device_state(cur->interface);
4375 ao2_ref(cur, -1);
4377 ast_mutex_unlock(&q->lock);
4380 AST_LIST_TRAVERSE_SAFE_END;
4381 AST_LIST_UNLOCK(&queues);
4382 return 1;
4385 static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv)
4387 struct call_queue *q;
4388 struct queue_ent *qe;
4389 struct member *mem;
4390 int pos, queue_show;
4391 time_t now;
4392 char max_buf[150];
4393 char *max;
4394 size_t max_left;
4395 float sl = 0;
4396 char *term = manager ? "\r\n" : "\n";
4397 struct ao2_iterator mem_iter;
4399 time(&now);
4400 if (argc == 2)
4401 queue_show = 0;
4402 else if (argc == 3)
4403 queue_show = 1;
4404 else
4405 return RESULT_SHOWUSAGE;
4407 /* We only want to load realtime queues when a specific queue is asked for. */
4408 if (queue_show) {
4409 load_realtime_queue(argv[2]);
4410 } else if (ast_check_realtime("queues")) {
4411 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
4412 char *queuename;
4413 if (cfg) {
4414 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
4415 load_realtime_queue(queuename);
4417 ast_config_destroy(cfg);
4421 AST_LIST_LOCK(&queues);
4422 if (AST_LIST_EMPTY(&queues)) {
4423 AST_LIST_UNLOCK(&queues);
4424 if (queue_show) {
4425 if (s)
4426 astman_append(s, "No such queue: %s.%s",argv[2], term);
4427 else
4428 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
4429 } else {
4430 if (s)
4431 astman_append(s, "No queues.%s", term);
4432 else
4433 ast_cli(fd, "No queues.%s", term);
4435 return RESULT_SUCCESS;
4437 AST_LIST_TRAVERSE(&queues, q, list) {
4438 ast_mutex_lock(&q->lock);
4439 if (queue_show) {
4440 if (strcasecmp(q->name, argv[2]) != 0) {
4441 ast_mutex_unlock(&q->lock);
4442 if (!AST_LIST_NEXT(q, list)) {
4443 ast_cli(fd, "No such queue: %s.%s",argv[2], term);
4444 break;
4446 continue;
4449 max_buf[0] = '\0';
4450 max = max_buf;
4451 max_left = sizeof(max_buf);
4452 if (q->maxlen)
4453 ast_build_string(&max, &max_left, "%d", q->maxlen);
4454 else
4455 ast_build_string(&max, &max_left, "unlimited");
4456 sl = 0;
4457 if (q->callscompleted > 0)
4458 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4459 if (s)
4460 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",
4461 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight,
4462 q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
4463 else
4464 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",
4465 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term);
4466 if (ao2_container_count(q->members)) {
4467 if (s)
4468 astman_append(s, " Members: %s", term);
4469 else
4470 ast_cli(fd, " Members: %s", term);
4471 mem_iter = ao2_iterator_init(q->members, 0);
4472 while ((mem = ao2_iterator_next(&mem_iter))) {
4473 max_buf[0] = '\0';
4474 max = max_buf;
4475 max_left = sizeof(max_buf);
4476 if (strcasecmp(mem->membername, mem->interface)) {
4477 ast_build_string(&max, &max_left, " (%s)", mem->interface);
4479 if (mem->penalty)
4480 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
4481 if (mem->dynamic)
4482 ast_build_string(&max, &max_left, " (dynamic)");
4483 if (mem->realtime)
4484 ast_build_string(&max, &max_left, " (realtime)");
4485 if (mem->paused)
4486 ast_build_string(&max, &max_left, " (paused)");
4487 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
4488 if (mem->calls) {
4489 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
4490 mem->calls, (long) (time(NULL) - mem->lastcall));
4491 } else
4492 ast_build_string(&max, &max_left, " has taken no calls yet");
4493 if (s)
4494 astman_append(s, " %s%s%s", mem->membername, max_buf, term);
4495 else
4496 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term);
4497 ao2_ref(mem, -1);
4499 } else if (s)
4500 astman_append(s, " No Members%s", term);
4501 else
4502 ast_cli(fd, " No Members%s", term);
4503 if (q->head) {
4504 pos = 1;
4505 if (s)
4506 astman_append(s, " Callers: %s", term);
4507 else
4508 ast_cli(fd, " Callers: %s", term);
4509 for (qe = q->head; qe; qe = qe->next) {
4510 if (s)
4511 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
4512 pos++, qe->chan->name, (long) (now - qe->start) / 60,
4513 (long) (now - qe->start) % 60, qe->prio, term);
4514 else
4515 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
4516 qe->chan->name, (long) (now - qe->start) / 60,
4517 (long) (now - qe->start) % 60, qe->prio, term);
4519 } else if (s)
4520 astman_append(s, " No Callers%s", term);
4521 else
4522 ast_cli(fd, " No Callers%s", term);
4523 if (s)
4524 astman_append(s, "%s", term);
4525 else
4526 ast_cli(fd, "%s", term);
4527 ast_mutex_unlock(&q->lock);
4528 if (queue_show)
4529 break;
4531 AST_LIST_UNLOCK(&queues);
4532 return RESULT_SUCCESS;
4535 static int queue_show(int fd, int argc, char **argv)
4537 return __queues_show(NULL, 0, fd, argc, argv);
4540 static char *complete_queue(const char *line, const char *word, int pos, int state)
4542 struct call_queue *q;
4543 char *ret = NULL;
4544 int which = 0;
4545 int wordlen = strlen(word);
4547 AST_LIST_LOCK(&queues);
4548 AST_LIST_TRAVERSE(&queues, q, list) {
4549 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
4550 ret = ast_strdup(q->name);
4551 break;
4554 AST_LIST_UNLOCK(&queues);
4556 return ret;
4559 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
4561 if (pos == 2)
4562 return complete_queue(line, word, pos, state);
4563 return NULL;
4566 /*!\brief callback to display queues status in manager
4567 \addtogroup Group_AMI
4569 static int manager_queues_show(struct mansession *s, const struct message *m)
4571 char *a[] = { "queue", "show" };
4573 __queues_show(s, 1, -1, 2, a);
4574 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
4576 return RESULT_SUCCESS;
4579 /* Dump queue status */
4580 static int manager_queues_status(struct mansession *s, const struct message *m)
4582 time_t now;
4583 int pos;
4584 const char *id = astman_get_header(m,"ActionID");
4585 const char *queuefilter = astman_get_header(m,"Queue");
4586 const char *memberfilter = astman_get_header(m,"Member");
4587 char idText[256] = "";
4588 struct call_queue *q;
4589 struct queue_ent *qe;
4590 float sl = 0;
4591 struct member *mem;
4592 struct ao2_iterator mem_iter;
4594 astman_send_ack(s, m, "Queue status will follow");
4595 time(&now);
4596 AST_LIST_LOCK(&queues);
4597 if (!ast_strlen_zero(id))
4598 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
4600 AST_LIST_TRAVERSE(&queues, q, list) {
4601 ast_mutex_lock(&q->lock);
4603 /* List queue properties */
4604 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
4605 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
4606 astman_append(s, "Event: QueueParams\r\n"
4607 "Queue: %s\r\n"
4608 "Max: %d\r\n"
4609 "Calls: %d\r\n"
4610 "Holdtime: %d\r\n"
4611 "Completed: %d\r\n"
4612 "Abandoned: %d\r\n"
4613 "ServiceLevel: %d\r\n"
4614 "ServicelevelPerf: %2.1f\r\n"
4615 "Weight: %d\r\n"
4616 "%s"
4617 "\r\n",
4618 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
4619 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
4620 /* List Queue Members */
4621 mem_iter = ao2_iterator_init(q->members, 0);
4622 while ((mem = ao2_iterator_next(&mem_iter))) {
4623 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
4624 astman_append(s, "Event: QueueMember\r\n"
4625 "Queue: %s\r\n"
4626 "Name: %s\r\n"
4627 "Location: %s\r\n"
4628 "Membership: %s\r\n"
4629 "Penalty: %d\r\n"
4630 "CallsTaken: %d\r\n"
4631 "LastCall: %d\r\n"
4632 "Status: %d\r\n"
4633 "Paused: %d\r\n"
4634 "%s"
4635 "\r\n",
4636 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
4637 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
4639 ao2_ref(mem, -1);
4641 /* List Queue Entries */
4642 pos = 1;
4643 for (qe = q->head; qe; qe = qe->next) {
4644 astman_append(s, "Event: QueueEntry\r\n"
4645 "Queue: %s\r\n"
4646 "Position: %d\r\n"
4647 "Channel: %s\r\n"
4648 "CallerID: %s\r\n"
4649 "CallerIDName: %s\r\n"
4650 "Wait: %ld\r\n"
4651 "%s"
4652 "\r\n",
4653 q->name, pos++, qe->chan->name,
4654 S_OR(qe->chan->cid.cid_num, "unknown"),
4655 S_OR(qe->chan->cid.cid_name, "unknown"),
4656 (long) (now - qe->start), idText);
4659 ast_mutex_unlock(&q->lock);
4662 astman_append(s,
4663 "Event: QueueStatusComplete\r\n"
4664 "%s"
4665 "\r\n",idText);
4667 AST_LIST_UNLOCK(&queues);
4670 return RESULT_SUCCESS;
4673 static int manager_add_queue_member(struct mansession *s, const struct message *m)
4675 const char *queuename, *interface, *penalty_s, *paused_s, *membername;
4676 int paused, penalty = 0;
4678 queuename = astman_get_header(m, "Queue");
4679 interface = astman_get_header(m, "Interface");
4680 penalty_s = astman_get_header(m, "Penalty");
4681 paused_s = astman_get_header(m, "Paused");
4682 membername = astman_get_header(m, "MemberName");
4684 if (ast_strlen_zero(queuename)) {
4685 astman_send_error(s, m, "'Queue' not specified.");
4686 return 0;
4689 if (ast_strlen_zero(interface)) {
4690 astman_send_error(s, m, "'Interface' not specified.");
4691 return 0;
4694 if (ast_strlen_zero(penalty_s))
4695 penalty = 0;
4696 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
4697 penalty = 0;
4699 if (ast_strlen_zero(paused_s))
4700 paused = 0;
4701 else
4702 paused = abs(ast_true(paused_s));
4704 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
4705 case RES_OKAY:
4706 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
4707 astman_send_ack(s, m, "Added interface to queue");
4708 break;
4709 case RES_EXISTS:
4710 astman_send_error(s, m, "Unable to add interface: Already there");
4711 break;
4712 case RES_NOSUCHQUEUE:
4713 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
4714 break;
4715 case RES_OUTOFMEMORY:
4716 astman_send_error(s, m, "Out of memory");
4717 break;
4720 return 0;
4723 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
4725 const char *queuename, *interface;
4727 queuename = astman_get_header(m, "Queue");
4728 interface = astman_get_header(m, "Interface");
4730 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
4731 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
4732 return 0;
4735 switch (remove_from_queue(queuename, interface)) {
4736 case RES_OKAY:
4737 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
4738 astman_send_ack(s, m, "Removed interface from queue");
4739 break;
4740 case RES_EXISTS:
4741 astman_send_error(s, m, "Unable to remove interface: Not there");
4742 break;
4743 case RES_NOSUCHQUEUE:
4744 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
4745 break;
4746 case RES_OUTOFMEMORY:
4747 astman_send_error(s, m, "Out of memory");
4748 break;
4749 case RES_NOT_DYNAMIC:
4750 astman_send_error(s, m, "Member not dynamic");
4751 break;
4754 return 0;
4757 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
4759 const char *queuename, *interface, *paused_s;
4760 int paused;
4762 interface = astman_get_header(m, "Interface");
4763 paused_s = astman_get_header(m, "Paused");
4764 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
4766 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
4767 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
4768 return 0;
4771 paused = abs(ast_true(paused_s));
4773 if (set_member_paused(queuename, interface, paused))
4774 astman_send_error(s, m, "Interface not found");
4775 else
4776 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
4777 return 0;
4780 static int handle_queue_add_member(int fd, int argc, char *argv[])
4782 char *queuename, *interface, *membername = NULL;
4783 int penalty;
4785 if ((argc != 6) && (argc != 8) && (argc != 10)) {
4786 return RESULT_SHOWUSAGE;
4787 } else if (strcmp(argv[4], "to")) {
4788 return RESULT_SHOWUSAGE;
4789 } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
4790 return RESULT_SHOWUSAGE;
4791 } else if ((argc == 10) && strcmp(argv[8], "as")) {
4792 return RESULT_SHOWUSAGE;
4795 queuename = argv[5];
4796 interface = argv[3];
4797 if (argc >= 8) {
4798 if (sscanf(argv[7], "%d", &penalty) == 1) {
4799 if (penalty < 0) {
4800 ast_cli(fd, "Penalty must be >= 0\n");
4801 penalty = 0;
4803 } else {
4804 ast_cli(fd, "Penalty must be an integer >= 0\n");
4805 penalty = 0;
4807 } else {
4808 penalty = 0;
4811 if (argc >= 10) {
4812 membername = argv[9];
4815 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
4816 case RES_OKAY:
4817 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
4818 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
4819 return RESULT_SUCCESS;
4820 case RES_EXISTS:
4821 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
4822 return RESULT_FAILURE;
4823 case RES_NOSUCHQUEUE:
4824 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
4825 return RESULT_FAILURE;
4826 case RES_OUTOFMEMORY:
4827 ast_cli(fd, "Out of memory\n");
4828 return RESULT_FAILURE;
4829 default:
4830 return RESULT_FAILURE;
4834 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
4836 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
4837 switch (pos) {
4838 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
4839 return NULL;
4840 case 4: /* only one possible match, "to" */
4841 return state == 0 ? ast_strdup("to") : NULL;
4842 case 5: /* <queue> */
4843 return complete_queue(line, word, pos, state);
4844 case 6: /* only one possible match, "penalty" */
4845 return state == 0 ? ast_strdup("penalty") : NULL;
4846 case 7:
4847 if (state < 100) { /* 0-99 */
4848 char *num;
4849 if ((num = ast_malloc(3))) {
4850 sprintf(num, "%d", state);
4852 return num;
4853 } else {
4854 return NULL;
4856 case 8: /* only one possible match, "as" */
4857 return state == 0 ? ast_strdup("as") : NULL;
4858 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
4859 return NULL;
4860 default:
4861 return NULL;
4865 static int handle_queue_remove_member(int fd, int argc, char *argv[])
4867 char *queuename, *interface;
4869 if (argc != 6) {
4870 return RESULT_SHOWUSAGE;
4871 } else if (strcmp(argv[4], "from")) {
4872 return RESULT_SHOWUSAGE;
4875 queuename = argv[5];
4876 interface = argv[3];
4878 switch (remove_from_queue(queuename, interface)) {
4879 case RES_OKAY:
4880 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
4881 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
4882 return RESULT_SUCCESS;
4883 case RES_EXISTS:
4884 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
4885 return RESULT_FAILURE;
4886 case RES_NOSUCHQUEUE:
4887 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
4888 return RESULT_FAILURE;
4889 case RES_OUTOFMEMORY:
4890 ast_cli(fd, "Out of memory\n");
4891 return RESULT_FAILURE;
4892 case RES_NOT_DYNAMIC:
4893 ast_cli(fd, "Member not dynamic\n");
4894 return RESULT_FAILURE;
4895 default:
4896 return RESULT_FAILURE;
4900 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
4902 int which = 0;
4903 struct call_queue *q;
4904 struct member *m;
4905 struct ao2_iterator mem_iter;
4907 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
4908 if (pos > 5 || pos < 3)
4909 return NULL;
4910 if (pos == 4) /* only one possible match, 'from' */
4911 return state == 0 ? ast_strdup("from") : NULL;
4913 if (pos == 5) /* No need to duplicate code */
4914 return complete_queue(line, word, pos, state);
4916 /* here is the case for 3, <member> */
4917 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
4918 AST_LIST_TRAVERSE(&queues, q, list) {
4919 ast_mutex_lock(&q->lock);
4920 mem_iter = ao2_iterator_init(q->members, 0);
4921 while ((m = ao2_iterator_next(&mem_iter))) {
4922 if (++which > state) {
4923 char *tmp;
4924 ast_mutex_unlock(&q->lock);
4925 tmp = ast_strdup(m->interface);
4926 ao2_ref(m, -1);
4927 return tmp;
4929 ao2_ref(m, -1);
4931 ast_mutex_unlock(&q->lock);
4935 return NULL;
4938 static char queue_show_usage[] =
4939 "Usage: queue show\n"
4940 " Provides summary information on a specified queue.\n";
4942 static char qam_cmd_usage[] =
4943 "Usage: queue add member <channel> to <queue> [penalty <penalty>]\n";
4945 static char qrm_cmd_usage[] =
4946 "Usage: queue remove member <channel> from <queue>\n";
4948 static struct ast_cli_entry cli_show_queue_deprecated = {
4949 { "show", "queue", NULL },
4950 queue_show, NULL,
4951 NULL, complete_queue_show };
4953 static struct ast_cli_entry cli_add_queue_member_deprecated = {
4954 { "add", "queue", "member", NULL },
4955 handle_queue_add_member, NULL,
4956 NULL, complete_queue_add_member };
4958 static struct ast_cli_entry cli_remove_queue_member_deprecated = {
4959 { "remove", "queue", "member", NULL },
4960 handle_queue_remove_member, NULL,
4961 NULL, complete_queue_remove_member };
4963 static struct ast_cli_entry cli_queue[] = {
4964 /* Deprecated */
4965 { { "show", "queues", NULL },
4966 queue_show, NULL,
4967 NULL, NULL },
4969 { { "queue", "show", NULL },
4970 queue_show, "Show status of a specified queue",
4971 queue_show_usage, complete_queue_show, &cli_show_queue_deprecated },
4973 { { "queue", "add", "member", NULL },
4974 handle_queue_add_member, "Add a channel to a specified queue",
4975 qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated },
4977 { { "queue", "remove", "member", NULL },
4978 handle_queue_remove_member, "Removes a channel from a specified queue",
4979 qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated },
4982 static int unload_module(void)
4984 int res;
4986 if (device_state.thread != AST_PTHREADT_NULL) {
4987 device_state.stop = 1;
4988 ast_mutex_lock(&device_state.lock);
4989 ast_cond_signal(&device_state.cond);
4990 ast_mutex_unlock(&device_state.lock);
4991 pthread_join(device_state.thread, NULL);
4994 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
4995 res = ast_manager_unregister("QueueStatus");
4996 res |= ast_manager_unregister("Queues");
4997 res |= ast_manager_unregister("QueueStatus");
4998 res |= ast_manager_unregister("QueueAdd");
4999 res |= ast_manager_unregister("QueueRemove");
5000 res |= ast_manager_unregister("QueuePause");
5001 res |= ast_unregister_application(app_aqm);
5002 res |= ast_unregister_application(app_rqm);
5003 res |= ast_unregister_application(app_pqm);
5004 res |= ast_unregister_application(app_upqm);
5005 res |= ast_unregister_application(app_ql);
5006 res |= ast_unregister_application(app);
5007 res |= ast_custom_function_unregister(&queueagentcount_function);
5008 res |= ast_custom_function_unregister(&queuemembercount_function);
5009 res |= ast_custom_function_unregister(&queuememberlist_function);
5010 res |= ast_custom_function_unregister(&queuewaitingcount_function);
5011 ast_devstate_del(statechange_queue, NULL);
5013 ast_module_user_hangup_all();
5015 clear_and_free_interfaces();
5017 return res;
5020 static int load_module(void)
5022 int res;
5024 if (!reload_queues())
5025 return AST_MODULE_LOAD_DECLINE;
5027 if (queue_persistent_members)
5028 reload_queue_members();
5030 ast_mutex_init(&device_state.lock);
5031 ast_cond_init(&device_state.cond, NULL);
5032 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
5034 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
5035 res = ast_register_application(app, queue_exec, synopsis, descrip);
5036 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
5037 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
5038 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
5039 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
5040 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
5041 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
5042 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
5043 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
5044 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
5045 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
5046 res |= ast_custom_function_register(&queueagentcount_function);
5047 res |= ast_custom_function_register(&queuemembercount_function);
5048 res |= ast_custom_function_register(&queuememberlist_function);
5049 res |= ast_custom_function_register(&queuewaitingcount_function);
5050 res |= ast_devstate_add(statechange_queue, NULL);
5052 return res;
5055 static int reload(void)
5057 reload_queues();
5058 return 0;
5061 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
5062 .load = load_module,
5063 .unload = unload_module,
5064 .reload = reload,