Blocked revisions 117809 via svnmerge
[asterisk-bristuff.git] / apps / app_queue.c
blob8bbf733dd1fe9aee58a72e50c77618f98a735dc5
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 <sys/time.h>
68 #include <sys/signal.h>
69 #include <netinet/in.h>
71 #include "asterisk/lock.h"
72 #include "asterisk/file.h"
73 #include "asterisk/channel.h"
74 #include "asterisk/pbx.h"
75 #include "asterisk/app.h"
76 #include "asterisk/linkedlists.h"
77 #include "asterisk/module.h"
78 #include "asterisk/translate.h"
79 #include "asterisk/say.h"
80 #include "asterisk/features.h"
81 #include "asterisk/musiconhold.h"
82 #include "asterisk/cli.h"
83 #include "asterisk/manager.h"
84 #include "asterisk/config.h"
85 #include "asterisk/monitor.h"
86 #include "asterisk/utils.h"
87 #include "asterisk/causes.h"
88 #include "asterisk/astdb.h"
89 #include "asterisk/devicestate.h"
90 #include "asterisk/stringfields.h"
91 #include "asterisk/event.h"
92 #include "asterisk/astobj2.h"
93 #include "asterisk/strings.h"
94 #include "asterisk/global_datastores.h"
95 #include "asterisk/taskprocessor.h"
97 /*!
98 * \par Please read before modifying this file.
99 * There are three locks which are regularly used
100 * throughout this file, the queue list lock, the lock
101 * for each individual queue, and the interface list lock.
102 * Please be extra careful to always lock in the following order
103 * 1) queue list lock
104 * 2) individual queue lock
105 * 3) interface list lock
106 * This order has sort of "evolved" over the lifetime of this
107 * application, but it is now in place this way, so please adhere
108 * to this order!
112 enum {
113 QUEUE_STRATEGY_RINGALL = 0,
114 QUEUE_STRATEGY_LEASTRECENT,
115 QUEUE_STRATEGY_FEWESTCALLS,
116 QUEUE_STRATEGY_RANDOM,
117 QUEUE_STRATEGY_RRMEMORY,
118 QUEUE_STRATEGY_LINEAR,
119 QUEUE_STRATEGY_WRANDOM
122 static const struct strategy {
123 int strategy;
124 const char *name;
125 } strategies[] = {
126 { QUEUE_STRATEGY_RINGALL, "ringall" },
127 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
128 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
129 { QUEUE_STRATEGY_RANDOM, "random" },
130 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
131 { QUEUE_STRATEGY_LINEAR, "linear" },
132 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
135 static struct ast_taskprocessor *devicestate_tps;
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 */
141 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /*!< The minimum number of seconds between position announcements \
142 The default value of 15 provides backwards compatibility */
143 #define MAX_QUEUE_BUCKETS 53
145 #define RES_OKAY 0 /*!< Action completed */
146 #define RES_EXISTS (-1) /*!< Entry already exists */
147 #define RES_OUTOFMEMORY (-2) /*!< Out of memory */
148 #define RES_NOSUCHQUEUE (-3) /*!< No such queue */
149 #define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
151 static char *app = "Queue";
153 static char *synopsis = "Queue a call for a call queue";
155 static char *descrip =
156 " Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
157 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
158 "This application will return to the dialplan if the queue does not exist, or\n"
159 "any of the join options cause the caller to not enter the queue.\n"
160 "The option string may contain zero or more of the following characters:\n"
161 " 'c' -- continue in the dialplan if the callee hangs up.\n"
162 " 'd' -- data-quality (modem) call (minimum delay).\n"
163 " 'h' -- allow callee to hang up by pressing *.\n"
164 " 'H' -- allow caller to hang up by pressing *.\n"
165 " 'n' -- no retries on the timeout; will exit this application and \n"
166 " go to the next step.\n"
167 " 'i' -- ignore call forward requests from queue members and do nothing\n"
168 " when they are requested.\n"
169 " 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
170 " 't' -- allow the called user to transfer the calling user.\n"
171 " 'T' -- allow the calling user to transfer the call.\n"
172 " 'w' -- allow the called user to write the conversation to disk via Monitor.\n"
173 " 'W' -- allow the calling user to write the conversation to disk via Monitor.\n"
174 " 'k' -- Allow the called party to enable parking of the call by sending\n"
175 " the DTMF sequence defined for call parking in features.conf.\n"
176 " 'K' -- Allow the calling party to enable parking of the call by sending\n"
177 " the DTMF sequence defined for call parking in features.conf.\n"
178 " 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
179 " 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
181 " In addition to transferring the call, a call may be parked and then picked\n"
182 "up by another user.\n"
183 " The optional URL will be sent to the called party if the channel supports\n"
184 "it.\n"
185 " The optional AGI parameter will setup an AGI script to be executed on the \n"
186 "calling party's channel once they are connected to a queue member.\n"
187 " The optional macro parameter will run a macro on the \n"
188 "calling party's channel once they are connected to a queue member.\n"
189 " The optional gosub parameter will run a gosub on the \n"
190 "calling party's channel once they are connected to a queue member.\n"
191 " The optional rule parameter will cause the queue's defaultrule to be\n"
192 "overridden by the rule specified.\n"
193 " The timeout will cause the queue to fail out after a specified number of\n"
194 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
195 " This application sets the following channel variable upon completion:\n"
196 " QUEUESTATUS The status of the call as a text string, one of\n"
197 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
199 static char *app_aqm = "AddQueueMember" ;
200 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
201 static char *app_aqm_descrip =
202 " AddQueueMember(queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]):\n"
203 "Dynamically adds interface to an existing queue.\n"
204 "If the interface is already in the queue it will return an error.\n"
205 " This application sets the following channel variable upon completion:\n"
206 " AQMSTATUS The status of the attempt to add a queue member as a \n"
207 " text string, one of\n"
208 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
209 "Example: AddQueueMember(techsupport,SIP/3000)\n"
212 static char *app_rqm = "RemoveQueueMember" ;
213 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
214 static char *app_rqm_descrip =
215 " RemoveQueueMember(queuename[,interface[,options]]):\n"
216 "Dynamically removes interface to an existing queue\n"
217 "If the interface is NOT in the queue it will return an error.\n"
218 " This application sets the following channel variable upon completion:\n"
219 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
220 " text string, one of\n"
221 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
222 "Example: RemoveQueueMember(techsupport,SIP/3000)\n"
225 static char *app_pqm = "PauseQueueMember" ;
226 static char *app_pqm_synopsis = "Pauses a queue member" ;
227 static char *app_pqm_descrip =
228 " PauseQueueMember([queuename],interface[,options[,reason]]):\n"
229 "Pauses (blocks calls for) a queue member.\n"
230 "The given interface will be paused in the given queue. This prevents\n"
231 "any calls from being sent from the queue to the interface until it is\n"
232 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
233 "queuename is given, the interface is paused in every queue it is a\n"
234 "member of. The application will fail if the interface is not found.\n"
235 "The reason string is entirely optional and is used to add extra information\n"
236 "to the appropriate queue_log entries and manager events.\n"
237 " This application sets the following channel variable upon completion:\n"
238 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
239 " text string, one of\n"
240 " PAUSED | NOTFOUND\n"
241 "Example: PauseQueueMember(,SIP/3000)\n";
243 static char *app_upqm = "UnpauseQueueMember" ;
244 static char *app_upqm_synopsis = "Unpauses a queue member" ;
245 static char *app_upqm_descrip =
246 " UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
247 "Unpauses (resumes calls to) a queue member.\n"
248 "This is the counterpart to PauseQueueMember and operates exactly the\n"
249 "same way, except it unpauses instead of pausing the given interface.\n"
250 "The reason string is entirely optional and is used to add extra information\n"
251 "to the appropriate queue_log entries and manager events.\n"
252 " This application sets the following channel variable upon completion:\n"
253 " UPQMSTATUS The status of the attempt to unpause a queue \n"
254 " member as a text string, one of\n"
255 " UNPAUSED | NOTFOUND\n"
256 "Example: UnpauseQueueMember(,SIP/3000)\n";
258 static char *app_ql = "QueueLog" ;
259 static char *app_ql_synopsis = "Writes to the queue_log" ;
260 static char *app_ql_descrip =
261 " QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
262 "Allows you to write your own events into the queue log\n"
263 "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
265 /*! \brief Persistent Members astdb family */
266 static const char *pm_family = "Queue/PersistentMembers";
267 /* The maximum length of each persistent member queue database entry */
268 #define PM_MAX_LEN 8192
270 /*! \brief queues.conf [general] option */
271 static int queue_keep_stats = 0;
273 /*! \brief queues.conf [general] option */
274 static int queue_persistent_members = 0;
276 /*! \brief queues.conf per-queue weight option */
277 static int use_weight = 0;
279 /*! \brief queues.conf [general] option */
280 static int autofill_default = 0;
282 /*! \brief queues.conf [general] option */
283 static int montype_default = 0;
285 /*! \brief queues.conf [general] option */
286 static int shared_lastcall = 0;
288 /*! \brief Subscription to device state change events */
289 static struct ast_event_sub *device_state_sub;
291 /*! \brief queues.conf [general] option */
292 static int update_cdr = 0;
294 enum queue_result {
295 QUEUE_UNKNOWN = 0,
296 QUEUE_TIMEOUT = 1,
297 QUEUE_JOINEMPTY = 2,
298 QUEUE_LEAVEEMPTY = 3,
299 QUEUE_JOINUNAVAIL = 4,
300 QUEUE_LEAVEUNAVAIL = 5,
301 QUEUE_FULL = 6,
302 QUEUE_CONTINUE = 7,
305 const struct {
306 enum queue_result id;
307 char *text;
308 } queue_results[] = {
309 { QUEUE_UNKNOWN, "UNKNOWN" },
310 { QUEUE_TIMEOUT, "TIMEOUT" },
311 { QUEUE_JOINEMPTY,"JOINEMPTY" },
312 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
313 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
314 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
315 { QUEUE_FULL, "FULL" },
316 { QUEUE_CONTINUE, "CONTINUE" },
319 /*! \brief We define a custom "local user" structure because we
320 * use it not only for keeping track of what is in use but
321 * also for keeping track of who we're dialing.
323 * There are two "links" defined in this structure, q_next and call_next.
324 * q_next links ALL defined callattempt structures into a linked list. call_next is
325 * a link which allows for a subset of the callattempts to be traversed. This subset
326 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
327 * also is helpful so that queue logs are always accurate in the case where a call to
328 * a member times out, especially if using the ringall strategy.
331 struct callattempt {
332 struct callattempt *q_next;
333 struct callattempt *call_next;
334 struct ast_channel *chan;
335 char interface[256];
336 int stillgoing;
337 int metric;
338 int oldstatus;
339 time_t lastcall;
340 struct call_queue *lastqueue;
341 struct member *member;
345 struct queue_ent {
346 struct call_queue *parent; /*!< What queue is our parent */
347 char moh[80]; /*!< Name of musiconhold to be used */
348 char announce[80]; /*!< Announcement to play for member when call is answered */
349 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
350 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
351 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
352 int pos; /*!< Where we are in the queue */
353 int prio; /*!< Our priority */
354 int last_pos_said; /*!< Last position we told the user */
355 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
356 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
357 time_t last_pos; /*!< Last time we told the user their position */
358 int opos; /*!< Where we started in the queue */
359 int handled; /*!< Whether our call was handled */
360 int pending; /*!< Non-zero if we are attempting to call a member */
361 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
362 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
363 int linpos; /*!< If using linear strategy, what position are we at? */
364 int linwrapped; /*!< Is the linpos wrapped? */
365 time_t start; /*!< When we started holding */
366 time_t expire; /*!< When this entry should expire (time out of queue) */
367 struct ast_channel *chan; /*!< Our channel */
368 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
369 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
370 struct queue_ent *next; /*!< The next queue entry */
373 struct member {
374 char interface[80]; /*!< Technology/Location to dial to reach this member*/
375 char state_interface[80]; /*!< Technology/Location from which to read devicestate changes */
376 char membername[80]; /*!< Member name to use in queue logs */
377 int penalty; /*!< Are we a last resort? */
378 int calls; /*!< Number of calls serviced by this member */
379 int dynamic; /*!< Are we dynamically added? */
380 int realtime; /*!< Is this member realtime? */
381 int status; /*!< Status of queue member */
382 int paused; /*!< Are we paused (not accepting calls)? */
383 time_t lastcall; /*!< When last successful call was hungup */
384 struct call_queue *lastqueue; /*!< Last queue we received a call */
385 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
386 unsigned int delme:1; /*!< Flag to delete entry on reload */
387 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
390 struct member_interface {
391 char interface[80];
392 AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
395 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
397 /* values used in multi-bit flags in call_queue */
398 #define QUEUE_EMPTY_NORMAL 1
399 #define QUEUE_EMPTY_STRICT 2
400 #define QUEUE_EMPTY_LOOSE 3
401 #define ANNOUNCEHOLDTIME_ALWAYS 1
402 #define ANNOUNCEHOLDTIME_ONCE 2
403 #define QUEUE_EVENT_VARIABLES 3
405 struct penalty_rule {
406 int time; /*!< Number of seconds that need to pass before applying this rule */
407 int max_value; /*!< The amount specified in the penalty rule for max penalty */
408 int min_value; /*!< The amount specified in the penalty rule for min penalty */
409 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
410 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
411 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
414 #define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
415 #define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
416 #define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
417 #define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
419 struct call_queue {
420 AST_DECLARE_STRING_FIELDS(
421 /*! Queue name */
422 AST_STRING_FIELD(name);
423 /*! Music on Hold class */
424 AST_STRING_FIELD(moh);
425 /*! Announcement to play when call is answered */
426 AST_STRING_FIELD(announce);
427 /*! Exit context */
428 AST_STRING_FIELD(context);
429 /*! Macro to run upon member connection */
430 AST_STRING_FIELD(membermacro);
431 /*! Gosub to run upon member connection */
432 AST_STRING_FIELD(membergosub);
433 /*! Default rule to use if none specified in call to Queue() */
434 AST_STRING_FIELD(defaultrule);
435 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
436 AST_STRING_FIELD(sound_next);
437 /*! Sound file: "There are currently" (def. queue-thereare) */
438 AST_STRING_FIELD(sound_thereare);
439 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
440 AST_STRING_FIELD(sound_calls);
441 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
442 AST_STRING_FIELD(queue_quantity1);
443 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
444 AST_STRING_FIELD(queue_quantity2);
445 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
446 AST_STRING_FIELD(sound_holdtime);
447 /*! Sound file: "minutes." (def. queue-minutes) */
448 AST_STRING_FIELD(sound_minutes);
449 /*! Sound file: "minute." (def. queue-minute) */
450 AST_STRING_FIELD(sound_minute);
451 /*! Sound file: "seconds." (def. queue-seconds) */
452 AST_STRING_FIELD(sound_seconds);
453 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
454 AST_STRING_FIELD(sound_thanks);
455 /*! Sound file: Custom announce for caller, no default */
456 AST_STRING_FIELD(sound_callerannounce);
457 /*! Sound file: "Hold time" (def. queue-reporthold) */
458 AST_STRING_FIELD(sound_reporthold);
460 /*! Sound files: Custom announce, no default */
461 struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
462 unsigned int dead:1;
463 unsigned int joinempty:2;
464 unsigned int eventwhencalled:2;
465 unsigned int leavewhenempty:2;
466 unsigned int ringinuse:1;
467 unsigned int setinterfacevar:1;
468 unsigned int setqueuevar:1;
469 unsigned int setqueueentryvar:1;
470 unsigned int reportholdtime:1;
471 unsigned int wrapped:1;
472 unsigned int timeoutrestart:1;
473 unsigned int announceholdtime:2;
474 unsigned int announceposition:3;
475 int strategy:4;
476 unsigned int maskmemberstatus:1;
477 unsigned int realtime:1;
478 unsigned int found:1;
479 int announcepositionlimit; /*!< How many positions we announce? */
480 int announcefrequency; /*!< How often to announce their position */
481 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
482 int periodicannouncefrequency; /*!< How often to play periodic announcement */
483 int numperiodicannounce; /*!< The number of periodic announcements configured */
484 int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
485 int roundingseconds; /*!< How many seconds do we round to? */
486 int holdtime; /*!< Current avg holdtime, based on recursive boxcar filter */
487 int callscompleted; /*!< Number of queue calls completed */
488 int callsabandoned; /*!< Number of queue calls abandoned */
489 int servicelevel; /*!< seconds setting for servicelevel*/
490 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
491 char monfmt[8]; /*!< Format to use when recording calls */
492 int montype; /*!< Monitor type Monitor vs. MixMonitor */
493 int count; /*!< How many entries */
494 int maxlen; /*!< Max number of entries */
495 int wrapuptime; /*!< Wrapup Time */
497 int retry; /*!< Retry calling everyone after this amount of time */
498 int timeout; /*!< How long to wait for an answer */
499 int weight; /*!< Respective weight */
500 int autopause; /*!< Auto pause queue members if they fail to answer */
502 /* Queue strategy things */
503 int rrpos; /*!< Round Robin - position */
504 int memberdelay; /*!< Seconds to delay connecting member to caller */
505 int autofill; /*!< Ignore the head call status and ring an available agent */
507 struct ao2_container *members; /*!< Head of the list of members */
508 /*!
509 * \brief Number of members _logged in_
510 * \note There will be members in the members container that are not logged
511 * in, so this can not simply be replaced with ao2_container_count().
513 int membercount;
514 struct queue_ent *head; /*!< Head of the list of callers */
515 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
516 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
519 struct rule_list {
520 char name[80];
521 AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
522 AST_LIST_ENTRY(rule_list) list;
525 AST_LIST_HEAD_STATIC(rule_lists, rule_list);
527 static struct ao2_container *queues;
529 static void update_realtime_members(struct call_queue *q);
530 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
532 /*! \brief sets the QUEUESTATUS channel variable */
533 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
535 int i;
537 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
538 if (queue_results[i].id == res) {
539 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
540 return;
545 static const char *int2strat(int strategy)
547 int x;
549 for (x = 0; x < ARRAY_LEN(strategies); x++) {
550 if (strategy == strategies[x].strategy)
551 return strategies[x].name;
554 return "<unknown>";
557 static int strat2int(const char *strategy)
559 int x;
561 for (x = 0; x < ARRAY_LEN(strategies); x++) {
562 if (!strcasecmp(strategy, strategies[x].name))
563 return strategies[x].strategy;
566 return -1;
569 static int queue_hash_cb(const void *obj, const int flags)
571 const struct call_queue *q = obj;
572 return ast_str_hash(q->name);
575 static int queue_cmp_cb(void *obj, void *arg, int flags)
577 struct call_queue *q = obj, *q2 = arg;
578 return !strcasecmp(q->name, q2->name) ? CMP_MATCH : 0;
581 static inline struct call_queue *queue_ref(struct call_queue *q)
583 ao2_ref(q, 1);
584 return q;
587 static inline struct call_queue *queue_unref(struct call_queue *q)
589 ao2_ref(q, -1);
590 return q;
593 /*! \brief Set variables of queue */
594 static void set_queue_variables(struct queue_ent *qe)
596 char interfacevar[256]="";
597 float sl = 0;
599 if (qe->parent->setqueuevar) {
600 sl = 0;
601 if (qe->parent->callscompleted > 0)
602 sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
604 snprintf(interfacevar, sizeof(interfacevar),
605 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
606 qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
607 qe->parent->callsabandoned, qe->parent->servicelevel, sl);
609 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
613 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
614 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
616 struct queue_ent *cur;
618 if (!q || !new)
619 return;
620 if (prev) {
621 cur = prev->next;
622 prev->next = new;
623 } else {
624 cur = q->head;
625 q->head = new;
627 new->next = cur;
628 new->parent = q;
629 new->pos = ++(*pos);
630 new->opos = *pos;
633 enum queue_member_status {
634 QUEUE_NO_MEMBERS,
635 QUEUE_NO_REACHABLE_MEMBERS,
636 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
637 QUEUE_NORMAL
640 /*! \brief Check if members are available
642 * This function checks to see if members are available to be called. If any member
643 * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
644 * the appropriate reason why is returned
646 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
648 struct member *member;
649 struct ao2_iterator mem_iter;
650 enum queue_member_status result = QUEUE_NO_MEMBERS;
652 ao2_lock(q);
653 mem_iter = ao2_iterator_init(q->members, 0);
654 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
655 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
656 continue;
658 switch (member->status) {
659 case AST_DEVICE_INVALID:
660 /* nothing to do */
661 break;
662 case AST_DEVICE_UNAVAILABLE:
663 if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)
664 result = QUEUE_NO_REACHABLE_MEMBERS;
665 break;
666 default:
667 if (member->paused) {
668 result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
669 } else {
670 ao2_unlock(q);
671 ao2_ref(member, -1);
672 return QUEUE_NORMAL;
674 break;
678 ao2_unlock(q);
679 return result;
682 struct statechange {
683 AST_LIST_ENTRY(statechange) entry;
684 int state;
685 char dev[0];
688 /*! \brief set a member's status based on device state of that member's state_interface.
690 * Lock interface list find sc, iterate through each queues queue_member list for member to
691 * update state inside queues
693 static int update_status(const char *interface, const int status)
695 struct member *cur;
696 struct ao2_iterator mem_iter, queue_iter;
697 struct call_queue *q;
699 queue_iter = ao2_iterator_init(queues, 0);
700 while ((q = ao2_iterator_next(&queue_iter))) {
701 ao2_lock(q);
702 mem_iter = ao2_iterator_init(q->members, 0);
703 while ((cur = ao2_iterator_next(&mem_iter))) {
704 char *tmp_interface;
705 char *slash_pos;
706 tmp_interface = ast_strdupa(cur->state_interface);
707 if ((slash_pos = strchr(interface, '/')))
708 if ((slash_pos = strchr(slash_pos + 1, '/')))
709 *slash_pos = '\0';
711 if (strcasecmp(interface, tmp_interface)) {
712 ao2_ref(cur, -1);
713 continue;
716 if (cur->status != status) {
717 cur->status = status;
718 if (q->maskmemberstatus) {
719 ao2_ref(cur, -1);
720 continue;
723 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
724 "Queue: %s\r\n"
725 "Location: %s\r\n"
726 "MemberName: %s\r\n"
727 "Membership: %s\r\n"
728 "Penalty: %d\r\n"
729 "CallsTaken: %d\r\n"
730 "LastCall: %d\r\n"
731 "Status: %d\r\n"
732 "Paused: %d\r\n",
733 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
734 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
736 ao2_ref(cur, -1);
738 queue_unref(q);
739 ao2_unlock(q);
742 return 0;
745 /*! \brief set a member's status based on device state of that member's interface*/
746 static int handle_statechange(void *datap)
748 struct member_interface *curint;
749 char *loc;
750 char *technology;
751 struct statechange *sc = datap;
753 technology = ast_strdupa(sc->dev);
754 loc = strchr(technology, '/');
755 if (loc) {
756 *loc++ = '\0';
757 } else {
758 ast_free(sc);
759 return 0;
762 AST_LIST_LOCK(&interfaces);
763 AST_LIST_TRAVERSE(&interfaces, curint, list) {
764 char *interface;
765 char *slash_pos;
766 interface = ast_strdupa(curint->interface);
767 if ((slash_pos = strchr(interface, '/')))
768 if ((slash_pos = strchr(slash_pos + 1, '/')))
769 *slash_pos = '\0';
771 if (!strcasecmp(interface, sc->dev))
772 break;
774 AST_LIST_UNLOCK(&interfaces);
776 if (!curint) {
777 if (option_debug > 2)
778 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));
779 return 0;
782 if (option_debug)
783 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
785 update_status(sc->dev, sc->state);
786 ast_free(sc);
787 return 0;
790 static void device_state_cb(const struct ast_event *event, void *unused)
792 enum ast_device_state state;
793 const char *device;
794 struct statechange *sc;
795 size_t datapsize;
797 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
798 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
800 if (ast_strlen_zero(device)) {
801 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
802 return;
804 datapsize = sizeof(*sc) + strlen(device) + 1;
805 if (!(sc = ast_calloc(1, datapsize))) {
806 ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
807 return;
809 sc->state = state;
810 strcpy(sc->dev, device);
811 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
812 ast_free(sc);
816 /*! \brief allocate space for new queue member and set fields based on parameters passed */
817 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
819 struct member *cur;
821 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
822 cur->penalty = penalty;
823 cur->paused = paused;
824 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
825 if (!ast_strlen_zero(state_interface))
826 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
827 else
828 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
829 if (!ast_strlen_zero(membername))
830 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
831 else
832 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
833 if (!strchr(cur->interface, '/'))
834 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
835 cur->status = ast_device_state(cur->state_interface);
838 return cur;
842 static int compress_char(const char c)
844 if (c < 32)
845 return 0;
846 else if (c > 96)
847 return c - 64;
848 else
849 return c - 32;
852 static int member_hash_fn(const void *obj, const int flags)
854 const struct member *mem = obj;
855 const char *chname = strchr(mem->interface, '/');
856 int ret = 0, i;
857 if (!chname)
858 chname = mem->interface;
859 for (i = 0; i < 5 && chname[i]; i++)
860 ret += compress_char(chname[i]) << (i * 6);
861 return ret;
864 static int member_cmp_fn(void *obj1, void *obj2, int flags)
866 struct member *mem1 = obj1, *mem2 = obj2;
867 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
870 /*!
871 * \brief Initialize Queue default values.
872 * \note the queue's lock must be held before executing this function
874 static void init_queue(struct call_queue *q)
876 int i;
877 struct penalty_rule *pr_iter;
879 q->dead = 0;
880 q->retry = DEFAULT_RETRY;
881 q->timeout = -1;
882 q->maxlen = 0;
883 q->announcefrequency = 0;
884 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
885 q->announceholdtime = 1;
886 q->announcepositionlimit = 10; /* Default 10 positions */
887 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
888 q->roundingseconds = 0; /* Default - don't announce seconds */
889 q->servicelevel = 0;
890 q->ringinuse = 1;
891 q->setinterfacevar = 0;
892 q->setqueuevar = 0;
893 q->setqueueentryvar = 0;
894 q->autofill = autofill_default;
895 q->montype = montype_default;
896 q->monfmt[0] = '\0';
897 q->reportholdtime = 0;
898 q->wrapuptime = 0;
899 q->joinempty = 0;
900 q->leavewhenempty = 0;
901 q->memberdelay = 0;
902 q->maskmemberstatus = 0;
903 q->eventwhencalled = 0;
904 q->weight = 0;
905 q->timeoutrestart = 0;
906 q->periodicannouncefrequency = 0;
907 q->randomperiodicannounce = 0;
908 q->numperiodicannounce = 0;
909 if (!q->members) {
910 if (q->strategy == QUEUE_STRATEGY_LINEAR)
911 /* linear strategy depends on order, so we have to place all members in a single bucket */
912 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
913 else
914 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
916 q->membercount = 0;
917 q->found = 1;
919 ast_string_field_set(q, sound_next, "queue-youarenext");
920 ast_string_field_set(q, sound_thereare, "queue-thereare");
921 ast_string_field_set(q, sound_calls, "queue-callswaiting");
922 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
923 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
924 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
925 ast_string_field_set(q, sound_minutes, "queue-minutes");
926 ast_string_field_set(q, sound_minute, "queue-minute");
927 ast_string_field_set(q, sound_seconds, "queue-seconds");
928 ast_string_field_set(q, sound_thanks, "queue-thankyou");
929 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
931 if ((q->sound_periodicannounce[0] = ast_str_create(32)))
932 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
934 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
935 if (q->sound_periodicannounce[i])
936 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
939 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
940 ast_free(pr_iter);
943 static void clear_queue(struct call_queue *q)
945 q->holdtime = 0;
946 q->callscompleted = 0;
947 q->callsabandoned = 0;
948 q->callscompletedinsl = 0;
949 q->wrapuptime = 0;
952 static int add_to_interfaces(const char *interface)
954 struct member_interface *curint;
956 AST_LIST_LOCK(&interfaces);
957 AST_LIST_TRAVERSE(&interfaces, curint, list) {
958 if (!strcasecmp(curint->interface, interface))
959 break;
962 if (curint) {
963 AST_LIST_UNLOCK(&interfaces);
964 return 0;
967 ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
969 if ((curint = ast_calloc(1, sizeof(*curint)))) {
970 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
971 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
973 AST_LIST_UNLOCK(&interfaces);
975 return 0;
978 static int interface_exists_global(const char *interface)
980 struct call_queue *q;
981 struct member *mem, tmpmem;
982 struct ao2_iterator queue_iter, mem_iter;
983 int ret = 0;
985 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
986 queue_iter = ao2_iterator_init(queues, 0);
987 while ((q = ao2_iterator_next(&queue_iter))) {
988 ao2_lock(q);
989 mem_iter = ao2_iterator_init(q->members, 0);
990 while ((mem = ao2_iterator_next(&mem_iter))) {
991 if (!strcasecmp(mem->state_interface, interface)) {
992 ao2_ref(mem, -1);
993 ret = 1;
994 break;
997 ao2_unlock(q);
998 queue_unref(q);
1001 return ret;
1004 static int remove_from_interfaces(const char *interface)
1006 struct member_interface *curint;
1008 if (interface_exists_global(interface))
1009 return 0;
1011 AST_LIST_LOCK(&interfaces);
1012 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
1013 if (!strcasecmp(curint->interface, interface)) {
1014 ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
1015 AST_LIST_REMOVE_CURRENT(list);
1016 ast_free(curint);
1017 break;
1020 AST_LIST_TRAVERSE_SAFE_END;
1021 AST_LIST_UNLOCK(&interfaces);
1023 return 0;
1026 static void clear_and_free_interfaces(void)
1028 struct member_interface *curint;
1030 AST_LIST_LOCK(&interfaces);
1031 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
1032 ast_free(curint);
1033 AST_LIST_UNLOCK(&interfaces);
1036 /*!
1037 * \brief Change queue penalty by adding rule.
1039 * Check rule for errors with time or fomatting, see if rule is relative to rest
1040 * of queue, iterate list of rules to find correct insertion point, insert and return.
1041 * \retval -1 on failure
1042 * \retval 0 on success
1043 * \note Call this with the rule_lists locked
1045 static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
1047 char *timestr, *maxstr, *minstr, *contentdup;
1048 struct penalty_rule *rule = NULL, *rule_iter;
1049 struct rule_list *rl_iter;
1050 int time, inserted = 0;
1052 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
1053 ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
1054 return -1;
1057 contentdup = ast_strdupa(content);
1059 if (!(maxstr = strchr(contentdup, ','))) {
1060 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
1061 ast_free(rule);
1062 return -1;
1065 *maxstr++ = '\0';
1066 timestr = contentdup;
1068 if ((time = atoi(timestr)) < 0) {
1069 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
1070 ast_free(rule);
1071 return -1;
1074 rule->time = time;
1076 if ((minstr = strchr(maxstr,',')))
1077 *minstr++ = '\0';
1079 /* The last check will evaluate true if either no penalty change is indicated for a given rule
1080 * OR if a min penalty change is indicated but no max penalty change is */
1081 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
1082 rule->max_relative = 1;
1085 rule->max_value = atoi(maxstr);
1087 if (!ast_strlen_zero(minstr)) {
1088 if (*minstr == '+' || *minstr == '-')
1089 rule->min_relative = 1;
1090 rule->min_value = atoi(minstr);
1091 } else /*there was no minimum specified, so assume this means no change*/
1092 rule->min_relative = 1;
1094 /*We have the rule made, now we need to insert it where it belongs*/
1095 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
1096 if (strcasecmp(rl_iter->name, list_name))
1097 continue;
1099 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
1100 if (rule->time < rule_iter->time) {
1101 AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
1102 inserted = 1;
1103 break;
1106 AST_LIST_TRAVERSE_SAFE_END;
1108 if (!inserted) {
1109 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
1113 return 0;
1116 /*! \brief Configure a queue parameter.
1118 * The failunknown flag is set for config files (and static realtime) to show
1119 * errors for unknown parameters. It is cleared for dynamic realtime to allow
1120 * extra fields in the tables.
1121 * \note For error reporting, line number is passed for .conf static configuration,
1122 * for Realtime queues, linenum is -1.
1124 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
1126 if (!strcasecmp(param, "musicclass") ||
1127 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
1128 ast_string_field_set(q, moh, val);
1129 } else if (!strcasecmp(param, "announce")) {
1130 ast_string_field_set(q, announce, val);
1131 } else if (!strcasecmp(param, "context")) {
1132 ast_string_field_set(q, context, val);
1133 } else if (!strcasecmp(param, "timeout")) {
1134 q->timeout = atoi(val);
1135 if (q->timeout < 0)
1136 q->timeout = DEFAULT_TIMEOUT;
1137 } else if (!strcasecmp(param, "ringinuse")) {
1138 q->ringinuse = ast_true(val);
1139 } else if (!strcasecmp(param, "setinterfacevar")) {
1140 q->setinterfacevar = ast_true(val);
1141 } else if (!strcasecmp(param, "setqueuevar")) {
1142 q->setqueuevar = ast_true(val);
1143 } else if (!strcasecmp(param, "setqueueentryvar")) {
1144 q->setqueueentryvar = ast_true(val);
1145 } else if (!strcasecmp(param, "monitor-format")) {
1146 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
1147 } else if (!strcasecmp(param, "membermacro")) {
1148 ast_string_field_set(q, membermacro, val);
1149 } else if (!strcasecmp(param, "membergosub")) {
1150 ast_string_field_set(q, membergosub, val);
1151 } else if (!strcasecmp(param, "queue-youarenext")) {
1152 ast_string_field_set(q, sound_next, val);
1153 } else if (!strcasecmp(param, "queue-thereare")) {
1154 ast_string_field_set(q, sound_thereare, val);
1155 } else if (!strcasecmp(param, "queue-callswaiting")) {
1156 ast_string_field_set(q, sound_calls, val);
1157 } else if (!strcasecmp(param, "queue-quantity1")) {
1158 ast_string_field_set(q, queue_quantity1, val);
1159 } else if (!strcasecmp(param, "queue-quantity2")) {
1160 ast_string_field_set(q, queue_quantity2, val);
1161 } else if (!strcasecmp(param, "queue-holdtime")) {
1162 ast_string_field_set(q, sound_holdtime, val);
1163 } else if (!strcasecmp(param, "queue-minutes")) {
1164 ast_string_field_set(q, sound_minutes, val);
1165 } else if (!strcasecmp(param, "queue-minute")) {
1166 ast_string_field_set(q, sound_minute, val);
1167 } else if (!strcasecmp(param, "queue-seconds")) {
1168 ast_string_field_set(q, sound_seconds, val);
1169 } else if (!strcasecmp(param, "queue-thankyou")) {
1170 ast_string_field_set(q, sound_thanks, val);
1171 } else if (!strcasecmp(param, "queue-callerannounce")) {
1172 ast_string_field_set(q, sound_callerannounce, val);
1173 } else if (!strcasecmp(param, "queue-reporthold")) {
1174 ast_string_field_set(q, sound_reporthold, val);
1175 } else if (!strcasecmp(param, "announce-frequency")) {
1176 q->announcefrequency = atoi(val);
1177 } else if (!strcasecmp(param, "min-announce-frequency")) {
1178 q->minannouncefrequency = atoi(val);
1179 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
1180 } else if (!strcasecmp(param, "announce-round-seconds")) {
1181 q->roundingseconds = atoi(val);
1182 /* Rounding to any other values just doesn't make sense... */
1183 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
1184 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
1185 if (linenum >= 0) {
1186 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1187 "using 0 instead for queue '%s' at line %d of queues.conf\n",
1188 val, param, q->name, linenum);
1189 } else {
1190 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1191 "using 0 instead for queue '%s'\n", val, param, q->name);
1193 q->roundingseconds=0;
1195 } else if (!strcasecmp(param, "announce-holdtime")) {
1196 if (!strcasecmp(val, "once"))
1197 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
1198 else if (ast_true(val))
1199 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
1200 else
1201 q->announceholdtime = 0;
1202 } else if (!strcasecmp(param, "announce-position")) {
1203 if (!strcasecmp(val, "limit"))
1204 q->announceposition = ANNOUNCEPOSITION_LIMIT;
1205 else if (!strcasecmp(val, "more"))
1206 q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
1207 else if (ast_true(val))
1208 q->announceposition = ANNOUNCEPOSITION_YES;
1209 else
1210 q->announceposition = ANNOUNCEPOSITION_NO;
1211 } else if (!strcasecmp(param, "announce-position-limit")) {
1212 q->announcepositionlimit = atoi(val);
1213 } else if (!strcasecmp(param, "periodic-announce")) {
1214 if (strchr(val, ',')) {
1215 char *s, *buf = ast_strdupa(val);
1216 unsigned int i = 0;
1218 while ((s = strsep(&buf, ",|"))) {
1219 if (!q->sound_periodicannounce[i])
1220 q->sound_periodicannounce[i] = ast_str_create(16);
1221 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
1222 i++;
1223 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
1224 break;
1226 q->numperiodicannounce = i;
1227 } else {
1228 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
1229 q->numperiodicannounce = 1;
1231 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
1232 q->periodicannouncefrequency = atoi(val);
1233 } else if (!strcasecmp(param, "random-periodic-announce")) {
1234 q->randomperiodicannounce = ast_true(val);
1235 } else if (!strcasecmp(param, "retry")) {
1236 q->retry = atoi(val);
1237 if (q->retry <= 0)
1238 q->retry = DEFAULT_RETRY;
1239 } else if (!strcasecmp(param, "wrapuptime")) {
1240 q->wrapuptime = atoi(val);
1241 } else if (!strcasecmp(param, "autofill")) {
1242 q->autofill = ast_true(val);
1243 } else if (!strcasecmp(param, "monitor-type")) {
1244 if (!strcasecmp(val, "mixmonitor"))
1245 q->montype = 1;
1246 } else if (!strcasecmp(param, "autopause")) {
1247 q->autopause = ast_true(val);
1248 } else if (!strcasecmp(param, "maxlen")) {
1249 q->maxlen = atoi(val);
1250 if (q->maxlen < 0)
1251 q->maxlen = 0;
1252 } else if (!strcasecmp(param, "servicelevel")) {
1253 q->servicelevel= atoi(val);
1254 } else if (!strcasecmp(param, "strategy")) {
1255 /* We already have set this, no need to do it again */
1256 return;
1257 } else if (!strcasecmp(param, "joinempty")) {
1258 if (!strcasecmp(val, "loose"))
1259 q->joinempty = QUEUE_EMPTY_LOOSE;
1260 else if (!strcasecmp(val, "strict"))
1261 q->joinempty = QUEUE_EMPTY_STRICT;
1262 else if (ast_true(val))
1263 q->joinempty = QUEUE_EMPTY_NORMAL;
1264 else
1265 q->joinempty = 0;
1266 } else if (!strcasecmp(param, "leavewhenempty")) {
1267 if (!strcasecmp(val, "loose"))
1268 q->leavewhenempty = QUEUE_EMPTY_LOOSE;
1269 else if (!strcasecmp(val, "strict"))
1270 q->leavewhenempty = QUEUE_EMPTY_STRICT;
1271 else if (ast_true(val))
1272 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
1273 else
1274 q->leavewhenempty = 0;
1275 } else if (!strcasecmp(param, "eventmemberstatus")) {
1276 q->maskmemberstatus = !ast_true(val);
1277 } else if (!strcasecmp(param, "eventwhencalled")) {
1278 if (!strcasecmp(val, "vars")) {
1279 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
1280 } else {
1281 q->eventwhencalled = ast_true(val) ? 1 : 0;
1283 } else if (!strcasecmp(param, "reportholdtime")) {
1284 q->reportholdtime = ast_true(val);
1285 } else if (!strcasecmp(param, "memberdelay")) {
1286 q->memberdelay = atoi(val);
1287 } else if (!strcasecmp(param, "weight")) {
1288 q->weight = atoi(val);
1289 if (q->weight)
1290 use_weight++;
1291 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1292 we will not see any effect on use_weight until next reload. */
1293 } else if (!strcasecmp(param, "timeoutrestart")) {
1294 q->timeoutrestart = ast_true(val);
1295 } else if (!strcasecmp(param, "defaultrule")) {
1296 ast_string_field_set(q, defaultrule, val);
1297 } else if (failunknown) {
1298 if (linenum >= 0) {
1299 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1300 q->name, param, linenum);
1301 } else {
1302 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
1308 * \brief Find rt member record to update otherwise create one.
1310 * Search for member in queue, if found update penalty/paused state,
1311 * if no memeber exists create one flag it as a RT member and add to queue member list.
1313 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
1315 struct member *m;
1316 struct ao2_iterator mem_iter;
1317 int penalty = 0;
1318 int paused = 0;
1319 int found = 0;
1321 if (penalty_str) {
1322 penalty = atoi(penalty_str);
1323 if (penalty < 0)
1324 penalty = 0;
1327 if (paused_str) {
1328 paused = atoi(paused_str);
1329 if (paused < 0)
1330 paused = 0;
1333 /* Find member by realtime uniqueid and update */
1334 mem_iter = ao2_iterator_init(q->members, 0);
1335 while ((m = ao2_iterator_next(&mem_iter))) {
1336 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
1337 m->dead = 0; /* Do not delete this one. */
1338 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1339 if (paused_str)
1340 m->paused = paused;
1341 if (strcasecmp(state_interface, m->state_interface)) {
1342 remove_from_interfaces(m->state_interface);
1343 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
1344 add_to_interfaces(m->state_interface);
1346 m->penalty = penalty;
1347 found = 1;
1348 ao2_ref(m, -1);
1349 break;
1351 ao2_ref(m, -1);
1354 /* Create a new member */
1355 if (!found) {
1356 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
1357 m->dead = 0;
1358 m->realtime = 1;
1359 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1360 add_to_interfaces(m->state_interface);
1361 ao2_link(q->members, m);
1362 ao2_ref(m, -1);
1363 m = NULL;
1364 q->membercount++;
1369 /*! \brief Iterate through queue's member list and delete them */
1370 static void free_members(struct call_queue *q, int all)
1372 /* Free non-dynamic members */
1373 struct member *cur;
1374 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
1376 while ((cur = ao2_iterator_next(&mem_iter))) {
1377 if (all || !cur->dynamic) {
1378 ao2_unlink(q->members, cur);
1379 remove_from_interfaces(cur->state_interface);
1380 q->membercount--;
1382 ao2_ref(cur, -1);
1386 /*! \brief Free queue's member list then its string fields */
1387 static void destroy_queue(void *obj)
1389 struct call_queue *q = obj;
1390 int i;
1392 free_members(q, 1);
1393 ast_string_field_free_memory(q);
1394 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
1395 if (q->sound_periodicannounce[i])
1396 free(q->sound_periodicannounce[i]);
1398 ao2_ref(q->members, -1);
1401 static struct call_queue *alloc_queue(const char *queuename)
1403 struct call_queue *q;
1405 if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
1406 if (ast_string_field_init(q, 64)) {
1407 free(q);
1408 return NULL;
1410 ast_string_field_set(q, name, queuename);
1412 return q;
1416 * \brief Reload a single queue via realtime.
1418 * Check for statically defined queue first, check if deleted RT queue,
1419 * check for new RT queue, if queue vars are not defined init them with defaults.
1420 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
1421 * \retval the queue,
1422 * \retval NULL if it doesn't exist.
1423 * \note Should be called with the "queues" container locked.
1425 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
1427 struct ast_variable *v;
1428 struct call_queue *q, tmpq = {
1429 .name = queuename,
1431 struct member *m;
1432 struct ao2_iterator mem_iter;
1433 char *interface = NULL;
1434 const char *tmp_name;
1435 char *tmp;
1436 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
1438 /* Static queues override realtime. */
1439 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
1440 ao2_lock(q);
1441 if (!q->realtime) {
1442 if (q->dead) {
1443 ao2_unlock(q);
1444 queue_unref(q);
1445 return NULL;
1446 } else {
1447 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
1448 ao2_unlock(q);
1449 return q;
1452 queue_unref(q);
1453 } else if (!member_config)
1454 /* Not found in the list, and it's not realtime ... */
1455 return NULL;
1457 /* Check if queue is defined in realtime. */
1458 if (!queue_vars) {
1459 /* Delete queue from in-core list if it has been deleted in realtime. */
1460 if (q) {
1461 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1462 found condition... So we might delete an in-core queue
1463 in case of DB failure. */
1464 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
1466 q->dead = 1;
1467 /* Delete if unused (else will be deleted when last caller leaves). */
1468 ao2_unlink(queues, q);
1469 ao2_unlock(q);
1470 queue_unref(q);
1472 return NULL;
1475 /* Create a new queue if an in-core entry does not exist yet. */
1476 if (!q) {
1477 struct ast_variable *tmpvar = NULL;
1478 if (!(q = alloc_queue(queuename)))
1479 return NULL;
1480 ao2_lock(q);
1481 clear_queue(q);
1482 q->realtime = 1;
1483 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
1484 * will allocate the members properly
1486 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
1487 if (!strcasecmp(tmpvar->name, "strategy")) {
1488 q->strategy = strat2int(tmpvar->value);
1489 if (q->strategy < 0) {
1490 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1491 tmpvar->value, q->name);
1492 q->strategy = QUEUE_STRATEGY_RINGALL;
1494 break;
1497 /* We traversed all variables and didn't find a strategy */
1498 if (!tmpvar)
1499 q->strategy = QUEUE_STRATEGY_RINGALL;
1500 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
1501 ao2_link(queues, q);
1504 memset(tmpbuf, 0, sizeof(tmpbuf));
1505 for (v = queue_vars; v; v = v->next) {
1506 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1507 if ((tmp = strchr(v->name, '_'))) {
1508 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
1509 tmp_name = tmpbuf;
1510 tmp = tmpbuf;
1511 while ((tmp = strchr(tmp, '_')))
1512 *tmp++ = '-';
1513 } else
1514 tmp_name = v->name;
1516 if (!ast_strlen_zero(v->value)) {
1517 /* Don't want to try to set the option if the value is empty */
1518 queue_set_param(q, tmp_name, v->value, -1, 0);
1522 /* Temporarily set realtime members dead so we can detect deleted ones.
1523 * Also set the membercount correctly for realtime*/
1524 mem_iter = ao2_iterator_init(q->members, 0);
1525 while ((m = ao2_iterator_next(&mem_iter))) {
1526 q->membercount++;
1527 if (m->realtime)
1528 m->dead = 1;
1529 ao2_ref(m, -1);
1532 while ((interface = ast_category_browse(member_config, interface))) {
1533 rt_handle_member_record(q, interface,
1534 ast_variable_retrieve(member_config, interface, "uniqueid"),
1535 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
1536 ast_variable_retrieve(member_config, interface, "penalty"),
1537 ast_variable_retrieve(member_config, interface, "paused"),
1538 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
1541 /* Delete all realtime members that have been deleted in DB. */
1542 mem_iter = ao2_iterator_init(q->members, 0);
1543 while ((m = ao2_iterator_next(&mem_iter))) {
1544 if (m->dead) {
1545 ao2_unlink(q->members, m);
1546 remove_from_interfaces(m->state_interface);
1547 q->membercount--;
1549 ao2_ref(m, -1);
1552 ao2_unlock(q);
1554 return q;
1557 static struct call_queue *load_realtime_queue(const char *queuename)
1559 struct ast_variable *queue_vars;
1560 struct ast_config *member_config = NULL;
1561 struct call_queue *q = NULL, tmpq = {
1562 .name = queuename,
1565 /* Find the queue in the in-core list first. */
1566 q = ao2_find(queues, &tmpq, OBJ_POINTER);
1568 if (!q || q->realtime) {
1569 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
1570 queue operations while waiting for the DB.
1572 This will be two separate database transactions, so we might
1573 see queue parameters as they were before another process
1574 changed the queue and member list as it was after the change.
1575 Thus we might see an empty member list when a queue is
1576 deleted. In practise, this is unlikely to cause a problem. */
1578 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
1579 if (queue_vars) {
1580 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
1581 if (!member_config) {
1582 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
1583 ast_variables_destroy(queue_vars);
1584 return NULL;
1588 ao2_lock(queues);
1589 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
1590 if (member_config)
1591 ast_config_destroy(member_config);
1592 if (queue_vars)
1593 ast_variables_destroy(queue_vars);
1594 ao2_unlock(queues);
1596 } else {
1597 update_realtime_members(q);
1599 return q;
1602 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
1604 int ret = -1;
1606 if (ast_strlen_zero(mem->rt_uniqueid))
1607 return ret;
1609 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, NULL)) > 0)
1610 ret = 0;
1612 return ret;
1616 static void update_realtime_members(struct call_queue *q)
1618 struct ast_config *member_config = NULL;
1619 struct member *m;
1620 char *interface = NULL;
1621 struct ao2_iterator mem_iter;
1623 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
1624 /*This queue doesn't have realtime members*/
1625 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
1626 return;
1629 ao2_lock(q);
1631 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1632 mem_iter = ao2_iterator_init(q->members, 0);
1633 while ((m = ao2_iterator_next(&mem_iter))) {
1634 if (m->realtime)
1635 m->dead = 1;
1636 ao2_ref(m, -1);
1639 while ((interface = ast_category_browse(member_config, interface))) {
1640 rt_handle_member_record(q, interface,
1641 ast_variable_retrieve(member_config, interface, "uniqueid"),
1642 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
1643 ast_variable_retrieve(member_config, interface, "penalty"),
1644 ast_variable_retrieve(member_config, interface, "paused"),
1645 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
1648 /* Delete all realtime members that have been deleted in DB. */
1649 mem_iter = ao2_iterator_init(q->members, 0);
1650 while ((m = ao2_iterator_next(&mem_iter))) {
1651 if (m->dead) {
1652 ao2_unlink(q->members, m);
1653 remove_from_interfaces(m->state_interface);
1654 q->membercount--;
1656 ao2_ref(m, -1);
1658 ao2_unlock(q);
1659 ast_config_destroy(member_config);
1662 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
1664 struct call_queue *q;
1665 struct queue_ent *cur, *prev = NULL;
1666 int res = -1;
1667 int pos = 0;
1668 int inserted = 0;
1669 enum queue_member_status stat;
1671 if (!(q = load_realtime_queue(queuename)))
1672 return res;
1674 ao2_lock(queues);
1675 ao2_lock(q);
1677 /* This is our one */
1678 stat = get_member_status(q, qe->max_penalty, qe->min_penalty);
1679 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
1680 *reason = QUEUE_JOINEMPTY;
1681 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS))
1682 *reason = QUEUE_JOINUNAVAIL;
1683 else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS))
1684 *reason = QUEUE_JOINUNAVAIL;
1685 else if (q->maxlen && (q->count >= q->maxlen))
1686 *reason = QUEUE_FULL;
1687 else {
1688 /* There's space for us, put us at the right position inside
1689 * the queue.
1690 * Take into account the priority of the calling user */
1691 inserted = 0;
1692 prev = NULL;
1693 cur = q->head;
1694 while (cur) {
1695 /* We have higher priority than the current user, enter
1696 * before him, after all the other users with priority
1697 * higher or equal to our priority. */
1698 if ((!inserted) && (qe->prio > cur->prio)) {
1699 insert_entry(q, prev, qe, &pos);
1700 inserted = 1;
1702 cur->pos = ++pos;
1703 prev = cur;
1704 cur = cur->next;
1706 /* No luck, join at the end of the queue */
1707 if (!inserted)
1708 insert_entry(q, prev, qe, &pos);
1709 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
1710 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
1711 ast_copy_string(qe->context, q->context, sizeof(qe->context));
1712 q->count++;
1713 res = 0;
1714 manager_event(EVENT_FLAG_CALL, "Join",
1715 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1716 qe->chan->name,
1717 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
1718 S_OR(qe->chan->cid.cid_name, "unknown"),
1719 q->name, qe->pos, q->count, qe->chan->uniqueid );
1720 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
1722 ao2_unlock(q);
1723 ao2_unlock(queues);
1725 return res;
1728 static int play_file(struct ast_channel *chan, const char *filename)
1730 int res;
1732 ast_stopstream(chan);
1734 res = ast_streamfile(chan, filename, chan->language);
1735 if (!res)
1736 res = ast_waitstream(chan, AST_DIGIT_ANY);
1738 ast_stopstream(chan);
1740 return res;
1744 * \brief Check for valid exit from queue via goto
1745 * \retval 0 if failure
1746 * \retval 1 if successful
1748 static int valid_exit(struct queue_ent *qe, char digit)
1750 int digitlen = strlen(qe->digits);
1752 /* Prevent possible buffer overflow */
1753 if (digitlen < sizeof(qe->digits) - 2) {
1754 qe->digits[digitlen] = digit;
1755 qe->digits[digitlen + 1] = '\0';
1756 } else {
1757 qe->digits[0] = '\0';
1758 return 0;
1761 /* If there's no context to goto, short-circuit */
1762 if (ast_strlen_zero(qe->context))
1763 return 0;
1765 /* If the extension is bad, then reset the digits to blank */
1766 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
1767 qe->digits[0] = '\0';
1768 return 0;
1771 /* We have an exact match */
1772 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
1773 qe->valid_digits = 1;
1774 /* Return 1 on a successful goto */
1775 return 1;
1778 return 0;
1781 static int say_position(struct queue_ent *qe, int ringing)
1783 int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
1784 time_t now;
1786 /* Let minannouncefrequency seconds pass between the start of each position announcement */
1787 time(&now);
1788 if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
1789 return 0;
1791 /* If either our position has changed, or we are over the freq timer, say position */
1792 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
1793 return 0;
1795 if (ringing) {
1796 ast_indicate(qe->chan,-1);
1797 } else {
1798 ast_moh_stop(qe->chan);
1801 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
1802 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
1803 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
1804 qe->pos <= qe->parent->announcepositionlimit))
1805 announceposition = 1;
1808 if (announceposition == 1) {
1809 /* Say we're next, if we are */
1810 if (qe->pos == 1) {
1811 res = play_file(qe->chan, qe->parent->sound_next);
1812 if (res)
1813 goto playout;
1814 else
1815 goto posout;
1816 } else {
1817 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
1818 /* More than Case*/
1819 res = play_file(qe->chan, qe->parent->queue_quantity1);
1820 if (res)
1821 goto playout;
1822 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
1823 if (res)
1824 goto playout;
1825 } else {
1826 /* Normal Case */
1827 res = play_file(qe->chan, qe->parent->sound_thereare);
1828 if (res)
1829 goto playout;
1830 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
1831 if (res)
1832 goto playout;
1834 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
1835 /* More than Case*/
1836 res = play_file(qe->chan, qe->parent->queue_quantity2);
1837 if (res)
1838 goto playout;
1839 } else {
1840 res = play_file(qe->chan, qe->parent->sound_calls);
1841 if (res)
1842 goto playout;
1846 /* Round hold time to nearest minute */
1847 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
1849 /* If they have specified a rounding then round the seconds as well */
1850 if (qe->parent->roundingseconds) {
1851 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
1852 avgholdsecs *= qe->parent->roundingseconds;
1853 } else {
1854 avgholdsecs = 0;
1857 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
1859 /* If the hold time is >1 min, if it's enabled, and if it's not
1860 supposed to be only once and we have already said it, say it */
1861 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
1862 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
1863 res = play_file(qe->chan, qe->parent->sound_holdtime);
1864 if (res)
1865 goto playout;
1867 if (avgholdmins > 1) {
1868 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
1869 if (res)
1870 goto playout;
1872 if (avgholdmins == 1) {
1873 res = play_file(qe->chan, qe->parent->sound_minute);
1874 if (res)
1875 goto playout;
1876 } else {
1877 res = play_file(qe->chan, qe->parent->sound_minutes);
1878 if (res)
1879 goto playout;
1882 if (avgholdsecs > 1) {
1883 res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
1884 if (res)
1885 goto playout;
1887 res = play_file(qe->chan, qe->parent->sound_seconds);
1888 if (res)
1889 goto playout;
1894 posout:
1895 if (announceposition == 1){
1896 if (qe->parent->announceposition) {
1897 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
1898 qe->chan->name, qe->parent->name, qe->pos);
1900 res = play_file(qe->chan, qe->parent->sound_thanks);
1902 playout:
1903 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
1904 res = 0;
1906 /* Set our last_pos indicators */
1907 qe->last_pos = now;
1908 qe->last_pos_said = qe->pos;
1910 /* Don't restart music on hold if we're about to exit the caller from the queue */
1911 if (!res) {
1912 if (ringing) {
1913 ast_indicate(qe->chan, AST_CONTROL_RINGING);
1914 } else {
1915 ast_moh_start(qe->chan, qe->moh, NULL);
1918 return res;
1921 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
1923 int oldvalue;
1925 /* Calculate holdtime using a recursive boxcar filter */
1926 /* Thanks to SRT for this contribution */
1927 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1929 ao2_lock(qe->parent);
1930 oldvalue = qe->parent->holdtime;
1931 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
1932 ao2_unlock(qe->parent);
1935 /*! \brief Caller leaving queue.
1937 * Search the queue to find the leaving client, if found remove from queue
1938 * create manager event, move others up the queue.
1940 static void leave_queue(struct queue_ent *qe)
1942 struct call_queue *q;
1943 struct queue_ent *cur, *prev = NULL;
1944 struct penalty_rule *pr_iter;
1945 int pos = 0;
1947 if (!(q = qe->parent))
1948 return;
1949 queue_ref(q);
1950 ao2_lock(q);
1952 prev = NULL;
1953 for (cur = q->head; cur; cur = cur->next) {
1954 if (cur == qe) {
1955 q->count--;
1957 /* Take us out of the queue */
1958 manager_event(EVENT_FLAG_CALL, "Leave",
1959 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1960 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
1961 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
1962 /* Take us out of the queue */
1963 if (prev)
1964 prev->next = cur->next;
1965 else
1966 q->head = cur->next;
1967 /* Free penalty rules */
1968 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
1969 ast_free(pr_iter);
1970 } else {
1971 /* Renumber the people after us in the queue based on a new count */
1972 cur->pos = ++pos;
1973 prev = cur;
1976 ao2_unlock(q);
1978 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
1979 if (q->realtime) {
1980 if (!ast_load_realtime("queues", "name", q->name, NULL))
1981 q->dead = 1;
1984 if (q->dead) {
1985 /* It's dead and nobody is in it, so kill it */
1986 ao2_unlink(queues, q);
1987 /* unref the container's reference to the queue */
1988 queue_unref(q);
1990 /* unref the explicit ref earlier in the function */
1991 queue_unref(q);
1994 /*! \brief Hang up a list of outgoing calls */
1995 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
1997 struct callattempt *oo;
1999 while (outgoing) {
2000 /* Hangup any existing lines we have open */
2001 if (outgoing->chan && (outgoing->chan != exception))
2002 ast_hangup(outgoing->chan);
2003 oo = outgoing;
2004 outgoing = outgoing->q_next;
2005 if (oo->member)
2006 ao2_ref(oo->member, -1);
2007 ast_free(oo);
2011 /*!
2012 * \brief traverse all defined queues which have calls waiting and contain this member
2013 * \retval 0 if no other queue has precedence (higher weight)
2014 * \retval 1 if found
2016 static int compare_weight(struct call_queue *rq, struct member *member)
2018 struct call_queue *q;
2019 struct member *mem;
2020 int found = 0;
2021 struct ao2_iterator queue_iter;
2023 /* q's lock and rq's lock already set by try_calling()
2024 * to solve deadlock */
2025 queue_iter = ao2_iterator_init(queues, 0);
2026 while ((q = ao2_iterator_next(&queue_iter))) {
2027 if (q == rq) { /* don't check myself, could deadlock */
2028 queue_unref(q);
2029 continue;
2031 ao2_lock(q);
2032 if (q->count && q->members) {
2033 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
2034 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
2035 if (q->weight > rq->weight) {
2036 ast_debug(1, "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);
2037 found = 1;
2039 ao2_ref(mem, -1);
2042 ao2_unlock(q);
2043 if (found) {
2044 queue_unref(q);
2045 break;
2047 queue_unref(q);
2049 return found;
2052 /*! \brief common hangup actions */
2053 static void do_hang(struct callattempt *o)
2055 o->stillgoing = 0;
2056 ast_hangup(o->chan);
2057 o->chan = NULL;
2060 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2061 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
2063 struct ast_str *buf = ast_str_alloca(len + 1);
2064 char *tmp;
2066 if (pbx_builtin_serialize_variables(chan, &buf)) {
2067 int i, j;
2069 /* convert "\n" to "\nVariable: " */
2070 strcpy(vars, "Variable: ");
2071 tmp = buf->str;
2073 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
2074 vars[j] = tmp[i];
2076 if (tmp[i + 1] == '\0')
2077 break;
2078 if (tmp[i] == '\n') {
2079 vars[j++] = '\r';
2080 vars[j++] = '\n';
2082 ast_copy_string(&(vars[j]), "Variable: ", len - j);
2083 j += 9;
2086 if (j > len - 3)
2087 j = len - 3;
2088 vars[j++] = '\r';
2089 vars[j++] = '\n';
2090 vars[j] = '\0';
2091 } else {
2092 /* there are no channel variables; leave it blank */
2093 *vars = '\0';
2095 return vars;
2098 /*!
2099 * \brief Part 2 of ring_one
2101 * Does error checking before attempting to request a channel and call a member.
2102 * This function is only called from ring_one().
2103 * Failure can occur if:
2104 * - Agent on call
2105 * - Agent is paused
2106 * - Wrapup time not expired
2107 * - Priority by another queue
2109 * \retval 1 on success to reach a free agent
2110 * \retval 0 on failure to get agent.
2112 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
2114 int res;
2115 int status;
2116 char tech[256];
2117 char *location;
2118 const char *macrocontext, *macroexten;
2120 /* on entry here, we know that tmp->chan == NULL */
2121 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
2122 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
2123 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
2124 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
2125 if (qe->chan->cdr)
2126 ast_cdr_busy(qe->chan->cdr);
2127 tmp->stillgoing = 0;
2128 (*busies)++;
2129 return 0;
2132 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
2133 ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
2134 if (qe->chan->cdr)
2135 ast_cdr_busy(qe->chan->cdr);
2136 tmp->stillgoing = 0;
2137 return 0;
2140 if (tmp->member->paused) {
2141 ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
2142 if (qe->chan->cdr)
2143 ast_cdr_busy(qe->chan->cdr);
2144 tmp->stillgoing = 0;
2145 return 0;
2147 if (use_weight && compare_weight(qe->parent,tmp->member)) {
2148 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
2149 if (qe->chan->cdr)
2150 ast_cdr_busy(qe->chan->cdr);
2151 tmp->stillgoing = 0;
2152 (*busies)++;
2153 return 0;
2156 ast_copy_string(tech, tmp->interface, sizeof(tech));
2157 if ((location = strchr(tech, '/')))
2158 *location++ = '\0';
2159 else
2160 location = "";
2162 /* Request the peer */
2163 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
2164 if (!tmp->chan) { /* If we can't, just go on to the next call */
2165 if (qe->chan->cdr)
2166 ast_cdr_busy(qe->chan->cdr);
2167 tmp->stillgoing = 0;
2169 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
2171 ao2_lock(qe->parent);
2172 qe->parent->rrpos++;
2173 qe->linpos++;
2174 ao2_unlock(qe->parent);
2177 (*busies)++;
2178 return 0;
2181 tmp->chan->appl = "AppQueue";
2182 tmp->chan->data = "(Outgoing Line)";
2183 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
2184 if (tmp->chan->cid.cid_num)
2185 ast_free(tmp->chan->cid.cid_num);
2186 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
2187 if (tmp->chan->cid.cid_name)
2188 ast_free(tmp->chan->cid.cid_name);
2189 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
2190 if (tmp->chan->cid.cid_ani)
2191 ast_free(tmp->chan->cid.cid_ani);
2192 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
2194 /* Inherit specially named variables from parent channel */
2195 ast_channel_inherit_variables(qe->chan, tmp->chan);
2197 /* Presense of ADSI CPE on outgoing channel follows ours */
2198 tmp->chan->adsicpe = qe->chan->adsicpe;
2200 /* Inherit context and extension */
2201 ast_channel_lock(qe->chan);
2202 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
2203 if (!ast_strlen_zero(macrocontext))
2204 ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
2205 else
2206 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
2207 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
2208 if (!ast_strlen_zero(macroexten))
2209 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
2210 else
2211 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
2212 ast_channel_unlock(qe->chan);
2214 /* Place the call, but don't wait on the answer */
2215 if ((res = ast_call(tmp->chan, location, 0))) {
2216 /* Again, keep going even if there's an error */
2217 ast_debug(1, "ast call on peer returned %d\n", res);
2218 ast_verb(3, "Couldn't call %s\n", tmp->interface);
2219 do_hang(tmp);
2220 (*busies)++;
2221 return 0;
2222 } else if (qe->parent->eventwhencalled) {
2223 char vars[2048];
2225 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
2226 "Queue: %s\r\n"
2227 "AgentCalled: %s\r\n"
2228 "AgentName: %s\r\n"
2229 "ChannelCalling: %s\r\n"
2230 "DestinationChannel: %s\r\n"
2231 "CallerIDNum: %s\r\n"
2232 "CallerIDName: %s\r\n"
2233 "Context: %s\r\n"
2234 "Extension: %s\r\n"
2235 "Priority: %d\r\n"
2236 "Uniqueid: %s\r\n"
2237 "%s",
2238 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
2239 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
2240 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
2241 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
2242 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2243 ast_verb(3, "Called %s\n", tmp->interface);
2246 return 1;
2249 /*! \brief find the entry with the best metric, or NULL */
2250 static struct callattempt *find_best(struct callattempt *outgoing)
2252 struct callattempt *best = NULL, *cur;
2254 for (cur = outgoing; cur; cur = cur->q_next) {
2255 if (cur->stillgoing && /* Not already done */
2256 !cur->chan && /* Isn't already going */
2257 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
2258 best = cur;
2262 return best;
2265 /*!
2266 * \brief Place a call to a queue member.
2268 * Once metrics have been calculated for each member, this function is used
2269 * to place a call to the appropriate member (or members). The low-level
2270 * channel-handling and error detection is handled in ring_entry
2272 * \retval 1 if a member was called successfully
2273 * \retval 0 otherwise
2275 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
2277 int ret = 0;
2279 while (ret == 0) {
2280 struct callattempt *best = find_best(outgoing);
2281 if (!best) {
2282 ast_debug(1, "Nobody left to try ringing in queue\n");
2283 break;
2285 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2286 struct callattempt *cur;
2287 /* Ring everyone who shares this best metric (for ringall) */
2288 for (cur = outgoing; cur; cur = cur->q_next) {
2289 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
2290 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
2291 ret |= ring_entry(qe, cur, busies);
2294 } else {
2295 /* Ring just the best channel */
2296 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
2297 ret = ring_entry(qe, best, busies);
2301 return ret;
2304 /*! \brief Search for best metric and add to Round Robbin queue */
2305 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
2307 struct callattempt *best = find_best(outgoing);
2309 if (best) {
2310 /* Ring just the best channel */
2311 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
2312 qe->parent->rrpos = best->metric % 1000;
2313 } else {
2314 /* Just increment rrpos */
2315 if (qe->parent->wrapped) {
2316 /* No more channels, start over */
2317 qe->parent->rrpos = 0;
2318 } else {
2319 /* Prioritize next entry */
2320 qe->parent->rrpos++;
2323 qe->parent->wrapped = 0;
2325 return 0;
2328 /*! \brief Search for best metric and add to Linear queue */
2329 static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
2331 struct callattempt *best = find_best(outgoing);
2333 if (best) {
2334 /* Ring just the best channel */
2335 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
2336 qe->linpos = best->metric % 1000;
2337 } else {
2338 /* Just increment rrpos */
2339 if (qe->linwrapped) {
2340 /* No more channels, start over */
2341 qe->linpos = 0;
2342 } else {
2343 /* Prioritize next entry */
2344 qe->linpos++;
2347 qe->linwrapped = 0;
2349 return 0;
2352 /*! \brief Playback announcement to queued members if peroid has elapsed */
2353 static int say_periodic_announcement(struct queue_ent *qe, int ringing)
2355 int res = 0;
2356 time_t now;
2358 /* Get the current time */
2359 time(&now);
2361 /* Check to see if it is time to announce */
2362 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
2363 return 0;
2365 /* Stop the music on hold so we can play our own file */
2366 if (ringing)
2367 ast_indicate(qe->chan,-1);
2368 else
2369 ast_moh_stop(qe->chan);
2371 ast_verb(3, "Playing periodic announcement\n");
2373 if (qe->parent->randomperiodicannounce) {
2374 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
2375 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
2376 ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
2377 qe->last_periodic_announce_sound = 0;
2380 /* play the announcement */
2381 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
2383 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
2384 res = 0;
2386 /* Resume Music on Hold if the caller is going to stay in the queue */
2387 if (!res) {
2388 if (ringing)
2389 ast_indicate(qe->chan, AST_CONTROL_RINGING);
2390 else
2391 ast_moh_start(qe->chan, qe->moh, NULL);
2394 /* update last_periodic_announce_time */
2395 qe->last_periodic_announce_time = now;
2397 /* Update the current periodic announcement to the next announcement */
2398 if (!qe->parent->randomperiodicannounce) {
2399 qe->last_periodic_announce_sound++;
2402 return res;
2405 /*! \brief Record that a caller gave up on waiting in queue */
2406 static void record_abandoned(struct queue_ent *qe)
2408 ao2_lock(qe->parent);
2409 set_queue_variables(qe);
2410 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
2411 "Queue: %s\r\n"
2412 "Uniqueid: %s\r\n"
2413 "Position: %d\r\n"
2414 "OriginalPosition: %d\r\n"
2415 "HoldTime: %d\r\n",
2416 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
2418 qe->parent->callsabandoned++;
2419 ao2_unlock(qe->parent);
2422 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2423 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
2425 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
2426 if (qe->parent->eventwhencalled)
2427 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
2428 "Queue: %s\r\n"
2429 "Uniqueid: %s\r\n"
2430 "Channel: %s\r\n"
2431 "Member: %s\r\n"
2432 "MemberName: %s\r\n"
2433 "Ringtime: %d\r\n",
2434 qe->parent->name,
2435 qe->chan->uniqueid,
2436 qe->chan->name,
2437 interface,
2438 membername,
2439 rnatime);
2440 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
2441 if (qe->parent->autopause) {
2442 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
2443 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
2444 } else {
2445 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
2448 return;
2451 #define AST_MAX_WATCHERS 256
2452 /*! \brief Wait for a member to answer the call
2454 * \param[in] qe the queue_ent corresponding to the caller in the queue
2455 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
2456 * \param[in] to the amount of time (in milliseconds) to wait for a response
2457 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
2458 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
2459 * \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
2460 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
2462 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2464 const char *queue = qe->parent->name;
2465 struct callattempt *o, *start = NULL, *prev = NULL;
2466 int status;
2467 int numbusies = prebusies;
2468 int numnochan = 0;
2469 int stillgoing = 0;
2470 int orig = *to;
2471 struct ast_frame *f;
2472 struct callattempt *peer = NULL;
2473 struct ast_channel *winner;
2474 struct ast_channel *in = qe->chan;
2475 char on[80] = "";
2476 char membername[80] = "";
2477 long starttime = 0;
2478 long endtime = 0;
2479 #ifdef HAVE_EPOLL
2480 struct callattempt *epollo;
2481 #endif
2483 starttime = (long) time(NULL);
2484 #ifdef HAVE_EPOLL
2485 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
2486 if (epollo->chan)
2487 ast_poll_channel_add(in, epollo->chan);
2489 #endif
2491 while (*to && !peer) {
2492 int numlines, retry, pos = 1;
2493 struct ast_channel *watchers[AST_MAX_WATCHERS];
2494 watchers[0] = in;
2495 start = NULL;
2497 for (retry = 0; retry < 2; retry++) {
2498 numlines = 0;
2499 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
2500 if (o->stillgoing) { /* Keep track of important channels */
2501 stillgoing = 1;
2502 if (o->chan) {
2503 watchers[pos++] = o->chan;
2504 if (!start)
2505 start = o;
2506 else
2507 prev->call_next = o;
2508 prev = o;
2511 numlines++;
2513 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
2514 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
2515 break;
2516 /* On "ringall" strategy we only move to the next penalty level
2517 when *all* ringing phones are done in the current penalty level */
2518 ring_one(qe, outgoing, &numbusies);
2519 /* and retry... */
2521 if (pos == 1 /* not found */) {
2522 if (numlines == (numbusies + numnochan)) {
2523 ast_debug(1, "Everyone is busy at this time\n");
2524 } else {
2525 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
2527 *to = 0;
2528 return NULL;
2530 winner = ast_waitfor_n(watchers, pos, to);
2531 for (o = start; o; o = o->call_next) {
2532 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
2533 if (!peer) {
2534 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
2535 peer = o;
2537 } else if (o->chan && (o->chan == winner)) {
2539 ast_copy_string(on, o->member->interface, sizeof(on));
2540 ast_copy_string(membername, o->member->membername, sizeof(membername));
2542 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
2543 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
2544 numnochan++;
2545 do_hang(o);
2546 winner = NULL;
2547 continue;
2548 } else if (!ast_strlen_zero(o->chan->call_forward)) {
2549 char tmpchan[256];
2550 char *stuff;
2551 char *tech;
2553 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
2554 if ((stuff = strchr(tmpchan, '/'))) {
2555 *stuff++ = '\0';
2556 tech = tmpchan;
2557 } else {
2558 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
2559 stuff = tmpchan;
2560 tech = "Local";
2562 /* Before processing channel, go ahead and check for forwarding */
2563 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
2564 /* Setup parameters */
2565 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
2566 if (!o->chan) {
2567 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
2568 o->stillgoing = 0;
2569 numnochan++;
2570 } else {
2571 ast_channel_inherit_variables(in, o->chan);
2572 ast_channel_datastore_inherit(in, o->chan);
2573 if (o->chan->cid.cid_num)
2574 ast_free(o->chan->cid.cid_num);
2575 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
2577 if (o->chan->cid.cid_name)
2578 ast_free(o->chan->cid.cid_name);
2579 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
2581 ast_string_field_set(o->chan, accountcode, in->accountcode);
2582 o->chan->cdrflags = in->cdrflags;
2584 if (in->cid.cid_ani) {
2585 if (o->chan->cid.cid_ani)
2586 ast_free(o->chan->cid.cid_ani);
2587 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
2589 if (o->chan->cid.cid_rdnis)
2590 ast_free(o->chan->cid.cid_rdnis);
2591 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
2592 if (ast_call(o->chan, tmpchan, 0)) {
2593 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
2594 do_hang(o);
2595 numnochan++;
2598 /* Hangup the original channel now, in case we needed it */
2599 ast_hangup(winner);
2600 continue;
2602 f = ast_read(winner);
2603 if (f) {
2604 if (f->frametype == AST_FRAME_CONTROL) {
2605 switch (f->subclass) {
2606 case AST_CONTROL_ANSWER:
2607 /* This is our guy if someone answered. */
2608 if (!peer) {
2609 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
2610 peer = o;
2612 break;
2613 case AST_CONTROL_BUSY:
2614 ast_verb(3, "%s is busy\n", o->chan->name);
2615 if (in->cdr)
2616 ast_cdr_busy(in->cdr);
2617 do_hang(o);
2618 endtime = (long) time(NULL);
2619 endtime -= starttime;
2620 rna(endtime*1000, qe, on, membername);
2621 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2622 if (qe->parent->timeoutrestart)
2623 *to = orig;
2624 ring_one(qe, outgoing, &numbusies);
2626 numbusies++;
2627 break;
2628 case AST_CONTROL_CONGESTION:
2629 ast_verb(3, "%s is circuit-busy\n", o->chan->name);
2630 if (in->cdr)
2631 ast_cdr_busy(in->cdr);
2632 endtime = (long) time(NULL);
2633 endtime -= starttime;
2634 rna(endtime*1000, qe, on, membername);
2635 do_hang(o);
2636 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2637 if (qe->parent->timeoutrestart)
2638 *to = orig;
2639 ring_one(qe, outgoing, &numbusies);
2641 numbusies++;
2642 break;
2643 case AST_CONTROL_RINGING:
2644 ast_verb(3, "%s is ringing\n", o->chan->name);
2645 break;
2646 case AST_CONTROL_OFFHOOK:
2647 /* Ignore going off hook */
2648 break;
2649 default:
2650 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
2653 ast_frfree(f);
2654 } else {
2655 endtime = (long) time(NULL) - starttime;
2656 rna(endtime * 1000, qe, on, membername);
2657 do_hang(o);
2658 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2659 if (qe->parent->timeoutrestart)
2660 *to = orig;
2661 ring_one(qe, outgoing, &numbusies);
2666 if (winner == in) {
2667 f = ast_read(in);
2668 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
2669 /* Got hung up */
2670 *to = -1;
2671 if (f) {
2672 if (f->data.uint32) {
2673 in->hangupcause = f->data.uint32;
2675 ast_frfree(f);
2677 return NULL;
2679 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
2680 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
2681 *to = 0;
2682 ast_frfree(f);
2683 return NULL;
2685 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
2686 ast_verb(3, "User pressed digit: %c\n", f->subclass);
2687 *to = 0;
2688 *digit = f->subclass;
2689 ast_frfree(f);
2690 return NULL;
2692 ast_frfree(f);
2694 if (!*to) {
2695 for (o = start; o; o = o->call_next)
2696 rna(orig, qe, o->interface, o->member->membername);
2700 #ifdef HAVE_EPOLL
2701 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
2702 if (epollo->chan)
2703 ast_poll_channel_del(in, epollo->chan);
2705 #endif
2707 return peer;
2710 /*!
2711 * \brief Check if we should start attempting to call queue members.
2713 * The behavior of this function is dependent first on whether autofill is enabled
2714 * and second on whether the ring strategy is ringall. If autofill is not enabled,
2715 * then return true if we're the head of the queue. If autofill is enabled, then
2716 * we count the available members and see if the number of available members is enough
2717 * that given our position in the queue, we would theoretically be able to connect to
2718 * one of those available members
2720 static int is_our_turn(struct queue_ent *qe)
2722 struct queue_ent *ch;
2723 struct member *cur;
2724 int avl = 0;
2725 int idx = 0;
2726 int res;
2728 if (!qe->parent->autofill) {
2729 /* Atomically read the parent head -- does not need a lock */
2730 ch = qe->parent->head;
2731 /* If we are now at the top of the head, break out */
2732 if (ch == qe) {
2733 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
2734 res = 1;
2735 } else {
2736 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
2737 res = 0;
2740 } else {
2741 /* This needs a lock. How many members are available to be served? */
2742 ao2_lock(qe->parent);
2744 ch = qe->parent->head;
2746 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2747 ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
2748 avl = 1;
2749 } else {
2750 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
2751 while ((cur = ao2_iterator_next(&mem_iter))) {
2752 switch (cur->status) {
2753 case AST_DEVICE_INUSE:
2754 if (!qe->parent->ringinuse)
2755 break;
2756 /* else fall through */
2757 case AST_DEVICE_NOT_INUSE:
2758 case AST_DEVICE_UNKNOWN:
2759 if (!cur->paused)
2760 avl++;
2761 break;
2763 ao2_ref(cur, -1);
2767 ast_debug(1, "There are %d available members.\n", avl);
2769 while ((idx < avl) && (ch) && (ch != qe)) {
2770 if (!ch->pending)
2771 idx++;
2772 ch = ch->next;
2775 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2776 if (ch && idx < avl) {
2777 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
2778 res = 1;
2779 } else {
2780 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
2781 res = 0;
2784 ao2_unlock(qe->parent);
2787 return res;
2791 * \brief update rules for queues
2793 * Calculate min/max penalties making sure if relative they stay within bounds.
2794 * Update queues penalty and set dialplan vars, goto next list entry.
2796 static void update_qe_rule(struct queue_ent *qe)
2798 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
2799 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
2800 char max_penalty_str[20], min_penalty_str[20];
2801 /* a relative change to the penalty could put it below 0 */
2802 if (max_penalty < 0)
2803 max_penalty = 0;
2804 if (min_penalty < 0)
2805 min_penalty = 0;
2806 if (min_penalty > max_penalty)
2807 min_penalty = max_penalty;
2808 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
2809 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
2810 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
2811 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
2812 qe->max_penalty = max_penalty;
2813 qe->min_penalty = min_penalty;
2814 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
2815 qe->pr = AST_LIST_NEXT(qe->pr, list);
2818 /*! \brief The waiting areas for callers who are not actively calling members
2820 * This function is one large loop. This function will return if a caller
2821 * either exits the queue or it becomes that caller's turn to attempt calling
2822 * queue members. Inside the loop, we service the caller with periodic announcements,
2823 * holdtime announcements, etc. as configured in queues.conf
2825 * \retval 0 if the caller's turn has arrived
2826 * \retval -1 if the caller should exit the queue.
2828 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
2830 int res = 0;
2832 /* This is the holding pen for callers 2 through maxlen */
2833 for (;;) {
2834 enum queue_member_status stat;
2836 if (is_our_turn(qe))
2837 break;
2839 /* If we have timed out, break out */
2840 if (qe->expire && (time(NULL) > qe->expire)) {
2841 *reason = QUEUE_TIMEOUT;
2842 break;
2845 stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
2847 /* leave the queue if no agents, if enabled */
2848 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
2849 *reason = QUEUE_LEAVEEMPTY;
2850 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2851 leave_queue(qe);
2852 break;
2855 /* leave the queue if no reachable agents, if enabled */
2856 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
2857 *reason = QUEUE_LEAVEUNAVAIL;
2858 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2859 leave_queue(qe);
2860 break;
2862 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
2863 *reason = QUEUE_LEAVEUNAVAIL;
2864 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2865 leave_queue(qe);
2866 break;
2869 /* Make a position announcement, if enabled */
2870 if (qe->parent->announcefrequency &&
2871 (res = say_position(qe,ringing)))
2872 break;
2874 /* Make a periodic announcement, if enabled */
2875 if (qe->parent->periodicannouncefrequency &&
2876 (res = say_periodic_announcement(qe,ringing)))
2877 break;
2879 /* see if we need to move to the next penalty level for this queue */
2880 while (qe->pr && ((time(NULL) - qe->start) > qe->pr->time)) {
2881 update_qe_rule(qe);
2884 /* Wait a second before checking again */
2885 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
2886 if (res > 0 && !valid_exit(qe, res))
2887 res = 0;
2888 else
2889 break;
2893 return res;
2897 * \brief update the queue status
2898 * \retval Always 0
2900 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
2902 struct member *mem;
2903 struct call_queue *qtmp;
2904 struct ao2_iterator queue_iter;
2906 if (shared_lastcall) {
2907 queue_iter = ao2_iterator_init(queues, 0);
2908 while ((qtmp = ao2_iterator_next(&queue_iter))) {
2909 ao2_lock(qtmp);
2910 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
2911 time(&mem->lastcall);
2912 mem->calls++;
2913 mem->lastqueue = q;
2914 ao2_ref(mem, -1);
2916 ao2_unlock(qtmp);
2917 ao2_ref(qtmp, -1);
2919 } else {
2920 ao2_lock(q);
2921 time(&member->lastcall);
2922 member->calls++;
2923 member->lastqueue = q;
2924 ao2_unlock(q);
2926 ao2_lock(q);
2927 q->callscompleted++;
2928 if (callcompletedinsl)
2929 q->callscompletedinsl++;
2930 ao2_unlock(q);
2931 return 0;
2934 /*! \brief Calculate the metric of each member in the outgoing callattempts
2936 * A numeric metric is given to each member depending on the ring strategy used
2937 * by the queue. Members with lower metrics will be called before members with
2938 * higher metrics
2939 * \retval -1 if penalties are exceeded
2940 * \retval 0 otherwise
2942 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
2944 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
2945 return -1;
2947 switch (q->strategy) {
2948 case QUEUE_STRATEGY_RINGALL:
2949 /* Everyone equal, except for penalty */
2950 tmp->metric = mem->penalty * 1000000;
2951 break;
2952 case QUEUE_STRATEGY_LINEAR:
2953 if (pos < qe->linpos) {
2954 tmp->metric = 1000 + pos;
2955 } else {
2956 if (pos > qe->linpos)
2957 /* Indicate there is another priority */
2958 qe->linwrapped = 1;
2959 tmp->metric = pos;
2961 tmp->metric += mem->penalty * 1000000;
2962 break;
2963 case QUEUE_STRATEGY_RRMEMORY:
2964 if (pos < q->rrpos) {
2965 tmp->metric = 1000 + pos;
2966 } else {
2967 if (pos > q->rrpos)
2968 /* Indicate there is another priority */
2969 q->wrapped = 1;
2970 tmp->metric = pos;
2972 tmp->metric += mem->penalty * 1000000;
2973 break;
2974 case QUEUE_STRATEGY_RANDOM:
2975 tmp->metric = ast_random() % 1000;
2976 tmp->metric += mem->penalty * 1000000;
2977 break;
2978 case QUEUE_STRATEGY_WRANDOM:
2979 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
2980 break;
2981 case QUEUE_STRATEGY_FEWESTCALLS:
2982 tmp->metric = mem->calls;
2983 tmp->metric += mem->penalty * 1000000;
2984 break;
2985 case QUEUE_STRATEGY_LEASTRECENT:
2986 if (!mem->lastcall)
2987 tmp->metric = 0;
2988 else
2989 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
2990 tmp->metric += mem->penalty * 1000000;
2991 break;
2992 default:
2993 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
2994 break;
2996 return 0;
2999 enum agent_complete_reason {
3000 CALLER,
3001 AGENT,
3002 TRANSFER
3005 /*! \brief Send out AMI message with member call completion status information */
3006 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
3007 const struct ast_channel *peer, const struct member *member, time_t callstart,
3008 char *vars, size_t vars_len, enum agent_complete_reason rsn)
3010 const char *reason = NULL; /* silence dumb compilers */
3012 if (!qe->parent->eventwhencalled)
3013 return;
3015 switch (rsn) {
3016 case CALLER:
3017 reason = "caller";
3018 break;
3019 case AGENT:
3020 reason = "agent";
3021 break;
3022 case TRANSFER:
3023 reason = "transfer";
3024 break;
3027 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
3028 "Queue: %s\r\n"
3029 "Uniqueid: %s\r\n"
3030 "Channel: %s\r\n"
3031 "Member: %s\r\n"
3032 "MemberName: %s\r\n"
3033 "HoldTime: %ld\r\n"
3034 "TalkTime: %ld\r\n"
3035 "Reason: %s\r\n"
3036 "%s",
3037 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3038 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
3039 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
3042 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
3044 * Here is the process of this function
3045 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
3046 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
3047 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
3048 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
3049 * during each iteration, we call calc_metric to determine which members should be rung when.
3050 * 3. Call ring_one to place a call to the appropriate member(s)
3051 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
3052 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
3053 * 6. Start the monitor or mixmonitor if the option is set
3054 * 7. Remove the caller from the queue to allow other callers to advance
3055 * 8. Bridge the call.
3056 * 9. Do any post processing after the call has disconnected.
3058 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
3059 * \param[in] options the options passed as the third parameter to the Queue() application
3060 * \param[in] announceoverride filename to play to user when waiting
3061 * \param[in] url the url passed as the fourth parameter to the Queue() application
3062 * \param[in,out] tries the number of times we have tried calling queue members
3063 * \param[out] noption set if the call to Queue() has the 'n' option set.
3064 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
3065 * \param[in] macro the macro passed as the sixth parameter to the Queue() application
3066 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
3067 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
3069 static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
3071 struct member *cur;
3072 struct callattempt *outgoing = NULL; /* the list of calls we are building */
3073 int to, orig;
3074 char oldexten[AST_MAX_EXTENSION]="";
3075 char oldcontext[AST_MAX_CONTEXT]="";
3076 char queuename[256]="";
3077 char interfacevar[256]="";
3078 struct ast_channel *peer;
3079 struct ast_channel *which;
3080 struct callattempt *lpeer;
3081 struct member *member;
3082 struct ast_app *app;
3083 int res = 0, bridge = 0;
3084 int numbusies = 0;
3085 int x=0;
3086 char *announce = NULL;
3087 char digit = 0;
3088 time_t callstart;
3089 time_t now = time(NULL);
3090 struct ast_bridge_config bridge_config;
3091 char nondataquality = 1;
3092 char *agiexec = NULL;
3093 char *macroexec = NULL;
3094 char *gosubexec = NULL;
3095 int ret = 0;
3096 const char *monitorfilename;
3097 const char *monitor_exec;
3098 const char *monitor_options;
3099 char tmpid[256], tmpid2[256];
3100 char meid[1024], meid2[1024];
3101 char mixmonargs[1512];
3102 struct ast_app *mixmonapp = NULL;
3103 char *p;
3104 char vars[2048];
3105 int forwardsallowed = 1;
3106 int callcompletedinsl;
3107 struct ao2_iterator memi;
3108 struct ast_datastore *datastore;
3110 ast_channel_lock(qe->chan);
3111 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
3112 ast_channel_unlock(qe->chan);
3114 memset(&bridge_config, 0, sizeof(bridge_config));
3115 tmpid[0] = 0;
3116 meid[0] = 0;
3117 time(&now);
3119 for (; options && *options; options++)
3120 switch (*options) {
3121 case 't':
3122 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
3123 break;
3124 case 'T':
3125 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
3126 break;
3127 case 'w':
3128 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
3129 break;
3130 case 'W':
3131 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
3132 break;
3133 case 'd':
3134 nondataquality = 0;
3135 break;
3136 case 'h':
3137 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
3138 break;
3139 case 'H':
3140 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
3141 break;
3142 case 'k':
3143 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
3144 break;
3145 case 'K':
3146 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
3147 break;
3148 case 'n':
3149 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
3150 (*tries)++;
3151 else
3152 *tries = qe->parent->membercount;
3153 *noption = 1;
3154 break;
3155 case 'i':
3156 forwardsallowed = 0;
3157 break;
3158 case 'x':
3159 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
3160 break;
3161 case 'X':
3162 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
3163 break;
3167 /* Hold the lock while we setup the outgoing calls */
3168 if (use_weight)
3169 ao2_lock(queues);
3170 ao2_lock(qe->parent);
3171 ast_debug(1, "%s is trying to call a queue member.\n",
3172 qe->chan->name);
3173 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
3174 if (!ast_strlen_zero(qe->announce))
3175 announce = qe->announce;
3176 if (!ast_strlen_zero(announceoverride))
3177 announce = announceoverride;
3179 memi = ao2_iterator_init(qe->parent->members, 0);
3180 while ((cur = ao2_iterator_next(&memi))) {
3181 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
3182 struct ast_dialed_interface *di;
3183 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
3184 if (!tmp) {
3185 ao2_ref(cur, -1);
3186 ao2_unlock(qe->parent);
3187 if (use_weight)
3188 ao2_unlock(queues);
3189 goto out;
3191 if (!datastore) {
3192 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
3193 ao2_ref(cur, -1);
3194 ao2_unlock(qe->parent);
3195 if (use_weight)
3196 ao2_unlock(queues);
3197 free(tmp);
3198 goto out;
3200 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
3201 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
3202 ao2_ref(cur, -1);
3203 ao2_unlock(&qe->parent);
3204 if (use_weight)
3205 ao2_unlock(queues);
3206 free(tmp);
3207 goto out;
3209 datastore->data = dialed_interfaces;
3210 AST_LIST_HEAD_INIT(dialed_interfaces);
3212 ast_channel_lock(qe->chan);
3213 ast_channel_datastore_add(qe->chan, datastore);
3214 ast_channel_unlock(qe->chan);
3215 } else
3216 dialed_interfaces = datastore->data;
3218 AST_LIST_LOCK(dialed_interfaces);
3219 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
3220 if (!strcasecmp(cur->interface, di->interface)) {
3221 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
3222 di->interface);
3223 break;
3226 AST_LIST_UNLOCK(dialed_interfaces);
3228 if (di) {
3229 free(tmp);
3230 continue;
3233 /* It is always ok to dial a Local interface. We only keep track of
3234 * which "real" interfaces have been dialed. The Local channel will
3235 * inherit this list so that if it ends up dialing a real interface,
3236 * it won't call one that has already been called. */
3237 if (strncasecmp(cur->interface, "Local/", 6)) {
3238 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
3239 ao2_ref(cur, -1);
3240 ao2_unlock(qe->parent);
3241 if (use_weight)
3242 ao2_unlock(queues);
3243 free(tmp);
3244 goto out;
3246 strcpy(di->interface, cur->interface);
3248 AST_LIST_LOCK(dialed_interfaces);
3249 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
3250 AST_LIST_UNLOCK(dialed_interfaces);
3253 tmp->stillgoing = -1;
3254 tmp->member = cur;
3255 tmp->oldstatus = cur->status;
3256 tmp->lastcall = cur->lastcall;
3257 tmp->lastqueue = cur->lastqueue;
3258 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
3259 /* Special case: If we ring everyone, go ahead and ring them, otherwise
3260 just calculate their metric for the appropriate strategy */
3261 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
3262 /* Put them in the list of outgoing thingies... We're ready now.
3263 XXX If we're forcibly removed, these outgoing calls won't get
3264 hung up XXX */
3265 tmp->q_next = outgoing;
3266 outgoing = tmp;
3267 /* If this line is up, don't try anybody else */
3268 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
3269 break;
3270 } else {
3271 ao2_ref(cur, -1);
3272 ast_free(tmp);
3275 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
3276 to = (qe->expire - now) * 1000;
3277 else
3278 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
3279 orig = to;
3280 ++qe->pending;
3281 ao2_unlock(qe->parent);
3282 ring_one(qe, outgoing, &numbusies);
3283 if (use_weight)
3284 ao2_unlock(queues);
3285 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
3286 /* The ast_channel_datastore_remove() function could fail here if the
3287 * datastore was moved to another channel during a masquerade. If this is
3288 * the case, don't free the datastore here because later, when the channel
3289 * to which the datastore was moved hangs up, it will attempt to free this
3290 * datastore again, causing a crash
3292 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
3293 ast_channel_datastore_free(datastore);
3295 ao2_lock(qe->parent);
3296 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
3297 store_next_rr(qe, outgoing);
3299 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
3300 store_next_lin(qe, outgoing);
3302 ao2_unlock(qe->parent);
3303 peer = lpeer ? lpeer->chan : NULL;
3304 if (!peer) {
3305 qe->pending = 0;
3306 if (to) {
3307 /* Must gotten hung up */
3308 res = -1;
3309 } else {
3310 /* User exited by pressing a digit */
3311 res = digit;
3313 if (res == -1)
3314 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
3315 } else { /* peer is valid */
3316 /* Ah ha! Someone answered within the desired timeframe. Of course after this
3317 we will always return with -1 so that it is hung up properly after the
3318 conversation. */
3319 if (!strcmp(qe->chan->tech->type, "Zap"))
3320 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
3321 if (!strcmp(peer->tech->type, "Zap"))
3322 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
3323 /* Update parameters for the queue */
3324 time(&now);
3325 recalc_holdtime(qe, (now - qe->start));
3326 ao2_lock(qe->parent);
3327 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
3328 ao2_unlock(qe->parent);
3329 member = lpeer->member;
3330 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
3331 ao2_ref(member, 1);
3332 hangupcalls(outgoing, peer);
3333 outgoing = NULL;
3334 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
3335 int res2;
3337 res2 = ast_autoservice_start(qe->chan);
3338 if (!res2) {
3339 if (qe->parent->memberdelay) {
3340 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
3341 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
3343 if (!res2 && announce) {
3344 play_file(peer, announce);
3346 if (!res2 && qe->parent->reportholdtime) {
3347 if (!play_file(peer, qe->parent->sound_reporthold)) {
3348 int holdtime, holdtimesecs;
3350 time(&now);
3351 holdtime = abs((now - qe->start) / 60);
3352 holdtimesecs = abs((now - qe->start));
3353 if (holdtime == 1) {
3354 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
3355 play_file(peer, qe->parent->sound_minute);
3356 } else {
3357 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
3358 play_file(peer, qe->parent->sound_minutes);
3360 if (holdtimesecs > 1) {
3361 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
3362 play_file(peer, qe->parent->sound_seconds);
3367 res2 |= ast_autoservice_stop(qe->chan);
3368 if (ast_check_hangup(peer)) {
3369 /* Agent must have hung up */
3370 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
3371 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
3372 if (qe->parent->eventwhencalled)
3373 manager_event(EVENT_FLAG_AGENT, "AgentDump",
3374 "Queue: %s\r\n"
3375 "Uniqueid: %s\r\n"
3376 "Channel: %s\r\n"
3377 "Member: %s\r\n"
3378 "MemberName: %s\r\n"
3379 "%s",
3380 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3381 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3382 ast_hangup(peer);
3383 ao2_ref(member, -1);
3384 goto out;
3385 } else if (res2) {
3386 /* Caller must have hung up just before being connected*/
3387 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
3388 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
3389 record_abandoned(qe);
3390 ast_hangup(peer);
3391 ao2_ref(member, -1);
3392 return -1;
3395 /* Stop music on hold */
3396 if (ringing)
3397 ast_indicate(qe->chan,-1);
3398 else
3399 ast_moh_stop(qe->chan);
3400 /* If appropriate, log that we have a destination channel */
3401 if (qe->chan->cdr)
3402 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
3403 /* Make sure channels are compatible */
3404 res = ast_channel_make_compatible(qe->chan, peer);
3405 if (res < 0) {
3406 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
3407 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
3408 record_abandoned(qe);
3409 ast_hangup(peer);
3410 ao2_ref(member, -1);
3411 return -1;
3414 /* Play announcement to the caller telling it's his turn if defined */
3415 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
3416 if (play_file(qe->chan, qe->parent->sound_callerannounce))
3417 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
3420 ao2_lock(qe->parent);
3421 /* if setinterfacevar is defined, make member variables available to the channel */
3422 /* use pbx_builtin_setvar to set a load of variables with one call */
3423 if (qe->parent->setinterfacevar) {
3424 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
3425 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
3426 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
3429 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
3430 /* use pbx_builtin_setvar to set a load of variables with one call */
3431 if (qe->parent->setqueueentryvar) {
3432 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
3433 (long) time(NULL) - qe->start, qe->opos);
3434 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
3437 /* try to set queue variables if configured to do so*/
3438 set_queue_variables(qe);
3439 ao2_unlock(qe->parent);
3441 /* Begin Monitoring */
3442 if (qe->parent->monfmt && *qe->parent->monfmt) {
3443 if (!qe->parent->montype) {
3444 ast_debug(1, "Starting Monitor as requested.\n");
3445 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
3446 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
3447 which = qe->chan;
3448 else
3449 which = peer;
3450 if (monitorfilename)
3451 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
3452 else if (qe->chan->cdr)
3453 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
3454 else {
3455 /* Last ditch effort -- no CDR, make up something */
3456 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3457 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
3459 } else {
3460 ast_debug(1, "Starting MixMonitor as requested.\n");
3461 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
3462 if (!monitorfilename) {
3463 if (qe->chan->cdr)
3464 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
3465 else
3466 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3467 } else {
3468 const char *m = monitorfilename;
3469 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
3470 switch (*m) {
3471 case '^':
3472 if (*(m + 1) == '{')
3473 *p = '$';
3474 break;
3475 case ',':
3476 *p++ = '\\';
3477 /* Fall through */
3478 default:
3479 *p = *m;
3481 if (*m == '\0')
3482 break;
3484 if (p == tmpid2 + sizeof(tmpid2))
3485 tmpid2[sizeof(tmpid2) - 1] = '\0';
3487 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
3490 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
3491 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
3493 if (monitor_exec) {
3494 const char *m = monitor_exec;
3495 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
3496 switch (*m) {
3497 case '^':
3498 if (*(m + 1) == '{')
3499 *p = '$';
3500 break;
3501 case ',':
3502 *p++ = '\\';
3503 /* Fall through */
3504 default:
3505 *p = *m;
3507 if (*m == '\0')
3508 break;
3510 if (p == meid2 + sizeof(meid2))
3511 meid2[sizeof(meid2) - 1] = '\0';
3513 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
3516 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
3518 mixmonapp = pbx_findapp("MixMonitor");
3520 if (!monitor_options)
3521 monitor_options = "";
3523 if (mixmonapp) {
3524 if (!ast_strlen_zero(monitor_exec))
3525 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
3526 else
3527 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
3529 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
3530 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3531 if (qe->chan->cdr)
3532 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3533 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
3534 if (qe->chan->cdr)
3535 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3537 } else
3538 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3542 /* Drop out of the queue at this point, to prepare for next caller */
3543 leave_queue(qe);
3544 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
3545 ast_debug(1, "app_queue: sendurl=%s.\n", url);
3546 ast_channel_sendurl(peer, url);
3549 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
3550 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
3551 if (!ast_strlen_zero(macro)) {
3552 macroexec = ast_strdupa(macro);
3553 } else {
3554 if (qe->parent->membermacro)
3555 macroexec = ast_strdupa(qe->parent->membermacro);
3558 if (!ast_strlen_zero(macroexec)) {
3559 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
3561 res = ast_autoservice_start(qe->chan);
3562 if (res) {
3563 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3564 res = -1;
3567 app = pbx_findapp("Macro");
3569 if (app) {
3570 res = pbx_exec(qe->chan, app, macroexec);
3571 ast_debug(1, "Macro exited with status %d\n", res);
3572 res = 0;
3573 } else {
3574 ast_log(LOG_ERROR, "Could not find application Macro\n");
3575 res = -1;
3578 if (ast_autoservice_stop(qe->chan) < 0) {
3579 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3580 res = -1;
3584 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
3585 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
3586 if (!ast_strlen_zero(gosub)) {
3587 gosubexec = ast_strdupa(gosub);
3588 } else {
3589 if (qe->parent->membergosub)
3590 gosubexec = ast_strdupa(qe->parent->membergosub);
3593 if (!ast_strlen_zero(gosubexec)) {
3594 if (option_debug)
3595 ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
3597 res = ast_autoservice_start(qe->chan);
3598 if (res) {
3599 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3600 res = -1;
3603 app = pbx_findapp("Gosub");
3605 if (app) {
3606 char *gosub_args, *gosub_argstart;
3608 /* Set where we came from */
3609 ast_copy_string(qe->chan->context, "app_dial_gosub_virtual_context", sizeof(qe->chan->context));
3610 ast_copy_string(qe->chan->exten, "s", sizeof(qe->chan->exten));
3611 qe->chan->priority = 0;
3613 gosub_argstart = strchr(gosubexec, ',');
3614 if (gosub_argstart) {
3615 *gosub_argstart = 0;
3616 asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1);
3617 *gosub_argstart = '|';
3618 } else {
3619 asprintf(&gosub_args, "%s,s,1", gosubexec);
3621 if (gosub_args) {
3622 res = pbx_exec(qe->chan, app, gosub_args);
3623 ast_pbx_run(qe->chan);
3624 free(gosub_args);
3625 if (option_debug)
3626 ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
3627 } else
3628 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
3630 res = 0;
3631 } else {
3632 ast_log(LOG_ERROR, "Could not find application Gosub\n");
3633 res = -1;
3636 if (ast_autoservice_stop(qe->chan) < 0) {
3637 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3638 res = -1;
3642 if (!ast_strlen_zero(agi)) {
3643 ast_debug(1, "app_queue: agi=%s.\n", agi);
3644 app = pbx_findapp("agi");
3645 if (app) {
3646 agiexec = ast_strdupa(agi);
3647 ret = pbx_exec(qe->chan, app, agiexec);
3648 } else
3649 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3651 qe->handled++;
3652 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
3653 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
3654 if (update_cdr && qe->chan->cdr)
3655 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
3656 if (qe->parent->eventwhencalled)
3657 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
3658 "Queue: %s\r\n"
3659 "Uniqueid: %s\r\n"
3660 "Channel: %s\r\n"
3661 "Member: %s\r\n"
3662 "MemberName: %s\r\n"
3663 "Holdtime: %ld\r\n"
3664 "BridgedChannel: %s\r\n"
3665 "Ringtime: %ld\r\n"
3666 "%s",
3667 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3668 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
3669 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3670 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
3671 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
3672 time(&callstart);
3674 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3676 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3677 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
3678 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
3679 (long) (time(NULL) - callstart));
3680 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
3681 } else if (ast_check_hangup(qe->chan)) {
3682 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
3683 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3684 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
3685 } else {
3686 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
3687 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3688 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
3691 if (bridge != AST_PBX_NO_HANGUP_PEER)
3692 ast_hangup(peer);
3693 update_queue(qe->parent, member, callcompletedinsl);
3694 res = bridge ? bridge : 1;
3695 ao2_ref(member, -1);
3697 out:
3698 hangupcalls(outgoing, NULL);
3700 return res;
3703 static int wait_a_bit(struct queue_ent *qe)
3705 /* Don't need to hold the lock while we setup the outgoing calls */
3706 int retrywait = qe->parent->retry * 1000;
3708 int res = ast_waitfordigit(qe->chan, retrywait);
3709 if (res > 0 && !valid_exit(qe, res))
3710 res = 0;
3712 return res;
3715 static struct member *interface_exists(struct call_queue *q, const char *interface)
3717 struct member *mem;
3718 struct ao2_iterator mem_iter;
3720 if (!q)
3721 return NULL;
3723 mem_iter = ao2_iterator_init(q->members, 0);
3724 while ((mem = ao2_iterator_next(&mem_iter))) {
3725 if (!strcasecmp(interface, mem->interface))
3726 return mem;
3727 ao2_ref(mem, -1);
3730 return NULL;
3734 /*! \brief Dump all members in a specific queue to the database
3736 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
3738 static void dump_queue_members(struct call_queue *pm_queue)
3740 struct member *cur_member;
3741 char value[PM_MAX_LEN];
3742 int value_len = 0;
3743 int res;
3744 struct ao2_iterator mem_iter;
3746 memset(value, 0, sizeof(value));
3748 if (!pm_queue)
3749 return;
3751 mem_iter = ao2_iterator_init(pm_queue->members, 0);
3752 while ((cur_member = ao2_iterator_next(&mem_iter))) {
3753 if (!cur_member->dynamic) {
3754 ao2_ref(cur_member, -1);
3755 continue;
3758 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
3759 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
3761 ao2_ref(cur_member, -1);
3763 if (res != strlen(value + value_len)) {
3764 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
3765 break;
3767 value_len += res;
3770 if (value_len && !cur_member) {
3771 if (ast_db_put(pm_family, pm_queue->name, value))
3772 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
3773 } else
3774 /* Delete the entry if the queue is empty or there is an error */
3775 ast_db_del(pm_family, pm_queue->name);
3778 /*! \brief Remove member from queue
3779 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3780 * \retval RES_NOSUCHQUEUE queue does not exist
3781 * \retval RES_OKAY removed member from queue
3782 * \retval RES_EXISTS queue exists but no members
3784 static int remove_from_queue(const char *queuename, const char *interface)
3786 struct call_queue *q, tmpq = {
3787 .name = queuename,
3789 struct member *mem, tmpmem;
3790 int res = RES_NOSUCHQUEUE;
3792 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
3793 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
3794 ao2_lock(q);
3795 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
3796 /* XXX future changes should beware of this assumption!! */
3797 if (!mem->dynamic) {
3798 ao2_ref(mem, -1);
3799 ao2_unlock(q);
3800 return RES_NOT_DYNAMIC;
3802 q->membercount--;
3803 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
3804 "Queue: %s\r\n"
3805 "Location: %s\r\n"
3806 "MemberName: %s\r\n",
3807 q->name, mem->interface, mem->membername);
3808 ao2_unlink(q->members, mem);
3809 remove_from_interfaces(mem->state_interface);
3810 ao2_ref(mem, -1);
3812 if (queue_persistent_members)
3813 dump_queue_members(q);
3815 res = RES_OKAY;
3816 } else {
3817 res = RES_EXISTS;
3819 ao2_unlock(q);
3820 queue_unref(q);
3823 return res;
3826 /*! \brief Add member to queue
3827 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3828 * \retval RES_NOSUCHQUEUE queue does not exist
3829 * \retval RES_OKAY added member from queue
3830 * \retval RES_EXISTS queue exists but no members
3831 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
3833 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
3835 struct call_queue *q;
3836 struct member *new_member, *old_member;
3837 int res = RES_NOSUCHQUEUE;
3839 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
3840 * short-circuits if the queue is already in memory. */
3841 if (!(q = load_realtime_queue(queuename)))
3842 return res;
3844 ao2_lock(queues);
3846 ao2_lock(q);
3847 if ((old_member = interface_exists(q, interface)) == NULL) {
3848 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
3849 add_to_interfaces(new_member->state_interface);
3850 new_member->dynamic = 1;
3851 ao2_link(q->members, new_member);
3852 q->membercount++;
3853 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
3854 "Queue: %s\r\n"
3855 "Location: %s\r\n"
3856 "MemberName: %s\r\n"
3857 "Membership: %s\r\n"
3858 "Penalty: %d\r\n"
3859 "CallsTaken: %d\r\n"
3860 "LastCall: %d\r\n"
3861 "Status: %d\r\n"
3862 "Paused: %d\r\n",
3863 q->name, new_member->interface, new_member->membername,
3864 "dynamic",
3865 new_member->penalty, new_member->calls, (int) new_member->lastcall,
3866 new_member->status, new_member->paused);
3868 ao2_ref(new_member, -1);
3869 new_member = NULL;
3871 if (dump)
3872 dump_queue_members(q);
3874 res = RES_OKAY;
3875 } else {
3876 res = RES_OUTOFMEMORY;
3878 } else {
3879 ao2_ref(old_member, -1);
3880 res = RES_EXISTS;
3882 ao2_unlock(q);
3883 ao2_unlock(queues);
3885 return res;
3888 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
3890 int found = 0;
3891 struct call_queue *q;
3892 struct member *mem;
3893 struct ao2_iterator queue_iter;
3894 int failed;
3896 /* Special event for when all queues are paused - individual events still generated */
3897 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3898 if (ast_strlen_zero(queuename))
3899 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3901 queue_iter = ao2_iterator_init(queues, 0);
3902 while ((q = ao2_iterator_next(&queue_iter))) {
3903 ao2_lock(q);
3904 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3905 if ((mem = interface_exists(q, interface))) {
3906 if (mem->paused == paused) {
3907 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
3910 failed = 0;
3911 if (mem->realtime) {
3912 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
3915 if (failed) {
3916 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
3917 ao2_ref(mem, -1);
3918 continue;
3920 found++;
3921 mem->paused = paused;
3923 if (queue_persistent_members)
3924 dump_queue_members(q);
3926 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
3928 if (!ast_strlen_zero(reason)) {
3929 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3930 "Queue: %s\r\n"
3931 "Location: %s\r\n"
3932 "MemberName: %s\r\n"
3933 "Paused: %d\r\n"
3934 "Reason: %s\r\n",
3935 q->name, mem->interface, mem->membername, paused, reason);
3936 } else {
3937 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3938 "Queue: %s\r\n"
3939 "Location: %s\r\n"
3940 "MemberName: %s\r\n"
3941 "Paused: %d\r\n",
3942 q->name, mem->interface, mem->membername, paused);
3944 ao2_ref(mem, -1);
3948 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
3949 ao2_unlock(q);
3950 queue_unref(q);
3951 break;
3954 ao2_unlock(q);
3955 queue_unref(q);
3958 return found ? RESULT_SUCCESS : RESULT_FAILURE;
3961 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
3962 static int set_member_penalty(char *queuename, char *interface, int penalty)
3964 int foundinterface = 0, foundqueue = 0;
3965 struct call_queue *q;
3966 struct member *mem;
3967 struct ao2_iterator queue_iter;
3969 if (penalty < 0) {
3970 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
3971 return RESULT_FAILURE;
3974 queue_iter = ao2_iterator_init(queues, 0);
3975 while ((q = ao2_iterator_next(&queue_iter))) {
3976 ao2_lock(q);
3977 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3978 foundqueue++;
3979 if ((mem = interface_exists(q, interface))) {
3980 foundinterface++;
3981 mem->penalty = penalty;
3983 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
3984 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
3985 "Queue: %s\r\n"
3986 "Location: %s\r\n"
3987 "Penalty: %d\r\n",
3988 q->name, mem->interface, penalty);
3992 ao2_unlock(q);
3993 queue_unref(q);
3996 if (foundinterface) {
3997 return RESULT_SUCCESS;
3998 } else if (!foundqueue) {
3999 ast_log (LOG_ERROR, "Invalid queuename\n");
4000 } else {
4001 ast_log (LOG_ERROR, "Invalid interface\n");
4004 return RESULT_FAILURE;
4007 /* \brief Gets members penalty.
4008 * \return Return the members penalty or RESULT_FAILURE on error.
4010 static int get_member_penalty(char *queuename, char *interface)
4012 int foundqueue = 0, penalty;
4013 struct call_queue *q, tmpq = {
4014 .name = queuename,
4016 struct member *mem;
4018 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4019 foundqueue = 1;
4020 ao2_lock(q);
4021 if ((mem = interface_exists(q, interface))) {
4022 penalty = mem->penalty;
4023 ao2_unlock(q);
4024 queue_unref(q);
4025 return penalty;
4027 ao2_unlock(q);
4028 queue_unref(q);
4031 /* some useful debuging */
4032 if (foundqueue)
4033 ast_log (LOG_ERROR, "Invalid queuename\n");
4034 else
4035 ast_log (LOG_ERROR, "Invalid interface\n");
4037 return RESULT_FAILURE;
4040 /*! \brief Reload dynamic queue members persisted into the astdb */
4041 static void reload_queue_members(void)
4043 char *cur_ptr;
4044 const char *queue_name;
4045 char *member;
4046 char *interface;
4047 char *membername = NULL;
4048 char *state_interface;
4049 char *penalty_tok;
4050 int penalty = 0;
4051 char *paused_tok;
4052 int paused = 0;
4053 struct ast_db_entry *db_tree;
4054 struct ast_db_entry *entry;
4055 struct call_queue *cur_queue;
4056 char queue_data[PM_MAX_LEN];
4058 ao2_lock(queues);
4060 /* Each key in 'pm_family' is the name of a queue */
4061 db_tree = ast_db_gettree(pm_family, NULL);
4062 for (entry = db_tree; entry; entry = entry->next) {
4064 queue_name = entry->key + strlen(pm_family) + 2;
4067 struct call_queue tmpq = {
4068 .name = queue_name,
4070 cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
4073 if (!cur_queue)
4074 cur_queue = load_realtime_queue(queue_name);
4076 if (!cur_queue) {
4077 /* If the queue no longer exists, remove it from the
4078 * database */
4079 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
4080 ast_db_del(pm_family, queue_name);
4081 continue;
4084 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
4085 queue_unref(cur_queue);
4086 continue;
4089 cur_ptr = queue_data;
4090 while ((member = strsep(&cur_ptr, ",|"))) {
4091 if (ast_strlen_zero(member))
4092 continue;
4094 interface = strsep(&member, ";");
4095 penalty_tok = strsep(&member, ";");
4096 paused_tok = strsep(&member, ";");
4097 membername = strsep(&member, ";");
4098 state_interface = strsep(&member, ";");
4100 if (!penalty_tok) {
4101 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
4102 break;
4104 penalty = strtol(penalty_tok, NULL, 10);
4105 if (errno == ERANGE) {
4106 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
4107 break;
4110 if (!paused_tok) {
4111 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
4112 break;
4114 paused = strtol(paused_tok, NULL, 10);
4115 if ((errno == ERANGE) || paused < 0 || paused > 1) {
4116 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
4117 break;
4120 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
4122 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
4123 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
4124 break;
4127 queue_unref(cur_queue);
4130 ao2_unlock(queues);
4131 if (db_tree) {
4132 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
4133 ast_db_freetree(db_tree);
4137 /*! \brief PauseQueueMember application */
4138 static int pqm_exec(struct ast_channel *chan, void *data)
4140 char *parse;
4141 AST_DECLARE_APP_ARGS(args,
4142 AST_APP_ARG(queuename);
4143 AST_APP_ARG(interface);
4144 AST_APP_ARG(options);
4145 AST_APP_ARG(reason);
4148 if (ast_strlen_zero(data)) {
4149 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
4150 return -1;
4153 parse = ast_strdupa(data);
4155 AST_STANDARD_APP_ARGS(args, parse);
4157 if (ast_strlen_zero(args.interface)) {
4158 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4159 return -1;
4162 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
4163 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
4164 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
4165 return 0;
4168 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
4170 return 0;
4173 /*! \brief UnPauseQueueMember application */
4174 static int upqm_exec(struct ast_channel *chan, void *data)
4176 char *parse;
4177 AST_DECLARE_APP_ARGS(args,
4178 AST_APP_ARG(queuename);
4179 AST_APP_ARG(interface);
4180 AST_APP_ARG(options);
4181 AST_APP_ARG(reason);
4184 if (ast_strlen_zero(data)) {
4185 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
4186 return -1;
4189 parse = ast_strdupa(data);
4191 AST_STANDARD_APP_ARGS(args, parse);
4193 if (ast_strlen_zero(args.interface)) {
4194 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4195 return -1;
4198 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
4199 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
4200 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
4201 return 0;
4204 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
4206 return 0;
4209 /*! \brief RemoveQueueMember application */
4210 static int rqm_exec(struct ast_channel *chan, void *data)
4212 int res=-1;
4213 char *parse, *temppos = NULL;
4214 AST_DECLARE_APP_ARGS(args,
4215 AST_APP_ARG(queuename);
4216 AST_APP_ARG(interface);
4217 AST_APP_ARG(options);
4221 if (ast_strlen_zero(data)) {
4222 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
4223 return -1;
4226 parse = ast_strdupa(data);
4228 AST_STANDARD_APP_ARGS(args, parse);
4230 if (ast_strlen_zero(args.interface)) {
4231 args.interface = ast_strdupa(chan->name);
4232 temppos = strrchr(args.interface, '-');
4233 if (temppos)
4234 *temppos = '\0';
4237 switch (remove_from_queue(args.queuename, args.interface)) {
4238 case RES_OKAY:
4239 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
4240 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
4241 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
4242 res = 0;
4243 break;
4244 case RES_EXISTS:
4245 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
4246 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
4247 res = 0;
4248 break;
4249 case RES_NOSUCHQUEUE:
4250 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
4251 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
4252 res = 0;
4253 break;
4254 case RES_NOT_DYNAMIC:
4255 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
4256 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
4257 res = 0;
4258 break;
4261 return res;
4264 /*! \brief AddQueueMember application */
4265 static int aqm_exec(struct ast_channel *chan, void *data)
4267 int res=-1;
4268 char *parse, *temppos = NULL;
4269 AST_DECLARE_APP_ARGS(args,
4270 AST_APP_ARG(queuename);
4271 AST_APP_ARG(interface);
4272 AST_APP_ARG(penalty);
4273 AST_APP_ARG(options);
4274 AST_APP_ARG(membername);
4275 AST_APP_ARG(state_interface);
4277 int penalty = 0;
4279 if (ast_strlen_zero(data)) {
4280 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
4281 return -1;
4284 parse = ast_strdupa(data);
4286 AST_STANDARD_APP_ARGS(args, parse);
4288 if (ast_strlen_zero(args.interface)) {
4289 args.interface = ast_strdupa(chan->name);
4290 temppos = strrchr(args.interface, '-');
4291 if (temppos)
4292 *temppos = '\0';
4295 if (!ast_strlen_zero(args.penalty)) {
4296 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
4297 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
4298 penalty = 0;
4302 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
4303 case RES_OKAY:
4304 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
4305 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
4306 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
4307 res = 0;
4308 break;
4309 case RES_EXISTS:
4310 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
4311 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
4312 res = 0;
4313 break;
4314 case RES_NOSUCHQUEUE:
4315 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
4316 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
4317 res = 0;
4318 break;
4319 case RES_OUTOFMEMORY:
4320 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
4321 break;
4324 return res;
4327 /*! \brief QueueLog application */
4328 static int ql_exec(struct ast_channel *chan, void *data)
4330 char *parse;
4332 AST_DECLARE_APP_ARGS(args,
4333 AST_APP_ARG(queuename);
4334 AST_APP_ARG(uniqueid);
4335 AST_APP_ARG(membername);
4336 AST_APP_ARG(event);
4337 AST_APP_ARG(params);
4340 if (ast_strlen_zero(data)) {
4341 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
4342 return -1;
4345 parse = ast_strdupa(data);
4347 AST_STANDARD_APP_ARGS(args, parse);
4349 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
4350 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
4351 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
4352 return -1;
4355 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
4356 "%s", args.params ? args.params : "");
4358 return 0;
4361 /*! \brief Copy rule from global list into specified queue */
4362 static void copy_rules(struct queue_ent *qe, const char *rulename)
4364 struct penalty_rule *pr_iter;
4365 struct rule_list *rl_iter;
4366 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
4367 AST_LIST_LOCK(&rule_lists);
4368 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
4369 if (!strcasecmp(rl_iter->name, tmp))
4370 break;
4372 if (rl_iter) {
4373 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
4374 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
4375 if (!new_pr) {
4376 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
4377 AST_LIST_UNLOCK(&rule_lists);
4378 break;
4380 new_pr->time = pr_iter->time;
4381 new_pr->max_value = pr_iter->max_value;
4382 new_pr->min_value = pr_iter->min_value;
4383 new_pr->max_relative = pr_iter->max_relative;
4384 new_pr->min_relative = pr_iter->min_relative;
4385 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
4388 AST_LIST_UNLOCK(&rule_lists);
4391 /*!\brief The starting point for all queue calls
4393 * The process involved here is to
4394 * 1. Parse the options specified in the call to Queue()
4395 * 2. Join the queue
4396 * 3. Wait in a loop until it is our turn to try calling a queue member
4397 * 4. Attempt to call a queue member
4398 * 5. If 4. did not result in a bridged call, then check for between
4399 * call options such as periodic announcements etc.
4400 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
4401 * exit the queue.
4403 static int queue_exec(struct ast_channel *chan, void *data)
4405 int res=-1;
4406 int ringing=0;
4407 const char *user_priority;
4408 const char *max_penalty_str;
4409 const char *min_penalty_str;
4410 int prio;
4411 int qcontinue = 0;
4412 int max_penalty, min_penalty;
4413 enum queue_result reason = QUEUE_UNKNOWN;
4414 /* whether to exit Queue application after the timeout hits */
4415 int tries = 0;
4416 int noption = 0;
4417 char *parse;
4418 int makeannouncement = 0;
4419 AST_DECLARE_APP_ARGS(args,
4420 AST_APP_ARG(queuename);
4421 AST_APP_ARG(options);
4422 AST_APP_ARG(url);
4423 AST_APP_ARG(announceoverride);
4424 AST_APP_ARG(queuetimeoutstr);
4425 AST_APP_ARG(agi);
4426 AST_APP_ARG(macro);
4427 AST_APP_ARG(gosub);
4428 AST_APP_ARG(rule);
4430 /* Our queue entry */
4431 struct queue_ent qe;
4433 if (ast_strlen_zero(data)) {
4434 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
4435 return -1;
4438 parse = ast_strdupa(data);
4439 AST_STANDARD_APP_ARGS(args, parse);
4441 /* Setup our queue entry */
4442 memset(&qe, 0, sizeof(qe));
4443 qe.start = time(NULL);
4445 /* set the expire time based on the supplied timeout; */
4446 if (!ast_strlen_zero(args.queuetimeoutstr))
4447 qe.expire = qe.start + atoi(args.queuetimeoutstr);
4448 else
4449 qe.expire = 0;
4451 /* Get the priority from the variable ${QUEUE_PRIO} */
4452 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
4453 if (user_priority) {
4454 if (sscanf(user_priority, "%d", &prio) == 1) {
4455 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
4456 } else {
4457 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
4458 user_priority, chan->name);
4459 prio = 0;
4461 } else {
4462 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
4463 prio = 0;
4466 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
4468 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
4469 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
4470 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
4471 } else {
4472 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
4473 max_penalty_str, chan->name);
4474 max_penalty = 0;
4476 } else {
4477 max_penalty = 0;
4480 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
4481 if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
4482 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
4483 } else {
4484 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
4485 min_penalty_str, chan->name);
4486 min_penalty = 0;
4488 } else {
4489 min_penalty = 0;
4492 if (args.options && (strchr(args.options, 'r')))
4493 ringing = 1;
4495 if (args.options && (strchr(args.options, 'c')))
4496 qcontinue = 1;
4498 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
4499 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
4501 qe.chan = chan;
4502 qe.prio = prio;
4503 qe.max_penalty = max_penalty;
4504 qe.min_penalty = min_penalty;
4505 qe.last_pos_said = 0;
4506 qe.last_pos = 0;
4507 qe.last_periodic_announce_time = time(NULL);
4508 qe.last_periodic_announce_sound = 0;
4509 qe.valid_digits = 0;
4510 if (join_queue(args.queuename, &qe, &reason)) {
4511 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
4512 set_queue_result(chan, reason);
4513 return 0;
4515 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
4516 S_OR(chan->cid.cid_num, ""));
4517 copy_rules(&qe, args.rule);
4518 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
4519 check_turns:
4520 if (ringing) {
4521 ast_indicate(chan, AST_CONTROL_RINGING);
4522 } else {
4523 ast_moh_start(chan, qe.moh, NULL);
4526 /* This is the wait loop for callers 2 through maxlen */
4527 res = wait_our_turn(&qe, ringing, &reason);
4528 if (res) {
4529 goto stop;
4532 makeannouncement = 0;
4534 for (;;) {
4535 /* This is the wait loop for the head caller*/
4536 /* To exit, they may get their call answered; */
4537 /* they may dial a digit from the queue context; */
4538 /* or, they may timeout. */
4540 enum queue_member_status stat;
4542 /* Leave if we have exceeded our queuetimeout */
4543 if (qe.expire && (time(NULL) > qe.expire)) {
4544 record_abandoned(&qe);
4545 reason = QUEUE_TIMEOUT;
4546 res = 0;
4547 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
4548 qe.pos, qe.opos, (long) time(NULL) - qe.start);
4549 break;
4552 if (makeannouncement) {
4553 /* Make a position announcement, if enabled */
4554 if (qe.parent->announcefrequency)
4555 if ((res = say_position(&qe,ringing)))
4556 goto stop;
4558 makeannouncement = 1;
4560 /* Make a periodic announcement, if enabled */
4561 if (qe.parent->periodicannouncefrequency)
4562 if ((res = say_periodic_announcement(&qe,ringing)))
4563 goto stop;
4565 /* see if we need to move to the next penalty level for this queue */
4566 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
4567 update_qe_rule(&qe);
4570 /* Try calling all queue members for 'timeout' seconds */
4571 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
4572 if (res) {
4573 goto stop;
4576 stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
4578 /* exit after 'timeout' cycle if 'n' option enabled */
4579 if (noption && tries >= qe.parent->membercount) {
4580 ast_verb(3, "Exiting on time-out cycle\n");
4581 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
4582 record_abandoned(&qe);
4583 reason = QUEUE_TIMEOUT;
4584 res = 0;
4585 break;
4588 /* leave the queue if no agents, if enabled */
4589 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
4590 record_abandoned(&qe);
4591 reason = QUEUE_LEAVEEMPTY;
4592 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4593 res = 0;
4594 break;
4597 /* leave the queue if no reachable agents, if enabled */
4598 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
4599 record_abandoned(&qe);
4600 reason = QUEUE_LEAVEUNAVAIL;
4601 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4602 res = 0;
4603 break;
4605 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
4606 record_abandoned(&qe);
4607 reason = QUEUE_LEAVEUNAVAIL;
4608 res = 0;
4609 break;
4612 /* Leave if we have exceeded our queuetimeout */
4613 if (qe.expire && (time(NULL) > qe.expire)) {
4614 record_abandoned(&qe);
4615 reason = QUEUE_TIMEOUT;
4616 res = 0;
4617 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
4618 break;
4621 /* If using dynamic realtime members, we should regenerate the member list for this queue */
4622 update_realtime_members(qe.parent);
4624 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
4625 res = wait_a_bit(&qe);
4626 if (res)
4627 goto stop;
4629 /* Since this is a priority queue and
4630 * it is not sure that we are still at the head
4631 * of the queue, go and check for our turn again.
4633 if (!is_our_turn(&qe)) {
4634 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
4635 goto check_turns;
4639 stop:
4640 if (res) {
4641 if (res < 0) {
4642 if (!qe.handled) {
4643 record_abandoned(&qe);
4644 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
4645 "%d|%d|%ld", qe.pos, qe.opos,
4646 (long) time(NULL) - qe.start);
4648 res = -1;
4649 } else if (qe.valid_digits) {
4650 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
4651 "%s|%d", qe.digits, qe.pos);
4655 /* Don't allow return code > 0 */
4656 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
4657 res = 0;
4658 if (ringing) {
4659 ast_indicate(chan, -1);
4660 } else {
4661 ast_moh_stop(chan);
4663 ast_stopstream(chan);
4666 set_queue_variables(&qe);
4668 leave_queue(&qe);
4669 if (reason != QUEUE_UNKNOWN)
4670 set_queue_result(chan, reason);
4672 return res;
4676 * \brief create interface var with all queue details.
4677 * \retval 0 on success
4678 * \retval -1 on error
4680 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4682 int res = -1;
4683 struct call_queue *q, tmpq = {
4684 .name = data,
4687 char interfacevar[256] = "";
4688 float sl = 0;
4690 if (ast_strlen_zero(data)) {
4691 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4692 return -1;
4695 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4696 ao2_lock(q);
4697 if (q->setqueuevar) {
4698 sl = 0;
4699 res = 0;
4701 if (q->callscompleted > 0) {
4702 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4705 snprintf(interfacevar, sizeof(interfacevar),
4706 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
4707 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
4709 pbx_builtin_setvar_multiple(chan, interfacevar);
4712 ao2_unlock(q);
4713 queue_unref(q);
4714 } else {
4715 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4718 snprintf(buf, len, "%d", res);
4720 return 0;
4723 /*!
4724 * \brief Get number either busy / free or total members of a specific queue
4725 * \retval number of members (busy / free / total)
4726 * \retval -1 on error
4728 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4730 int count = 0;
4731 struct member *m;
4732 struct ao2_iterator mem_iter;
4733 struct call_queue *q;
4734 char *option;
4736 if (ast_strlen_zero(data)) {
4737 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4738 return -1;
4741 if ((option = strchr(data, ',')))
4742 *option++ = '\0';
4743 else
4744 option = "logged";
4745 if ((q = load_realtime_queue(data))) {
4746 ao2_lock(q);
4747 if (!strcasecmp(option, "logged")) {
4748 mem_iter = ao2_iterator_init(q->members, 0);
4749 while ((m = ao2_iterator_next(&mem_iter))) {
4750 /* Count the agents who are logged in and presently answering calls */
4751 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4752 count++;
4754 ao2_ref(m, -1);
4756 } else if (!strcasecmp(option, "free")) {
4757 mem_iter = ao2_iterator_init(q->members, 0);
4758 while ((m = ao2_iterator_next(&mem_iter))) {
4759 /* Count the agents who are logged in and presently answering calls */
4760 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
4761 count++;
4763 ao2_ref(m, -1);
4765 } else /* must be "count" */
4766 count = q->membercount;
4767 ao2_unlock(q);
4768 queue_unref(q);
4769 } else
4770 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4772 snprintf(buf, len, "%d", count);
4774 return 0;
4777 /*!
4778 * \brief Get the total number of members in a specific queue (Deprecated)
4779 * \retval number of members
4780 * \retval -1 on error
4782 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4784 int count = 0;
4785 struct member *m;
4786 struct call_queue *q;
4787 struct ao2_iterator mem_iter;
4788 static int depflag = 1;
4790 if (depflag) {
4791 depflag = 0;
4792 ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
4795 if (ast_strlen_zero(data)) {
4796 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4797 return -1;
4800 if ((q = load_realtime_queue(data))) {
4801 ao2_lock(q);
4802 mem_iter = ao2_iterator_init(q->members, 0);
4803 while ((m = ao2_iterator_next(&mem_iter))) {
4804 /* Count the agents who are logged in and presently answering calls */
4805 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4806 count++;
4808 ao2_ref(m, -1);
4810 ao2_unlock(q);
4811 queue_unref(q);
4812 } else
4813 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4815 snprintf(buf, len, "%d", count);
4817 return 0;
4820 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
4821 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4823 int count = 0;
4824 struct call_queue *q, tmpq = {
4825 .name = data,
4827 struct ast_variable *var = NULL;
4829 buf[0] = '\0';
4831 if (ast_strlen_zero(data)) {
4832 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
4833 return -1;
4836 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4837 ao2_lock(q);
4838 count = q->count;
4839 ao2_unlock(q);
4840 queue_unref(q);
4841 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
4842 /* if the queue is realtime but was not found in memory, this
4843 * means that the queue had been deleted from memory since it was
4844 * "dead." This means it has a 0 waiting count
4846 count = 0;
4847 ast_variables_destroy(var);
4848 } else
4849 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4851 snprintf(buf, len, "%d", count);
4853 return 0;
4856 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
4857 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4859 struct call_queue *q, tmpq = {
4860 .name = data,
4862 struct member *m;
4864 /* Ensure an otherwise empty list doesn't return garbage */
4865 buf[0] = '\0';
4867 if (ast_strlen_zero(data)) {
4868 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
4869 return -1;
4872 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4873 int buflen = 0, count = 0;
4874 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
4876 ao2_lock(q);
4877 while ((m = ao2_iterator_next(&mem_iter))) {
4878 /* strcat() is always faster than printf() */
4879 if (count++) {
4880 strncat(buf + buflen, ",", len - buflen - 1);
4881 buflen++;
4883 strncat(buf + buflen, m->membername, len - buflen - 1);
4884 buflen += strlen(m->membername);
4885 /* Safeguard against overflow (negative length) */
4886 if (buflen >= len - 2) {
4887 ao2_ref(m, -1);
4888 ast_log(LOG_WARNING, "Truncating list\n");
4889 break;
4891 ao2_ref(m, -1);
4893 ao2_unlock(q);
4894 queue_unref(q);
4895 } else
4896 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4898 /* We should already be terminated, but let's make sure. */
4899 buf[len - 1] = '\0';
4901 return 0;
4904 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
4905 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4907 int penalty;
4908 AST_DECLARE_APP_ARGS(args,
4909 AST_APP_ARG(queuename);
4910 AST_APP_ARG(interface);
4912 /* Make sure the returned value on error is NULL. */
4913 buf[0] = '\0';
4915 if (ast_strlen_zero(data)) {
4916 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4917 return -1;
4920 AST_STANDARD_APP_ARGS(args, data);
4922 if (args.argc < 2) {
4923 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4924 return -1;
4927 penalty = get_member_penalty (args.queuename, args.interface);
4929 if (penalty >= 0) /* remember that buf is already '\0' */
4930 snprintf (buf, len, "%d", penalty);
4932 return 0;
4935 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
4936 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
4938 int penalty;
4939 AST_DECLARE_APP_ARGS(args,
4940 AST_APP_ARG(queuename);
4941 AST_APP_ARG(interface);
4944 if (ast_strlen_zero(data)) {
4945 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4946 return -1;
4949 AST_STANDARD_APP_ARGS(args, data);
4951 if (args.argc < 2) {
4952 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4953 return -1;
4956 penalty = atoi(value);
4958 if (ast_strlen_zero(args.interface)) {
4959 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
4960 return -1;
4963 /* if queuename = NULL then penalty will be set for interface in all the queues. */
4964 if (set_member_penalty(args.queuename, args.interface, penalty)) {
4965 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
4966 return -1;
4969 return 0;
4972 static struct ast_custom_function queuevar_function = {
4973 .name = "QUEUE_VARIABLES",
4974 .synopsis = "Return Queue information in variables",
4975 .syntax = "QUEUE_VARIABLES(<queuename>)",
4976 .desc =
4977 "Makes the following queue variables available.\n"
4978 "QUEUEMAX maxmimum number of calls allowed\n"
4979 "QUEUESTRATEGY the strategy of the queue\n"
4980 "QUEUECALLS number of calls currently in the queue\n"
4981 "QUEUEHOLDTIME current average hold time\n"
4982 "QUEUECOMPLETED number of completed calls for the queue\n"
4983 "QUEUEABANDONED number of abandoned calls\n"
4984 "QUEUESRVLEVEL queue service level\n"
4985 "QUEUESRVLEVELPERF current service level performance\n"
4986 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
4987 .read = queue_function_var,
4990 static struct ast_custom_function queuemembercount_function = {
4991 .name = "QUEUE_MEMBER",
4992 .synopsis = "Count number of members answering a queue",
4993 .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
4994 .desc =
4995 "Returns the number of members currently associated with the specified queue.\n"
4996 "One of three options may be passed to determine the count returned:\n"
4997 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
4998 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
4999 "\"count\" - Returns the total number of members for the specified queue\n",
5000 .read = queue_function_qac,
5003 static struct ast_custom_function queuemembercount_dep = {
5004 .name = "QUEUE_MEMBER_COUNT",
5005 .synopsis = "Count number of members answering a queue",
5006 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
5007 .desc =
5008 "Returns the number of members currently associated with the specified queue.\n\n"
5009 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
5010 .read = queue_function_qac_dep,
5013 static struct ast_custom_function queuewaitingcount_function = {
5014 .name = "QUEUE_WAITING_COUNT",
5015 .synopsis = "Count number of calls currently waiting in a queue",
5016 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
5017 .desc =
5018 "Returns the number of callers currently waiting in the specified queue.\n",
5019 .read = queue_function_queuewaitingcount,
5022 static struct ast_custom_function queuememberlist_function = {
5023 .name = "QUEUE_MEMBER_LIST",
5024 .synopsis = "Returns a list of interfaces on a queue",
5025 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
5026 .desc =
5027 "Returns a comma-separated list of members associated with the specified queue.\n",
5028 .read = queue_function_queuememberlist,
5031 static struct ast_custom_function queuememberpenalty_function = {
5032 .name = "QUEUE_MEMBER_PENALTY",
5033 .synopsis = "Gets or sets queue members penalty.",
5034 .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
5035 .desc =
5036 "Gets or sets queue members penalty\n",
5037 .read = queue_function_memberpenalty_read,
5038 .write = queue_function_memberpenalty_write,
5041 static int reload_queue_rules(int reload)
5043 struct ast_config *cfg;
5044 struct rule_list *rl_iter, *new_rl;
5045 struct penalty_rule *pr_iter;
5046 char *rulecat = NULL;
5047 struct ast_variable *rulevar = NULL;
5048 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5050 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
5051 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
5052 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
5053 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
5054 return AST_MODULE_LOAD_SUCCESS;
5055 } else {
5056 AST_LIST_LOCK(&rule_lists);
5057 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
5058 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
5059 ast_free(pr_iter);
5060 ast_free(rl_iter);
5062 while ((rulecat = ast_category_browse(cfg, rulecat))) {
5063 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
5064 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
5065 AST_LIST_UNLOCK(&rule_lists);
5066 return AST_MODULE_LOAD_FAILURE;
5067 } else {
5068 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
5069 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
5070 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
5071 if(!strcasecmp(rulevar->name, "penaltychange"))
5072 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
5073 else
5074 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
5077 AST_LIST_UNLOCK(&rule_lists);
5080 ast_config_destroy(cfg);
5082 return AST_MODULE_LOAD_SUCCESS;
5086 static int reload_queues(int reload)
5088 struct call_queue *q;
5089 struct ast_config *cfg;
5090 char *cat, *tmp;
5091 struct ast_variable *var;
5092 struct member *cur, *newm;
5093 struct ao2_iterator mem_iter;
5094 int new;
5095 const char *general_val = NULL;
5096 char parse[80];
5097 char *interface, *state_interface;
5098 char *membername = NULL;
5099 int penalty;
5100 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5101 struct ao2_iterator queue_iter;
5102 AST_DECLARE_APP_ARGS(args,
5103 AST_APP_ARG(interface);
5104 AST_APP_ARG(penalty);
5105 AST_APP_ARG(membername);
5106 AST_APP_ARG(state_interface);
5109 /*First things first. Let's load queuerules.conf*/
5110 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
5111 return AST_MODULE_LOAD_FAILURE;
5113 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
5114 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
5115 return 0;
5116 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
5117 return 0;
5118 ao2_lock(queues);
5119 use_weight=0;
5120 /* Mark all queues as dead for the moment */
5121 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
5122 while ((q = ao2_iterator_next(&queue_iter))) {
5123 if (!q->realtime) {
5124 q->dead = 1;
5125 q->found = 0;
5127 queue_unref(q);
5130 /* Chug through config file */
5131 cat = NULL;
5132 while ((cat = ast_category_browse(cfg, cat)) ) {
5133 if (!strcasecmp(cat, "general")) {
5134 /* Initialize global settings */
5135 queue_keep_stats = 0;
5136 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
5137 queue_keep_stats = ast_true(general_val);
5138 queue_persistent_members = 0;
5139 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
5140 queue_persistent_members = ast_true(general_val);
5141 autofill_default = 0;
5142 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
5143 autofill_default = ast_true(general_val);
5144 montype_default = 0;
5145 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
5146 if (!strcasecmp(general_val, "mixmonitor"))
5147 montype_default = 1;
5149 update_cdr = 0;
5150 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
5151 update_cdr = ast_true(general_val);
5152 shared_lastcall = 0;
5153 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
5154 shared_lastcall = ast_true(general_val);
5155 } else { /* Define queue */
5156 /* Look for an existing one */
5157 struct call_queue tmpq = {
5158 .name = cat,
5160 if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
5161 /* Make one then */
5162 if (!(q = alloc_queue(cat))) {
5163 /* TODO: Handle memory allocation failure */
5165 new = 1;
5166 } else
5167 new = 0;
5168 if (q) {
5169 const char *tmpvar = NULL;
5170 if (!new)
5171 ao2_lock(q);
5172 /* Check if a queue with this name already exists */
5173 if (q->found) {
5174 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
5175 if (!new) {
5176 ao2_unlock(q);
5177 queue_unref(q);
5179 continue;
5181 /* Due to the fact that the "linear" strategy will have a different allocation
5182 * scheme for queue members, we must devise the queue's strategy before other initializations
5184 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
5185 q->strategy = strat2int(tmpvar);
5186 if (q->strategy < 0) {
5187 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
5188 tmpvar, q->name);
5189 q->strategy = QUEUE_STRATEGY_RINGALL;
5191 } else
5192 q->strategy = QUEUE_STRATEGY_RINGALL;
5193 /* Re-initialize the queue, and clear statistics */
5194 init_queue(q);
5195 if (!queue_keep_stats)
5196 clear_queue(q);
5197 mem_iter = ao2_iterator_init(q->members, 0);
5198 while ((cur = ao2_iterator_next(&mem_iter))) {
5199 if (!cur->dynamic) {
5200 cur->delme = 1;
5202 ao2_ref(cur, -1);
5204 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
5205 if (!strcasecmp(var->name, "member")) {
5206 struct member tmpmem;
5207 membername = NULL;
5209 /* Add a new member */
5210 ast_copy_string(parse, var->value, sizeof(parse));
5212 AST_STANDARD_APP_ARGS(args, parse);
5214 interface = args.interface;
5215 if (!ast_strlen_zero(args.penalty)) {
5216 tmp = args.penalty;
5217 while (*tmp && *tmp < 33) tmp++;
5218 penalty = atoi(tmp);
5219 if (penalty < 0) {
5220 penalty = 0;
5222 } else
5223 penalty = 0;
5225 if (!ast_strlen_zero(args.membername)) {
5226 membername = args.membername;
5227 while (*membername && *membername < 33) membername++;
5230 if (!ast_strlen_zero(args.state_interface)) {
5231 state_interface = args.state_interface;
5232 while (*state_interface && *state_interface < 33) state_interface++;
5233 } else
5234 state_interface = interface;
5236 /* Find the old position in the list */
5237 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
5238 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
5239 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
5240 if (cur && strcasecmp(cur->state_interface, state_interface)) {
5241 remove_from_interfaces(cur->state_interface);
5243 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
5244 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
5245 add_to_interfaces(state_interface);
5246 ao2_link(q->members, newm);
5247 ao2_ref(newm, -1);
5248 newm = NULL;
5250 if (cur)
5251 ao2_ref(cur, -1);
5252 else {
5253 q->membercount++;
5255 } else {
5256 queue_set_param(q, var->name, var->value, var->lineno, 1);
5260 /* Free remaining members marked as delme */
5261 mem_iter = ao2_iterator_init(q->members, 0);
5262 while ((cur = ao2_iterator_next(&mem_iter))) {
5263 if (! cur->delme) {
5264 ao2_ref(cur, -1);
5265 continue;
5267 q->membercount--;
5268 ao2_unlink(q->members, cur);
5269 remove_from_interfaces(cur->interface);
5270 ao2_ref(cur, -1);
5273 if (new) {
5274 ao2_link(queues, q);
5275 } else
5276 ao2_unlock(q);
5277 queue_unref(q);
5281 ast_config_destroy(cfg);
5282 queue_iter = ao2_iterator_init(queues, 0);
5283 while ((q = ao2_iterator_next(&queue_iter))) {
5284 if (q->dead) {
5285 ao2_unlink(queues, q);
5286 } else {
5287 ao2_lock(q);
5288 mem_iter = ao2_iterator_init(q->members, 0);
5289 while ((cur = ao2_iterator_next(&mem_iter))) {
5290 if (cur->dynamic)
5291 q->membercount++;
5292 cur->status = ast_device_state(cur->interface);
5293 ao2_ref(cur, -1);
5295 ao2_unlock(q);
5297 queue_unref(q);
5299 ao2_unlock(queues);
5300 return 1;
5303 /*! \brief direct ouput to manager or cli with proper terminator */
5304 static void do_print(struct mansession *s, int fd, const char *str)
5306 if (s)
5307 astman_append(s, "%s\r\n", str);
5308 else
5309 ast_cli(fd, "%s\n", str);
5312 /*!
5313 * \brief Show queue(s) status and statistics
5315 * List the queues strategy, calls processed, members logged in,
5316 * other queue statistics such as avg hold time.
5318 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
5320 struct call_queue *q;
5321 struct ast_str *out = ast_str_alloca(240);
5322 int found = 0;
5323 time_t now = time(NULL);
5324 struct ao2_iterator queue_iter;
5325 struct ao2_iterator mem_iter;
5327 if (argc != 2 && argc != 3)
5328 return CLI_SHOWUSAGE;
5330 /* We only want to load realtime queues when a specific queue is asked for. */
5331 if (argc == 3) /* specific queue */
5332 load_realtime_queue(argv[2]);
5334 queue_iter = ao2_iterator_init(queues, 0);
5335 while ((q = ao2_iterator_next(&queue_iter))) {
5336 float sl;
5338 ao2_lock(q);
5339 if (argc == 3 && strcasecmp(q->name, argv[2])) {
5340 ao2_unlock(q);
5341 continue;
5343 found = 1;
5345 ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
5346 if (q->maxlen)
5347 ast_str_append(&out, 0, "%d", q->maxlen);
5348 else
5349 ast_str_append(&out, 0, "unlimited");
5350 sl = 0;
5351 if (q->callscompleted > 0)
5352 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
5353 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
5354 int2strat(q->strategy), q->holdtime, q->weight,
5355 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
5356 do_print(s, fd, out->str);
5357 if (!ao2_container_count(q->members))
5358 do_print(s, fd, " No Members");
5359 else {
5360 struct member *mem;
5362 do_print(s, fd, " Members: ");
5363 mem_iter = ao2_iterator_init(q->members, 0);
5364 while ((mem = ao2_iterator_next(&mem_iter))) {
5365 ast_str_set(&out, 0, " %s", mem->membername);
5366 if (mem->penalty)
5367 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
5368 ast_str_append(&out, 0, "%s%s%s (%s)",
5369 mem->dynamic ? " (dynamic)" : "",
5370 mem->realtime ? " (realtime)" : "",
5371 mem->paused ? " (paused)" : "",
5372 devstate2str(mem->status));
5373 if (mem->calls)
5374 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
5375 mem->calls, (long) (time(NULL) - mem->lastcall));
5376 else
5377 ast_str_append(&out, 0, " has taken no calls yet");
5378 do_print(s, fd, out->str);
5379 ao2_ref(mem, -1);
5382 if (!q->head)
5383 do_print(s, fd, " No Callers");
5384 else {
5385 struct queue_ent *qe;
5386 int pos = 1;
5388 do_print(s, fd, " Callers: ");
5389 for (qe = q->head; qe; qe = qe->next) {
5390 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
5391 pos++, qe->chan->name, (long) (now - qe->start) / 60,
5392 (long) (now - qe->start) % 60, qe->prio);
5393 do_print(s, fd, out->str);
5396 do_print(s, fd, ""); /* blank line between entries */
5397 ao2_unlock(q);
5398 if (argc == 3) { /* print a specific entry */
5399 queue_unref(q);
5400 break;
5402 queue_unref(q);
5404 if (!found) {
5405 if (argc == 3)
5406 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
5407 else
5408 ast_str_set(&out, 0, "No queues.");
5409 do_print(s, fd, out->str);
5411 return CLI_SUCCESS;
5414 static char *complete_queue(const char *line, const char *word, int pos, int state)
5416 struct call_queue *q;
5417 char *ret = NULL;
5418 int which = 0;
5419 int wordlen = strlen(word);
5420 struct ao2_iterator queue_iter;
5422 queue_iter = ao2_iterator_init(queues, 0);
5423 while ((q = ao2_iterator_next(&queue_iter))) {
5424 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
5425 ret = ast_strdup(q->name);
5426 queue_unref(q);
5427 break;
5429 queue_unref(q);
5432 return ret;
5435 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
5437 if (pos == 2)
5438 return complete_queue(line, word, pos, state);
5439 return NULL;
5442 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5444 switch ( cmd ) {
5445 case CLI_INIT:
5446 e->command = "queue show";
5447 e->usage =
5448 "Usage: queue show\n"
5449 " Provides summary information on a specified queue.\n";
5450 return NULL;
5451 case CLI_GENERATE:
5452 return complete_queue_show(a->line, a->word, a->pos, a->n);
5455 return __queues_show(NULL, a->fd, a->argc, a->argv);
5458 /*!\brief callback to display queues status in manager
5459 \addtogroup Group_AMI
5461 static int manager_queues_show(struct mansession *s, const struct message *m)
5463 char *a[] = { "queue", "show" };
5465 __queues_show(s, -1, 2, a);
5466 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
5468 return RESULT_SUCCESS;
5471 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
5473 const char *rule = astman_get_header(m, "Rule");
5474 struct rule_list *rl_iter;
5475 struct penalty_rule *pr_iter;
5477 AST_LIST_LOCK(&rule_lists);
5478 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
5479 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
5480 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
5481 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
5482 astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
5484 if (!ast_strlen_zero(rule))
5485 break;
5488 AST_LIST_UNLOCK(&rule_lists);
5490 astman_append(s, "\r\n\r\n");
5492 return RESULT_SUCCESS;
5495 /*! \brief Summary of queue info via the AMI */
5496 static int manager_queues_summary(struct mansession *s, const struct message *m)
5498 time_t now;
5499 int qmemcount = 0;
5500 int qmemavail = 0;
5501 int qchancount = 0;
5502 int qlongestholdtime = 0;
5503 const char *id = astman_get_header(m, "ActionID");
5504 const char *queuefilter = astman_get_header(m, "Queue");
5505 char idText[256] = "";
5506 struct call_queue *q;
5507 struct queue_ent *qe;
5508 struct member *mem;
5509 struct ao2_iterator queue_iter;
5510 struct ao2_iterator mem_iter;
5512 astman_send_ack(s, m, "Queue summary will follow");
5513 time(&now);
5514 if (!ast_strlen_zero(id))
5515 snprintf(idText, 256, "ActionID: %s\r\n", id);
5516 queue_iter = ao2_iterator_init(queues, 0);
5517 while ((q = ao2_iterator_next(&queue_iter))) {
5518 ao2_lock(q);
5520 /* List queue properties */
5521 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5522 /* Reset the necessary local variables if no queuefilter is set*/
5523 qmemcount = 0;
5524 qmemavail = 0;
5525 qchancount = 0;
5526 qlongestholdtime = 0;
5528 /* List Queue Members */
5529 mem_iter = ao2_iterator_init(q->members, 0);
5530 while ((mem = ao2_iterator_next(&mem_iter))) {
5531 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
5532 ++qmemcount;
5533 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
5534 ++qmemavail;
5537 ao2_ref(mem, -1);
5539 for (qe = q->head; qe; qe = qe->next) {
5540 if ((now - qe->start) > qlongestholdtime) {
5541 qlongestholdtime = now - qe->start;
5543 ++qchancount;
5545 astman_append(s, "Event: QueueSummary\r\n"
5546 "Queue: %s\r\n"
5547 "LoggedIn: %d\r\n"
5548 "Available: %d\r\n"
5549 "Callers: %d\r\n"
5550 "HoldTime: %d\r\n"
5551 "LongestHoldTime: %d\r\n"
5552 "%s"
5553 "\r\n",
5554 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
5556 ao2_unlock(q);
5557 queue_unref(q);
5559 astman_append(s,
5560 "Event: QueueSummaryComplete\r\n"
5561 "%s"
5562 "\r\n", idText);
5564 return RESULT_SUCCESS;
5567 /*! \brief Queue status info via AMI */
5568 static int manager_queues_status(struct mansession *s, const struct message *m)
5570 time_t now;
5571 int pos;
5572 const char *id = astman_get_header(m,"ActionID");
5573 const char *queuefilter = astman_get_header(m,"Queue");
5574 const char *memberfilter = astman_get_header(m,"Member");
5575 char idText[256] = "";
5576 struct call_queue *q;
5577 struct queue_ent *qe;
5578 float sl = 0;
5579 struct member *mem;
5580 struct ao2_iterator queue_iter;
5581 struct ao2_iterator mem_iter;
5583 astman_send_ack(s, m, "Queue status will follow");
5584 time(&now);
5585 if (!ast_strlen_zero(id))
5586 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
5588 queue_iter = ao2_iterator_init(queues, 0);
5589 while ((q = ao2_iterator_next(&queue_iter))) {
5590 ao2_lock(q);
5592 /* List queue properties */
5593 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5594 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
5595 astman_append(s, "Event: QueueParams\r\n"
5596 "Queue: %s\r\n"
5597 "Max: %d\r\n"
5598 "Strategy: %s\r\n"
5599 "Calls: %d\r\n"
5600 "Holdtime: %d\r\n"
5601 "Completed: %d\r\n"
5602 "Abandoned: %d\r\n"
5603 "ServiceLevel: %d\r\n"
5604 "ServicelevelPerf: %2.1f\r\n"
5605 "Weight: %d\r\n"
5606 "%s"
5607 "\r\n",
5608 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
5609 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
5610 /* List Queue Members */
5611 mem_iter = ao2_iterator_init(q->members, 0);
5612 while ((mem = ao2_iterator_next(&mem_iter))) {
5613 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
5614 astman_append(s, "Event: QueueMember\r\n"
5615 "Queue: %s\r\n"
5616 "Name: %s\r\n"
5617 "Location: %s\r\n"
5618 "Membership: %s\r\n"
5619 "Penalty: %d\r\n"
5620 "CallsTaken: %d\r\n"
5621 "LastCall: %d\r\n"
5622 "Status: %d\r\n"
5623 "Paused: %d\r\n"
5624 "%s"
5625 "\r\n",
5626 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
5627 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
5629 ao2_ref(mem, -1);
5631 /* List Queue Entries */
5632 pos = 1;
5633 for (qe = q->head; qe; qe = qe->next) {
5634 astman_append(s, "Event: QueueEntry\r\n"
5635 "Queue: %s\r\n"
5636 "Position: %d\r\n"
5637 "Channel: %s\r\n"
5638 "CallerIDNum: %s\r\n"
5639 "CallerIDName: %s\r\n"
5640 "Wait: %ld\r\n"
5641 "%s"
5642 "\r\n",
5643 q->name, pos++, qe->chan->name,
5644 S_OR(qe->chan->cid.cid_num, "unknown"),
5645 S_OR(qe->chan->cid.cid_name, "unknown"),
5646 (long) (now - qe->start), idText);
5649 ao2_unlock(q);
5650 queue_unref(q);
5653 astman_append(s,
5654 "Event: QueueStatusComplete\r\n"
5655 "%s"
5656 "\r\n",idText);
5658 return RESULT_SUCCESS;
5661 static int manager_add_queue_member(struct mansession *s, const struct message *m)
5663 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
5664 int paused, penalty = 0;
5666 queuename = astman_get_header(m, "Queue");
5667 interface = astman_get_header(m, "Interface");
5668 penalty_s = astman_get_header(m, "Penalty");
5669 paused_s = astman_get_header(m, "Paused");
5670 membername = astman_get_header(m, "MemberName");
5671 state_interface = astman_get_header(m, "StateInterface");
5673 if (ast_strlen_zero(queuename)) {
5674 astman_send_error(s, m, "'Queue' not specified.");
5675 return 0;
5678 if (ast_strlen_zero(interface)) {
5679 astman_send_error(s, m, "'Interface' not specified.");
5680 return 0;
5683 if (ast_strlen_zero(penalty_s))
5684 penalty = 0;
5685 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
5686 penalty = 0;
5688 if (ast_strlen_zero(paused_s))
5689 paused = 0;
5690 else
5691 paused = abs(ast_true(paused_s));
5693 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
5694 case RES_OKAY:
5695 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
5696 astman_send_ack(s, m, "Added interface to queue");
5697 break;
5698 case RES_EXISTS:
5699 astman_send_error(s, m, "Unable to add interface: Already there");
5700 break;
5701 case RES_NOSUCHQUEUE:
5702 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
5703 break;
5704 case RES_OUTOFMEMORY:
5705 astman_send_error(s, m, "Out of memory");
5706 break;
5709 return 0;
5712 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
5714 const char *queuename, *interface;
5716 queuename = astman_get_header(m, "Queue");
5717 interface = astman_get_header(m, "Interface");
5719 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
5720 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
5721 return 0;
5724 switch (remove_from_queue(queuename, interface)) {
5725 case RES_OKAY:
5726 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
5727 astman_send_ack(s, m, "Removed interface from queue");
5728 break;
5729 case RES_EXISTS:
5730 astman_send_error(s, m, "Unable to remove interface: Not there");
5731 break;
5732 case RES_NOSUCHQUEUE:
5733 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
5734 break;
5735 case RES_OUTOFMEMORY:
5736 astman_send_error(s, m, "Out of memory");
5737 break;
5738 case RES_NOT_DYNAMIC:
5739 astman_send_error(s, m, "Member not dynamic");
5740 break;
5743 return 0;
5746 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
5748 const char *queuename, *interface, *paused_s, *reason;
5749 int paused;
5751 interface = astman_get_header(m, "Interface");
5752 paused_s = astman_get_header(m, "Paused");
5753 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
5754 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */
5756 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
5757 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
5758 return 0;
5761 paused = abs(ast_true(paused_s));
5763 if (set_member_paused(queuename, interface, reason, paused))
5764 astman_send_error(s, m, "Interface not found");
5765 else
5766 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
5767 return 0;
5770 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
5772 const char *queuename, *event, *message, *interface, *uniqueid;
5774 queuename = astman_get_header(m, "Queue");
5775 uniqueid = astman_get_header(m, "UniqueId");
5776 interface = astman_get_header(m, "Interface");
5777 event = astman_get_header(m, "Event");
5778 message = astman_get_header(m, "Message");
5780 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
5781 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
5782 return 0;
5785 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
5786 astman_send_ack(s, m, "Event added successfully");
5788 return 0;
5791 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
5793 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
5794 switch (pos) {
5795 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
5796 return NULL;
5797 case 4: /* only one possible match, "to" */
5798 return state == 0 ? ast_strdup("to") : NULL;
5799 case 5: /* <queue> */
5800 return complete_queue(line, word, pos, state);
5801 case 6: /* only one possible match, "penalty" */
5802 return state == 0 ? ast_strdup("penalty") : NULL;
5803 case 7:
5804 if (state < 100) { /* 0-99 */
5805 char *num;
5806 if ((num = ast_malloc(3))) {
5807 sprintf(num, "%d", state);
5809 return num;
5810 } else {
5811 return NULL;
5813 case 8: /* only one possible match, "as" */
5814 return state == 0 ? ast_strdup("as") : NULL;
5815 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
5816 return NULL;
5817 default:
5818 return NULL;
5822 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
5824 const char *queuename, *interface, *penalty_s;
5825 int penalty;
5827 interface = astman_get_header(m, "Interface");
5828 penalty_s = astman_get_header(m, "Penalty");
5829 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
5830 queuename = astman_get_header(m, "Queue");
5832 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
5833 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
5834 return 0;
5837 penalty = atoi(penalty_s);
5839 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
5840 astman_send_error(s, m, "Invalid interface, queuename or penalty");
5841 else
5842 astman_send_ack(s, m, "Interface penalty set successfully");
5844 return 0;
5847 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5849 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
5850 int penalty;
5852 switch ( cmd ) {
5853 case CLI_INIT:
5854 e->command = "queue add member";
5855 e->usage =
5856 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
5857 return NULL;
5858 case CLI_GENERATE:
5859 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
5862 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
5863 return CLI_SHOWUSAGE;
5864 } else if (strcmp(a->argv[4], "to")) {
5865 return CLI_SHOWUSAGE;
5866 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
5867 return CLI_SHOWUSAGE;
5868 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
5869 return CLI_SHOWUSAGE;
5870 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
5871 return CLI_SHOWUSAGE;
5874 queuename = a->argv[5];
5875 interface = a->argv[3];
5876 if (a->argc >= 8) {
5877 if (sscanf(a->argv[7], "%d", &penalty) == 1) {
5878 if (penalty < 0) {
5879 ast_cli(a->fd, "Penalty must be >= 0\n");
5880 penalty = 0;
5882 } else {
5883 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
5884 penalty = 0;
5886 } else {
5887 penalty = 0;
5890 if (a->argc >= 10) {
5891 membername = a->argv[9];
5894 if (a->argc >= 12) {
5895 state_interface = a->argv[11];
5898 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
5899 case RES_OKAY:
5900 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
5901 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
5902 return CLI_SUCCESS;
5903 case RES_EXISTS:
5904 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
5905 return CLI_FAILURE;
5906 case RES_NOSUCHQUEUE:
5907 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
5908 return CLI_FAILURE;
5909 case RES_OUTOFMEMORY:
5910 ast_cli(a->fd, "Out of memory\n");
5911 return CLI_FAILURE;
5912 case RES_NOT_DYNAMIC:
5913 ast_cli(a->fd, "Member not dynamic\n");
5914 return CLI_FAILURE;
5915 default:
5916 return CLI_FAILURE;
5920 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
5922 int which = 0;
5923 struct call_queue *q;
5924 struct member *m;
5925 struct ao2_iterator queue_iter;
5926 struct ao2_iterator mem_iter;
5927 int wordlen = strlen(word);
5929 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
5930 if (pos > 5 || pos < 3)
5931 return NULL;
5932 if (pos == 4) /* only one possible match, 'from' */
5933 return (state == 0 ? ast_strdup("from") : NULL);
5935 if (pos == 5) /* No need to duplicate code */
5936 return complete_queue(line, word, pos, state);
5938 /* here is the case for 3, <member> */
5939 queue_iter = ao2_iterator_init(queues, 0);
5940 while ((q = ao2_iterator_next(&queue_iter))) {
5941 ao2_lock(q);
5942 mem_iter = ao2_iterator_init(q->members, 0);
5943 while ((m = ao2_iterator_next(&mem_iter))) {
5944 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
5945 char *tmp;
5946 ao2_unlock(q);
5947 tmp = m->membername;
5948 ao2_ref(m, -1);
5949 queue_unref(q);
5950 return ast_strdup(tmp);
5952 ao2_ref(m, -1);
5954 ao2_unlock(q);
5955 queue_unref(q);
5958 return NULL;
5961 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5963 char *queuename, *interface;
5965 switch (cmd) {
5966 case CLI_INIT:
5967 e->command = "queue remove member";
5968 e->usage = "Usage: queue remove member <channel> from <queue>\n";
5969 return NULL;
5970 case CLI_GENERATE:
5971 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
5974 if (a->argc != 6) {
5975 return CLI_SHOWUSAGE;
5976 } else if (strcmp(a->argv[4], "from")) {
5977 return CLI_SHOWUSAGE;
5980 queuename = a->argv[5];
5981 interface = a->argv[3];
5983 switch (remove_from_queue(queuename, interface)) {
5984 case RES_OKAY:
5985 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
5986 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
5987 return CLI_SUCCESS;
5988 case RES_EXISTS:
5989 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
5990 return CLI_FAILURE;
5991 case RES_NOSUCHQUEUE:
5992 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
5993 return CLI_FAILURE;
5994 case RES_OUTOFMEMORY:
5995 ast_cli(a->fd, "Out of memory\n");
5996 return CLI_FAILURE;
5997 default:
5998 return CLI_FAILURE;
6002 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
6004 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
6005 switch (pos) {
6006 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
6007 return NULL;
6008 case 4: /* only one possible match, "queue" */
6009 return state == 0 ? ast_strdup("queue") : NULL;
6010 case 5: /* <queue> */
6011 return complete_queue(line, word, pos, state);
6012 case 6: /* "reason" */
6013 return state == 0 ? ast_strdup("reason") : NULL;
6014 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
6015 return NULL;
6016 default:
6017 return NULL;
6021 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6023 char *queuename, *interface, *reason;
6024 int paused;
6026 switch (cmd) {
6027 case CLI_INIT:
6028 e->command = "queue {pause|unpause} member";
6029 e->usage =
6030 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
6031 " Pause or unpause a queue member. Not specifying a particular queue\n"
6032 " will pause or unpause a member across all queues to which the member\n"
6033 " belongs.\n";
6034 return NULL;
6035 case CLI_GENERATE:
6036 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
6039 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
6040 return CLI_SHOWUSAGE;
6041 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
6042 return CLI_SHOWUSAGE;
6043 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
6044 return CLI_SHOWUSAGE;
6048 interface = a->argv[3];
6049 queuename = a->argc >= 6 ? a->argv[5] : NULL;
6050 reason = a->argc == 8 ? a->argv[7] : NULL;
6051 paused = !strcasecmp(a->argv[1], "pause");
6053 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
6054 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
6055 if (!ast_strlen_zero(queuename))
6056 ast_cli(a->fd, " in queue '%s'", queuename);
6057 if (!ast_strlen_zero(reason))
6058 ast_cli(a->fd, " for reason '%s'", reason);
6059 ast_cli(a->fd, "\n");
6060 return CLI_SUCCESS;
6061 } else {
6062 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
6063 if (!ast_strlen_zero(queuename))
6064 ast_cli(a->fd, " in queue '%s'", queuename);
6065 if (!ast_strlen_zero(reason))
6066 ast_cli(a->fd, " for reason '%s'", reason);
6067 ast_cli(a->fd, "\n");
6068 return CLI_FAILURE;
6072 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
6074 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
6075 switch (pos) {
6076 case 4:
6077 if (state == 0) {
6078 return ast_strdup("on");
6079 } else {
6080 return NULL;
6082 case 6:
6083 if (state == 0) {
6084 return ast_strdup("in");
6085 } else {
6086 return NULL;
6088 case 7:
6089 return complete_queue(line, word, pos, state);
6090 default:
6091 return NULL;
6095 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6097 char *queuename = NULL, *interface;
6098 int penalty = 0;
6100 switch (cmd) {
6101 case CLI_INIT:
6102 e->command = "queue set penalty";
6103 e->usage =
6104 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
6105 "Set a member's penalty in the queue specified. If no queue is specified\n"
6106 "then that interface's penalty is set in all queues to which that interface is a member\n";
6107 return NULL;
6108 case CLI_GENERATE:
6109 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
6112 if (a->argc != 6 && a->argc != 8) {
6113 return CLI_SHOWUSAGE;
6114 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
6115 return CLI_SHOWUSAGE;
6118 if (a->argc == 8)
6119 queuename = a->argv[7];
6120 interface = a->argv[5];
6121 penalty = atoi(a->argv[3]);
6123 switch (set_member_penalty(queuename, interface, penalty)) {
6124 case RESULT_SUCCESS:
6125 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6126 return CLI_SUCCESS;
6127 case RESULT_FAILURE:
6128 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6129 return CLI_FAILURE;
6130 default:
6131 return CLI_FAILURE;
6135 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
6137 int which = 0;
6138 struct rule_list *rl_iter;
6139 int wordlen = strlen(word);
6140 char *ret = NULL;
6141 if (pos != 3) /* Wha? */ {
6142 ast_log(LOG_DEBUG, "Hitting this???, pos is %d\n", pos);
6143 return NULL;
6146 AST_LIST_LOCK(&rule_lists);
6147 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6148 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
6149 ret = ast_strdup(rl_iter->name);
6150 break;
6153 AST_LIST_UNLOCK(&rule_lists);
6155 return ret;
6158 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6160 char *rule;
6161 struct rule_list *rl_iter;
6162 struct penalty_rule *pr_iter;
6163 switch (cmd) {
6164 case CLI_INIT:
6165 e->command = "queue rules show";
6166 e->usage =
6167 "Usage: queue rules show [rulename]\n"
6168 "Show the list of rules associated with rulename. If no\n"
6169 "rulename is specified, list all rules defined in queuerules.conf\n";
6170 return NULL;
6171 case CLI_GENERATE:
6172 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
6175 if (a->argc != 3 && a->argc != 4)
6176 return CLI_SHOWUSAGE;
6178 rule = a->argc == 4 ? a->argv[3] : "";
6179 AST_LIST_LOCK(&rule_lists);
6180 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6181 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
6182 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
6183 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
6184 ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
6188 AST_LIST_UNLOCK(&rule_lists);
6189 return CLI_SUCCESS;
6192 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6194 switch (cmd) {
6195 case CLI_INIT:
6196 e->command = "queue rules reload";
6197 e->usage =
6198 "Usage: queue rules reload\n"
6199 "Reloads rules defined in queuerules.conf\n";
6200 return NULL;
6201 case CLI_GENERATE:
6202 return NULL;
6204 reload_queue_rules(1);
6205 return CLI_SUCCESS;
6208 static const char qpm_cmd_usage[] =
6209 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
6211 static const char qum_cmd_usage[] =
6212 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
6214 static const char qsmp_cmd_usage[] =
6215 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
6217 static struct ast_cli_entry cli_queue[] = {
6218 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
6219 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
6220 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
6221 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
6222 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
6223 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
6224 AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
6227 static int unload_module(void)
6229 int res;
6230 struct ast_context *con;
6231 struct ao2_iterator q_iter;
6232 struct call_queue *q = NULL;
6234 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6235 res = ast_manager_unregister("QueueStatus");
6236 res |= ast_manager_unregister("Queues");
6237 res |= ast_manager_unregister("QueueRule");
6238 res |= ast_manager_unregister("QueueSummary");
6239 res |= ast_manager_unregister("QueueAdd");
6240 res |= ast_manager_unregister("QueueRemove");
6241 res |= ast_manager_unregister("QueuePause");
6242 res |= ast_manager_unregister("QueueLog");
6243 res |= ast_manager_unregister("QueuePenalty");
6244 res |= ast_unregister_application(app_aqm);
6245 res |= ast_unregister_application(app_rqm);
6246 res |= ast_unregister_application(app_pqm);
6247 res |= ast_unregister_application(app_upqm);
6248 res |= ast_unregister_application(app_ql);
6249 res |= ast_unregister_application(app);
6250 res |= ast_custom_function_unregister(&queuevar_function);
6251 res |= ast_custom_function_unregister(&queuemembercount_function);
6252 res |= ast_custom_function_unregister(&queuemembercount_dep);
6253 res |= ast_custom_function_unregister(&queuememberlist_function);
6254 res |= ast_custom_function_unregister(&queuewaitingcount_function);
6255 res |= ast_custom_function_unregister(&queuememberpenalty_function);
6257 if (device_state_sub)
6258 ast_event_unsubscribe(device_state_sub);
6260 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
6261 ast_context_remove_extension2(con, "s", 1, NULL);
6262 ast_context_destroy(con, "app_queue"); /* leave no trace */
6265 clear_and_free_interfaces();
6267 q_iter = ao2_iterator_init(queues, 0);
6268 while ((q = ao2_iterator_next(&q_iter))) {
6269 ao2_unlink(queues, q);
6270 queue_unref(q);
6272 ao2_ref(queues, -1);
6273 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
6274 return res;
6277 static int load_module(void)
6279 int res;
6280 struct ast_context *con;
6282 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
6284 if (!reload_queues(0))
6285 return AST_MODULE_LOAD_DECLINE;
6287 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
6288 if (!con)
6289 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
6290 else
6291 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_queue");
6293 if (queue_persistent_members)
6294 reload_queue_members();
6296 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6297 res = ast_register_application(app, queue_exec, synopsis, descrip);
6298 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
6299 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
6300 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
6301 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
6302 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
6303 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
6304 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
6305 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
6306 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
6307 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
6308 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
6309 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
6310 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
6311 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
6312 res |= ast_custom_function_register(&queuevar_function);
6313 res |= ast_custom_function_register(&queuemembercount_function);
6314 res |= ast_custom_function_register(&queuemembercount_dep);
6315 res |= ast_custom_function_register(&queuememberlist_function);
6316 res |= ast_custom_function_register(&queuewaitingcount_function);
6317 res |= ast_custom_function_register(&queuememberpenalty_function);
6319 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
6320 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
6323 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END)))
6324 res = -1;
6326 return res ? AST_MODULE_LOAD_DECLINE : 0;
6329 static int reload(void)
6331 reload_queues(1);
6332 return 0;
6335 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
6336 .load = load_module,
6337 .unload = unload_module,
6338 .reload = reload,