Merged revisions 119404 via svnmerge from
[asterisk-bristuff.git] / apps / app_queue.c
blobc50549120f4fbad3cb17dede517a5e9696d6d5a3
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 || stat == QUEUE_NO_MEMBERS))
1682 *reason = QUEUE_JOINUNAVAIL;
1683 else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_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 ast_channel_lock(qe->chan);
3442 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
3443 monitorfilename = ast_strdupa(monitorfilename);
3445 ast_channel_unlock(qe->chan);
3446 /* Begin Monitoring */
3447 if (qe->parent->monfmt && *qe->parent->monfmt) {
3448 if (!qe->parent->montype) {
3449 ast_debug(1, "Starting Monitor as requested.\n");
3450 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
3451 which = qe->chan;
3452 else
3453 which = peer;
3454 if (monitorfilename)
3455 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
3456 else if (qe->chan->cdr)
3457 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
3458 else {
3459 /* Last ditch effort -- no CDR, make up something */
3460 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3461 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
3463 } else {
3464 mixmonapp = pbx_findapp("MixMonitor");
3466 if (mixmonapp) {
3467 ast_debug(1, "Starting MixMonitor as requested.\n");
3468 if (!monitorfilename) {
3469 if (qe->chan->cdr)
3470 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
3471 else
3472 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3473 } else {
3474 const char *m = monitorfilename;
3475 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
3476 switch (*m) {
3477 case '^':
3478 if (*(m + 1) == '{')
3479 *p = '$';
3480 break;
3481 case ',':
3482 *p++ = '\\';
3483 /* Fall through */
3484 default:
3485 *p = *m;
3487 if (*m == '\0')
3488 break;
3490 if (p == tmpid2 + sizeof(tmpid2))
3491 tmpid2[sizeof(tmpid2) - 1] = '\0';
3493 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
3496 ast_channel_lock(qe->chan);
3497 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
3498 monitor_exec = ast_strdupa(monitor_exec);
3500 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
3501 monitor_options = ast_strdupa(monitor_options);
3502 } else {
3503 monitor_options = "";
3505 ast_channel_unlock(qe->chan);
3507 if (monitor_exec) {
3508 const char *m = monitor_exec;
3509 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
3510 switch (*m) {
3511 case '^':
3512 if (*(m + 1) == '{')
3513 *p = '$';
3514 break;
3515 case ',':
3516 *p++ = '\\';
3517 /* Fall through */
3518 default:
3519 *p = *m;
3521 if (*m == '\0')
3522 break;
3524 if (p == meid2 + sizeof(meid2))
3525 meid2[sizeof(meid2) - 1] = '\0';
3527 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
3530 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
3532 if (!ast_strlen_zero(monitor_exec))
3533 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
3534 else
3535 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
3537 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
3538 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3539 if (qe->chan->cdr)
3540 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3541 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
3542 if (qe->chan->cdr)
3543 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3545 } else {
3546 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3550 /* Drop out of the queue at this point, to prepare for next caller */
3551 leave_queue(qe);
3552 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
3553 ast_debug(1, "app_queue: sendurl=%s.\n", url);
3554 ast_channel_sendurl(peer, url);
3557 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
3558 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
3559 if (!ast_strlen_zero(macro)) {
3560 macroexec = ast_strdupa(macro);
3561 } else {
3562 if (qe->parent->membermacro)
3563 macroexec = ast_strdupa(qe->parent->membermacro);
3566 if (!ast_strlen_zero(macroexec)) {
3567 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
3569 res = ast_autoservice_start(qe->chan);
3570 if (res) {
3571 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3572 res = -1;
3575 app = pbx_findapp("Macro");
3577 if (app) {
3578 res = pbx_exec(qe->chan, app, macroexec);
3579 ast_debug(1, "Macro exited with status %d\n", res);
3580 res = 0;
3581 } else {
3582 ast_log(LOG_ERROR, "Could not find application Macro\n");
3583 res = -1;
3586 if (ast_autoservice_stop(qe->chan) < 0) {
3587 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3588 res = -1;
3592 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
3593 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
3594 if (!ast_strlen_zero(gosub)) {
3595 gosubexec = ast_strdupa(gosub);
3596 } else {
3597 if (qe->parent->membergosub)
3598 gosubexec = ast_strdupa(qe->parent->membergosub);
3601 if (!ast_strlen_zero(gosubexec)) {
3602 if (option_debug)
3603 ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
3605 res = ast_autoservice_start(qe->chan);
3606 if (res) {
3607 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3608 res = -1;
3611 app = pbx_findapp("Gosub");
3613 if (app) {
3614 char *gosub_args, *gosub_argstart;
3616 /* Set where we came from */
3617 ast_copy_string(qe->chan->context, "app_dial_gosub_virtual_context", sizeof(qe->chan->context));
3618 ast_copy_string(qe->chan->exten, "s", sizeof(qe->chan->exten));
3619 qe->chan->priority = 0;
3621 gosub_argstart = strchr(gosubexec, ',');
3622 if (gosub_argstart) {
3623 *gosub_argstart = 0;
3624 asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1);
3625 *gosub_argstart = '|';
3626 } else {
3627 asprintf(&gosub_args, "%s,s,1", gosubexec);
3629 if (gosub_args) {
3630 res = pbx_exec(qe->chan, app, gosub_args);
3631 ast_pbx_run(qe->chan);
3632 free(gosub_args);
3633 if (option_debug)
3634 ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
3635 } else
3636 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
3638 res = 0;
3639 } else {
3640 ast_log(LOG_ERROR, "Could not find application Gosub\n");
3641 res = -1;
3644 if (ast_autoservice_stop(qe->chan) < 0) {
3645 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3646 res = -1;
3650 if (!ast_strlen_zero(agi)) {
3651 ast_debug(1, "app_queue: agi=%s.\n", agi);
3652 app = pbx_findapp("agi");
3653 if (app) {
3654 agiexec = ast_strdupa(agi);
3655 ret = pbx_exec(qe->chan, app, agiexec);
3656 } else
3657 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3659 qe->handled++;
3660 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
3661 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
3662 if (update_cdr && qe->chan->cdr)
3663 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
3664 if (qe->parent->eventwhencalled)
3665 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
3666 "Queue: %s\r\n"
3667 "Uniqueid: %s\r\n"
3668 "Channel: %s\r\n"
3669 "Member: %s\r\n"
3670 "MemberName: %s\r\n"
3671 "Holdtime: %ld\r\n"
3672 "BridgedChannel: %s\r\n"
3673 "Ringtime: %ld\r\n"
3674 "%s",
3675 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3676 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
3677 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3678 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
3679 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
3680 time(&callstart);
3682 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3684 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3685 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
3686 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
3687 (long) (time(NULL) - callstart));
3688 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
3689 } else if (ast_check_hangup(qe->chan)) {
3690 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
3691 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3692 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
3693 } else {
3694 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
3695 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3696 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
3699 if (bridge != AST_PBX_NO_HANGUP_PEER)
3700 ast_hangup(peer);
3701 update_queue(qe->parent, member, callcompletedinsl);
3702 res = bridge ? bridge : 1;
3703 ao2_ref(member, -1);
3705 out:
3706 hangupcalls(outgoing, NULL);
3708 return res;
3711 static int wait_a_bit(struct queue_ent *qe)
3713 /* Don't need to hold the lock while we setup the outgoing calls */
3714 int retrywait = qe->parent->retry * 1000;
3716 int res = ast_waitfordigit(qe->chan, retrywait);
3717 if (res > 0 && !valid_exit(qe, res))
3718 res = 0;
3720 return res;
3723 static struct member *interface_exists(struct call_queue *q, const char *interface)
3725 struct member *mem;
3726 struct ao2_iterator mem_iter;
3728 if (!q)
3729 return NULL;
3731 mem_iter = ao2_iterator_init(q->members, 0);
3732 while ((mem = ao2_iterator_next(&mem_iter))) {
3733 if (!strcasecmp(interface, mem->interface))
3734 return mem;
3735 ao2_ref(mem, -1);
3738 return NULL;
3742 /*! \brief Dump all members in a specific queue to the database
3744 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
3746 static void dump_queue_members(struct call_queue *pm_queue)
3748 struct member *cur_member;
3749 char value[PM_MAX_LEN];
3750 int value_len = 0;
3751 int res;
3752 struct ao2_iterator mem_iter;
3754 memset(value, 0, sizeof(value));
3756 if (!pm_queue)
3757 return;
3759 mem_iter = ao2_iterator_init(pm_queue->members, 0);
3760 while ((cur_member = ao2_iterator_next(&mem_iter))) {
3761 if (!cur_member->dynamic) {
3762 ao2_ref(cur_member, -1);
3763 continue;
3766 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
3767 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
3769 ao2_ref(cur_member, -1);
3771 if (res != strlen(value + value_len)) {
3772 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
3773 break;
3775 value_len += res;
3778 if (value_len && !cur_member) {
3779 if (ast_db_put(pm_family, pm_queue->name, value))
3780 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
3781 } else
3782 /* Delete the entry if the queue is empty or there is an error */
3783 ast_db_del(pm_family, pm_queue->name);
3786 /*! \brief Remove member from queue
3787 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3788 * \retval RES_NOSUCHQUEUE queue does not exist
3789 * \retval RES_OKAY removed member from queue
3790 * \retval RES_EXISTS queue exists but no members
3792 static int remove_from_queue(const char *queuename, const char *interface)
3794 struct call_queue *q, tmpq = {
3795 .name = queuename,
3797 struct member *mem, tmpmem;
3798 int res = RES_NOSUCHQUEUE;
3800 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
3801 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
3802 ao2_lock(q);
3803 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
3804 /* XXX future changes should beware of this assumption!! */
3805 if (!mem->dynamic) {
3806 ao2_ref(mem, -1);
3807 ao2_unlock(q);
3808 return RES_NOT_DYNAMIC;
3810 q->membercount--;
3811 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
3812 "Queue: %s\r\n"
3813 "Location: %s\r\n"
3814 "MemberName: %s\r\n",
3815 q->name, mem->interface, mem->membername);
3816 ao2_unlink(q->members, mem);
3817 remove_from_interfaces(mem->state_interface);
3818 ao2_ref(mem, -1);
3820 if (queue_persistent_members)
3821 dump_queue_members(q);
3823 res = RES_OKAY;
3824 } else {
3825 res = RES_EXISTS;
3827 ao2_unlock(q);
3828 queue_unref(q);
3831 return res;
3834 /*! \brief Add member to queue
3835 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3836 * \retval RES_NOSUCHQUEUE queue does not exist
3837 * \retval RES_OKAY added member from queue
3838 * \retval RES_EXISTS queue exists but no members
3839 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
3841 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
3843 struct call_queue *q;
3844 struct member *new_member, *old_member;
3845 int res = RES_NOSUCHQUEUE;
3847 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
3848 * short-circuits if the queue is already in memory. */
3849 if (!(q = load_realtime_queue(queuename)))
3850 return res;
3852 ao2_lock(queues);
3854 ao2_lock(q);
3855 if ((old_member = interface_exists(q, interface)) == NULL) {
3856 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
3857 add_to_interfaces(new_member->state_interface);
3858 new_member->dynamic = 1;
3859 ao2_link(q->members, new_member);
3860 q->membercount++;
3861 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
3862 "Queue: %s\r\n"
3863 "Location: %s\r\n"
3864 "MemberName: %s\r\n"
3865 "Membership: %s\r\n"
3866 "Penalty: %d\r\n"
3867 "CallsTaken: %d\r\n"
3868 "LastCall: %d\r\n"
3869 "Status: %d\r\n"
3870 "Paused: %d\r\n",
3871 q->name, new_member->interface, new_member->membername,
3872 "dynamic",
3873 new_member->penalty, new_member->calls, (int) new_member->lastcall,
3874 new_member->status, new_member->paused);
3876 ao2_ref(new_member, -1);
3877 new_member = NULL;
3879 if (dump)
3880 dump_queue_members(q);
3882 res = RES_OKAY;
3883 } else {
3884 res = RES_OUTOFMEMORY;
3886 } else {
3887 ao2_ref(old_member, -1);
3888 res = RES_EXISTS;
3890 ao2_unlock(q);
3891 ao2_unlock(queues);
3893 return res;
3896 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
3898 int found = 0;
3899 struct call_queue *q;
3900 struct member *mem;
3901 struct ao2_iterator queue_iter;
3902 int failed;
3904 /* Special event for when all queues are paused - individual events still generated */
3905 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3906 if (ast_strlen_zero(queuename))
3907 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3909 queue_iter = ao2_iterator_init(queues, 0);
3910 while ((q = ao2_iterator_next(&queue_iter))) {
3911 ao2_lock(q);
3912 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3913 if ((mem = interface_exists(q, interface))) {
3914 if (mem->paused == paused) {
3915 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
3918 failed = 0;
3919 if (mem->realtime) {
3920 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
3923 if (failed) {
3924 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
3925 ao2_ref(mem, -1);
3926 continue;
3928 found++;
3929 mem->paused = paused;
3931 if (queue_persistent_members)
3932 dump_queue_members(q);
3934 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
3936 if (!ast_strlen_zero(reason)) {
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 "Reason: %s\r\n",
3943 q->name, mem->interface, mem->membername, paused, reason);
3944 } else {
3945 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3946 "Queue: %s\r\n"
3947 "Location: %s\r\n"
3948 "MemberName: %s\r\n"
3949 "Paused: %d\r\n",
3950 q->name, mem->interface, mem->membername, paused);
3952 ao2_ref(mem, -1);
3956 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
3957 ao2_unlock(q);
3958 queue_unref(q);
3959 break;
3962 ao2_unlock(q);
3963 queue_unref(q);
3966 return found ? RESULT_SUCCESS : RESULT_FAILURE;
3969 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
3970 static int set_member_penalty(char *queuename, char *interface, int penalty)
3972 int foundinterface = 0, foundqueue = 0;
3973 struct call_queue *q;
3974 struct member *mem;
3975 struct ao2_iterator queue_iter;
3977 if (penalty < 0) {
3978 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
3979 return RESULT_FAILURE;
3982 queue_iter = ao2_iterator_init(queues, 0);
3983 while ((q = ao2_iterator_next(&queue_iter))) {
3984 ao2_lock(q);
3985 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3986 foundqueue++;
3987 if ((mem = interface_exists(q, interface))) {
3988 foundinterface++;
3989 mem->penalty = penalty;
3991 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
3992 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
3993 "Queue: %s\r\n"
3994 "Location: %s\r\n"
3995 "Penalty: %d\r\n",
3996 q->name, mem->interface, penalty);
4000 ao2_unlock(q);
4001 queue_unref(q);
4004 if (foundinterface) {
4005 return RESULT_SUCCESS;
4006 } else if (!foundqueue) {
4007 ast_log (LOG_ERROR, "Invalid queuename\n");
4008 } else {
4009 ast_log (LOG_ERROR, "Invalid interface\n");
4012 return RESULT_FAILURE;
4015 /* \brief Gets members penalty.
4016 * \return Return the members penalty or RESULT_FAILURE on error.
4018 static int get_member_penalty(char *queuename, char *interface)
4020 int foundqueue = 0, penalty;
4021 struct call_queue *q, tmpq = {
4022 .name = queuename,
4024 struct member *mem;
4026 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4027 foundqueue = 1;
4028 ao2_lock(q);
4029 if ((mem = interface_exists(q, interface))) {
4030 penalty = mem->penalty;
4031 ao2_unlock(q);
4032 queue_unref(q);
4033 return penalty;
4035 ao2_unlock(q);
4036 queue_unref(q);
4039 /* some useful debuging */
4040 if (foundqueue)
4041 ast_log (LOG_ERROR, "Invalid queuename\n");
4042 else
4043 ast_log (LOG_ERROR, "Invalid interface\n");
4045 return RESULT_FAILURE;
4048 /*! \brief Reload dynamic queue members persisted into the astdb */
4049 static void reload_queue_members(void)
4051 char *cur_ptr;
4052 const char *queue_name;
4053 char *member;
4054 char *interface;
4055 char *membername = NULL;
4056 char *state_interface;
4057 char *penalty_tok;
4058 int penalty = 0;
4059 char *paused_tok;
4060 int paused = 0;
4061 struct ast_db_entry *db_tree;
4062 struct ast_db_entry *entry;
4063 struct call_queue *cur_queue;
4064 char queue_data[PM_MAX_LEN];
4066 ao2_lock(queues);
4068 /* Each key in 'pm_family' is the name of a queue */
4069 db_tree = ast_db_gettree(pm_family, NULL);
4070 for (entry = db_tree; entry; entry = entry->next) {
4072 queue_name = entry->key + strlen(pm_family) + 2;
4075 struct call_queue tmpq = {
4076 .name = queue_name,
4078 cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
4081 if (!cur_queue)
4082 cur_queue = load_realtime_queue(queue_name);
4084 if (!cur_queue) {
4085 /* If the queue no longer exists, remove it from the
4086 * database */
4087 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
4088 ast_db_del(pm_family, queue_name);
4089 continue;
4092 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
4093 queue_unref(cur_queue);
4094 continue;
4097 cur_ptr = queue_data;
4098 while ((member = strsep(&cur_ptr, ",|"))) {
4099 if (ast_strlen_zero(member))
4100 continue;
4102 interface = strsep(&member, ";");
4103 penalty_tok = strsep(&member, ";");
4104 paused_tok = strsep(&member, ";");
4105 membername = strsep(&member, ";");
4106 state_interface = strsep(&member, ";");
4108 if (!penalty_tok) {
4109 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
4110 break;
4112 penalty = strtol(penalty_tok, NULL, 10);
4113 if (errno == ERANGE) {
4114 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
4115 break;
4118 if (!paused_tok) {
4119 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
4120 break;
4122 paused = strtol(paused_tok, NULL, 10);
4123 if ((errno == ERANGE) || paused < 0 || paused > 1) {
4124 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
4125 break;
4128 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
4130 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
4131 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
4132 break;
4135 queue_unref(cur_queue);
4138 ao2_unlock(queues);
4139 if (db_tree) {
4140 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
4141 ast_db_freetree(db_tree);
4145 /*! \brief PauseQueueMember application */
4146 static int pqm_exec(struct ast_channel *chan, void *data)
4148 char *parse;
4149 AST_DECLARE_APP_ARGS(args,
4150 AST_APP_ARG(queuename);
4151 AST_APP_ARG(interface);
4152 AST_APP_ARG(options);
4153 AST_APP_ARG(reason);
4156 if (ast_strlen_zero(data)) {
4157 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
4158 return -1;
4161 parse = ast_strdupa(data);
4163 AST_STANDARD_APP_ARGS(args, parse);
4165 if (ast_strlen_zero(args.interface)) {
4166 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4167 return -1;
4170 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
4171 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
4172 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
4173 return 0;
4176 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
4178 return 0;
4181 /*! \brief UnPauseQueueMember application */
4182 static int upqm_exec(struct ast_channel *chan, void *data)
4184 char *parse;
4185 AST_DECLARE_APP_ARGS(args,
4186 AST_APP_ARG(queuename);
4187 AST_APP_ARG(interface);
4188 AST_APP_ARG(options);
4189 AST_APP_ARG(reason);
4192 if (ast_strlen_zero(data)) {
4193 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
4194 return -1;
4197 parse = ast_strdupa(data);
4199 AST_STANDARD_APP_ARGS(args, parse);
4201 if (ast_strlen_zero(args.interface)) {
4202 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4203 return -1;
4206 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
4207 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
4208 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
4209 return 0;
4212 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
4214 return 0;
4217 /*! \brief RemoveQueueMember application */
4218 static int rqm_exec(struct ast_channel *chan, void *data)
4220 int res=-1;
4221 char *parse, *temppos = NULL;
4222 AST_DECLARE_APP_ARGS(args,
4223 AST_APP_ARG(queuename);
4224 AST_APP_ARG(interface);
4225 AST_APP_ARG(options);
4229 if (ast_strlen_zero(data)) {
4230 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
4231 return -1;
4234 parse = ast_strdupa(data);
4236 AST_STANDARD_APP_ARGS(args, parse);
4238 if (ast_strlen_zero(args.interface)) {
4239 args.interface = ast_strdupa(chan->name);
4240 temppos = strrchr(args.interface, '-');
4241 if (temppos)
4242 *temppos = '\0';
4245 switch (remove_from_queue(args.queuename, args.interface)) {
4246 case RES_OKAY:
4247 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
4248 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
4249 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
4250 res = 0;
4251 break;
4252 case RES_EXISTS:
4253 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
4254 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
4255 res = 0;
4256 break;
4257 case RES_NOSUCHQUEUE:
4258 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
4259 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
4260 res = 0;
4261 break;
4262 case RES_NOT_DYNAMIC:
4263 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
4264 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
4265 res = 0;
4266 break;
4269 return res;
4272 /*! \brief AddQueueMember application */
4273 static int aqm_exec(struct ast_channel *chan, void *data)
4275 int res=-1;
4276 char *parse, *temppos = NULL;
4277 AST_DECLARE_APP_ARGS(args,
4278 AST_APP_ARG(queuename);
4279 AST_APP_ARG(interface);
4280 AST_APP_ARG(penalty);
4281 AST_APP_ARG(options);
4282 AST_APP_ARG(membername);
4283 AST_APP_ARG(state_interface);
4285 int penalty = 0;
4287 if (ast_strlen_zero(data)) {
4288 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
4289 return -1;
4292 parse = ast_strdupa(data);
4294 AST_STANDARD_APP_ARGS(args, parse);
4296 if (ast_strlen_zero(args.interface)) {
4297 args.interface = ast_strdupa(chan->name);
4298 temppos = strrchr(args.interface, '-');
4299 if (temppos)
4300 *temppos = '\0';
4303 if (!ast_strlen_zero(args.penalty)) {
4304 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
4305 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
4306 penalty = 0;
4310 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
4311 case RES_OKAY:
4312 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
4313 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
4314 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
4315 res = 0;
4316 break;
4317 case RES_EXISTS:
4318 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
4319 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
4320 res = 0;
4321 break;
4322 case RES_NOSUCHQUEUE:
4323 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
4324 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
4325 res = 0;
4326 break;
4327 case RES_OUTOFMEMORY:
4328 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
4329 break;
4332 return res;
4335 /*! \brief QueueLog application */
4336 static int ql_exec(struct ast_channel *chan, void *data)
4338 char *parse;
4340 AST_DECLARE_APP_ARGS(args,
4341 AST_APP_ARG(queuename);
4342 AST_APP_ARG(uniqueid);
4343 AST_APP_ARG(membername);
4344 AST_APP_ARG(event);
4345 AST_APP_ARG(params);
4348 if (ast_strlen_zero(data)) {
4349 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
4350 return -1;
4353 parse = ast_strdupa(data);
4355 AST_STANDARD_APP_ARGS(args, parse);
4357 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
4358 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
4359 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
4360 return -1;
4363 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
4364 "%s", args.params ? args.params : "");
4366 return 0;
4369 /*! \brief Copy rule from global list into specified queue */
4370 static void copy_rules(struct queue_ent *qe, const char *rulename)
4372 struct penalty_rule *pr_iter;
4373 struct rule_list *rl_iter;
4374 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
4375 AST_LIST_LOCK(&rule_lists);
4376 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
4377 if (!strcasecmp(rl_iter->name, tmp))
4378 break;
4380 if (rl_iter) {
4381 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
4382 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
4383 if (!new_pr) {
4384 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
4385 AST_LIST_UNLOCK(&rule_lists);
4386 break;
4388 new_pr->time = pr_iter->time;
4389 new_pr->max_value = pr_iter->max_value;
4390 new_pr->min_value = pr_iter->min_value;
4391 new_pr->max_relative = pr_iter->max_relative;
4392 new_pr->min_relative = pr_iter->min_relative;
4393 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
4396 AST_LIST_UNLOCK(&rule_lists);
4399 /*!\brief The starting point for all queue calls
4401 * The process involved here is to
4402 * 1. Parse the options specified in the call to Queue()
4403 * 2. Join the queue
4404 * 3. Wait in a loop until it is our turn to try calling a queue member
4405 * 4. Attempt to call a queue member
4406 * 5. If 4. did not result in a bridged call, then check for between
4407 * call options such as periodic announcements etc.
4408 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
4409 * exit the queue.
4411 static int queue_exec(struct ast_channel *chan, void *data)
4413 int res=-1;
4414 int ringing=0;
4415 const char *user_priority;
4416 const char *max_penalty_str;
4417 const char *min_penalty_str;
4418 int prio;
4419 int qcontinue = 0;
4420 int max_penalty, min_penalty;
4421 enum queue_result reason = QUEUE_UNKNOWN;
4422 /* whether to exit Queue application after the timeout hits */
4423 int tries = 0;
4424 int noption = 0;
4425 char *parse;
4426 int makeannouncement = 0;
4427 AST_DECLARE_APP_ARGS(args,
4428 AST_APP_ARG(queuename);
4429 AST_APP_ARG(options);
4430 AST_APP_ARG(url);
4431 AST_APP_ARG(announceoverride);
4432 AST_APP_ARG(queuetimeoutstr);
4433 AST_APP_ARG(agi);
4434 AST_APP_ARG(macro);
4435 AST_APP_ARG(gosub);
4436 AST_APP_ARG(rule);
4438 /* Our queue entry */
4439 struct queue_ent qe;
4441 if (ast_strlen_zero(data)) {
4442 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
4443 return -1;
4446 parse = ast_strdupa(data);
4447 AST_STANDARD_APP_ARGS(args, parse);
4449 /* Setup our queue entry */
4450 memset(&qe, 0, sizeof(qe));
4451 qe.start = time(NULL);
4453 /* set the expire time based on the supplied timeout; */
4454 if (!ast_strlen_zero(args.queuetimeoutstr))
4455 qe.expire = qe.start + atoi(args.queuetimeoutstr);
4456 else
4457 qe.expire = 0;
4459 /* Get the priority from the variable ${QUEUE_PRIO} */
4460 ast_channel_lock(chan);
4461 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
4462 if (user_priority) {
4463 if (sscanf(user_priority, "%d", &prio) == 1) {
4464 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
4465 } else {
4466 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
4467 user_priority, chan->name);
4468 prio = 0;
4470 } else {
4471 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
4472 prio = 0;
4475 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
4477 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
4478 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
4479 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
4480 } else {
4481 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
4482 max_penalty_str, chan->name);
4483 max_penalty = 0;
4485 } else {
4486 max_penalty = 0;
4489 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
4490 if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
4491 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
4492 } else {
4493 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
4494 min_penalty_str, chan->name);
4495 min_penalty = 0;
4497 } else {
4498 min_penalty = 0;
4500 ast_channel_unlock(chan);
4502 if (args.options && (strchr(args.options, 'r')))
4503 ringing = 1;
4505 if (args.options && (strchr(args.options, 'c')))
4506 qcontinue = 1;
4508 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
4509 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
4511 qe.chan = chan;
4512 qe.prio = prio;
4513 qe.max_penalty = max_penalty;
4514 qe.min_penalty = min_penalty;
4515 qe.last_pos_said = 0;
4516 qe.last_pos = 0;
4517 qe.last_periodic_announce_time = time(NULL);
4518 qe.last_periodic_announce_sound = 0;
4519 qe.valid_digits = 0;
4520 if (join_queue(args.queuename, &qe, &reason)) {
4521 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
4522 set_queue_result(chan, reason);
4523 return 0;
4525 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
4526 S_OR(chan->cid.cid_num, ""));
4527 copy_rules(&qe, args.rule);
4528 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
4529 check_turns:
4530 if (ringing) {
4531 ast_indicate(chan, AST_CONTROL_RINGING);
4532 } else {
4533 ast_moh_start(chan, qe.moh, NULL);
4536 /* This is the wait loop for callers 2 through maxlen */
4537 res = wait_our_turn(&qe, ringing, &reason);
4538 if (res) {
4539 goto stop;
4542 makeannouncement = 0;
4544 for (;;) {
4545 /* This is the wait loop for the head caller*/
4546 /* To exit, they may get their call answered; */
4547 /* they may dial a digit from the queue context; */
4548 /* or, they may timeout. */
4550 enum queue_member_status stat;
4552 /* Leave if we have exceeded our queuetimeout */
4553 if (qe.expire && (time(NULL) > qe.expire)) {
4554 record_abandoned(&qe);
4555 reason = QUEUE_TIMEOUT;
4556 res = 0;
4557 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
4558 qe.pos, qe.opos, (long) time(NULL) - qe.start);
4559 break;
4562 if (makeannouncement) {
4563 /* Make a position announcement, if enabled */
4564 if (qe.parent->announcefrequency)
4565 if ((res = say_position(&qe,ringing)))
4566 goto stop;
4568 makeannouncement = 1;
4570 /* Make a periodic announcement, if enabled */
4571 if (qe.parent->periodicannouncefrequency)
4572 if ((res = say_periodic_announcement(&qe,ringing)))
4573 goto stop;
4575 /* see if we need to move to the next penalty level for this queue */
4576 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
4577 update_qe_rule(&qe);
4580 /* Try calling all queue members for 'timeout' seconds */
4581 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
4582 if (res) {
4583 goto stop;
4586 stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
4588 /* exit after 'timeout' cycle if 'n' option enabled */
4589 if (noption && tries >= qe.parent->membercount) {
4590 ast_verb(3, "Exiting on time-out cycle\n");
4591 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
4592 record_abandoned(&qe);
4593 reason = QUEUE_TIMEOUT;
4594 res = 0;
4595 break;
4598 /* leave the queue if no agents, if enabled */
4599 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
4600 record_abandoned(&qe);
4601 reason = QUEUE_LEAVEEMPTY;
4602 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4603 res = 0;
4604 break;
4607 /* leave the queue if no reachable agents, if enabled */
4608 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
4609 record_abandoned(&qe);
4610 reason = QUEUE_LEAVEUNAVAIL;
4611 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4612 res = 0;
4613 break;
4615 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
4616 record_abandoned(&qe);
4617 reason = QUEUE_LEAVEUNAVAIL;
4618 res = 0;
4619 break;
4622 /* Leave if we have exceeded our queuetimeout */
4623 if (qe.expire && (time(NULL) > qe.expire)) {
4624 record_abandoned(&qe);
4625 reason = QUEUE_TIMEOUT;
4626 res = 0;
4627 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
4628 break;
4631 /* If using dynamic realtime members, we should regenerate the member list for this queue */
4632 update_realtime_members(qe.parent);
4634 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
4635 res = wait_a_bit(&qe);
4636 if (res)
4637 goto stop;
4639 /* Since this is a priority queue and
4640 * it is not sure that we are still at the head
4641 * of the queue, go and check for our turn again.
4643 if (!is_our_turn(&qe)) {
4644 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
4645 goto check_turns;
4649 stop:
4650 if (res) {
4651 if (res < 0) {
4652 if (!qe.handled) {
4653 record_abandoned(&qe);
4654 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
4655 "%d|%d|%ld", qe.pos, qe.opos,
4656 (long) time(NULL) - qe.start);
4658 res = -1;
4659 } else if (qe.valid_digits) {
4660 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
4661 "%s|%d", qe.digits, qe.pos);
4665 /* Don't allow return code > 0 */
4666 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
4667 res = 0;
4668 if (ringing) {
4669 ast_indicate(chan, -1);
4670 } else {
4671 ast_moh_stop(chan);
4673 ast_stopstream(chan);
4676 set_queue_variables(&qe);
4678 leave_queue(&qe);
4679 if (reason != QUEUE_UNKNOWN)
4680 set_queue_result(chan, reason);
4682 return res;
4686 * \brief create interface var with all queue details.
4687 * \retval 0 on success
4688 * \retval -1 on error
4690 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4692 int res = -1;
4693 struct call_queue *q, tmpq = {
4694 .name = data,
4697 char interfacevar[256] = "";
4698 float sl = 0;
4700 if (ast_strlen_zero(data)) {
4701 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4702 return -1;
4705 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4706 ao2_lock(q);
4707 if (q->setqueuevar) {
4708 sl = 0;
4709 res = 0;
4711 if (q->callscompleted > 0) {
4712 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4715 snprintf(interfacevar, sizeof(interfacevar),
4716 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
4717 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
4719 pbx_builtin_setvar_multiple(chan, interfacevar);
4722 ao2_unlock(q);
4723 queue_unref(q);
4724 } else {
4725 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4728 snprintf(buf, len, "%d", res);
4730 return 0;
4733 /*!
4734 * \brief Get number either busy / free or total members of a specific queue
4735 * \retval number of members (busy / free / total)
4736 * \retval -1 on error
4738 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4740 int count = 0;
4741 struct member *m;
4742 struct ao2_iterator mem_iter;
4743 struct call_queue *q;
4744 char *option;
4746 if (ast_strlen_zero(data)) {
4747 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4748 return -1;
4751 if ((option = strchr(data, ',')))
4752 *option++ = '\0';
4753 else
4754 option = "logged";
4755 if ((q = load_realtime_queue(data))) {
4756 ao2_lock(q);
4757 if (!strcasecmp(option, "logged")) {
4758 mem_iter = ao2_iterator_init(q->members, 0);
4759 while ((m = ao2_iterator_next(&mem_iter))) {
4760 /* Count the agents who are logged in and presently answering calls */
4761 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4762 count++;
4764 ao2_ref(m, -1);
4766 } else if (!strcasecmp(option, "free")) {
4767 mem_iter = ao2_iterator_init(q->members, 0);
4768 while ((m = ao2_iterator_next(&mem_iter))) {
4769 /* Count the agents who are logged in and presently answering calls */
4770 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
4771 count++;
4773 ao2_ref(m, -1);
4775 } else /* must be "count" */
4776 count = q->membercount;
4777 ao2_unlock(q);
4778 queue_unref(q);
4779 } else
4780 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4782 snprintf(buf, len, "%d", count);
4784 return 0;
4787 /*!
4788 * \brief Get the total number of members in a specific queue (Deprecated)
4789 * \retval number of members
4790 * \retval -1 on error
4792 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4794 int count = 0;
4795 struct member *m;
4796 struct call_queue *q;
4797 struct ao2_iterator mem_iter;
4798 static int depflag = 1;
4800 if (depflag) {
4801 depflag = 0;
4802 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");
4805 if (ast_strlen_zero(data)) {
4806 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4807 return -1;
4810 if ((q = load_realtime_queue(data))) {
4811 ao2_lock(q);
4812 mem_iter = ao2_iterator_init(q->members, 0);
4813 while ((m = ao2_iterator_next(&mem_iter))) {
4814 /* Count the agents who are logged in and presently answering calls */
4815 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4816 count++;
4818 ao2_ref(m, -1);
4820 ao2_unlock(q);
4821 queue_unref(q);
4822 } else
4823 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4825 snprintf(buf, len, "%d", count);
4827 return 0;
4830 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
4831 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4833 int count = 0;
4834 struct call_queue *q, tmpq = {
4835 .name = data,
4837 struct ast_variable *var = NULL;
4839 buf[0] = '\0';
4841 if (ast_strlen_zero(data)) {
4842 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
4843 return -1;
4846 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4847 ao2_lock(q);
4848 count = q->count;
4849 ao2_unlock(q);
4850 queue_unref(q);
4851 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
4852 /* if the queue is realtime but was not found in memory, this
4853 * means that the queue had been deleted from memory since it was
4854 * "dead." This means it has a 0 waiting count
4856 count = 0;
4857 ast_variables_destroy(var);
4858 } else
4859 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4861 snprintf(buf, len, "%d", count);
4863 return 0;
4866 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
4867 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4869 struct call_queue *q, tmpq = {
4870 .name = data,
4872 struct member *m;
4874 /* Ensure an otherwise empty list doesn't return garbage */
4875 buf[0] = '\0';
4877 if (ast_strlen_zero(data)) {
4878 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
4879 return -1;
4882 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4883 int buflen = 0, count = 0;
4884 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
4886 ao2_lock(q);
4887 while ((m = ao2_iterator_next(&mem_iter))) {
4888 /* strcat() is always faster than printf() */
4889 if (count++) {
4890 strncat(buf + buflen, ",", len - buflen - 1);
4891 buflen++;
4893 strncat(buf + buflen, m->membername, len - buflen - 1);
4894 buflen += strlen(m->membername);
4895 /* Safeguard against overflow (negative length) */
4896 if (buflen >= len - 2) {
4897 ao2_ref(m, -1);
4898 ast_log(LOG_WARNING, "Truncating list\n");
4899 break;
4901 ao2_ref(m, -1);
4903 ao2_unlock(q);
4904 queue_unref(q);
4905 } else
4906 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4908 /* We should already be terminated, but let's make sure. */
4909 buf[len - 1] = '\0';
4911 return 0;
4914 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
4915 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4917 int penalty;
4918 AST_DECLARE_APP_ARGS(args,
4919 AST_APP_ARG(queuename);
4920 AST_APP_ARG(interface);
4922 /* Make sure the returned value on error is NULL. */
4923 buf[0] = '\0';
4925 if (ast_strlen_zero(data)) {
4926 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4927 return -1;
4930 AST_STANDARD_APP_ARGS(args, data);
4932 if (args.argc < 2) {
4933 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4934 return -1;
4937 penalty = get_member_penalty (args.queuename, args.interface);
4939 if (penalty >= 0) /* remember that buf is already '\0' */
4940 snprintf (buf, len, "%d", penalty);
4942 return 0;
4945 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
4946 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
4948 int penalty;
4949 AST_DECLARE_APP_ARGS(args,
4950 AST_APP_ARG(queuename);
4951 AST_APP_ARG(interface);
4954 if (ast_strlen_zero(data)) {
4955 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4956 return -1;
4959 AST_STANDARD_APP_ARGS(args, data);
4961 if (args.argc < 2) {
4962 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4963 return -1;
4966 penalty = atoi(value);
4968 if (ast_strlen_zero(args.interface)) {
4969 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
4970 return -1;
4973 /* if queuename = NULL then penalty will be set for interface in all the queues. */
4974 if (set_member_penalty(args.queuename, args.interface, penalty)) {
4975 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
4976 return -1;
4979 return 0;
4982 static struct ast_custom_function queuevar_function = {
4983 .name = "QUEUE_VARIABLES",
4984 .synopsis = "Return Queue information in variables",
4985 .syntax = "QUEUE_VARIABLES(<queuename>)",
4986 .desc =
4987 "Makes the following queue variables available.\n"
4988 "QUEUEMAX maxmimum number of calls allowed\n"
4989 "QUEUESTRATEGY the strategy of the queue\n"
4990 "QUEUECALLS number of calls currently in the queue\n"
4991 "QUEUEHOLDTIME current average hold time\n"
4992 "QUEUECOMPLETED number of completed calls for the queue\n"
4993 "QUEUEABANDONED number of abandoned calls\n"
4994 "QUEUESRVLEVEL queue service level\n"
4995 "QUEUESRVLEVELPERF current service level performance\n"
4996 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
4997 .read = queue_function_var,
5000 static struct ast_custom_function queuemembercount_function = {
5001 .name = "QUEUE_MEMBER",
5002 .synopsis = "Count number of members answering a queue",
5003 .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
5004 .desc =
5005 "Returns the number of members currently associated with the specified queue.\n"
5006 "One of three options may be passed to determine the count returned:\n"
5007 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
5008 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
5009 "\"count\" - Returns the total number of members for the specified queue\n",
5010 .read = queue_function_qac,
5013 static struct ast_custom_function queuemembercount_dep = {
5014 .name = "QUEUE_MEMBER_COUNT",
5015 .synopsis = "Count number of members answering a queue",
5016 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
5017 .desc =
5018 "Returns the number of members currently associated with the specified queue.\n\n"
5019 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
5020 .read = queue_function_qac_dep,
5023 static struct ast_custom_function queuewaitingcount_function = {
5024 .name = "QUEUE_WAITING_COUNT",
5025 .synopsis = "Count number of calls currently waiting in a queue",
5026 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
5027 .desc =
5028 "Returns the number of callers currently waiting in the specified queue.\n",
5029 .read = queue_function_queuewaitingcount,
5032 static struct ast_custom_function queuememberlist_function = {
5033 .name = "QUEUE_MEMBER_LIST",
5034 .synopsis = "Returns a list of interfaces on a queue",
5035 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
5036 .desc =
5037 "Returns a comma-separated list of members associated with the specified queue.\n",
5038 .read = queue_function_queuememberlist,
5041 static struct ast_custom_function queuememberpenalty_function = {
5042 .name = "QUEUE_MEMBER_PENALTY",
5043 .synopsis = "Gets or sets queue members penalty.",
5044 .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
5045 .desc =
5046 "Gets or sets queue members penalty\n",
5047 .read = queue_function_memberpenalty_read,
5048 .write = queue_function_memberpenalty_write,
5051 static int reload_queue_rules(int reload)
5053 struct ast_config *cfg;
5054 struct rule_list *rl_iter, *new_rl;
5055 struct penalty_rule *pr_iter;
5056 char *rulecat = NULL;
5057 struct ast_variable *rulevar = NULL;
5058 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5060 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
5061 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
5062 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
5063 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
5064 return AST_MODULE_LOAD_SUCCESS;
5065 } else {
5066 AST_LIST_LOCK(&rule_lists);
5067 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
5068 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
5069 ast_free(pr_iter);
5070 ast_free(rl_iter);
5072 while ((rulecat = ast_category_browse(cfg, rulecat))) {
5073 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
5074 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
5075 AST_LIST_UNLOCK(&rule_lists);
5076 return AST_MODULE_LOAD_FAILURE;
5077 } else {
5078 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
5079 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
5080 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
5081 if(!strcasecmp(rulevar->name, "penaltychange"))
5082 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
5083 else
5084 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
5087 AST_LIST_UNLOCK(&rule_lists);
5090 ast_config_destroy(cfg);
5092 return AST_MODULE_LOAD_SUCCESS;
5096 static int reload_queues(int reload)
5098 struct call_queue *q;
5099 struct ast_config *cfg;
5100 char *cat, *tmp;
5101 struct ast_variable *var;
5102 struct member *cur, *newm;
5103 struct ao2_iterator mem_iter;
5104 int new;
5105 const char *general_val = NULL;
5106 char parse[80];
5107 char *interface, *state_interface;
5108 char *membername = NULL;
5109 int penalty;
5110 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5111 struct ao2_iterator queue_iter;
5112 AST_DECLARE_APP_ARGS(args,
5113 AST_APP_ARG(interface);
5114 AST_APP_ARG(penalty);
5115 AST_APP_ARG(membername);
5116 AST_APP_ARG(state_interface);
5119 /*First things first. Let's load queuerules.conf*/
5120 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
5121 return AST_MODULE_LOAD_FAILURE;
5123 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
5124 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
5125 return 0;
5126 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
5127 return 0;
5128 ao2_lock(queues);
5129 use_weight=0;
5130 /* Mark all queues as dead for the moment */
5131 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
5132 while ((q = ao2_iterator_next(&queue_iter))) {
5133 if (!q->realtime) {
5134 q->dead = 1;
5135 q->found = 0;
5137 queue_unref(q);
5140 /* Chug through config file */
5141 cat = NULL;
5142 while ((cat = ast_category_browse(cfg, cat)) ) {
5143 if (!strcasecmp(cat, "general")) {
5144 /* Initialize global settings */
5145 queue_keep_stats = 0;
5146 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
5147 queue_keep_stats = ast_true(general_val);
5148 queue_persistent_members = 0;
5149 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
5150 queue_persistent_members = ast_true(general_val);
5151 autofill_default = 0;
5152 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
5153 autofill_default = ast_true(general_val);
5154 montype_default = 0;
5155 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
5156 if (!strcasecmp(general_val, "mixmonitor"))
5157 montype_default = 1;
5159 update_cdr = 0;
5160 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
5161 update_cdr = ast_true(general_val);
5162 shared_lastcall = 0;
5163 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
5164 shared_lastcall = ast_true(general_val);
5165 } else { /* Define queue */
5166 /* Look for an existing one */
5167 struct call_queue tmpq = {
5168 .name = cat,
5170 if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
5171 /* Make one then */
5172 if (!(q = alloc_queue(cat))) {
5173 /* TODO: Handle memory allocation failure */
5175 new = 1;
5176 } else
5177 new = 0;
5178 if (q) {
5179 const char *tmpvar = NULL;
5180 if (!new)
5181 ao2_lock(q);
5182 /* Check if a queue with this name already exists */
5183 if (q->found) {
5184 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
5185 if (!new) {
5186 ao2_unlock(q);
5187 queue_unref(q);
5189 continue;
5191 /* Due to the fact that the "linear" strategy will have a different allocation
5192 * scheme for queue members, we must devise the queue's strategy before other initializations
5194 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
5195 q->strategy = strat2int(tmpvar);
5196 if (q->strategy < 0) {
5197 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
5198 tmpvar, q->name);
5199 q->strategy = QUEUE_STRATEGY_RINGALL;
5201 } else
5202 q->strategy = QUEUE_STRATEGY_RINGALL;
5203 /* Re-initialize the queue, and clear statistics */
5204 init_queue(q);
5205 if (!queue_keep_stats)
5206 clear_queue(q);
5207 mem_iter = ao2_iterator_init(q->members, 0);
5208 while ((cur = ao2_iterator_next(&mem_iter))) {
5209 if (!cur->dynamic) {
5210 cur->delme = 1;
5212 ao2_ref(cur, -1);
5214 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
5215 if (!strcasecmp(var->name, "member")) {
5216 struct member tmpmem;
5217 membername = NULL;
5219 /* Add a new member */
5220 ast_copy_string(parse, var->value, sizeof(parse));
5222 AST_STANDARD_APP_ARGS(args, parse);
5224 interface = args.interface;
5225 if (!ast_strlen_zero(args.penalty)) {
5226 tmp = args.penalty;
5227 while (*tmp && *tmp < 33) tmp++;
5228 penalty = atoi(tmp);
5229 if (penalty < 0) {
5230 penalty = 0;
5232 } else
5233 penalty = 0;
5235 if (!ast_strlen_zero(args.membername)) {
5236 membername = args.membername;
5237 while (*membername && *membername < 33) membername++;
5240 if (!ast_strlen_zero(args.state_interface)) {
5241 state_interface = args.state_interface;
5242 while (*state_interface && *state_interface < 33) state_interface++;
5243 } else
5244 state_interface = interface;
5246 /* Find the old position in the list */
5247 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
5248 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
5249 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
5250 if (cur && strcasecmp(cur->state_interface, state_interface)) {
5251 remove_from_interfaces(cur->state_interface);
5253 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
5254 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
5255 add_to_interfaces(state_interface);
5256 ao2_link(q->members, newm);
5257 ao2_ref(newm, -1);
5258 newm = NULL;
5260 if (cur)
5261 ao2_ref(cur, -1);
5262 else {
5263 q->membercount++;
5265 } else {
5266 queue_set_param(q, var->name, var->value, var->lineno, 1);
5270 /* Free remaining members marked as delme */
5271 mem_iter = ao2_iterator_init(q->members, 0);
5272 while ((cur = ao2_iterator_next(&mem_iter))) {
5273 if (! cur->delme) {
5274 ao2_ref(cur, -1);
5275 continue;
5277 q->membercount--;
5278 ao2_unlink(q->members, cur);
5279 remove_from_interfaces(cur->interface);
5280 ao2_ref(cur, -1);
5283 if (new) {
5284 ao2_link(queues, q);
5285 } else
5286 ao2_unlock(q);
5287 queue_unref(q);
5291 ast_config_destroy(cfg);
5292 queue_iter = ao2_iterator_init(queues, 0);
5293 while ((q = ao2_iterator_next(&queue_iter))) {
5294 if (q->dead) {
5295 ao2_unlink(queues, q);
5296 } else {
5297 ao2_lock(q);
5298 mem_iter = ao2_iterator_init(q->members, 0);
5299 while ((cur = ao2_iterator_next(&mem_iter))) {
5300 if (cur->dynamic)
5301 q->membercount++;
5302 cur->status = ast_device_state(cur->interface);
5303 ao2_ref(cur, -1);
5305 ao2_unlock(q);
5307 queue_unref(q);
5309 ao2_unlock(queues);
5310 return 1;
5313 /*! \brief direct ouput to manager or cli with proper terminator */
5314 static void do_print(struct mansession *s, int fd, const char *str)
5316 if (s)
5317 astman_append(s, "%s\r\n", str);
5318 else
5319 ast_cli(fd, "%s\n", str);
5322 /*!
5323 * \brief Show queue(s) status and statistics
5325 * List the queues strategy, calls processed, members logged in,
5326 * other queue statistics such as avg hold time.
5328 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
5330 struct call_queue *q;
5331 struct ast_str *out = ast_str_alloca(240);
5332 int found = 0;
5333 time_t now = time(NULL);
5334 struct ao2_iterator queue_iter;
5335 struct ao2_iterator mem_iter;
5337 if (argc != 2 && argc != 3)
5338 return CLI_SHOWUSAGE;
5340 /* We only want to load realtime queues when a specific queue is asked for. */
5341 if (argc == 3) /* specific queue */
5342 load_realtime_queue(argv[2]);
5344 queue_iter = ao2_iterator_init(queues, 0);
5345 while ((q = ao2_iterator_next(&queue_iter))) {
5346 float sl;
5348 ao2_lock(q);
5349 if (argc == 3 && strcasecmp(q->name, argv[2])) {
5350 ao2_unlock(q);
5351 continue;
5353 found = 1;
5355 ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
5356 if (q->maxlen)
5357 ast_str_append(&out, 0, "%d", q->maxlen);
5358 else
5359 ast_str_append(&out, 0, "unlimited");
5360 sl = 0;
5361 if (q->callscompleted > 0)
5362 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
5363 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
5364 int2strat(q->strategy), q->holdtime, q->weight,
5365 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
5366 do_print(s, fd, out->str);
5367 if (!ao2_container_count(q->members))
5368 do_print(s, fd, " No Members");
5369 else {
5370 struct member *mem;
5372 do_print(s, fd, " Members: ");
5373 mem_iter = ao2_iterator_init(q->members, 0);
5374 while ((mem = ao2_iterator_next(&mem_iter))) {
5375 ast_str_set(&out, 0, " %s", mem->membername);
5376 if (mem->penalty)
5377 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
5378 ast_str_append(&out, 0, "%s%s%s (%s)",
5379 mem->dynamic ? " (dynamic)" : "",
5380 mem->realtime ? " (realtime)" : "",
5381 mem->paused ? " (paused)" : "",
5382 devstate2str(mem->status));
5383 if (mem->calls)
5384 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
5385 mem->calls, (long) (time(NULL) - mem->lastcall));
5386 else
5387 ast_str_append(&out, 0, " has taken no calls yet");
5388 do_print(s, fd, out->str);
5389 ao2_ref(mem, -1);
5392 if (!q->head)
5393 do_print(s, fd, " No Callers");
5394 else {
5395 struct queue_ent *qe;
5396 int pos = 1;
5398 do_print(s, fd, " Callers: ");
5399 for (qe = q->head; qe; qe = qe->next) {
5400 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
5401 pos++, qe->chan->name, (long) (now - qe->start) / 60,
5402 (long) (now - qe->start) % 60, qe->prio);
5403 do_print(s, fd, out->str);
5406 do_print(s, fd, ""); /* blank line between entries */
5407 ao2_unlock(q);
5408 if (argc == 3) { /* print a specific entry */
5409 queue_unref(q);
5410 break;
5412 queue_unref(q);
5414 if (!found) {
5415 if (argc == 3)
5416 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
5417 else
5418 ast_str_set(&out, 0, "No queues.");
5419 do_print(s, fd, out->str);
5421 return CLI_SUCCESS;
5424 static char *complete_queue(const char *line, const char *word, int pos, int state)
5426 struct call_queue *q;
5427 char *ret = NULL;
5428 int which = 0;
5429 int wordlen = strlen(word);
5430 struct ao2_iterator queue_iter;
5432 queue_iter = ao2_iterator_init(queues, 0);
5433 while ((q = ao2_iterator_next(&queue_iter))) {
5434 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
5435 ret = ast_strdup(q->name);
5436 queue_unref(q);
5437 break;
5439 queue_unref(q);
5442 return ret;
5445 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
5447 if (pos == 2)
5448 return complete_queue(line, word, pos, state);
5449 return NULL;
5452 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5454 switch ( cmd ) {
5455 case CLI_INIT:
5456 e->command = "queue show";
5457 e->usage =
5458 "Usage: queue show\n"
5459 " Provides summary information on a specified queue.\n";
5460 return NULL;
5461 case CLI_GENERATE:
5462 return complete_queue_show(a->line, a->word, a->pos, a->n);
5465 return __queues_show(NULL, a->fd, a->argc, a->argv);
5468 /*!\brief callback to display queues status in manager
5469 \addtogroup Group_AMI
5471 static int manager_queues_show(struct mansession *s, const struct message *m)
5473 char *a[] = { "queue", "show" };
5475 __queues_show(s, -1, 2, a);
5476 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
5478 return RESULT_SUCCESS;
5481 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
5483 const char *rule = astman_get_header(m, "Rule");
5484 struct rule_list *rl_iter;
5485 struct penalty_rule *pr_iter;
5487 AST_LIST_LOCK(&rule_lists);
5488 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
5489 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
5490 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
5491 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
5492 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 );
5494 if (!ast_strlen_zero(rule))
5495 break;
5498 AST_LIST_UNLOCK(&rule_lists);
5500 astman_append(s, "\r\n\r\n");
5502 return RESULT_SUCCESS;
5505 /*! \brief Summary of queue info via the AMI */
5506 static int manager_queues_summary(struct mansession *s, const struct message *m)
5508 time_t now;
5509 int qmemcount = 0;
5510 int qmemavail = 0;
5511 int qchancount = 0;
5512 int qlongestholdtime = 0;
5513 const char *id = astman_get_header(m, "ActionID");
5514 const char *queuefilter = astman_get_header(m, "Queue");
5515 char idText[256] = "";
5516 struct call_queue *q;
5517 struct queue_ent *qe;
5518 struct member *mem;
5519 struct ao2_iterator queue_iter;
5520 struct ao2_iterator mem_iter;
5522 astman_send_ack(s, m, "Queue summary will follow");
5523 time(&now);
5524 if (!ast_strlen_zero(id))
5525 snprintf(idText, 256, "ActionID: %s\r\n", id);
5526 queue_iter = ao2_iterator_init(queues, 0);
5527 while ((q = ao2_iterator_next(&queue_iter))) {
5528 ao2_lock(q);
5530 /* List queue properties */
5531 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5532 /* Reset the necessary local variables if no queuefilter is set*/
5533 qmemcount = 0;
5534 qmemavail = 0;
5535 qchancount = 0;
5536 qlongestholdtime = 0;
5538 /* List Queue Members */
5539 mem_iter = ao2_iterator_init(q->members, 0);
5540 while ((mem = ao2_iterator_next(&mem_iter))) {
5541 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
5542 ++qmemcount;
5543 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
5544 ++qmemavail;
5547 ao2_ref(mem, -1);
5549 for (qe = q->head; qe; qe = qe->next) {
5550 if ((now - qe->start) > qlongestholdtime) {
5551 qlongestholdtime = now - qe->start;
5553 ++qchancount;
5555 astman_append(s, "Event: QueueSummary\r\n"
5556 "Queue: %s\r\n"
5557 "LoggedIn: %d\r\n"
5558 "Available: %d\r\n"
5559 "Callers: %d\r\n"
5560 "HoldTime: %d\r\n"
5561 "LongestHoldTime: %d\r\n"
5562 "%s"
5563 "\r\n",
5564 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
5566 ao2_unlock(q);
5567 queue_unref(q);
5569 astman_append(s,
5570 "Event: QueueSummaryComplete\r\n"
5571 "%s"
5572 "\r\n", idText);
5574 return RESULT_SUCCESS;
5577 /*! \brief Queue status info via AMI */
5578 static int manager_queues_status(struct mansession *s, const struct message *m)
5580 time_t now;
5581 int pos;
5582 const char *id = astman_get_header(m,"ActionID");
5583 const char *queuefilter = astman_get_header(m,"Queue");
5584 const char *memberfilter = astman_get_header(m,"Member");
5585 char idText[256] = "";
5586 struct call_queue *q;
5587 struct queue_ent *qe;
5588 float sl = 0;
5589 struct member *mem;
5590 struct ao2_iterator queue_iter;
5591 struct ao2_iterator mem_iter;
5593 astman_send_ack(s, m, "Queue status will follow");
5594 time(&now);
5595 if (!ast_strlen_zero(id))
5596 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
5598 queue_iter = ao2_iterator_init(queues, 0);
5599 while ((q = ao2_iterator_next(&queue_iter))) {
5600 ao2_lock(q);
5602 /* List queue properties */
5603 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5604 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
5605 astman_append(s, "Event: QueueParams\r\n"
5606 "Queue: %s\r\n"
5607 "Max: %d\r\n"
5608 "Strategy: %s\r\n"
5609 "Calls: %d\r\n"
5610 "Holdtime: %d\r\n"
5611 "Completed: %d\r\n"
5612 "Abandoned: %d\r\n"
5613 "ServiceLevel: %d\r\n"
5614 "ServicelevelPerf: %2.1f\r\n"
5615 "Weight: %d\r\n"
5616 "%s"
5617 "\r\n",
5618 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
5619 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
5620 /* List Queue Members */
5621 mem_iter = ao2_iterator_init(q->members, 0);
5622 while ((mem = ao2_iterator_next(&mem_iter))) {
5623 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
5624 astman_append(s, "Event: QueueMember\r\n"
5625 "Queue: %s\r\n"
5626 "Name: %s\r\n"
5627 "Location: %s\r\n"
5628 "Membership: %s\r\n"
5629 "Penalty: %d\r\n"
5630 "CallsTaken: %d\r\n"
5631 "LastCall: %d\r\n"
5632 "Status: %d\r\n"
5633 "Paused: %d\r\n"
5634 "%s"
5635 "\r\n",
5636 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
5637 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
5639 ao2_ref(mem, -1);
5641 /* List Queue Entries */
5642 pos = 1;
5643 for (qe = q->head; qe; qe = qe->next) {
5644 astman_append(s, "Event: QueueEntry\r\n"
5645 "Queue: %s\r\n"
5646 "Position: %d\r\n"
5647 "Channel: %s\r\n"
5648 "CallerIDNum: %s\r\n"
5649 "CallerIDName: %s\r\n"
5650 "Wait: %ld\r\n"
5651 "%s"
5652 "\r\n",
5653 q->name, pos++, qe->chan->name,
5654 S_OR(qe->chan->cid.cid_num, "unknown"),
5655 S_OR(qe->chan->cid.cid_name, "unknown"),
5656 (long) (now - qe->start), idText);
5659 ao2_unlock(q);
5660 queue_unref(q);
5663 astman_append(s,
5664 "Event: QueueStatusComplete\r\n"
5665 "%s"
5666 "\r\n",idText);
5668 return RESULT_SUCCESS;
5671 static int manager_add_queue_member(struct mansession *s, const struct message *m)
5673 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
5674 int paused, penalty = 0;
5676 queuename = astman_get_header(m, "Queue");
5677 interface = astman_get_header(m, "Interface");
5678 penalty_s = astman_get_header(m, "Penalty");
5679 paused_s = astman_get_header(m, "Paused");
5680 membername = astman_get_header(m, "MemberName");
5681 state_interface = astman_get_header(m, "StateInterface");
5683 if (ast_strlen_zero(queuename)) {
5684 astman_send_error(s, m, "'Queue' not specified.");
5685 return 0;
5688 if (ast_strlen_zero(interface)) {
5689 astman_send_error(s, m, "'Interface' not specified.");
5690 return 0;
5693 if (ast_strlen_zero(penalty_s))
5694 penalty = 0;
5695 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
5696 penalty = 0;
5698 if (ast_strlen_zero(paused_s))
5699 paused = 0;
5700 else
5701 paused = abs(ast_true(paused_s));
5703 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
5704 case RES_OKAY:
5705 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
5706 astman_send_ack(s, m, "Added interface to queue");
5707 break;
5708 case RES_EXISTS:
5709 astman_send_error(s, m, "Unable to add interface: Already there");
5710 break;
5711 case RES_NOSUCHQUEUE:
5712 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
5713 break;
5714 case RES_OUTOFMEMORY:
5715 astman_send_error(s, m, "Out of memory");
5716 break;
5719 return 0;
5722 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
5724 const char *queuename, *interface;
5726 queuename = astman_get_header(m, "Queue");
5727 interface = astman_get_header(m, "Interface");
5729 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
5730 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
5731 return 0;
5734 switch (remove_from_queue(queuename, interface)) {
5735 case RES_OKAY:
5736 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
5737 astman_send_ack(s, m, "Removed interface from queue");
5738 break;
5739 case RES_EXISTS:
5740 astman_send_error(s, m, "Unable to remove interface: Not there");
5741 break;
5742 case RES_NOSUCHQUEUE:
5743 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
5744 break;
5745 case RES_OUTOFMEMORY:
5746 astman_send_error(s, m, "Out of memory");
5747 break;
5748 case RES_NOT_DYNAMIC:
5749 astman_send_error(s, m, "Member not dynamic");
5750 break;
5753 return 0;
5756 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
5758 const char *queuename, *interface, *paused_s, *reason;
5759 int paused;
5761 interface = astman_get_header(m, "Interface");
5762 paused_s = astman_get_header(m, "Paused");
5763 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
5764 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */
5766 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
5767 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
5768 return 0;
5771 paused = abs(ast_true(paused_s));
5773 if (set_member_paused(queuename, interface, reason, paused))
5774 astman_send_error(s, m, "Interface not found");
5775 else
5776 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
5777 return 0;
5780 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
5782 const char *queuename, *event, *message, *interface, *uniqueid;
5784 queuename = astman_get_header(m, "Queue");
5785 uniqueid = astman_get_header(m, "UniqueId");
5786 interface = astman_get_header(m, "Interface");
5787 event = astman_get_header(m, "Event");
5788 message = astman_get_header(m, "Message");
5790 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
5791 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
5792 return 0;
5795 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
5796 astman_send_ack(s, m, "Event added successfully");
5798 return 0;
5801 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
5803 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
5804 switch (pos) {
5805 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
5806 return NULL;
5807 case 4: /* only one possible match, "to" */
5808 return state == 0 ? ast_strdup("to") : NULL;
5809 case 5: /* <queue> */
5810 return complete_queue(line, word, pos, state);
5811 case 6: /* only one possible match, "penalty" */
5812 return state == 0 ? ast_strdup("penalty") : NULL;
5813 case 7:
5814 if (state < 100) { /* 0-99 */
5815 char *num;
5816 if ((num = ast_malloc(3))) {
5817 sprintf(num, "%d", state);
5819 return num;
5820 } else {
5821 return NULL;
5823 case 8: /* only one possible match, "as" */
5824 return state == 0 ? ast_strdup("as") : NULL;
5825 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
5826 return NULL;
5827 default:
5828 return NULL;
5832 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
5834 const char *queuename, *interface, *penalty_s;
5835 int penalty;
5837 interface = astman_get_header(m, "Interface");
5838 penalty_s = astman_get_header(m, "Penalty");
5839 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
5840 queuename = astman_get_header(m, "Queue");
5842 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
5843 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
5844 return 0;
5847 penalty = atoi(penalty_s);
5849 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
5850 astman_send_error(s, m, "Invalid interface, queuename or penalty");
5851 else
5852 astman_send_ack(s, m, "Interface penalty set successfully");
5854 return 0;
5857 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5859 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
5860 int penalty;
5862 switch ( cmd ) {
5863 case CLI_INIT:
5864 e->command = "queue add member";
5865 e->usage =
5866 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
5867 return NULL;
5868 case CLI_GENERATE:
5869 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
5872 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
5873 return CLI_SHOWUSAGE;
5874 } else if (strcmp(a->argv[4], "to")) {
5875 return CLI_SHOWUSAGE;
5876 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
5877 return CLI_SHOWUSAGE;
5878 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
5879 return CLI_SHOWUSAGE;
5880 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
5881 return CLI_SHOWUSAGE;
5884 queuename = a->argv[5];
5885 interface = a->argv[3];
5886 if (a->argc >= 8) {
5887 if (sscanf(a->argv[7], "%d", &penalty) == 1) {
5888 if (penalty < 0) {
5889 ast_cli(a->fd, "Penalty must be >= 0\n");
5890 penalty = 0;
5892 } else {
5893 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
5894 penalty = 0;
5896 } else {
5897 penalty = 0;
5900 if (a->argc >= 10) {
5901 membername = a->argv[9];
5904 if (a->argc >= 12) {
5905 state_interface = a->argv[11];
5908 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
5909 case RES_OKAY:
5910 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
5911 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
5912 return CLI_SUCCESS;
5913 case RES_EXISTS:
5914 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
5915 return CLI_FAILURE;
5916 case RES_NOSUCHQUEUE:
5917 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
5918 return CLI_FAILURE;
5919 case RES_OUTOFMEMORY:
5920 ast_cli(a->fd, "Out of memory\n");
5921 return CLI_FAILURE;
5922 case RES_NOT_DYNAMIC:
5923 ast_cli(a->fd, "Member not dynamic\n");
5924 return CLI_FAILURE;
5925 default:
5926 return CLI_FAILURE;
5930 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
5932 int which = 0;
5933 struct call_queue *q;
5934 struct member *m;
5935 struct ao2_iterator queue_iter;
5936 struct ao2_iterator mem_iter;
5937 int wordlen = strlen(word);
5939 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
5940 if (pos > 5 || pos < 3)
5941 return NULL;
5942 if (pos == 4) /* only one possible match, 'from' */
5943 return (state == 0 ? ast_strdup("from") : NULL);
5945 if (pos == 5) /* No need to duplicate code */
5946 return complete_queue(line, word, pos, state);
5948 /* here is the case for 3, <member> */
5949 queue_iter = ao2_iterator_init(queues, 0);
5950 while ((q = ao2_iterator_next(&queue_iter))) {
5951 ao2_lock(q);
5952 mem_iter = ao2_iterator_init(q->members, 0);
5953 while ((m = ao2_iterator_next(&mem_iter))) {
5954 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
5955 char *tmp;
5956 ao2_unlock(q);
5957 tmp = m->membername;
5958 ao2_ref(m, -1);
5959 queue_unref(q);
5960 return ast_strdup(tmp);
5962 ao2_ref(m, -1);
5964 ao2_unlock(q);
5965 queue_unref(q);
5968 return NULL;
5971 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5973 char *queuename, *interface;
5975 switch (cmd) {
5976 case CLI_INIT:
5977 e->command = "queue remove member";
5978 e->usage = "Usage: queue remove member <channel> from <queue>\n";
5979 return NULL;
5980 case CLI_GENERATE:
5981 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
5984 if (a->argc != 6) {
5985 return CLI_SHOWUSAGE;
5986 } else if (strcmp(a->argv[4], "from")) {
5987 return CLI_SHOWUSAGE;
5990 queuename = a->argv[5];
5991 interface = a->argv[3];
5993 switch (remove_from_queue(queuename, interface)) {
5994 case RES_OKAY:
5995 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
5996 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
5997 return CLI_SUCCESS;
5998 case RES_EXISTS:
5999 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
6000 return CLI_FAILURE;
6001 case RES_NOSUCHQUEUE:
6002 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
6003 return CLI_FAILURE;
6004 case RES_OUTOFMEMORY:
6005 ast_cli(a->fd, "Out of memory\n");
6006 return CLI_FAILURE;
6007 default:
6008 return CLI_FAILURE;
6012 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
6014 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
6015 switch (pos) {
6016 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
6017 return NULL;
6018 case 4: /* only one possible match, "queue" */
6019 return state == 0 ? ast_strdup("queue") : NULL;
6020 case 5: /* <queue> */
6021 return complete_queue(line, word, pos, state);
6022 case 6: /* "reason" */
6023 return state == 0 ? ast_strdup("reason") : NULL;
6024 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
6025 return NULL;
6026 default:
6027 return NULL;
6031 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6033 char *queuename, *interface, *reason;
6034 int paused;
6036 switch (cmd) {
6037 case CLI_INIT:
6038 e->command = "queue {pause|unpause} member";
6039 e->usage =
6040 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
6041 " Pause or unpause a queue member. Not specifying a particular queue\n"
6042 " will pause or unpause a member across all queues to which the member\n"
6043 " belongs.\n";
6044 return NULL;
6045 case CLI_GENERATE:
6046 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
6049 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
6050 return CLI_SHOWUSAGE;
6051 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
6052 return CLI_SHOWUSAGE;
6053 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
6054 return CLI_SHOWUSAGE;
6058 interface = a->argv[3];
6059 queuename = a->argc >= 6 ? a->argv[5] : NULL;
6060 reason = a->argc == 8 ? a->argv[7] : NULL;
6061 paused = !strcasecmp(a->argv[1], "pause");
6063 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
6064 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
6065 if (!ast_strlen_zero(queuename))
6066 ast_cli(a->fd, " in queue '%s'", queuename);
6067 if (!ast_strlen_zero(reason))
6068 ast_cli(a->fd, " for reason '%s'", reason);
6069 ast_cli(a->fd, "\n");
6070 return CLI_SUCCESS;
6071 } else {
6072 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
6073 if (!ast_strlen_zero(queuename))
6074 ast_cli(a->fd, " in queue '%s'", queuename);
6075 if (!ast_strlen_zero(reason))
6076 ast_cli(a->fd, " for reason '%s'", reason);
6077 ast_cli(a->fd, "\n");
6078 return CLI_FAILURE;
6082 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
6084 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
6085 switch (pos) {
6086 case 4:
6087 if (state == 0) {
6088 return ast_strdup("on");
6089 } else {
6090 return NULL;
6092 case 6:
6093 if (state == 0) {
6094 return ast_strdup("in");
6095 } else {
6096 return NULL;
6098 case 7:
6099 return complete_queue(line, word, pos, state);
6100 default:
6101 return NULL;
6105 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6107 char *queuename = NULL, *interface;
6108 int penalty = 0;
6110 switch (cmd) {
6111 case CLI_INIT:
6112 e->command = "queue set penalty";
6113 e->usage =
6114 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
6115 "Set a member's penalty in the queue specified. If no queue is specified\n"
6116 "then that interface's penalty is set in all queues to which that interface is a member\n";
6117 return NULL;
6118 case CLI_GENERATE:
6119 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
6122 if (a->argc != 6 && a->argc != 8) {
6123 return CLI_SHOWUSAGE;
6124 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
6125 return CLI_SHOWUSAGE;
6128 if (a->argc == 8)
6129 queuename = a->argv[7];
6130 interface = a->argv[5];
6131 penalty = atoi(a->argv[3]);
6133 switch (set_member_penalty(queuename, interface, penalty)) {
6134 case RESULT_SUCCESS:
6135 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6136 return CLI_SUCCESS;
6137 case RESULT_FAILURE:
6138 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6139 return CLI_FAILURE;
6140 default:
6141 return CLI_FAILURE;
6145 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
6147 int which = 0;
6148 struct rule_list *rl_iter;
6149 int wordlen = strlen(word);
6150 char *ret = NULL;
6151 if (pos != 3) /* Wha? */ {
6152 ast_log(LOG_DEBUG, "Hitting this???, pos is %d\n", pos);
6153 return NULL;
6156 AST_LIST_LOCK(&rule_lists);
6157 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6158 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
6159 ret = ast_strdup(rl_iter->name);
6160 break;
6163 AST_LIST_UNLOCK(&rule_lists);
6165 return ret;
6168 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6170 char *rule;
6171 struct rule_list *rl_iter;
6172 struct penalty_rule *pr_iter;
6173 switch (cmd) {
6174 case CLI_INIT:
6175 e->command = "queue rules show";
6176 e->usage =
6177 "Usage: queue rules show [rulename]\n"
6178 "Show the list of rules associated with rulename. If no\n"
6179 "rulename is specified, list all rules defined in queuerules.conf\n";
6180 return NULL;
6181 case CLI_GENERATE:
6182 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
6185 if (a->argc != 3 && a->argc != 4)
6186 return CLI_SHOWUSAGE;
6188 rule = a->argc == 4 ? a->argv[3] : "";
6189 AST_LIST_LOCK(&rule_lists);
6190 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6191 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
6192 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
6193 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
6194 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);
6198 AST_LIST_UNLOCK(&rule_lists);
6199 return CLI_SUCCESS;
6202 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6204 switch (cmd) {
6205 case CLI_INIT:
6206 e->command = "queue rules reload";
6207 e->usage =
6208 "Usage: queue rules reload\n"
6209 "Reloads rules defined in queuerules.conf\n";
6210 return NULL;
6211 case CLI_GENERATE:
6212 return NULL;
6214 reload_queue_rules(1);
6215 return CLI_SUCCESS;
6218 static const char qpm_cmd_usage[] =
6219 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
6221 static const char qum_cmd_usage[] =
6222 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
6224 static const char qsmp_cmd_usage[] =
6225 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
6227 static struct ast_cli_entry cli_queue[] = {
6228 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
6229 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
6230 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
6231 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
6232 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
6233 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
6234 AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
6237 static int unload_module(void)
6239 int res;
6240 struct ast_context *con;
6241 struct ao2_iterator q_iter;
6242 struct call_queue *q = NULL;
6244 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6245 res = ast_manager_unregister("QueueStatus");
6246 res |= ast_manager_unregister("Queues");
6247 res |= ast_manager_unregister("QueueRule");
6248 res |= ast_manager_unregister("QueueSummary");
6249 res |= ast_manager_unregister("QueueAdd");
6250 res |= ast_manager_unregister("QueueRemove");
6251 res |= ast_manager_unregister("QueuePause");
6252 res |= ast_manager_unregister("QueueLog");
6253 res |= ast_manager_unregister("QueuePenalty");
6254 res |= ast_unregister_application(app_aqm);
6255 res |= ast_unregister_application(app_rqm);
6256 res |= ast_unregister_application(app_pqm);
6257 res |= ast_unregister_application(app_upqm);
6258 res |= ast_unregister_application(app_ql);
6259 res |= ast_unregister_application(app);
6260 res |= ast_custom_function_unregister(&queuevar_function);
6261 res |= ast_custom_function_unregister(&queuemembercount_function);
6262 res |= ast_custom_function_unregister(&queuemembercount_dep);
6263 res |= ast_custom_function_unregister(&queuememberlist_function);
6264 res |= ast_custom_function_unregister(&queuewaitingcount_function);
6265 res |= ast_custom_function_unregister(&queuememberpenalty_function);
6267 if (device_state_sub)
6268 ast_event_unsubscribe(device_state_sub);
6270 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
6271 ast_context_remove_extension2(con, "s", 1, NULL);
6272 ast_context_destroy(con, "app_queue"); /* leave no trace */
6275 clear_and_free_interfaces();
6277 q_iter = ao2_iterator_init(queues, 0);
6278 while ((q = ao2_iterator_next(&q_iter))) {
6279 ao2_unlink(queues, q);
6280 queue_unref(q);
6282 ao2_ref(queues, -1);
6283 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
6284 return res;
6287 static int load_module(void)
6289 int res;
6290 struct ast_context *con;
6292 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
6294 if (!reload_queues(0))
6295 return AST_MODULE_LOAD_DECLINE;
6297 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
6298 if (!con)
6299 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
6300 else
6301 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_queue");
6303 if (queue_persistent_members)
6304 reload_queue_members();
6306 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6307 res = ast_register_application(app, queue_exec, synopsis, descrip);
6308 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
6309 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
6310 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
6311 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
6312 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
6313 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
6314 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
6315 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
6316 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
6317 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
6318 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
6319 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
6320 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
6321 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
6322 res |= ast_custom_function_register(&queuevar_function);
6323 res |= ast_custom_function_register(&queuemembercount_function);
6324 res |= ast_custom_function_register(&queuemembercount_dep);
6325 res |= ast_custom_function_register(&queuememberlist_function);
6326 res |= ast_custom_function_register(&queuewaitingcount_function);
6327 res |= ast_custom_function_register(&queuememberpenalty_function);
6329 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
6330 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
6333 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END)))
6334 res = -1;
6336 return res ? AST_MODULE_LOAD_DECLINE : 0;
6339 static int reload(void)
6341 reload_queues(1);
6342 return 0;
6345 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
6346 .load = load_module,
6347 .unload = unload_module,
6348 .reload = reload,