Merged revisions 131369 via svnmerge from
[asterisk-bristuff.git] / apps / app_queue.c
bloba68d35e4b5a3cbded107d40ed95011b1a26d317f
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!
111 enum {
112 QUEUE_STRATEGY_RINGALL = 0,
113 QUEUE_STRATEGY_LEASTRECENT,
114 QUEUE_STRATEGY_FEWESTCALLS,
115 QUEUE_STRATEGY_RANDOM,
116 QUEUE_STRATEGY_RRMEMORY,
117 QUEUE_STRATEGY_LINEAR,
118 QUEUE_STRATEGY_WRANDOM
121 static const struct strategy {
122 int strategy;
123 const char *name;
124 } strategies[] = {
125 { QUEUE_STRATEGY_RINGALL, "ringall" },
126 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
127 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
128 { QUEUE_STRATEGY_RANDOM, "random" },
129 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
130 { QUEUE_STRATEGY_LINEAR, "linear" },
131 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
134 static struct ast_taskprocessor *devicestate_tps;
136 #define DEFAULT_RETRY 5
137 #define DEFAULT_TIMEOUT 15
138 #define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
139 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
140 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /*!< The minimum number of seconds between position announcements \
141 The default value of 15 provides backwards compatibility */
142 #define MAX_QUEUE_BUCKETS 53
144 #define RES_OKAY 0 /*!< Action completed */
145 #define RES_EXISTS (-1) /*!< Entry already exists */
146 #define RES_OUTOFMEMORY (-2) /*!< Out of memory */
147 #define RES_NOSUCHQUEUE (-3) /*!< No such queue */
148 #define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
150 static char *app = "Queue";
152 static char *synopsis = "Queue a call for a call queue";
154 static char *descrip =
155 " Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
156 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
157 "This application will return to the dialplan if the queue does not exist, or\n"
158 "any of the join options cause the caller to not enter the queue.\n"
159 "The option string may contain zero or more of the following characters:\n"
160 " 'c' -- continue in the dialplan if the callee hangs up.\n"
161 " 'd' -- data-quality (modem) call (minimum delay).\n"
162 " 'h' -- allow callee to hang up by pressing *.\n"
163 " 'H' -- allow caller to hang up by pressing *.\n"
164 " 'n' -- no retries on the timeout; will exit this application and \n"
165 " go to the next step.\n"
166 " 'i' -- ignore call forward requests from queue members and do nothing\n"
167 " when they are requested.\n"
168 " 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
169 " 't' -- allow the called user to transfer the calling user.\n"
170 " 'T' -- allow the calling user to transfer the call.\n"
171 " 'w' -- allow the called user to write the conversation to disk via Monitor.\n"
172 " 'W' -- allow the calling user to write the conversation to disk via Monitor.\n"
173 " 'k' -- Allow the called party to enable parking of the call by sending\n"
174 " the DTMF sequence defined for call parking in features.conf.\n"
175 " 'K' -- Allow the calling party to enable parking of the call by sending\n"
176 " the DTMF sequence defined for call parking in features.conf.\n"
177 " 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
178 " 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
180 " In addition to transferring the call, a call may be parked and then picked\n"
181 "up by another user.\n"
182 " The optional URL will be sent to the called party if the channel supports\n"
183 "it.\n"
184 " The optional AGI parameter will setup an AGI script to be executed on the \n"
185 "calling party's channel once they are connected to a queue member.\n"
186 " The optional macro parameter will run a macro on the \n"
187 "calling party's channel once they are connected to a queue member.\n"
188 " The optional gosub parameter will run a gosub on the \n"
189 "calling party's channel once they are connected to a queue member.\n"
190 " The optional rule parameter will cause the queue's defaultrule to be\n"
191 "overridden by the rule specified.\n"
192 " The timeout will cause the queue to fail out after a specified number of\n"
193 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
194 " This application sets the following channel variable upon completion:\n"
195 " QUEUESTATUS The status of the call as a text string, one of\n"
196 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
198 static char *app_aqm = "AddQueueMember" ;
199 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
200 static char *app_aqm_descrip =
201 " AddQueueMember(queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]):\n"
202 "Dynamically adds interface to an existing queue.\n"
203 "If the interface is already in the queue it will return an error.\n"
204 " This application sets the following channel variable upon completion:\n"
205 " AQMSTATUS The status of the attempt to add a queue member as a \n"
206 " text string, one of\n"
207 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
208 "Example: AddQueueMember(techsupport,SIP/3000)\n"
211 static char *app_rqm = "RemoveQueueMember" ;
212 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
213 static char *app_rqm_descrip =
214 " RemoveQueueMember(queuename[,interface[,options]]):\n"
215 "Dynamically removes interface to an existing queue\n"
216 "If the interface is NOT in the queue it will return an error.\n"
217 " This application sets the following channel variable upon completion:\n"
218 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
219 " text string, one of\n"
220 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
221 "Example: RemoveQueueMember(techsupport,SIP/3000)\n"
224 static char *app_pqm = "PauseQueueMember" ;
225 static char *app_pqm_synopsis = "Pauses a queue member" ;
226 static char *app_pqm_descrip =
227 " PauseQueueMember([queuename],interface[,options[,reason]]):\n"
228 "Pauses (blocks calls for) a queue member.\n"
229 "The given interface will be paused in the given queue. This prevents\n"
230 "any calls from being sent from the queue to the interface until it is\n"
231 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
232 "queuename is given, the interface is paused in every queue it is a\n"
233 "member of. The application will fail if the interface is not found.\n"
234 "The reason string is entirely optional and is used to add extra information\n"
235 "to the appropriate queue_log entries and manager events.\n"
236 " This application sets the following channel variable upon completion:\n"
237 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
238 " text string, one of\n"
239 " PAUSED | NOTFOUND\n"
240 "Example: PauseQueueMember(,SIP/3000)\n";
242 static char *app_upqm = "UnpauseQueueMember" ;
243 static char *app_upqm_synopsis = "Unpauses a queue member" ;
244 static char *app_upqm_descrip =
245 " UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
246 "Unpauses (resumes calls to) a queue member.\n"
247 "This is the counterpart to PauseQueueMember and operates exactly the\n"
248 "same way, except it unpauses instead of pausing the given interface.\n"
249 "The reason string is entirely optional and is used to add extra information\n"
250 "to the appropriate queue_log entries and manager events.\n"
251 " This application sets the following channel variable upon completion:\n"
252 " UPQMSTATUS The status of the attempt to unpause a queue \n"
253 " member as a text string, one of\n"
254 " UNPAUSED | NOTFOUND\n"
255 "Example: UnpauseQueueMember(,SIP/3000)\n";
257 static char *app_ql = "QueueLog" ;
258 static char *app_ql_synopsis = "Writes to the queue_log" ;
259 static char *app_ql_descrip =
260 " QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
261 "Allows you to write your own events into the queue log\n"
262 "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
264 /*! \brief Persistent Members astdb family */
265 static const char *pm_family = "Queue/PersistentMembers";
266 /* The maximum length of each persistent member queue database entry */
267 #define PM_MAX_LEN 8192
269 /*! \brief queues.conf [general] option */
270 static int queue_keep_stats = 0;
272 /*! \brief queues.conf [general] option */
273 static int queue_persistent_members = 0;
275 /*! \brief queues.conf per-queue weight option */
276 static int use_weight = 0;
278 /*! \brief queues.conf [general] option */
279 static int autofill_default = 0;
281 /*! \brief queues.conf [general] option */
282 static int montype_default = 0;
284 /*! \brief queues.conf [general] option */
285 static int shared_lastcall = 0;
287 /*! \brief Subscription to device state change events */
288 static struct ast_event_sub *device_state_sub;
290 /*! \brief queues.conf [general] option */
291 static int update_cdr = 0;
293 enum queue_result {
294 QUEUE_UNKNOWN = 0,
295 QUEUE_TIMEOUT = 1,
296 QUEUE_JOINEMPTY = 2,
297 QUEUE_LEAVEEMPTY = 3,
298 QUEUE_JOINUNAVAIL = 4,
299 QUEUE_LEAVEUNAVAIL = 5,
300 QUEUE_FULL = 6,
301 QUEUE_CONTINUE = 7,
304 const struct {
305 enum queue_result id;
306 char *text;
307 } queue_results[] = {
308 { QUEUE_UNKNOWN, "UNKNOWN" },
309 { QUEUE_TIMEOUT, "TIMEOUT" },
310 { QUEUE_JOINEMPTY,"JOINEMPTY" },
311 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
312 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
313 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
314 { QUEUE_FULL, "FULL" },
315 { QUEUE_CONTINUE, "CONTINUE" },
318 enum queue_timeout_priority {
319 TIMEOUT_PRIORITY_APP,
320 TIMEOUT_PRIORITY_CONF,
323 /*! \brief We define a custom "local user" structure because we
324 * use it not only for keeping track of what is in use but
325 * also for keeping track of who we're dialing.
327 * There are two "links" defined in this structure, q_next and call_next.
328 * q_next links ALL defined callattempt structures into a linked list. call_next is
329 * a link which allows for a subset of the callattempts to be traversed. This subset
330 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
331 * also is helpful so that queue logs are always accurate in the case where a call to
332 * a member times out, especially if using the ringall strategy.
335 struct callattempt {
336 struct callattempt *q_next;
337 struct callattempt *call_next;
338 struct ast_channel *chan;
339 char interface[256];
340 int stillgoing;
341 int metric;
342 int oldstatus;
343 time_t lastcall;
344 struct call_queue *lastqueue;
345 struct member *member;
349 struct queue_ent {
350 struct call_queue *parent; /*!< What queue is our parent */
351 char moh[80]; /*!< Name of musiconhold to be used */
352 char announce[80]; /*!< Announcement to play for member when call is answered */
353 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
354 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
355 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
356 int pos; /*!< Where we are in the queue */
357 int prio; /*!< Our priority */
358 int last_pos_said; /*!< Last position we told the user */
359 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
360 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
361 time_t last_pos; /*!< Last time we told the user their position */
362 int opos; /*!< Where we started in the queue */
363 int handled; /*!< Whether our call was handled */
364 int pending; /*!< Non-zero if we are attempting to call a member */
365 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
366 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
367 int linpos; /*!< If using linear strategy, what position are we at? */
368 int linwrapped; /*!< Is the linpos wrapped? */
369 time_t start; /*!< When we started holding */
370 time_t expire; /*!< When this entry should expire (time out of queue) */
371 struct ast_channel *chan; /*!< Our channel */
372 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
373 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
374 struct queue_ent *next; /*!< The next queue entry */
377 struct member {
378 char interface[80]; /*!< Technology/Location to dial to reach this member*/
379 char state_interface[80]; /*!< Technology/Location from which to read devicestate changes */
380 char membername[80]; /*!< Member name to use in queue logs */
381 int penalty; /*!< Are we a last resort? */
382 int calls; /*!< Number of calls serviced by this member */
383 int dynamic; /*!< Are we dynamically added? */
384 int realtime; /*!< Is this member realtime? */
385 int status; /*!< Status of queue member */
386 int paused; /*!< Are we paused (not accepting calls)? */
387 time_t lastcall; /*!< When last successful call was hungup */
388 struct call_queue *lastqueue; /*!< Last queue we received a call */
389 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
390 unsigned int delme:1; /*!< Flag to delete entry on reload */
391 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
394 struct member_interface {
395 char interface[80];
396 AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
399 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
401 /* values used in multi-bit flags in call_queue */
402 #define QUEUE_EMPTY_NORMAL 1
403 #define QUEUE_EMPTY_STRICT 2
404 #define QUEUE_EMPTY_LOOSE 3
405 #define ANNOUNCEHOLDTIME_ALWAYS 1
406 #define ANNOUNCEHOLDTIME_ONCE 2
407 #define QUEUE_EVENT_VARIABLES 3
409 struct penalty_rule {
410 int time; /*!< Number of seconds that need to pass before applying this rule */
411 int max_value; /*!< The amount specified in the penalty rule for max penalty */
412 int min_value; /*!< The amount specified in the penalty rule for min penalty */
413 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
414 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
415 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
418 #define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
419 #define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
420 #define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
421 #define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
423 struct call_queue {
424 AST_DECLARE_STRING_FIELDS(
425 /*! Queue name */
426 AST_STRING_FIELD(name);
427 /*! Music on Hold class */
428 AST_STRING_FIELD(moh);
429 /*! Announcement to play when call is answered */
430 AST_STRING_FIELD(announce);
431 /*! Exit context */
432 AST_STRING_FIELD(context);
433 /*! Macro to run upon member connection */
434 AST_STRING_FIELD(membermacro);
435 /*! Gosub to run upon member connection */
436 AST_STRING_FIELD(membergosub);
437 /*! Default rule to use if none specified in call to Queue() */
438 AST_STRING_FIELD(defaultrule);
439 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
440 AST_STRING_FIELD(sound_next);
441 /*! Sound file: "There are currently" (def. queue-thereare) */
442 AST_STRING_FIELD(sound_thereare);
443 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
444 AST_STRING_FIELD(sound_calls);
445 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
446 AST_STRING_FIELD(queue_quantity1);
447 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
448 AST_STRING_FIELD(queue_quantity2);
449 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
450 AST_STRING_FIELD(sound_holdtime);
451 /*! Sound file: "minutes." (def. queue-minutes) */
452 AST_STRING_FIELD(sound_minutes);
453 /*! Sound file: "minute." (def. queue-minute) */
454 AST_STRING_FIELD(sound_minute);
455 /*! Sound file: "seconds." (def. queue-seconds) */
456 AST_STRING_FIELD(sound_seconds);
457 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
458 AST_STRING_FIELD(sound_thanks);
459 /*! Sound file: Custom announce for caller, no default */
460 AST_STRING_FIELD(sound_callerannounce);
461 /*! Sound file: "Hold time" (def. queue-reporthold) */
462 AST_STRING_FIELD(sound_reporthold);
464 /*! Sound files: Custom announce, no default */
465 struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
466 unsigned int dead:1;
467 unsigned int joinempty:2;
468 unsigned int eventwhencalled:2;
469 unsigned int leavewhenempty:2;
470 unsigned int ringinuse:1;
471 unsigned int setinterfacevar:1;
472 unsigned int setqueuevar:1;
473 unsigned int setqueueentryvar:1;
474 unsigned int reportholdtime:1;
475 unsigned int wrapped:1;
476 unsigned int timeoutrestart:1;
477 unsigned int announceholdtime:2;
478 unsigned int announceposition:3;
479 int strategy:4;
480 unsigned int maskmemberstatus:1;
481 unsigned int realtime:1;
482 unsigned int found:1;
483 int announcepositionlimit; /*!< How many positions we announce? */
484 int announcefrequency; /*!< How often to announce their position */
485 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
486 int periodicannouncefrequency; /*!< How often to play periodic announcement */
487 int numperiodicannounce; /*!< The number of periodic announcements configured */
488 int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
489 int roundingseconds; /*!< How many seconds do we round to? */
490 int holdtime; /*!< Current avg holdtime, based on an exponential average */
491 int callscompleted; /*!< Number of queue calls completed */
492 int callsabandoned; /*!< Number of queue calls abandoned */
493 int servicelevel; /*!< seconds setting for servicelevel*/
494 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
495 char monfmt[8]; /*!< Format to use when recording calls */
496 int montype; /*!< Monitor type Monitor vs. MixMonitor */
497 int count; /*!< How many entries */
498 int maxlen; /*!< Max number of entries */
499 int wrapuptime; /*!< Wrapup Time */
501 int retry; /*!< Retry calling everyone after this amount of time */
502 int timeout; /*!< How long to wait for an answer */
503 int weight; /*!< Respective weight */
504 int autopause; /*!< Auto pause queue members if they fail to answer */
505 int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
507 /* Queue strategy things */
508 int rrpos; /*!< Round Robin - position */
509 int memberdelay; /*!< Seconds to delay connecting member to caller */
510 int autofill; /*!< Ignore the head call status and ring an available agent */
512 struct ao2_container *members; /*!< Head of the list of members */
513 /*!
514 * \brief Number of members _logged in_
515 * \note There will be members in the members container that are not logged
516 * in, so this can not simply be replaced with ao2_container_count().
518 int membercount;
519 struct queue_ent *head; /*!< Head of the list of callers */
520 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
521 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
524 struct rule_list {
525 char name[80];
526 AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
527 AST_LIST_ENTRY(rule_list) list;
530 AST_LIST_HEAD_STATIC(rule_lists, rule_list);
532 static struct ao2_container *queues;
534 static void update_realtime_members(struct call_queue *q);
535 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
537 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
538 /*! \brief sets the QUEUESTATUS channel variable */
539 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
541 int i;
543 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
544 if (queue_results[i].id == res) {
545 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
546 return;
551 static const char *int2strat(int strategy)
553 int x;
555 for (x = 0; x < ARRAY_LEN(strategies); x++) {
556 if (strategy == strategies[x].strategy)
557 return strategies[x].name;
560 return "<unknown>";
563 static int strat2int(const char *strategy)
565 int x;
567 for (x = 0; x < ARRAY_LEN(strategies); x++) {
568 if (!strcasecmp(strategy, strategies[x].name))
569 return strategies[x].strategy;
572 return -1;
575 static int queue_hash_cb(const void *obj, const int flags)
577 const struct call_queue *q = obj;
578 return ast_str_hash(q->name);
581 static int queue_cmp_cb(void *obj, void *arg, int flags)
583 struct call_queue *q = obj, *q2 = arg;
584 return !strcasecmp(q->name, q2->name) ? CMP_MATCH : 0;
587 static inline struct call_queue *queue_ref(struct call_queue *q)
589 ao2_ref(q, 1);
590 return q;
593 static inline struct call_queue *queue_unref(struct call_queue *q)
595 ao2_ref(q, -1);
596 return q;
599 /*! \brief Set variables of queue */
600 static void set_queue_variables(struct queue_ent *qe)
602 char interfacevar[256]="";
603 float sl = 0;
605 if (qe->parent->setqueuevar) {
606 sl = 0;
607 if (qe->parent->callscompleted > 0)
608 sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
610 snprintf(interfacevar, sizeof(interfacevar),
611 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
612 qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
613 qe->parent->callsabandoned, qe->parent->servicelevel, sl);
615 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
619 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
620 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
622 struct queue_ent *cur;
624 if (!q || !new)
625 return;
626 if (prev) {
627 cur = prev->next;
628 prev->next = new;
629 } else {
630 cur = q->head;
631 q->head = new;
633 new->next = cur;
634 new->parent = q;
635 new->pos = ++(*pos);
636 new->opos = *pos;
639 enum queue_member_status {
640 QUEUE_NO_MEMBERS,
641 QUEUE_NO_REACHABLE_MEMBERS,
642 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
643 QUEUE_NORMAL
646 /*! \brief Check if members are available
648 * This function checks to see if members are available to be called. If any member
649 * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
650 * the appropriate reason why is returned
652 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
654 struct member *member;
655 struct ao2_iterator mem_iter;
656 enum queue_member_status result = QUEUE_NO_MEMBERS;
658 ao2_lock(q);
659 mem_iter = ao2_iterator_init(q->members, 0);
660 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
661 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
662 continue;
664 switch (member->status) {
665 case AST_DEVICE_INVALID:
666 /* nothing to do */
667 break;
668 case AST_DEVICE_UNAVAILABLE:
669 if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)
670 result = QUEUE_NO_REACHABLE_MEMBERS;
671 break;
672 default:
673 if (member->paused) {
674 result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
675 } else {
676 ao2_unlock(q);
677 ao2_ref(member, -1);
678 return QUEUE_NORMAL;
680 break;
684 ao2_unlock(q);
685 return result;
688 struct statechange {
689 AST_LIST_ENTRY(statechange) entry;
690 int state;
691 char dev[0];
694 /*! \brief set a member's status based on device state of that member's state_interface.
696 * Lock interface list find sc, iterate through each queues queue_member list for member to
697 * update state inside queues
699 static int update_status(const char *interface, const int status)
701 struct member *cur;
702 struct ao2_iterator mem_iter, queue_iter;
703 struct call_queue *q;
705 queue_iter = ao2_iterator_init(queues, 0);
706 while ((q = ao2_iterator_next(&queue_iter))) {
707 ao2_lock(q);
708 mem_iter = ao2_iterator_init(q->members, 0);
709 while ((cur = ao2_iterator_next(&mem_iter))) {
710 char *tmp_interface;
711 char *slash_pos;
712 tmp_interface = ast_strdupa(cur->state_interface);
713 if ((slash_pos = strchr(interface, '/')))
714 if ((slash_pos = strchr(slash_pos + 1, '/')))
715 *slash_pos = '\0';
717 if (strcasecmp(interface, tmp_interface)) {
718 ao2_ref(cur, -1);
719 continue;
722 if (cur->status != status) {
723 cur->status = status;
724 if (q->maskmemberstatus) {
725 ao2_ref(cur, -1);
726 continue;
729 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
730 "Queue: %s\r\n"
731 "Location: %s\r\n"
732 "MemberName: %s\r\n"
733 "Membership: %s\r\n"
734 "Penalty: %d\r\n"
735 "CallsTaken: %d\r\n"
736 "LastCall: %d\r\n"
737 "Status: %d\r\n"
738 "Paused: %d\r\n",
739 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
740 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
742 ao2_ref(cur, -1);
744 queue_unref(q);
745 ao2_unlock(q);
748 return 0;
751 /*! \brief set a member's status based on device state of that member's interface*/
752 static int handle_statechange(void *datap)
754 struct member_interface *curint;
755 char *loc;
756 char *technology;
757 struct statechange *sc = datap;
759 technology = ast_strdupa(sc->dev);
760 loc = strchr(technology, '/');
761 if (loc) {
762 *loc++ = '\0';
763 } else {
764 ast_free(sc);
765 return 0;
768 AST_LIST_LOCK(&interfaces);
769 AST_LIST_TRAVERSE(&interfaces, curint, list) {
770 char *interface;
771 char *slash_pos;
772 interface = ast_strdupa(curint->interface);
773 if ((slash_pos = strchr(interface, '/')))
774 if ((slash_pos = strchr(slash_pos + 1, '/')))
775 *slash_pos = '\0';
777 if (!strcasecmp(interface, sc->dev))
778 break;
780 AST_LIST_UNLOCK(&interfaces);
782 if (!curint) {
783 if (option_debug > 2)
784 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));
785 ast_free(sc);
786 return 0;
789 if (option_debug)
790 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
792 update_status(sc->dev, sc->state);
793 ast_free(sc);
794 return 0;
797 static void device_state_cb(const struct ast_event *event, void *unused)
799 enum ast_device_state state;
800 const char *device;
801 struct statechange *sc;
802 size_t datapsize;
804 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
805 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
807 if (ast_strlen_zero(device)) {
808 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
809 return;
811 datapsize = sizeof(*sc) + strlen(device) + 1;
812 if (!(sc = ast_calloc(1, datapsize))) {
813 ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
814 return;
816 sc->state = state;
817 strcpy(sc->dev, device);
818 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
819 ast_free(sc);
823 /*! \brief allocate space for new queue member and set fields based on parameters passed */
824 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
826 struct member *cur;
828 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
829 cur->penalty = penalty;
830 cur->paused = paused;
831 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
832 if (!ast_strlen_zero(state_interface))
833 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
834 else
835 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
836 if (!ast_strlen_zero(membername))
837 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
838 else
839 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
840 if (!strchr(cur->interface, '/'))
841 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
842 cur->status = ast_device_state(cur->state_interface);
845 return cur;
849 static int compress_char(const char c)
851 if (c < 32)
852 return 0;
853 else if (c > 96)
854 return c - 64;
855 else
856 return c - 32;
859 static int member_hash_fn(const void *obj, const int flags)
861 const struct member *mem = obj;
862 const char *chname = strchr(mem->interface, '/');
863 int ret = 0, i;
864 if (!chname)
865 chname = mem->interface;
866 for (i = 0; i < 5 && chname[i]; i++)
867 ret += compress_char(chname[i]) << (i * 6);
868 return ret;
871 static int member_cmp_fn(void *obj1, void *obj2, int flags)
873 struct member *mem1 = obj1, *mem2 = obj2;
874 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
877 /*!
878 * \brief Initialize Queue default values.
879 * \note the queue's lock must be held before executing this function
881 static void init_queue(struct call_queue *q)
883 int i;
884 struct penalty_rule *pr_iter;
886 q->dead = 0;
887 q->retry = DEFAULT_RETRY;
888 q->timeout = -1;
889 q->maxlen = 0;
890 q->announcefrequency = 0;
891 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
892 q->announceholdtime = 1;
893 q->announcepositionlimit = 10; /* Default 10 positions */
894 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
895 q->roundingseconds = 0; /* Default - don't announce seconds */
896 q->servicelevel = 0;
897 q->ringinuse = 1;
898 q->setinterfacevar = 0;
899 q->setqueuevar = 0;
900 q->setqueueentryvar = 0;
901 q->autofill = autofill_default;
902 q->montype = montype_default;
903 q->monfmt[0] = '\0';
904 q->reportholdtime = 0;
905 q->wrapuptime = 0;
906 q->joinempty = 0;
907 q->leavewhenempty = 0;
908 q->memberdelay = 0;
909 q->maskmemberstatus = 0;
910 q->eventwhencalled = 0;
911 q->weight = 0;
912 q->timeoutrestart = 0;
913 q->periodicannouncefrequency = 0;
914 q->randomperiodicannounce = 0;
915 q->numperiodicannounce = 0;
916 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
917 if (!q->members) {
918 if (q->strategy == QUEUE_STRATEGY_LINEAR)
919 /* linear strategy depends on order, so we have to place all members in a single bucket */
920 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
921 else
922 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
924 q->membercount = 0;
925 q->found = 1;
927 ast_string_field_set(q, sound_next, "queue-youarenext");
928 ast_string_field_set(q, sound_thereare, "queue-thereare");
929 ast_string_field_set(q, sound_calls, "queue-callswaiting");
930 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
931 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
932 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
933 ast_string_field_set(q, sound_minutes, "queue-minutes");
934 ast_string_field_set(q, sound_minute, "queue-minute");
935 ast_string_field_set(q, sound_seconds, "queue-seconds");
936 ast_string_field_set(q, sound_thanks, "queue-thankyou");
937 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
939 if ((q->sound_periodicannounce[0] = ast_str_create(32)))
940 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
942 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
943 if (q->sound_periodicannounce[i])
944 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
947 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
948 ast_free(pr_iter);
951 static void clear_queue(struct call_queue *q)
953 q->holdtime = 0;
954 q->callscompleted = 0;
955 q->callsabandoned = 0;
956 q->callscompletedinsl = 0;
957 q->wrapuptime = 0;
960 static int add_to_interfaces(const char *interface)
962 struct member_interface *curint;
964 AST_LIST_LOCK(&interfaces);
965 AST_LIST_TRAVERSE(&interfaces, curint, list) {
966 if (!strcasecmp(curint->interface, interface))
967 break;
970 if (curint) {
971 AST_LIST_UNLOCK(&interfaces);
972 return 0;
975 ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
977 if ((curint = ast_calloc(1, sizeof(*curint)))) {
978 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
979 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
981 AST_LIST_UNLOCK(&interfaces);
983 return 0;
986 static int interface_exists_global(const char *interface)
988 struct call_queue *q;
989 struct member *mem, tmpmem;
990 struct ao2_iterator queue_iter, mem_iter;
991 int ret = 0;
993 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
994 queue_iter = ao2_iterator_init(queues, 0);
995 while ((q = ao2_iterator_next(&queue_iter))) {
996 ao2_lock(q);
997 mem_iter = ao2_iterator_init(q->members, 0);
998 while ((mem = ao2_iterator_next(&mem_iter))) {
999 if (!strcasecmp(mem->state_interface, interface)) {
1000 ao2_ref(mem, -1);
1001 ret = 1;
1002 break;
1005 ao2_unlock(q);
1006 queue_unref(q);
1009 return ret;
1012 static int remove_from_interfaces(const char *interface)
1014 struct member_interface *curint;
1016 if (interface_exists_global(interface))
1017 return 0;
1019 AST_LIST_LOCK(&interfaces);
1020 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
1021 if (!strcasecmp(curint->interface, interface)) {
1022 ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
1023 AST_LIST_REMOVE_CURRENT(list);
1024 ast_free(curint);
1025 break;
1028 AST_LIST_TRAVERSE_SAFE_END;
1029 AST_LIST_UNLOCK(&interfaces);
1031 return 0;
1034 static void clear_and_free_interfaces(void)
1036 struct member_interface *curint;
1038 AST_LIST_LOCK(&interfaces);
1039 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
1040 ast_free(curint);
1041 AST_LIST_UNLOCK(&interfaces);
1044 /*!
1045 * \brief Change queue penalty by adding rule.
1047 * Check rule for errors with time or fomatting, see if rule is relative to rest
1048 * of queue, iterate list of rules to find correct insertion point, insert and return.
1049 * \retval -1 on failure
1050 * \retval 0 on success
1051 * \note Call this with the rule_lists locked
1053 static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
1055 char *timestr, *maxstr, *minstr, *contentdup;
1056 struct penalty_rule *rule = NULL, *rule_iter;
1057 struct rule_list *rl_iter;
1058 int time, inserted = 0;
1060 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
1061 ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
1062 return -1;
1065 contentdup = ast_strdupa(content);
1067 if (!(maxstr = strchr(contentdup, ','))) {
1068 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
1069 ast_free(rule);
1070 return -1;
1073 *maxstr++ = '\0';
1074 timestr = contentdup;
1076 if ((time = atoi(timestr)) < 0) {
1077 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
1078 ast_free(rule);
1079 return -1;
1082 rule->time = time;
1084 if ((minstr = strchr(maxstr,',')))
1085 *minstr++ = '\0';
1087 /* The last check will evaluate true if either no penalty change is indicated for a given rule
1088 * OR if a min penalty change is indicated but no max penalty change is */
1089 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
1090 rule->max_relative = 1;
1093 rule->max_value = atoi(maxstr);
1095 if (!ast_strlen_zero(minstr)) {
1096 if (*minstr == '+' || *minstr == '-')
1097 rule->min_relative = 1;
1098 rule->min_value = atoi(minstr);
1099 } else /*there was no minimum specified, so assume this means no change*/
1100 rule->min_relative = 1;
1102 /*We have the rule made, now we need to insert it where it belongs*/
1103 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
1104 if (strcasecmp(rl_iter->name, list_name))
1105 continue;
1107 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
1108 if (rule->time < rule_iter->time) {
1109 AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
1110 inserted = 1;
1111 break;
1114 AST_LIST_TRAVERSE_SAFE_END;
1116 if (!inserted) {
1117 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
1121 return 0;
1124 /*! \brief Configure a queue parameter.
1126 * The failunknown flag is set for config files (and static realtime) to show
1127 * errors for unknown parameters. It is cleared for dynamic realtime to allow
1128 * extra fields in the tables.
1129 * \note For error reporting, line number is passed for .conf static configuration,
1130 * for Realtime queues, linenum is -1.
1132 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
1134 if (!strcasecmp(param, "musicclass") ||
1135 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
1136 ast_string_field_set(q, moh, val);
1137 } else if (!strcasecmp(param, "announce")) {
1138 ast_string_field_set(q, announce, val);
1139 } else if (!strcasecmp(param, "context")) {
1140 ast_string_field_set(q, context, val);
1141 } else if (!strcasecmp(param, "timeout")) {
1142 q->timeout = atoi(val);
1143 if (q->timeout < 0)
1144 q->timeout = DEFAULT_TIMEOUT;
1145 } else if (!strcasecmp(param, "ringinuse")) {
1146 q->ringinuse = ast_true(val);
1147 } else if (!strcasecmp(param, "setinterfacevar")) {
1148 q->setinterfacevar = ast_true(val);
1149 } else if (!strcasecmp(param, "setqueuevar")) {
1150 q->setqueuevar = ast_true(val);
1151 } else if (!strcasecmp(param, "setqueueentryvar")) {
1152 q->setqueueentryvar = ast_true(val);
1153 } else if (!strcasecmp(param, "monitor-format")) {
1154 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
1155 } else if (!strcasecmp(param, "membermacro")) {
1156 ast_string_field_set(q, membermacro, val);
1157 } else if (!strcasecmp(param, "membergosub")) {
1158 ast_string_field_set(q, membergosub, val);
1159 } else if (!strcasecmp(param, "queue-youarenext")) {
1160 ast_string_field_set(q, sound_next, val);
1161 } else if (!strcasecmp(param, "queue-thereare")) {
1162 ast_string_field_set(q, sound_thereare, val);
1163 } else if (!strcasecmp(param, "queue-callswaiting")) {
1164 ast_string_field_set(q, sound_calls, val);
1165 } else if (!strcasecmp(param, "queue-quantity1")) {
1166 ast_string_field_set(q, queue_quantity1, val);
1167 } else if (!strcasecmp(param, "queue-quantity2")) {
1168 ast_string_field_set(q, queue_quantity2, val);
1169 } else if (!strcasecmp(param, "queue-holdtime")) {
1170 ast_string_field_set(q, sound_holdtime, val);
1171 } else if (!strcasecmp(param, "queue-minutes")) {
1172 ast_string_field_set(q, sound_minutes, val);
1173 } else if (!strcasecmp(param, "queue-minute")) {
1174 ast_string_field_set(q, sound_minute, val);
1175 } else if (!strcasecmp(param, "queue-seconds")) {
1176 ast_string_field_set(q, sound_seconds, val);
1177 } else if (!strcasecmp(param, "queue-thankyou")) {
1178 ast_string_field_set(q, sound_thanks, val);
1179 } else if (!strcasecmp(param, "queue-callerannounce")) {
1180 ast_string_field_set(q, sound_callerannounce, val);
1181 } else if (!strcasecmp(param, "queue-reporthold")) {
1182 ast_string_field_set(q, sound_reporthold, val);
1183 } else if (!strcasecmp(param, "announce-frequency")) {
1184 q->announcefrequency = atoi(val);
1185 } else if (!strcasecmp(param, "min-announce-frequency")) {
1186 q->minannouncefrequency = atoi(val);
1187 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
1188 } else if (!strcasecmp(param, "announce-round-seconds")) {
1189 q->roundingseconds = atoi(val);
1190 /* Rounding to any other values just doesn't make sense... */
1191 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
1192 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
1193 if (linenum >= 0) {
1194 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1195 "using 0 instead for queue '%s' at line %d of queues.conf\n",
1196 val, param, q->name, linenum);
1197 } else {
1198 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1199 "using 0 instead for queue '%s'\n", val, param, q->name);
1201 q->roundingseconds=0;
1203 } else if (!strcasecmp(param, "announce-holdtime")) {
1204 if (!strcasecmp(val, "once"))
1205 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
1206 else if (ast_true(val))
1207 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
1208 else
1209 q->announceholdtime = 0;
1210 } else if (!strcasecmp(param, "announce-position")) {
1211 if (!strcasecmp(val, "limit"))
1212 q->announceposition = ANNOUNCEPOSITION_LIMIT;
1213 else if (!strcasecmp(val, "more"))
1214 q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
1215 else if (ast_true(val))
1216 q->announceposition = ANNOUNCEPOSITION_YES;
1217 else
1218 q->announceposition = ANNOUNCEPOSITION_NO;
1219 } else if (!strcasecmp(param, "announce-position-limit")) {
1220 q->announcepositionlimit = atoi(val);
1221 } else if (!strcasecmp(param, "periodic-announce")) {
1222 if (strchr(val, ',')) {
1223 char *s, *buf = ast_strdupa(val);
1224 unsigned int i = 0;
1226 while ((s = strsep(&buf, ",|"))) {
1227 if (!q->sound_periodicannounce[i])
1228 q->sound_periodicannounce[i] = ast_str_create(16);
1229 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
1230 i++;
1231 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
1232 break;
1234 q->numperiodicannounce = i;
1235 } else {
1236 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
1237 q->numperiodicannounce = 1;
1239 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
1240 q->periodicannouncefrequency = atoi(val);
1241 } else if (!strcasecmp(param, "random-periodic-announce")) {
1242 q->randomperiodicannounce = ast_true(val);
1243 } else if (!strcasecmp(param, "retry")) {
1244 q->retry = atoi(val);
1245 if (q->retry <= 0)
1246 q->retry = DEFAULT_RETRY;
1247 } else if (!strcasecmp(param, "wrapuptime")) {
1248 q->wrapuptime = atoi(val);
1249 } else if (!strcasecmp(param, "autofill")) {
1250 q->autofill = ast_true(val);
1251 } else if (!strcasecmp(param, "monitor-type")) {
1252 if (!strcasecmp(val, "mixmonitor"))
1253 q->montype = 1;
1254 } else if (!strcasecmp(param, "autopause")) {
1255 q->autopause = ast_true(val);
1256 } else if (!strcasecmp(param, "maxlen")) {
1257 q->maxlen = atoi(val);
1258 if (q->maxlen < 0)
1259 q->maxlen = 0;
1260 } else if (!strcasecmp(param, "servicelevel")) {
1261 q->servicelevel= atoi(val);
1262 } else if (!strcasecmp(param, "strategy")) {
1263 /* We already have set this, no need to do it again */
1264 return;
1265 } else if (!strcasecmp(param, "joinempty")) {
1266 if (!strcasecmp(val, "loose"))
1267 q->joinempty = QUEUE_EMPTY_LOOSE;
1268 else if (!strcasecmp(val, "strict"))
1269 q->joinempty = QUEUE_EMPTY_STRICT;
1270 else if (ast_true(val))
1271 q->joinempty = QUEUE_EMPTY_NORMAL;
1272 else
1273 q->joinempty = 0;
1274 } else if (!strcasecmp(param, "leavewhenempty")) {
1275 if (!strcasecmp(val, "loose"))
1276 q->leavewhenempty = QUEUE_EMPTY_LOOSE;
1277 else if (!strcasecmp(val, "strict"))
1278 q->leavewhenempty = QUEUE_EMPTY_STRICT;
1279 else if (ast_true(val))
1280 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
1281 else
1282 q->leavewhenempty = 0;
1283 } else if (!strcasecmp(param, "eventmemberstatus")) {
1284 q->maskmemberstatus = !ast_true(val);
1285 } else if (!strcasecmp(param, "eventwhencalled")) {
1286 if (!strcasecmp(val, "vars")) {
1287 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
1288 } else {
1289 q->eventwhencalled = ast_true(val) ? 1 : 0;
1291 } else if (!strcasecmp(param, "reportholdtime")) {
1292 q->reportholdtime = ast_true(val);
1293 } else if (!strcasecmp(param, "memberdelay")) {
1294 q->memberdelay = atoi(val);
1295 } else if (!strcasecmp(param, "weight")) {
1296 q->weight = atoi(val);
1297 if (q->weight)
1298 use_weight++;
1299 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1300 we will not see any effect on use_weight until next reload. */
1301 } else if (!strcasecmp(param, "timeoutrestart")) {
1302 q->timeoutrestart = ast_true(val);
1303 } else if (!strcasecmp(param, "defaultrule")) {
1304 ast_string_field_set(q, defaultrule, val);
1305 } else if (!strcasecmp(param, "timeoutpriority")) {
1306 if (!strcasecmp(val, "conf")) {
1307 q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
1308 } else {
1309 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
1311 } else if (failunknown) {
1312 if (linenum >= 0) {
1313 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1314 q->name, param, linenum);
1315 } else {
1316 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
1322 * \brief Find rt member record to update otherwise create one.
1324 * Search for member in queue, if found update penalty/paused state,
1325 * if no memeber exists create one flag it as a RT member and add to queue member list.
1327 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)
1329 struct member *m;
1330 struct ao2_iterator mem_iter;
1331 int penalty = 0;
1332 int paused = 0;
1333 int found = 0;
1335 if (penalty_str) {
1336 penalty = atoi(penalty_str);
1337 if (penalty < 0)
1338 penalty = 0;
1341 if (paused_str) {
1342 paused = atoi(paused_str);
1343 if (paused < 0)
1344 paused = 0;
1347 /* Find member by realtime uniqueid and update */
1348 mem_iter = ao2_iterator_init(q->members, 0);
1349 while ((m = ao2_iterator_next(&mem_iter))) {
1350 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
1351 m->dead = 0; /* Do not delete this one. */
1352 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1353 if (paused_str)
1354 m->paused = paused;
1355 if (strcasecmp(state_interface, m->state_interface)) {
1356 remove_from_interfaces(m->state_interface);
1357 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
1358 add_to_interfaces(m->state_interface);
1360 m->penalty = penalty;
1361 found = 1;
1362 ao2_ref(m, -1);
1363 break;
1365 ao2_ref(m, -1);
1368 /* Create a new member */
1369 if (!found) {
1370 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
1371 m->dead = 0;
1372 m->realtime = 1;
1373 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1374 add_to_interfaces(m->state_interface);
1375 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
1376 ao2_link(q->members, m);
1377 ao2_ref(m, -1);
1378 m = NULL;
1379 q->membercount++;
1384 /*! \brief Iterate through queue's member list and delete them */
1385 static void free_members(struct call_queue *q, int all)
1387 /* Free non-dynamic members */
1388 struct member *cur;
1389 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
1391 while ((cur = ao2_iterator_next(&mem_iter))) {
1392 if (all || !cur->dynamic) {
1393 ao2_unlink(q->members, cur);
1394 remove_from_interfaces(cur->state_interface);
1395 q->membercount--;
1397 ao2_ref(cur, -1);
1401 /*! \brief Free queue's member list then its string fields */
1402 static void destroy_queue(void *obj)
1404 struct call_queue *q = obj;
1405 int i;
1407 free_members(q, 1);
1408 ast_string_field_free_memory(q);
1409 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
1410 if (q->sound_periodicannounce[i])
1411 free(q->sound_periodicannounce[i]);
1413 ao2_ref(q->members, -1);
1416 static struct call_queue *alloc_queue(const char *queuename)
1418 struct call_queue *q;
1420 if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
1421 if (ast_string_field_init(q, 64)) {
1422 free(q);
1423 return NULL;
1425 ast_string_field_set(q, name, queuename);
1427 return q;
1431 * \brief Reload a single queue via realtime.
1433 * Check for statically defined queue first, check if deleted RT queue,
1434 * check for new RT queue, if queue vars are not defined init them with defaults.
1435 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
1436 * \retval the queue,
1437 * \retval NULL if it doesn't exist.
1438 * \note Should be called with the "queues" container locked.
1440 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
1442 struct ast_variable *v;
1443 struct call_queue *q, tmpq = {
1444 .name = queuename,
1446 struct member *m;
1447 struct ao2_iterator mem_iter;
1448 char *interface = NULL;
1449 const char *tmp_name;
1450 char *tmp;
1451 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
1453 /* Static queues override realtime. */
1454 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
1455 ao2_lock(q);
1456 if (!q->realtime) {
1457 if (q->dead) {
1458 ao2_unlock(q);
1459 queue_unref(q);
1460 return NULL;
1461 } else {
1462 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
1463 ao2_unlock(q);
1464 return q;
1467 queue_unref(q);
1468 } else if (!member_config)
1469 /* Not found in the list, and it's not realtime ... */
1470 return NULL;
1472 /* Check if queue is defined in realtime. */
1473 if (!queue_vars) {
1474 /* Delete queue from in-core list if it has been deleted in realtime. */
1475 if (q) {
1476 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1477 found condition... So we might delete an in-core queue
1478 in case of DB failure. */
1479 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
1481 q->dead = 1;
1482 /* Delete if unused (else will be deleted when last caller leaves). */
1483 ao2_unlink(queues, q);
1484 ao2_unlock(q);
1485 queue_unref(q);
1487 return NULL;
1490 /* Create a new queue if an in-core entry does not exist yet. */
1491 if (!q) {
1492 struct ast_variable *tmpvar = NULL;
1493 if (!(q = alloc_queue(queuename)))
1494 return NULL;
1495 ao2_lock(q);
1496 clear_queue(q);
1497 q->realtime = 1;
1498 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
1499 * will allocate the members properly
1501 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
1502 if (!strcasecmp(tmpvar->name, "strategy")) {
1503 q->strategy = strat2int(tmpvar->value);
1504 if (q->strategy < 0) {
1505 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1506 tmpvar->value, q->name);
1507 q->strategy = QUEUE_STRATEGY_RINGALL;
1509 break;
1512 /* We traversed all variables and didn't find a strategy */
1513 if (!tmpvar)
1514 q->strategy = QUEUE_STRATEGY_RINGALL;
1515 ao2_link(queues, q);
1517 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
1519 memset(tmpbuf, 0, sizeof(tmpbuf));
1520 for (v = queue_vars; v; v = v->next) {
1521 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1522 if ((tmp = strchr(v->name, '_'))) {
1523 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
1524 tmp_name = tmpbuf;
1525 tmp = tmpbuf;
1526 while ((tmp = strchr(tmp, '_')))
1527 *tmp++ = '-';
1528 } else
1529 tmp_name = v->name;
1531 if (!ast_strlen_zero(v->value)) {
1532 /* Don't want to try to set the option if the value is empty */
1533 queue_set_param(q, tmp_name, v->value, -1, 0);
1537 /* Temporarily set realtime members dead so we can detect deleted ones.
1538 * Also set the membercount correctly for realtime*/
1539 mem_iter = ao2_iterator_init(q->members, 0);
1540 while ((m = ao2_iterator_next(&mem_iter))) {
1541 q->membercount++;
1542 if (m->realtime)
1543 m->dead = 1;
1544 ao2_ref(m, -1);
1547 while ((interface = ast_category_browse(member_config, interface))) {
1548 rt_handle_member_record(q, interface,
1549 ast_variable_retrieve(member_config, interface, "uniqueid"),
1550 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
1551 ast_variable_retrieve(member_config, interface, "penalty"),
1552 ast_variable_retrieve(member_config, interface, "paused"),
1553 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
1556 /* Delete all realtime members that have been deleted in DB. */
1557 mem_iter = ao2_iterator_init(q->members, 0);
1558 while ((m = ao2_iterator_next(&mem_iter))) {
1559 if (m->dead) {
1560 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
1561 ao2_unlink(q->members, m);
1562 remove_from_interfaces(m->state_interface);
1563 q->membercount--;
1565 ao2_ref(m, -1);
1568 ao2_unlock(q);
1570 return q;
1573 static struct call_queue *load_realtime_queue(const char *queuename)
1575 struct ast_variable *queue_vars;
1576 struct ast_config *member_config = NULL;
1577 struct call_queue *q = NULL, tmpq = {
1578 .name = queuename,
1581 /* Find the queue in the in-core list first. */
1582 q = ao2_find(queues, &tmpq, OBJ_POINTER);
1584 if (!q || q->realtime) {
1585 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
1586 queue operations while waiting for the DB.
1588 This will be two separate database transactions, so we might
1589 see queue parameters as they were before another process
1590 changed the queue and member list as it was after the change.
1591 Thus we might see an empty member list when a queue is
1592 deleted. In practise, this is unlikely to cause a problem. */
1594 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
1595 if (queue_vars) {
1596 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
1597 if (!member_config) {
1598 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
1599 ast_variables_destroy(queue_vars);
1600 return NULL;
1604 ao2_lock(queues);
1605 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
1606 if (member_config)
1607 ast_config_destroy(member_config);
1608 if (queue_vars)
1609 ast_variables_destroy(queue_vars);
1610 ao2_unlock(queues);
1612 } else {
1613 update_realtime_members(q);
1615 return q;
1618 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
1620 int ret = -1;
1622 if (ast_strlen_zero(mem->rt_uniqueid))
1623 return ret;
1625 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
1626 ret = 0;
1628 return ret;
1632 static void update_realtime_members(struct call_queue *q)
1634 struct ast_config *member_config = NULL;
1635 struct member *m;
1636 char *interface = NULL;
1637 struct ao2_iterator mem_iter;
1639 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
1640 /*This queue doesn't have realtime members*/
1641 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
1642 return;
1645 ao2_lock(q);
1647 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1648 mem_iter = ao2_iterator_init(q->members, 0);
1649 while ((m = ao2_iterator_next(&mem_iter))) {
1650 if (m->realtime)
1651 m->dead = 1;
1652 ao2_ref(m, -1);
1655 while ((interface = ast_category_browse(member_config, interface))) {
1656 rt_handle_member_record(q, interface,
1657 ast_variable_retrieve(member_config, interface, "uniqueid"),
1658 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
1659 ast_variable_retrieve(member_config, interface, "penalty"),
1660 ast_variable_retrieve(member_config, interface, "paused"),
1661 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
1664 /* Delete all realtime members that have been deleted in DB. */
1665 mem_iter = ao2_iterator_init(q->members, 0);
1666 while ((m = ao2_iterator_next(&mem_iter))) {
1667 if (m->dead) {
1668 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
1669 ao2_unlink(q->members, m);
1670 remove_from_interfaces(m->state_interface);
1671 q->membercount--;
1673 ao2_ref(m, -1);
1675 ao2_unlock(q);
1676 ast_config_destroy(member_config);
1679 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
1681 struct call_queue *q;
1682 struct queue_ent *cur, *prev = NULL;
1683 int res = -1;
1684 int pos = 0;
1685 int inserted = 0;
1686 enum queue_member_status stat;
1688 if (!(q = load_realtime_queue(queuename)))
1689 return res;
1691 ao2_lock(queues);
1692 ao2_lock(q);
1694 /* This is our one */
1695 stat = get_member_status(q, qe->max_penalty, qe->min_penalty);
1696 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
1697 *reason = QUEUE_JOINEMPTY;
1698 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
1699 *reason = QUEUE_JOINUNAVAIL;
1700 else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
1701 *reason = QUEUE_JOINUNAVAIL;
1702 else if (q->maxlen && (q->count >= q->maxlen))
1703 *reason = QUEUE_FULL;
1704 else {
1705 /* There's space for us, put us at the right position inside
1706 * the queue.
1707 * Take into account the priority of the calling user */
1708 inserted = 0;
1709 prev = NULL;
1710 cur = q->head;
1711 while (cur) {
1712 /* We have higher priority than the current user, enter
1713 * before him, after all the other users with priority
1714 * higher or equal to our priority. */
1715 if ((!inserted) && (qe->prio > cur->prio)) {
1716 insert_entry(q, prev, qe, &pos);
1717 inserted = 1;
1719 cur->pos = ++pos;
1720 prev = cur;
1721 cur = cur->next;
1723 /* No luck, join at the end of the queue */
1724 if (!inserted)
1725 insert_entry(q, prev, qe, &pos);
1726 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
1727 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
1728 ast_copy_string(qe->context, q->context, sizeof(qe->context));
1729 q->count++;
1730 res = 0;
1731 manager_event(EVENT_FLAG_CALL, "Join",
1732 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1733 qe->chan->name,
1734 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
1735 S_OR(qe->chan->cid.cid_name, "unknown"),
1736 q->name, qe->pos, q->count, qe->chan->uniqueid );
1737 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
1739 ao2_unlock(q);
1740 ao2_unlock(queues);
1742 return res;
1745 static int play_file(struct ast_channel *chan, const char *filename)
1747 int res;
1749 ast_stopstream(chan);
1751 res = ast_streamfile(chan, filename, chan->language);
1752 if (!res)
1753 res = ast_waitstream(chan, AST_DIGIT_ANY);
1755 ast_stopstream(chan);
1757 return res;
1761 * \brief Check for valid exit from queue via goto
1762 * \retval 0 if failure
1763 * \retval 1 if successful
1765 static int valid_exit(struct queue_ent *qe, char digit)
1767 int digitlen = strlen(qe->digits);
1769 /* Prevent possible buffer overflow */
1770 if (digitlen < sizeof(qe->digits) - 2) {
1771 qe->digits[digitlen] = digit;
1772 qe->digits[digitlen + 1] = '\0';
1773 } else {
1774 qe->digits[0] = '\0';
1775 return 0;
1778 /* If there's no context to goto, short-circuit */
1779 if (ast_strlen_zero(qe->context))
1780 return 0;
1782 /* If the extension is bad, then reset the digits to blank */
1783 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
1784 qe->digits[0] = '\0';
1785 return 0;
1788 /* We have an exact match */
1789 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
1790 qe->valid_digits = 1;
1791 /* Return 1 on a successful goto */
1792 return 1;
1795 return 0;
1798 static int say_position(struct queue_ent *qe, int ringing)
1800 int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
1801 time_t now;
1803 /* Let minannouncefrequency seconds pass between the start of each position announcement */
1804 time(&now);
1805 if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
1806 return 0;
1808 /* If either our position has changed, or we are over the freq timer, say position */
1809 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
1810 return 0;
1812 if (ringing) {
1813 ast_indicate(qe->chan,-1);
1814 } else {
1815 ast_moh_stop(qe->chan);
1818 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
1819 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
1820 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
1821 qe->pos <= qe->parent->announcepositionlimit))
1822 announceposition = 1;
1825 if (announceposition == 1) {
1826 /* Say we're next, if we are */
1827 if (qe->pos == 1) {
1828 res = play_file(qe->chan, qe->parent->sound_next);
1829 if (res)
1830 goto playout;
1831 else
1832 goto posout;
1833 } else {
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_quantity1);
1837 if (res)
1838 goto playout;
1839 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
1840 if (res)
1841 goto playout;
1842 } else {
1843 /* Normal Case */
1844 res = play_file(qe->chan, qe->parent->sound_thereare);
1845 if (res)
1846 goto playout;
1847 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
1848 if (res)
1849 goto playout;
1851 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
1852 /* More than Case*/
1853 res = play_file(qe->chan, qe->parent->queue_quantity2);
1854 if (res)
1855 goto playout;
1856 } else {
1857 res = play_file(qe->chan, qe->parent->sound_calls);
1858 if (res)
1859 goto playout;
1863 /* Round hold time to nearest minute */
1864 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
1866 /* If they have specified a rounding then round the seconds as well */
1867 if (qe->parent->roundingseconds) {
1868 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
1869 avgholdsecs *= qe->parent->roundingseconds;
1870 } else {
1871 avgholdsecs = 0;
1874 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
1876 /* If the hold time is >1 min, if it's enabled, and if it's not
1877 supposed to be only once and we have already said it, say it */
1878 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
1879 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
1880 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
1881 res = play_file(qe->chan, qe->parent->sound_holdtime);
1882 if (res)
1883 goto playout;
1885 if (avgholdmins > 1) {
1886 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
1887 if (res)
1888 goto playout;
1890 if (avgholdmins == 1) {
1891 res = play_file(qe->chan, qe->parent->sound_minute);
1892 if (res)
1893 goto playout;
1894 } else {
1895 res = play_file(qe->chan, qe->parent->sound_minutes);
1896 if (res)
1897 goto playout;
1900 if (avgholdsecs > 1) {
1901 res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
1902 if (res)
1903 goto playout;
1905 res = play_file(qe->chan, qe->parent->sound_seconds);
1906 if (res)
1907 goto playout;
1912 posout:
1913 if (announceposition == 1){
1914 if (qe->parent->announceposition) {
1915 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
1916 qe->chan->name, qe->parent->name, qe->pos);
1918 res = play_file(qe->chan, qe->parent->sound_thanks);
1920 playout:
1921 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
1922 res = 0;
1924 /* Set our last_pos indicators */
1925 qe->last_pos = now;
1926 qe->last_pos_said = qe->pos;
1928 /* Don't restart music on hold if we're about to exit the caller from the queue */
1929 if (!res) {
1930 if (ringing) {
1931 ast_indicate(qe->chan, AST_CONTROL_RINGING);
1932 } else {
1933 ast_moh_start(qe->chan, qe->moh, NULL);
1936 return res;
1939 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
1941 int oldvalue;
1943 /* Calculate holdtime using an exponential average */
1944 /* Thanks to SRT for this contribution */
1945 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1947 ao2_lock(qe->parent);
1948 oldvalue = qe->parent->holdtime;
1949 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
1950 ao2_unlock(qe->parent);
1953 /*! \brief Caller leaving queue.
1955 * Search the queue to find the leaving client, if found remove from queue
1956 * create manager event, move others up the queue.
1958 static void leave_queue(struct queue_ent *qe)
1960 struct call_queue *q;
1961 struct queue_ent *cur, *prev = NULL;
1962 struct penalty_rule *pr_iter;
1963 int pos = 0;
1965 if (!(q = qe->parent))
1966 return;
1967 queue_ref(q);
1968 ao2_lock(q);
1970 prev = NULL;
1971 for (cur = q->head; cur; cur = cur->next) {
1972 if (cur == qe) {
1973 q->count--;
1975 /* Take us out of the queue */
1976 manager_event(EVENT_FLAG_CALL, "Leave",
1977 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1978 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
1979 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
1980 /* Take us out of the queue */
1981 if (prev)
1982 prev->next = cur->next;
1983 else
1984 q->head = cur->next;
1985 /* Free penalty rules */
1986 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
1987 ast_free(pr_iter);
1988 } else {
1989 /* Renumber the people after us in the queue based on a new count */
1990 cur->pos = ++pos;
1991 prev = cur;
1994 ao2_unlock(q);
1996 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
1997 if (q->realtime) {
1998 if (!ast_load_realtime("queues", "name", q->name, SENTINEL))
1999 q->dead = 1;
2002 if (q->dead) {
2003 /* It's dead and nobody is in it, so kill it */
2004 ao2_unlink(queues, q);
2005 /* unref the container's reference to the queue */
2006 queue_unref(q);
2008 /* unref the explicit ref earlier in the function */
2009 queue_unref(q);
2012 /*! \brief Hang up a list of outgoing calls */
2013 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
2015 struct callattempt *oo;
2017 while (outgoing) {
2018 /* Hangup any existing lines we have open */
2019 if (outgoing->chan && (outgoing->chan != exception))
2020 ast_hangup(outgoing->chan);
2021 oo = outgoing;
2022 outgoing = outgoing->q_next;
2023 if (oo->member)
2024 ao2_ref(oo->member, -1);
2025 ast_free(oo);
2029 /*!
2030 * \brief traverse all defined queues which have calls waiting and contain this member
2031 * \retval 0 if no other queue has precedence (higher weight)
2032 * \retval 1 if found
2034 static int compare_weight(struct call_queue *rq, struct member *member)
2036 struct call_queue *q;
2037 struct member *mem;
2038 int found = 0;
2039 struct ao2_iterator queue_iter;
2041 /* q's lock and rq's lock already set by try_calling()
2042 * to solve deadlock */
2043 queue_iter = ao2_iterator_init(queues, 0);
2044 while ((q = ao2_iterator_next(&queue_iter))) {
2045 if (q == rq) { /* don't check myself, could deadlock */
2046 queue_unref(q);
2047 continue;
2049 ao2_lock(q);
2050 if (q->count && q->members) {
2051 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
2052 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
2053 if (q->weight > rq->weight) {
2054 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);
2055 found = 1;
2057 ao2_ref(mem, -1);
2060 ao2_unlock(q);
2061 if (found) {
2062 queue_unref(q);
2063 break;
2065 queue_unref(q);
2067 return found;
2070 /*! \brief common hangup actions */
2071 static void do_hang(struct callattempt *o)
2073 o->stillgoing = 0;
2074 ast_hangup(o->chan);
2075 o->chan = NULL;
2078 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2079 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
2081 struct ast_str *buf = ast_str_alloca(len + 1);
2082 char *tmp;
2084 if (pbx_builtin_serialize_variables(chan, &buf)) {
2085 int i, j;
2087 /* convert "\n" to "\nVariable: " */
2088 strcpy(vars, "Variable: ");
2089 tmp = buf->str;
2091 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
2092 vars[j] = tmp[i];
2094 if (tmp[i + 1] == '\0')
2095 break;
2096 if (tmp[i] == '\n') {
2097 vars[j++] = '\r';
2098 vars[j++] = '\n';
2100 ast_copy_string(&(vars[j]), "Variable: ", len - j);
2101 j += 9;
2104 if (j > len - 3)
2105 j = len - 3;
2106 vars[j++] = '\r';
2107 vars[j++] = '\n';
2108 vars[j] = '\0';
2109 } else {
2110 /* there are no channel variables; leave it blank */
2111 *vars = '\0';
2113 return vars;
2116 /*!
2117 * \brief Part 2 of ring_one
2119 * Does error checking before attempting to request a channel and call a member.
2120 * This function is only called from ring_one().
2121 * Failure can occur if:
2122 * - Agent on call
2123 * - Agent is paused
2124 * - Wrapup time not expired
2125 * - Priority by another queue
2127 * \retval 1 on success to reach a free agent
2128 * \retval 0 on failure to get agent.
2130 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
2132 int res;
2133 int status;
2134 char tech[256];
2135 char *location;
2136 const char *macrocontext, *macroexten;
2138 /* on entry here, we know that tmp->chan == NULL */
2139 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
2140 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
2141 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
2142 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
2143 if (qe->chan->cdr)
2144 ast_cdr_busy(qe->chan->cdr);
2145 tmp->stillgoing = 0;
2146 (*busies)++;
2147 return 0;
2150 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
2151 ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
2152 if (qe->chan->cdr)
2153 ast_cdr_busy(qe->chan->cdr);
2154 tmp->stillgoing = 0;
2155 return 0;
2158 if (tmp->member->paused) {
2159 ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
2160 if (qe->chan->cdr)
2161 ast_cdr_busy(qe->chan->cdr);
2162 tmp->stillgoing = 0;
2163 return 0;
2165 if (use_weight && compare_weight(qe->parent,tmp->member)) {
2166 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
2167 if (qe->chan->cdr)
2168 ast_cdr_busy(qe->chan->cdr);
2169 tmp->stillgoing = 0;
2170 (*busies)++;
2171 return 0;
2174 ast_copy_string(tech, tmp->interface, sizeof(tech));
2175 if ((location = strchr(tech, '/')))
2176 *location++ = '\0';
2177 else
2178 location = "";
2180 /* Request the peer */
2181 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
2182 if (!tmp->chan) { /* If we can't, just go on to the next call */
2183 if (qe->chan->cdr)
2184 ast_cdr_busy(qe->chan->cdr);
2185 tmp->stillgoing = 0;
2187 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
2189 ao2_lock(qe->parent);
2190 qe->parent->rrpos++;
2191 qe->linpos++;
2192 ao2_unlock(qe->parent);
2195 (*busies)++;
2196 return 0;
2199 tmp->chan->appl = "AppQueue";
2200 tmp->chan->data = "(Outgoing Line)";
2201 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
2202 if (tmp->chan->cid.cid_num)
2203 ast_free(tmp->chan->cid.cid_num);
2204 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
2205 if (tmp->chan->cid.cid_name)
2206 ast_free(tmp->chan->cid.cid_name);
2207 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
2208 if (tmp->chan->cid.cid_ani)
2209 ast_free(tmp->chan->cid.cid_ani);
2210 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
2212 /* Inherit specially named variables from parent channel */
2213 ast_channel_inherit_variables(qe->chan, tmp->chan);
2215 /* Presense of ADSI CPE on outgoing channel follows ours */
2216 tmp->chan->adsicpe = qe->chan->adsicpe;
2218 /* Inherit context and extension */
2219 ast_channel_lock(qe->chan);
2220 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
2221 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
2222 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
2223 if (!ast_strlen_zero(macroexten))
2224 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
2225 else
2226 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
2227 ast_channel_unlock(qe->chan);
2229 /* Place the call, but don't wait on the answer */
2230 if ((res = ast_call(tmp->chan, location, 0))) {
2231 /* Again, keep going even if there's an error */
2232 ast_debug(1, "ast call on peer returned %d\n", res);
2233 ast_verb(3, "Couldn't call %s\n", tmp->interface);
2234 do_hang(tmp);
2235 (*busies)++;
2236 return 0;
2237 } else if (qe->parent->eventwhencalled) {
2238 char vars[2048];
2240 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
2241 "Queue: %s\r\n"
2242 "AgentCalled: %s\r\n"
2243 "AgentName: %s\r\n"
2244 "ChannelCalling: %s\r\n"
2245 "DestinationChannel: %s\r\n"
2246 "CallerIDNum: %s\r\n"
2247 "CallerIDName: %s\r\n"
2248 "Context: %s\r\n"
2249 "Extension: %s\r\n"
2250 "Priority: %d\r\n"
2251 "Uniqueid: %s\r\n"
2252 "%s",
2253 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
2254 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
2255 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
2256 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
2257 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2258 ast_verb(3, "Called %s\n", tmp->interface);
2261 return 1;
2264 /*! \brief find the entry with the best metric, or NULL */
2265 static struct callattempt *find_best(struct callattempt *outgoing)
2267 struct callattempt *best = NULL, *cur;
2269 for (cur = outgoing; cur; cur = cur->q_next) {
2270 if (cur->stillgoing && /* Not already done */
2271 !cur->chan && /* Isn't already going */
2272 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
2273 best = cur;
2277 return best;
2280 /*!
2281 * \brief Place a call to a queue member.
2283 * Once metrics have been calculated for each member, this function is used
2284 * to place a call to the appropriate member (or members). The low-level
2285 * channel-handling and error detection is handled in ring_entry
2287 * \retval 1 if a member was called successfully
2288 * \retval 0 otherwise
2290 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
2292 int ret = 0;
2294 while (ret == 0) {
2295 struct callattempt *best = find_best(outgoing);
2296 if (!best) {
2297 ast_debug(1, "Nobody left to try ringing in queue\n");
2298 break;
2300 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2301 struct callattempt *cur;
2302 /* Ring everyone who shares this best metric (for ringall) */
2303 for (cur = outgoing; cur; cur = cur->q_next) {
2304 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
2305 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
2306 ret |= ring_entry(qe, cur, busies);
2309 } else {
2310 /* Ring just the best channel */
2311 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
2312 ret = ring_entry(qe, best, busies);
2316 return ret;
2319 /*! \brief Search for best metric and add to Round Robbin queue */
2320 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
2322 struct callattempt *best = find_best(outgoing);
2324 if (best) {
2325 /* Ring just the best channel */
2326 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
2327 qe->parent->rrpos = best->metric % 1000;
2328 } else {
2329 /* Just increment rrpos */
2330 if (qe->parent->wrapped) {
2331 /* No more channels, start over */
2332 qe->parent->rrpos = 0;
2333 } else {
2334 /* Prioritize next entry */
2335 qe->parent->rrpos++;
2338 qe->parent->wrapped = 0;
2340 return 0;
2343 /*! \brief Search for best metric and add to Linear queue */
2344 static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
2346 struct callattempt *best = find_best(outgoing);
2348 if (best) {
2349 /* Ring just the best channel */
2350 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
2351 qe->linpos = best->metric % 1000;
2352 } else {
2353 /* Just increment rrpos */
2354 if (qe->linwrapped) {
2355 /* No more channels, start over */
2356 qe->linpos = 0;
2357 } else {
2358 /* Prioritize next entry */
2359 qe->linpos++;
2362 qe->linwrapped = 0;
2364 return 0;
2367 /*! \brief Playback announcement to queued members if peroid has elapsed */
2368 static int say_periodic_announcement(struct queue_ent *qe, int ringing)
2370 int res = 0;
2371 time_t now;
2373 /* Get the current time */
2374 time(&now);
2376 /* Check to see if it is time to announce */
2377 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
2378 return 0;
2380 /* Stop the music on hold so we can play our own file */
2381 if (ringing)
2382 ast_indicate(qe->chan,-1);
2383 else
2384 ast_moh_stop(qe->chan);
2386 ast_verb(3, "Playing periodic announcement\n");
2388 if (qe->parent->randomperiodicannounce) {
2389 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
2390 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
2391 ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
2392 qe->last_periodic_announce_sound = 0;
2395 /* play the announcement */
2396 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
2398 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
2399 res = 0;
2401 /* Resume Music on Hold if the caller is going to stay in the queue */
2402 if (!res) {
2403 if (ringing)
2404 ast_indicate(qe->chan, AST_CONTROL_RINGING);
2405 else
2406 ast_moh_start(qe->chan, qe->moh, NULL);
2409 /* update last_periodic_announce_time */
2410 qe->last_periodic_announce_time = now;
2412 /* Update the current periodic announcement to the next announcement */
2413 if (!qe->parent->randomperiodicannounce) {
2414 qe->last_periodic_announce_sound++;
2417 return res;
2420 /*! \brief Record that a caller gave up on waiting in queue */
2421 static void record_abandoned(struct queue_ent *qe)
2423 ao2_lock(qe->parent);
2424 set_queue_variables(qe);
2425 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
2426 "Queue: %s\r\n"
2427 "Uniqueid: %s\r\n"
2428 "Position: %d\r\n"
2429 "OriginalPosition: %d\r\n"
2430 "HoldTime: %d\r\n",
2431 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
2433 qe->parent->callsabandoned++;
2434 ao2_unlock(qe->parent);
2437 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2438 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
2440 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
2441 if (qe->parent->eventwhencalled)
2442 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
2443 "Queue: %s\r\n"
2444 "Uniqueid: %s\r\n"
2445 "Channel: %s\r\n"
2446 "Member: %s\r\n"
2447 "MemberName: %s\r\n"
2448 "Ringtime: %d\r\n",
2449 qe->parent->name,
2450 qe->chan->uniqueid,
2451 qe->chan->name,
2452 interface,
2453 membername,
2454 rnatime);
2455 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
2456 if (qe->parent->autopause) {
2457 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
2458 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
2459 } else {
2460 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
2463 return;
2466 #define AST_MAX_WATCHERS 256
2467 /*! \brief Wait for a member to answer the call
2469 * \param[in] qe the queue_ent corresponding to the caller in the queue
2470 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
2471 * \param[in] to the amount of time (in milliseconds) to wait for a response
2472 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
2473 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
2474 * \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
2475 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
2477 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2479 const char *queue = qe->parent->name;
2480 struct callattempt *o, *start = NULL, *prev = NULL;
2481 int status;
2482 int numbusies = prebusies;
2483 int numnochan = 0;
2484 int stillgoing = 0;
2485 int orig = *to;
2486 struct ast_frame *f;
2487 struct callattempt *peer = NULL;
2488 struct ast_channel *winner;
2489 struct ast_channel *in = qe->chan;
2490 char on[80] = "";
2491 char membername[80] = "";
2492 long starttime = 0;
2493 long endtime = 0;
2494 #ifdef HAVE_EPOLL
2495 struct callattempt *epollo;
2496 #endif
2498 starttime = (long) time(NULL);
2499 #ifdef HAVE_EPOLL
2500 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
2501 if (epollo->chan)
2502 ast_poll_channel_add(in, epollo->chan);
2504 #endif
2506 while (*to && !peer) {
2507 int numlines, retry, pos = 1;
2508 struct ast_channel *watchers[AST_MAX_WATCHERS];
2509 watchers[0] = in;
2510 start = NULL;
2512 for (retry = 0; retry < 2; retry++) {
2513 numlines = 0;
2514 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
2515 if (o->stillgoing) { /* Keep track of important channels */
2516 stillgoing = 1;
2517 if (o->chan) {
2518 watchers[pos++] = o->chan;
2519 if (!start)
2520 start = o;
2521 else
2522 prev->call_next = o;
2523 prev = o;
2526 numlines++;
2528 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
2529 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
2530 break;
2531 /* On "ringall" strategy we only move to the next penalty level
2532 when *all* ringing phones are done in the current penalty level */
2533 ring_one(qe, outgoing, &numbusies);
2534 /* and retry... */
2536 if (pos == 1 /* not found */) {
2537 if (numlines == (numbusies + numnochan)) {
2538 ast_debug(1, "Everyone is busy at this time\n");
2539 } else {
2540 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
2542 *to = 0;
2543 return NULL;
2545 winner = ast_waitfor_n(watchers, pos, to);
2546 for (o = start; o; o = o->call_next) {
2547 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
2548 if (!peer) {
2549 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
2550 peer = o;
2552 } else if (o->chan && (o->chan == winner)) {
2554 ast_copy_string(on, o->member->interface, sizeof(on));
2555 ast_copy_string(membername, o->member->membername, sizeof(membername));
2557 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
2558 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
2559 numnochan++;
2560 do_hang(o);
2561 winner = NULL;
2562 continue;
2563 } else if (!ast_strlen_zero(o->chan->call_forward)) {
2564 char tmpchan[256];
2565 char *stuff;
2566 char *tech;
2568 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
2569 if ((stuff = strchr(tmpchan, '/'))) {
2570 *stuff++ = '\0';
2571 tech = tmpchan;
2572 } else {
2573 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
2574 stuff = tmpchan;
2575 tech = "Local";
2577 /* Before processing channel, go ahead and check for forwarding */
2578 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
2579 /* Setup parameters */
2580 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
2581 if (!o->chan) {
2582 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
2583 o->stillgoing = 0;
2584 numnochan++;
2585 } else {
2586 ast_channel_inherit_variables(in, o->chan);
2587 ast_channel_datastore_inherit(in, o->chan);
2588 if (o->chan->cid.cid_num)
2589 ast_free(o->chan->cid.cid_num);
2590 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
2592 if (o->chan->cid.cid_name)
2593 ast_free(o->chan->cid.cid_name);
2594 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
2596 ast_string_field_set(o->chan, accountcode, in->accountcode);
2597 o->chan->cdrflags = in->cdrflags;
2599 if (in->cid.cid_ani) {
2600 if (o->chan->cid.cid_ani)
2601 ast_free(o->chan->cid.cid_ani);
2602 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
2604 if (o->chan->cid.cid_rdnis)
2605 ast_free(o->chan->cid.cid_rdnis);
2606 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
2607 if (ast_call(o->chan, tmpchan, 0)) {
2608 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
2609 do_hang(o);
2610 numnochan++;
2613 /* Hangup the original channel now, in case we needed it */
2614 ast_hangup(winner);
2615 continue;
2617 f = ast_read(winner);
2618 if (f) {
2619 if (f->frametype == AST_FRAME_CONTROL) {
2620 switch (f->subclass) {
2621 case AST_CONTROL_ANSWER:
2622 /* This is our guy if someone answered. */
2623 if (!peer) {
2624 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
2625 peer = o;
2627 break;
2628 case AST_CONTROL_BUSY:
2629 ast_verb(3, "%s is busy\n", o->chan->name);
2630 if (in->cdr)
2631 ast_cdr_busy(in->cdr);
2632 do_hang(o);
2633 endtime = (long) time(NULL);
2634 endtime -= starttime;
2635 rna(endtime*1000, qe, on, membername);
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_CONGESTION:
2644 ast_verb(3, "%s is circuit-busy\n", o->chan->name);
2645 if (in->cdr)
2646 ast_cdr_busy(in->cdr);
2647 endtime = (long) time(NULL);
2648 endtime -= starttime;
2649 rna(endtime*1000, qe, on, membername);
2650 do_hang(o);
2651 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2652 if (qe->parent->timeoutrestart)
2653 *to = orig;
2654 ring_one(qe, outgoing, &numbusies);
2656 numbusies++;
2657 break;
2658 case AST_CONTROL_RINGING:
2659 ast_verb(3, "%s is ringing\n", o->chan->name);
2660 break;
2661 case AST_CONTROL_OFFHOOK:
2662 /* Ignore going off hook */
2663 break;
2664 default:
2665 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
2668 ast_frfree(f);
2669 } else {
2670 endtime = (long) time(NULL) - starttime;
2671 rna(endtime * 1000, qe, on, membername);
2672 do_hang(o);
2673 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2674 if (qe->parent->timeoutrestart)
2675 *to = orig;
2676 ring_one(qe, outgoing, &numbusies);
2681 if (winner == in) {
2682 f = ast_read(in);
2683 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
2684 /* Got hung up */
2685 *to = -1;
2686 if (f) {
2687 if (f->data.uint32) {
2688 in->hangupcause = f->data.uint32;
2690 ast_frfree(f);
2692 return NULL;
2694 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
2695 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
2696 *to = 0;
2697 ast_frfree(f);
2698 return NULL;
2700 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
2701 ast_verb(3, "User pressed digit: %c\n", f->subclass);
2702 *to = 0;
2703 *digit = f->subclass;
2704 ast_frfree(f);
2705 return NULL;
2707 ast_frfree(f);
2709 if (!*to) {
2710 for (o = start; o; o = o->call_next)
2711 rna(orig, qe, o->interface, o->member->membername);
2715 #ifdef HAVE_EPOLL
2716 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
2717 if (epollo->chan)
2718 ast_poll_channel_del(in, epollo->chan);
2720 #endif
2722 return peer;
2725 /*!
2726 * \brief Check if we should start attempting to call queue members.
2728 * The behavior of this function is dependent first on whether autofill is enabled
2729 * and second on whether the ring strategy is ringall. If autofill is not enabled,
2730 * then return true if we're the head of the queue. If autofill is enabled, then
2731 * we count the available members and see if the number of available members is enough
2732 * that given our position in the queue, we would theoretically be able to connect to
2733 * one of those available members
2735 static int is_our_turn(struct queue_ent *qe)
2737 struct queue_ent *ch;
2738 struct member *cur;
2739 int avl = 0;
2740 int idx = 0;
2741 int res;
2743 if (!qe->parent->autofill) {
2744 /* Atomically read the parent head -- does not need a lock */
2745 ch = qe->parent->head;
2746 /* If we are now at the top of the head, break out */
2747 if (ch == qe) {
2748 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
2749 res = 1;
2750 } else {
2751 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
2752 res = 0;
2755 } else {
2756 /* This needs a lock. How many members are available to be served? */
2757 ao2_lock(qe->parent);
2759 ch = qe->parent->head;
2761 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2762 ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
2763 avl = 1;
2764 } else {
2765 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
2766 while ((cur = ao2_iterator_next(&mem_iter))) {
2767 switch (cur->status) {
2768 case AST_DEVICE_INUSE:
2769 if (!qe->parent->ringinuse)
2770 break;
2771 /* else fall through */
2772 case AST_DEVICE_NOT_INUSE:
2773 case AST_DEVICE_UNKNOWN:
2774 if (!cur->paused)
2775 avl++;
2776 break;
2778 ao2_ref(cur, -1);
2782 ast_debug(1, "There are %d available members.\n", avl);
2784 while ((idx < avl) && (ch) && (ch != qe)) {
2785 if (!ch->pending)
2786 idx++;
2787 ch = ch->next;
2790 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2791 if (ch && idx < avl) {
2792 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
2793 res = 1;
2794 } else {
2795 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
2796 res = 0;
2799 ao2_unlock(qe->parent);
2802 return res;
2806 * \brief update rules for queues
2808 * Calculate min/max penalties making sure if relative they stay within bounds.
2809 * Update queues penalty and set dialplan vars, goto next list entry.
2811 static void update_qe_rule(struct queue_ent *qe)
2813 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
2814 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
2815 char max_penalty_str[20], min_penalty_str[20];
2816 /* a relative change to the penalty could put it below 0 */
2817 if (max_penalty < 0)
2818 max_penalty = 0;
2819 if (min_penalty < 0)
2820 min_penalty = 0;
2821 if (min_penalty > max_penalty)
2822 min_penalty = max_penalty;
2823 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
2824 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
2825 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
2826 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
2827 qe->max_penalty = max_penalty;
2828 qe->min_penalty = min_penalty;
2829 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);
2830 qe->pr = AST_LIST_NEXT(qe->pr, list);
2833 /*! \brief The waiting areas for callers who are not actively calling members
2835 * This function is one large loop. This function will return if a caller
2836 * either exits the queue or it becomes that caller's turn to attempt calling
2837 * queue members. Inside the loop, we service the caller with periodic announcements,
2838 * holdtime announcements, etc. as configured in queues.conf
2840 * \retval 0 if the caller's turn has arrived
2841 * \retval -1 if the caller should exit the queue.
2843 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
2845 int res = 0;
2847 /* This is the holding pen for callers 2 through maxlen */
2848 for (;;) {
2849 enum queue_member_status stat;
2851 if (is_our_turn(qe))
2852 break;
2854 /* If we have timed out, break out */
2855 if (qe->expire && (time(NULL) > qe->expire)) {
2856 *reason = QUEUE_TIMEOUT;
2857 break;
2860 stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
2862 /* leave the queue if no agents, if enabled */
2863 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
2864 *reason = QUEUE_LEAVEEMPTY;
2865 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2866 leave_queue(qe);
2867 break;
2870 /* leave the queue if no reachable agents, if enabled */
2871 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
2872 *reason = QUEUE_LEAVEUNAVAIL;
2873 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2874 leave_queue(qe);
2875 break;
2877 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
2878 *reason = QUEUE_LEAVEUNAVAIL;
2879 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2880 leave_queue(qe);
2881 break;
2884 /* Make a position announcement, if enabled */
2885 if (qe->parent->announcefrequency &&
2886 (res = say_position(qe,ringing)))
2887 break;
2889 /* Make a periodic announcement, if enabled */
2890 if (qe->parent->periodicannouncefrequency &&
2891 (res = say_periodic_announcement(qe,ringing)))
2892 break;
2894 /* see if we need to move to the next penalty level for this queue */
2895 while (qe->pr && ((time(NULL) - qe->start) > qe->pr->time)) {
2896 update_qe_rule(qe);
2899 /* Wait a second before checking again */
2900 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
2901 if (res > 0 && !valid_exit(qe, res))
2902 res = 0;
2903 else
2904 break;
2908 return res;
2912 * \brief update the queue status
2913 * \retval Always 0
2915 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
2917 struct member *mem;
2918 struct call_queue *qtmp;
2919 struct ao2_iterator queue_iter;
2921 if (shared_lastcall) {
2922 queue_iter = ao2_iterator_init(queues, 0);
2923 while ((qtmp = ao2_iterator_next(&queue_iter))) {
2924 ao2_lock(qtmp);
2925 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
2926 time(&mem->lastcall);
2927 mem->calls++;
2928 mem->lastqueue = q;
2929 ao2_ref(mem, -1);
2931 ao2_unlock(qtmp);
2932 ao2_ref(qtmp, -1);
2934 } else {
2935 ao2_lock(q);
2936 time(&member->lastcall);
2937 member->calls++;
2938 member->lastqueue = q;
2939 ao2_unlock(q);
2941 ao2_lock(q);
2942 q->callscompleted++;
2943 if (callcompletedinsl)
2944 q->callscompletedinsl++;
2945 ao2_unlock(q);
2946 return 0;
2949 /*! \brief Calculate the metric of each member in the outgoing callattempts
2951 * A numeric metric is given to each member depending on the ring strategy used
2952 * by the queue. Members with lower metrics will be called before members with
2953 * higher metrics
2954 * \retval -1 if penalties are exceeded
2955 * \retval 0 otherwise
2957 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
2959 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
2960 return -1;
2962 switch (q->strategy) {
2963 case QUEUE_STRATEGY_RINGALL:
2964 /* Everyone equal, except for penalty */
2965 tmp->metric = mem->penalty * 1000000;
2966 break;
2967 case QUEUE_STRATEGY_LINEAR:
2968 if (pos < qe->linpos) {
2969 tmp->metric = 1000 + pos;
2970 } else {
2971 if (pos > qe->linpos)
2972 /* Indicate there is another priority */
2973 qe->linwrapped = 1;
2974 tmp->metric = pos;
2976 tmp->metric += mem->penalty * 1000000;
2977 break;
2978 case QUEUE_STRATEGY_RRMEMORY:
2979 if (pos < q->rrpos) {
2980 tmp->metric = 1000 + pos;
2981 } else {
2982 if (pos > q->rrpos)
2983 /* Indicate there is another priority */
2984 q->wrapped = 1;
2985 tmp->metric = pos;
2987 tmp->metric += mem->penalty * 1000000;
2988 break;
2989 case QUEUE_STRATEGY_RANDOM:
2990 tmp->metric = ast_random() % 1000;
2991 tmp->metric += mem->penalty * 1000000;
2992 break;
2993 case QUEUE_STRATEGY_WRANDOM:
2994 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
2995 break;
2996 case QUEUE_STRATEGY_FEWESTCALLS:
2997 tmp->metric = mem->calls;
2998 tmp->metric += mem->penalty * 1000000;
2999 break;
3000 case QUEUE_STRATEGY_LEASTRECENT:
3001 if (!mem->lastcall)
3002 tmp->metric = 0;
3003 else
3004 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
3005 tmp->metric += mem->penalty * 1000000;
3006 break;
3007 default:
3008 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
3009 break;
3011 return 0;
3014 enum agent_complete_reason {
3015 CALLER,
3016 AGENT,
3017 TRANSFER
3020 /*! \brief Send out AMI message with member call completion status information */
3021 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
3022 const struct ast_channel *peer, const struct member *member, time_t callstart,
3023 char *vars, size_t vars_len, enum agent_complete_reason rsn)
3025 const char *reason = NULL; /* silence dumb compilers */
3027 if (!qe->parent->eventwhencalled)
3028 return;
3030 switch (rsn) {
3031 case CALLER:
3032 reason = "caller";
3033 break;
3034 case AGENT:
3035 reason = "agent";
3036 break;
3037 case TRANSFER:
3038 reason = "transfer";
3039 break;
3042 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
3043 "Queue: %s\r\n"
3044 "Uniqueid: %s\r\n"
3045 "Channel: %s\r\n"
3046 "Member: %s\r\n"
3047 "MemberName: %s\r\n"
3048 "HoldTime: %ld\r\n"
3049 "TalkTime: %ld\r\n"
3050 "Reason: %s\r\n"
3051 "%s",
3052 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3053 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
3054 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
3057 struct queue_transfer_ds {
3058 struct queue_ent *qe;
3059 struct member *member;
3060 int starttime;
3063 static void queue_transfer_destroy(void *data)
3065 struct queue_transfer_ds *qtds = data;
3066 ast_free(qtds);
3069 /*! \brief a datastore used to help correctly log attended transfers of queue callers
3071 static const struct ast_datastore_info queue_transfer_info = {
3072 .type = "queue_transfer",
3073 .chan_fixup = queue_transfer_fixup,
3074 .destroy = queue_transfer_destroy,
3077 /*! \brief Log an attended transfer when a queue caller channel is masqueraded
3079 * When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when
3080 * the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan
3081 * to new_chan. This is why new_chan is referenced for exten, context, and datastore information.
3083 * At the end of this, we want to remove the datastore so that this fixup function is not called on any
3084 * future masquerades of the caller during the current call.
3086 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
3088 struct queue_transfer_ds *qtds = data;
3089 struct queue_ent *qe = qtds->qe;
3090 struct member *member = qtds->member;
3091 int callstart = qtds->starttime;
3092 struct ast_datastore *datastore;
3094 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
3095 new_chan->exten, new_chan->context, (long) (callstart - qe->start),
3096 (long) (time(NULL) - callstart), qe->opos);
3098 if (!(datastore = ast_channel_datastore_find(new_chan, &queue_transfer_info, NULL))) {
3099 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
3100 return;
3103 ast_channel_datastore_remove(new_chan, datastore);
3104 ast_channel_datastore_free(datastore);
3107 /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
3109 * When a caller is atxferred, then the queue_transfer_info datastore
3110 * is removed from the channel. If it's still there after the bridge is
3111 * broken, then the caller was not atxferred.
3113 static int attended_transfer_occurred(struct ast_channel *chan)
3115 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
3118 /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
3120 static void setup_transfer_datastore(struct queue_ent *qe, struct member *member, int starttime)
3122 struct ast_datastore *ds;
3123 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
3125 if (!qtds) {
3126 ast_log(LOG_WARNING, "Memory allocation error!\n");
3127 return;
3130 ast_channel_lock(qe->chan);
3131 if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
3132 ast_channel_unlock(qe->chan);
3133 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
3134 return;
3137 qtds->qe = qe;
3138 /* This member is refcounted in try_calling, so no need to add it here, too */
3139 qtds->member = member;
3140 qtds->starttime = starttime;
3141 ds->data = qtds;
3142 ast_channel_datastore_add(qe->chan, ds);
3143 ast_channel_unlock(qe->chan);
3146 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
3148 * Here is the process of this function
3149 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
3150 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
3151 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
3152 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
3153 * during each iteration, we call calc_metric to determine which members should be rung when.
3154 * 3. Call ring_one to place a call to the appropriate member(s)
3155 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
3156 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
3157 * 6. Start the monitor or mixmonitor if the option is set
3158 * 7. Remove the caller from the queue to allow other callers to advance
3159 * 8. Bridge the call.
3160 * 9. Do any post processing after the call has disconnected.
3162 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
3163 * \param[in] options the options passed as the third parameter to the Queue() application
3164 * \param[in] announceoverride filename to play to user when waiting
3165 * \param[in] url the url passed as the fourth parameter to the Queue() application
3166 * \param[in,out] tries the number of times we have tried calling queue members
3167 * \param[out] noption set if the call to Queue() has the 'n' option set.
3168 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
3169 * \param[in] macro the macro passed as the sixth parameter to the Queue() application
3170 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
3171 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
3173 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)
3175 struct member *cur;
3176 struct callattempt *outgoing = NULL; /* the list of calls we are building */
3177 int to, orig;
3178 char oldexten[AST_MAX_EXTENSION]="";
3179 char oldcontext[AST_MAX_CONTEXT]="";
3180 char queuename[256]="";
3181 char interfacevar[256]="";
3182 struct ast_channel *peer;
3183 struct ast_channel *which;
3184 struct callattempt *lpeer;
3185 struct member *member;
3186 struct ast_app *app;
3187 int res = 0, bridge = 0;
3188 int numbusies = 0;
3189 int x=0;
3190 char *announce = NULL;
3191 char digit = 0;
3192 time_t callstart;
3193 time_t now = time(NULL);
3194 struct ast_bridge_config bridge_config;
3195 char nondataquality = 1;
3196 char *agiexec = NULL;
3197 char *macroexec = NULL;
3198 char *gosubexec = NULL;
3199 int ret = 0;
3200 const char *monitorfilename;
3201 const char *monitor_exec;
3202 const char *monitor_options;
3203 char tmpid[256], tmpid2[256];
3204 char meid[1024], meid2[1024];
3205 char mixmonargs[1512];
3206 struct ast_app *mixmonapp = NULL;
3207 char *p;
3208 char vars[2048];
3209 int forwardsallowed = 1;
3210 int callcompletedinsl;
3211 struct ao2_iterator memi;
3212 struct ast_datastore *datastore;
3214 ast_channel_lock(qe->chan);
3215 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
3216 ast_channel_unlock(qe->chan);
3218 memset(&bridge_config, 0, sizeof(bridge_config));
3219 tmpid[0] = 0;
3220 meid[0] = 0;
3221 time(&now);
3223 for (; options && *options; options++)
3224 switch (*options) {
3225 case 't':
3226 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
3227 break;
3228 case 'T':
3229 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
3230 break;
3231 case 'w':
3232 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
3233 break;
3234 case 'W':
3235 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
3236 break;
3237 case 'd':
3238 nondataquality = 0;
3239 break;
3240 case 'h':
3241 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
3242 break;
3243 case 'H':
3244 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
3245 break;
3246 case 'k':
3247 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
3248 break;
3249 case 'K':
3250 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
3251 break;
3252 case 'n':
3253 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
3254 (*tries)++;
3255 else
3256 *tries = qe->parent->membercount;
3257 *noption = 1;
3258 break;
3259 case 'i':
3260 forwardsallowed = 0;
3261 break;
3262 case 'x':
3263 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
3264 break;
3265 case 'X':
3266 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
3267 break;
3271 /* Hold the lock while we setup the outgoing calls */
3272 if (use_weight)
3273 ao2_lock(queues);
3274 ao2_lock(qe->parent);
3275 ast_debug(1, "%s is trying to call a queue member.\n",
3276 qe->chan->name);
3277 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
3278 if (!ast_strlen_zero(qe->announce))
3279 announce = qe->announce;
3280 if (!ast_strlen_zero(announceoverride))
3281 announce = announceoverride;
3283 memi = ao2_iterator_init(qe->parent->members, 0);
3284 while ((cur = ao2_iterator_next(&memi))) {
3285 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
3286 struct ast_dialed_interface *di;
3287 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
3288 if (!tmp) {
3289 ao2_ref(cur, -1);
3290 ao2_unlock(qe->parent);
3291 if (use_weight)
3292 ao2_unlock(queues);
3293 goto out;
3295 if (!datastore) {
3296 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
3297 ao2_ref(cur, -1);
3298 ao2_unlock(qe->parent);
3299 if (use_weight)
3300 ao2_unlock(queues);
3301 free(tmp);
3302 goto out;
3304 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
3305 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
3306 ao2_ref(cur, -1);
3307 ao2_unlock(&qe->parent);
3308 if (use_weight)
3309 ao2_unlock(queues);
3310 free(tmp);
3311 goto out;
3313 datastore->data = dialed_interfaces;
3314 AST_LIST_HEAD_INIT(dialed_interfaces);
3316 ast_channel_lock(qe->chan);
3317 ast_channel_datastore_add(qe->chan, datastore);
3318 ast_channel_unlock(qe->chan);
3319 } else
3320 dialed_interfaces = datastore->data;
3322 AST_LIST_LOCK(dialed_interfaces);
3323 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
3324 if (!strcasecmp(cur->interface, di->interface)) {
3325 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
3326 di->interface);
3327 break;
3330 AST_LIST_UNLOCK(dialed_interfaces);
3332 if (di) {
3333 free(tmp);
3334 continue;
3337 /* It is always ok to dial a Local interface. We only keep track of
3338 * which "real" interfaces have been dialed. The Local channel will
3339 * inherit this list so that if it ends up dialing a real interface,
3340 * it won't call one that has already been called. */
3341 if (strncasecmp(cur->interface, "Local/", 6)) {
3342 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
3343 ao2_ref(cur, -1);
3344 ao2_unlock(qe->parent);
3345 if (use_weight)
3346 ao2_unlock(queues);
3347 free(tmp);
3348 goto out;
3350 strcpy(di->interface, cur->interface);
3352 AST_LIST_LOCK(dialed_interfaces);
3353 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
3354 AST_LIST_UNLOCK(dialed_interfaces);
3357 tmp->stillgoing = -1;
3358 tmp->member = cur;
3359 tmp->oldstatus = cur->status;
3360 tmp->lastcall = cur->lastcall;
3361 tmp->lastqueue = cur->lastqueue;
3362 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
3363 /* Special case: If we ring everyone, go ahead and ring them, otherwise
3364 just calculate their metric for the appropriate strategy */
3365 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
3366 /* Put them in the list of outgoing thingies... We're ready now.
3367 XXX If we're forcibly removed, these outgoing calls won't get
3368 hung up XXX */
3369 tmp->q_next = outgoing;
3370 outgoing = tmp;
3371 /* If this line is up, don't try anybody else */
3372 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
3373 break;
3374 } else {
3375 ao2_ref(cur, -1);
3376 ast_free(tmp);
3380 if (qe->expire && (!qe->parent->timeout || (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP && (qe->expire - now) <= qe->parent->timeout)))
3381 to = (qe->expire - now) * 1000;
3382 else
3383 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
3384 orig = to;
3385 ++qe->pending;
3386 ao2_unlock(qe->parent);
3387 ring_one(qe, outgoing, &numbusies);
3388 if (use_weight)
3389 ao2_unlock(queues);
3390 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
3391 /* The ast_channel_datastore_remove() function could fail here if the
3392 * datastore was moved to another channel during a masquerade. If this is
3393 * the case, don't free the datastore here because later, when the channel
3394 * to which the datastore was moved hangs up, it will attempt to free this
3395 * datastore again, causing a crash
3397 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
3398 ast_channel_datastore_free(datastore);
3400 ao2_lock(qe->parent);
3401 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
3402 store_next_rr(qe, outgoing);
3404 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
3405 store_next_lin(qe, outgoing);
3407 ao2_unlock(qe->parent);
3408 peer = lpeer ? lpeer->chan : NULL;
3409 if (!peer) {
3410 qe->pending = 0;
3411 if (to) {
3412 /* Must gotten hung up */
3413 res = -1;
3414 } else {
3415 /* User exited by pressing a digit */
3416 res = digit;
3418 if (res == -1)
3419 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
3420 } else { /* peer is valid */
3421 /* Ah ha! Someone answered within the desired timeframe. Of course after this
3422 we will always return with -1 so that it is hung up properly after the
3423 conversation. */
3424 if (!strcmp(qe->chan->tech->type, "DAHDI"))
3425 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
3426 if (!strcmp(peer->tech->type, "DAHDI"))
3427 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
3428 /* Update parameters for the queue */
3429 time(&now);
3430 recalc_holdtime(qe, (now - qe->start));
3431 ao2_lock(qe->parent);
3432 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
3433 ao2_unlock(qe->parent);
3434 member = lpeer->member;
3435 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
3436 ao2_ref(member, 1);
3437 hangupcalls(outgoing, peer);
3438 outgoing = NULL;
3439 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
3440 int res2;
3442 res2 = ast_autoservice_start(qe->chan);
3443 if (!res2) {
3444 if (qe->parent->memberdelay) {
3445 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
3446 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
3448 if (!res2 && announce) {
3449 play_file(peer, announce);
3451 if (!res2 && qe->parent->reportholdtime) {
3452 if (!play_file(peer, qe->parent->sound_reporthold)) {
3453 int holdtime, holdtimesecs;
3455 time(&now);
3456 holdtime = abs((now - qe->start) / 60);
3457 holdtimesecs = abs((now - qe->start));
3458 if (holdtime == 1) {
3459 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
3460 play_file(peer, qe->parent->sound_minute);
3461 } else {
3462 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
3463 play_file(peer, qe->parent->sound_minutes);
3465 if (holdtimesecs > 1) {
3466 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
3467 play_file(peer, qe->parent->sound_seconds);
3472 res2 |= ast_autoservice_stop(qe->chan);
3473 if (ast_check_hangup(peer)) {
3474 /* Agent must have hung up */
3475 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
3476 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
3477 if (qe->parent->eventwhencalled)
3478 manager_event(EVENT_FLAG_AGENT, "AgentDump",
3479 "Queue: %s\r\n"
3480 "Uniqueid: %s\r\n"
3481 "Channel: %s\r\n"
3482 "Member: %s\r\n"
3483 "MemberName: %s\r\n"
3484 "%s",
3485 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3486 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3487 ast_hangup(peer);
3488 ao2_ref(member, -1);
3489 goto out;
3490 } else if (res2) {
3491 /* Caller must have hung up just before being connected*/
3492 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
3493 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
3494 record_abandoned(qe);
3495 ast_hangup(peer);
3496 ao2_ref(member, -1);
3497 return -1;
3500 /* Stop music on hold */
3501 if (ringing)
3502 ast_indicate(qe->chan,-1);
3503 else
3504 ast_moh_stop(qe->chan);
3505 /* If appropriate, log that we have a destination channel */
3506 if (qe->chan->cdr)
3507 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
3508 /* Make sure channels are compatible */
3509 res = ast_channel_make_compatible(qe->chan, peer);
3510 if (res < 0) {
3511 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
3512 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
3513 record_abandoned(qe);
3514 ast_hangup(peer);
3515 ao2_ref(member, -1);
3516 return -1;
3519 /* Play announcement to the caller telling it's his turn if defined */
3520 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
3521 if (play_file(qe->chan, qe->parent->sound_callerannounce))
3522 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
3525 ao2_lock(qe->parent);
3526 /* if setinterfacevar is defined, make member variables available to the channel */
3527 /* use pbx_builtin_setvar to set a load of variables with one call */
3528 if (qe->parent->setinterfacevar) {
3529 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
3530 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
3531 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
3534 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
3535 /* use pbx_builtin_setvar to set a load of variables with one call */
3536 if (qe->parent->setqueueentryvar) {
3537 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
3538 (long) time(NULL) - qe->start, qe->opos);
3539 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
3542 /* try to set queue variables if configured to do so*/
3543 set_queue_variables(qe);
3544 ao2_unlock(qe->parent);
3546 ast_channel_lock(qe->chan);
3547 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
3548 monitorfilename = ast_strdupa(monitorfilename);
3550 ast_channel_unlock(qe->chan);
3551 /* Begin Monitoring */
3552 if (qe->parent->monfmt && *qe->parent->monfmt) {
3553 if (!qe->parent->montype) {
3554 const char *monexec, *monargs;
3555 ast_debug(1, "Starting Monitor as requested.\n");
3556 ast_channel_lock(qe->chan);
3557 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
3558 which = qe->chan;
3559 monexec = monexec ? ast_strdupa(monexec) : NULL;
3561 else
3562 which = peer;
3563 ast_channel_unlock(qe->chan);
3564 if (monitorfilename)
3565 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
3566 else if (qe->chan->cdr)
3567 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
3568 else {
3569 /* Last ditch effort -- no CDR, make up something */
3570 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3571 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
3573 if (!ast_strlen_zero(monexec)) {
3574 ast_monitor_setjoinfiles(which, 1);
3576 } else {
3577 mixmonapp = pbx_findapp("MixMonitor");
3579 if (mixmonapp) {
3580 ast_debug(1, "Starting MixMonitor as requested.\n");
3581 if (!monitorfilename) {
3582 if (qe->chan->cdr)
3583 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
3584 else
3585 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3586 } else {
3587 const char *m = monitorfilename;
3588 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
3589 switch (*m) {
3590 case '^':
3591 if (*(m + 1) == '{')
3592 *p = '$';
3593 break;
3594 case ',':
3595 *p++ = '\\';
3596 /* Fall through */
3597 default:
3598 *p = *m;
3600 if (*m == '\0')
3601 break;
3603 if (p == tmpid2 + sizeof(tmpid2))
3604 tmpid2[sizeof(tmpid2) - 1] = '\0';
3606 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
3609 ast_channel_lock(qe->chan);
3610 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
3611 monitor_exec = ast_strdupa(monitor_exec);
3613 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
3614 monitor_options = ast_strdupa(monitor_options);
3615 } else {
3616 monitor_options = "";
3618 ast_channel_unlock(qe->chan);
3620 if (monitor_exec) {
3621 const char *m = monitor_exec;
3622 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
3623 switch (*m) {
3624 case '^':
3625 if (*(m + 1) == '{')
3626 *p = '$';
3627 break;
3628 case ',':
3629 *p++ = '\\';
3630 /* Fall through */
3631 default:
3632 *p = *m;
3634 if (*m == '\0')
3635 break;
3637 if (p == meid2 + sizeof(meid2))
3638 meid2[sizeof(meid2) - 1] = '\0';
3640 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
3643 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
3645 if (!ast_strlen_zero(monitor_exec))
3646 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
3647 else
3648 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
3650 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
3651 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3652 if (qe->chan->cdr)
3653 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3654 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
3655 if (qe->chan->cdr)
3656 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3658 } else {
3659 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3663 /* Drop out of the queue at this point, to prepare for next caller */
3664 leave_queue(qe);
3665 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
3666 ast_debug(1, "app_queue: sendurl=%s.\n", url);
3667 ast_channel_sendurl(peer, url);
3670 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
3671 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
3672 if (!ast_strlen_zero(macro)) {
3673 macroexec = ast_strdupa(macro);
3674 } else {
3675 if (qe->parent->membermacro)
3676 macroexec = ast_strdupa(qe->parent->membermacro);
3679 if (!ast_strlen_zero(macroexec)) {
3680 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
3682 res = ast_autoservice_start(qe->chan);
3683 if (res) {
3684 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3685 res = -1;
3688 app = pbx_findapp("Macro");
3690 if (app) {
3691 res = pbx_exec(qe->chan, app, macroexec);
3692 ast_debug(1, "Macro exited with status %d\n", res);
3693 res = 0;
3694 } else {
3695 ast_log(LOG_ERROR, "Could not find application Macro\n");
3696 res = -1;
3699 if (ast_autoservice_stop(qe->chan) < 0) {
3700 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3701 res = -1;
3705 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
3706 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
3707 if (!ast_strlen_zero(gosub)) {
3708 gosubexec = ast_strdupa(gosub);
3709 } else {
3710 if (qe->parent->membergosub)
3711 gosubexec = ast_strdupa(qe->parent->membergosub);
3714 if (!ast_strlen_zero(gosubexec)) {
3715 if (option_debug)
3716 ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
3718 res = ast_autoservice_start(qe->chan);
3719 if (res) {
3720 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3721 res = -1;
3724 app = pbx_findapp("Gosub");
3726 if (app) {
3727 char *gosub_args, *gosub_argstart;
3729 /* Set where we came from */
3730 ast_copy_string(qe->chan->context, "app_dial_gosub_virtual_context", sizeof(qe->chan->context));
3731 ast_copy_string(qe->chan->exten, "s", sizeof(qe->chan->exten));
3732 qe->chan->priority = 0;
3734 gosub_argstart = strchr(gosubexec, ',');
3735 if (gosub_argstart) {
3736 *gosub_argstart = 0;
3737 asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1);
3738 *gosub_argstart = '|';
3739 } else {
3740 asprintf(&gosub_args, "%s,s,1", gosubexec);
3742 if (gosub_args) {
3743 res = pbx_exec(qe->chan, app, gosub_args);
3744 ast_pbx_run(qe->chan);
3745 free(gosub_args);
3746 if (option_debug)
3747 ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
3748 } else
3749 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
3751 res = 0;
3752 } else {
3753 ast_log(LOG_ERROR, "Could not find application Gosub\n");
3754 res = -1;
3757 if (ast_autoservice_stop(qe->chan) < 0) {
3758 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3759 res = -1;
3763 if (!ast_strlen_zero(agi)) {
3764 ast_debug(1, "app_queue: agi=%s.\n", agi);
3765 app = pbx_findapp("agi");
3766 if (app) {
3767 agiexec = ast_strdupa(agi);
3768 ret = pbx_exec(qe->chan, app, agiexec);
3769 } else
3770 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3772 qe->handled++;
3773 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
3774 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
3775 if (update_cdr && qe->chan->cdr)
3776 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
3777 if (qe->parent->eventwhencalled)
3778 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
3779 "Queue: %s\r\n"
3780 "Uniqueid: %s\r\n"
3781 "Channel: %s\r\n"
3782 "Member: %s\r\n"
3783 "MemberName: %s\r\n"
3784 "Holdtime: %ld\r\n"
3785 "BridgedChannel: %s\r\n"
3786 "Ringtime: %ld\r\n"
3787 "%s",
3788 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3789 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
3790 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3791 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
3792 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
3793 time(&callstart);
3794 setup_transfer_datastore(qe, member, callstart);
3795 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3797 /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
3798 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
3800 if (!attended_transfer_occurred(qe->chan)) {
3801 struct ast_datastore *transfer_ds;
3802 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3803 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
3804 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
3805 (long) (time(NULL) - callstart), qe->opos);
3806 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
3807 } else if (ast_check_hangup(qe->chan)) {
3808 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
3809 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3810 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
3811 } else {
3812 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
3813 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3814 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
3816 ast_channel_lock(qe->chan);
3817 transfer_ds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL);
3818 if (transfer_ds) {
3819 ast_channel_datastore_remove(qe->chan, transfer_ds);
3820 ast_channel_datastore_free(transfer_ds);
3822 ast_channel_unlock(qe->chan);
3825 if (bridge != AST_PBX_NO_HANGUP_PEER)
3826 ast_hangup(peer);
3827 update_queue(qe->parent, member, callcompletedinsl);
3828 res = bridge ? bridge : 1;
3829 ao2_ref(member, -1);
3831 out:
3832 hangupcalls(outgoing, NULL);
3834 return res;
3837 static int wait_a_bit(struct queue_ent *qe)
3839 /* Don't need to hold the lock while we setup the outgoing calls */
3840 int retrywait = qe->parent->retry * 1000;
3842 int res = ast_waitfordigit(qe->chan, retrywait);
3843 if (res > 0 && !valid_exit(qe, res))
3844 res = 0;
3846 return res;
3849 static struct member *interface_exists(struct call_queue *q, const char *interface)
3851 struct member *mem;
3852 struct ao2_iterator mem_iter;
3854 if (!q)
3855 return NULL;
3857 mem_iter = ao2_iterator_init(q->members, 0);
3858 while ((mem = ao2_iterator_next(&mem_iter))) {
3859 if (!strcasecmp(interface, mem->interface))
3860 return mem;
3861 ao2_ref(mem, -1);
3864 return NULL;
3868 /*! \brief Dump all members in a specific queue to the database
3870 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
3872 static void dump_queue_members(struct call_queue *pm_queue)
3874 struct member *cur_member;
3875 char value[PM_MAX_LEN];
3876 int value_len = 0;
3877 int res;
3878 struct ao2_iterator mem_iter;
3880 memset(value, 0, sizeof(value));
3882 if (!pm_queue)
3883 return;
3885 mem_iter = ao2_iterator_init(pm_queue->members, 0);
3886 while ((cur_member = ao2_iterator_next(&mem_iter))) {
3887 if (!cur_member->dynamic) {
3888 ao2_ref(cur_member, -1);
3889 continue;
3892 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
3893 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
3895 ao2_ref(cur_member, -1);
3897 if (res != strlen(value + value_len)) {
3898 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
3899 break;
3901 value_len += res;
3904 if (value_len && !cur_member) {
3905 if (ast_db_put(pm_family, pm_queue->name, value))
3906 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
3907 } else
3908 /* Delete the entry if the queue is empty or there is an error */
3909 ast_db_del(pm_family, pm_queue->name);
3912 /*! \brief Remove member from queue
3913 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3914 * \retval RES_NOSUCHQUEUE queue does not exist
3915 * \retval RES_OKAY removed member from queue
3916 * \retval RES_EXISTS queue exists but no members
3918 static int remove_from_queue(const char *queuename, const char *interface)
3920 struct call_queue *q, tmpq = {
3921 .name = queuename,
3923 struct member *mem, tmpmem;
3924 int res = RES_NOSUCHQUEUE;
3926 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
3927 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
3928 ao2_lock(q);
3929 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
3930 /* XXX future changes should beware of this assumption!! */
3931 if (!mem->dynamic) {
3932 ao2_ref(mem, -1);
3933 ao2_unlock(q);
3934 return RES_NOT_DYNAMIC;
3936 q->membercount--;
3937 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
3938 "Queue: %s\r\n"
3939 "Location: %s\r\n"
3940 "MemberName: %s\r\n",
3941 q->name, mem->interface, mem->membername);
3942 ao2_unlink(q->members, mem);
3943 remove_from_interfaces(mem->state_interface);
3944 ao2_ref(mem, -1);
3946 if (queue_persistent_members)
3947 dump_queue_members(q);
3949 res = RES_OKAY;
3950 } else {
3951 res = RES_EXISTS;
3953 ao2_unlock(q);
3954 queue_unref(q);
3957 return res;
3960 /*! \brief Add member to queue
3961 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3962 * \retval RES_NOSUCHQUEUE queue does not exist
3963 * \retval RES_OKAY added member from queue
3964 * \retval RES_EXISTS queue exists but no members
3965 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
3967 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
3969 struct call_queue *q;
3970 struct member *new_member, *old_member;
3971 int res = RES_NOSUCHQUEUE;
3973 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
3974 * short-circuits if the queue is already in memory. */
3975 if (!(q = load_realtime_queue(queuename)))
3976 return res;
3978 ao2_lock(queues);
3980 ao2_lock(q);
3981 if ((old_member = interface_exists(q, interface)) == NULL) {
3982 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
3983 add_to_interfaces(new_member->state_interface);
3984 new_member->dynamic = 1;
3985 ao2_link(q->members, new_member);
3986 q->membercount++;
3987 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
3988 "Queue: %s\r\n"
3989 "Location: %s\r\n"
3990 "MemberName: %s\r\n"
3991 "Membership: %s\r\n"
3992 "Penalty: %d\r\n"
3993 "CallsTaken: %d\r\n"
3994 "LastCall: %d\r\n"
3995 "Status: %d\r\n"
3996 "Paused: %d\r\n",
3997 q->name, new_member->interface, new_member->membername,
3998 "dynamic",
3999 new_member->penalty, new_member->calls, (int) new_member->lastcall,
4000 new_member->status, new_member->paused);
4002 ao2_ref(new_member, -1);
4003 new_member = NULL;
4005 if (dump)
4006 dump_queue_members(q);
4008 res = RES_OKAY;
4009 } else {
4010 res = RES_OUTOFMEMORY;
4012 } else {
4013 ao2_ref(old_member, -1);
4014 res = RES_EXISTS;
4016 ao2_unlock(q);
4017 ao2_unlock(queues);
4019 return res;
4022 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
4024 int found = 0;
4025 struct call_queue *q;
4026 struct member *mem;
4027 struct ao2_iterator queue_iter;
4028 int failed;
4030 /* Special event for when all queues are paused - individual events still generated */
4031 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
4032 if (ast_strlen_zero(queuename))
4033 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
4035 queue_iter = ao2_iterator_init(queues, 0);
4036 while ((q = ao2_iterator_next(&queue_iter))) {
4037 ao2_lock(q);
4038 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
4039 if ((mem = interface_exists(q, interface))) {
4040 if (mem->paused == paused) {
4041 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
4044 failed = 0;
4045 if (mem->realtime) {
4046 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
4049 if (failed) {
4050 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
4051 ao2_ref(mem, -1);
4052 continue;
4054 found++;
4055 mem->paused = paused;
4057 if (queue_persistent_members)
4058 dump_queue_members(q);
4060 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
4062 if (!ast_strlen_zero(reason)) {
4063 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
4064 "Queue: %s\r\n"
4065 "Location: %s\r\n"
4066 "MemberName: %s\r\n"
4067 "Paused: %d\r\n"
4068 "Reason: %s\r\n",
4069 q->name, mem->interface, mem->membername, paused, reason);
4070 } else {
4071 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
4072 "Queue: %s\r\n"
4073 "Location: %s\r\n"
4074 "MemberName: %s\r\n"
4075 "Paused: %d\r\n",
4076 q->name, mem->interface, mem->membername, paused);
4078 ao2_ref(mem, -1);
4082 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
4083 ao2_unlock(q);
4084 queue_unref(q);
4085 break;
4088 ao2_unlock(q);
4089 queue_unref(q);
4092 return found ? RESULT_SUCCESS : RESULT_FAILURE;
4095 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
4096 static int set_member_penalty(char *queuename, char *interface, int penalty)
4098 int foundinterface = 0, foundqueue = 0;
4099 struct call_queue *q;
4100 struct member *mem;
4101 struct ao2_iterator queue_iter;
4103 if (penalty < 0) {
4104 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
4105 return RESULT_FAILURE;
4108 queue_iter = ao2_iterator_init(queues, 0);
4109 while ((q = ao2_iterator_next(&queue_iter))) {
4110 ao2_lock(q);
4111 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
4112 foundqueue++;
4113 if ((mem = interface_exists(q, interface))) {
4114 foundinterface++;
4115 mem->penalty = penalty;
4117 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
4118 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
4119 "Queue: %s\r\n"
4120 "Location: %s\r\n"
4121 "Penalty: %d\r\n",
4122 q->name, mem->interface, penalty);
4126 ao2_unlock(q);
4127 queue_unref(q);
4130 if (foundinterface) {
4131 return RESULT_SUCCESS;
4132 } else if (!foundqueue) {
4133 ast_log (LOG_ERROR, "Invalid queuename\n");
4134 } else {
4135 ast_log (LOG_ERROR, "Invalid interface\n");
4138 return RESULT_FAILURE;
4141 /* \brief Gets members penalty.
4142 * \return Return the members penalty or RESULT_FAILURE on error.
4144 static int get_member_penalty(char *queuename, char *interface)
4146 int foundqueue = 0, penalty;
4147 struct call_queue *q, tmpq = {
4148 .name = queuename,
4150 struct member *mem;
4152 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4153 foundqueue = 1;
4154 ao2_lock(q);
4155 if ((mem = interface_exists(q, interface))) {
4156 penalty = mem->penalty;
4157 ao2_unlock(q);
4158 queue_unref(q);
4159 return penalty;
4161 ao2_unlock(q);
4162 queue_unref(q);
4165 /* some useful debuging */
4166 if (foundqueue)
4167 ast_log (LOG_ERROR, "Invalid queuename\n");
4168 else
4169 ast_log (LOG_ERROR, "Invalid interface\n");
4171 return RESULT_FAILURE;
4174 /*! \brief Reload dynamic queue members persisted into the astdb */
4175 static void reload_queue_members(void)
4177 char *cur_ptr;
4178 const char *queue_name;
4179 char *member;
4180 char *interface;
4181 char *membername = NULL;
4182 char *state_interface;
4183 char *penalty_tok;
4184 int penalty = 0;
4185 char *paused_tok;
4186 int paused = 0;
4187 struct ast_db_entry *db_tree;
4188 struct ast_db_entry *entry;
4189 struct call_queue *cur_queue;
4190 char queue_data[PM_MAX_LEN];
4192 ao2_lock(queues);
4194 /* Each key in 'pm_family' is the name of a queue */
4195 db_tree = ast_db_gettree(pm_family, NULL);
4196 for (entry = db_tree; entry; entry = entry->next) {
4198 queue_name = entry->key + strlen(pm_family) + 2;
4201 struct call_queue tmpq = {
4202 .name = queue_name,
4204 cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
4207 if (!cur_queue)
4208 cur_queue = load_realtime_queue(queue_name);
4210 if (!cur_queue) {
4211 /* If the queue no longer exists, remove it from the
4212 * database */
4213 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
4214 ast_db_del(pm_family, queue_name);
4215 continue;
4218 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
4219 queue_unref(cur_queue);
4220 continue;
4223 cur_ptr = queue_data;
4224 while ((member = strsep(&cur_ptr, ",|"))) {
4225 if (ast_strlen_zero(member))
4226 continue;
4228 interface = strsep(&member, ";");
4229 penalty_tok = strsep(&member, ";");
4230 paused_tok = strsep(&member, ";");
4231 membername = strsep(&member, ";");
4232 state_interface = strsep(&member, ";");
4234 if (!penalty_tok) {
4235 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
4236 break;
4238 penalty = strtol(penalty_tok, NULL, 10);
4239 if (errno == ERANGE) {
4240 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
4241 break;
4244 if (!paused_tok) {
4245 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
4246 break;
4248 paused = strtol(paused_tok, NULL, 10);
4249 if ((errno == ERANGE) || paused < 0 || paused > 1) {
4250 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
4251 break;
4254 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
4256 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
4257 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
4258 break;
4261 queue_unref(cur_queue);
4264 ao2_unlock(queues);
4265 if (db_tree) {
4266 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
4267 ast_db_freetree(db_tree);
4271 /*! \brief PauseQueueMember application */
4272 static int pqm_exec(struct ast_channel *chan, void *data)
4274 char *parse;
4275 AST_DECLARE_APP_ARGS(args,
4276 AST_APP_ARG(queuename);
4277 AST_APP_ARG(interface);
4278 AST_APP_ARG(options);
4279 AST_APP_ARG(reason);
4282 if (ast_strlen_zero(data)) {
4283 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
4284 return -1;
4287 parse = ast_strdupa(data);
4289 AST_STANDARD_APP_ARGS(args, parse);
4291 if (ast_strlen_zero(args.interface)) {
4292 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4293 return -1;
4296 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
4297 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
4298 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
4299 return 0;
4302 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
4304 return 0;
4307 /*! \brief UnPauseQueueMember application */
4308 static int upqm_exec(struct ast_channel *chan, void *data)
4310 char *parse;
4311 AST_DECLARE_APP_ARGS(args,
4312 AST_APP_ARG(queuename);
4313 AST_APP_ARG(interface);
4314 AST_APP_ARG(options);
4315 AST_APP_ARG(reason);
4318 if (ast_strlen_zero(data)) {
4319 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
4320 return -1;
4323 parse = ast_strdupa(data);
4325 AST_STANDARD_APP_ARGS(args, parse);
4327 if (ast_strlen_zero(args.interface)) {
4328 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4329 return -1;
4332 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
4333 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
4334 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
4335 return 0;
4338 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
4340 return 0;
4343 /*! \brief RemoveQueueMember application */
4344 static int rqm_exec(struct ast_channel *chan, void *data)
4346 int res=-1;
4347 char *parse, *temppos = NULL;
4348 AST_DECLARE_APP_ARGS(args,
4349 AST_APP_ARG(queuename);
4350 AST_APP_ARG(interface);
4351 AST_APP_ARG(options);
4355 if (ast_strlen_zero(data)) {
4356 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
4357 return -1;
4360 parse = ast_strdupa(data);
4362 AST_STANDARD_APP_ARGS(args, parse);
4364 if (ast_strlen_zero(args.interface)) {
4365 args.interface = ast_strdupa(chan->name);
4366 temppos = strrchr(args.interface, '-');
4367 if (temppos)
4368 *temppos = '\0';
4371 switch (remove_from_queue(args.queuename, args.interface)) {
4372 case RES_OKAY:
4373 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
4374 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
4375 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
4376 res = 0;
4377 break;
4378 case RES_EXISTS:
4379 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
4380 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
4381 res = 0;
4382 break;
4383 case RES_NOSUCHQUEUE:
4384 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
4385 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
4386 res = 0;
4387 break;
4388 case RES_NOT_DYNAMIC:
4389 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
4390 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
4391 res = 0;
4392 break;
4395 return res;
4398 /*! \brief AddQueueMember application */
4399 static int aqm_exec(struct ast_channel *chan, void *data)
4401 int res=-1;
4402 char *parse, *temppos = NULL;
4403 AST_DECLARE_APP_ARGS(args,
4404 AST_APP_ARG(queuename);
4405 AST_APP_ARG(interface);
4406 AST_APP_ARG(penalty);
4407 AST_APP_ARG(options);
4408 AST_APP_ARG(membername);
4409 AST_APP_ARG(state_interface);
4411 int penalty = 0;
4413 if (ast_strlen_zero(data)) {
4414 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
4415 return -1;
4418 parse = ast_strdupa(data);
4420 AST_STANDARD_APP_ARGS(args, parse);
4422 if (ast_strlen_zero(args.interface)) {
4423 args.interface = ast_strdupa(chan->name);
4424 temppos = strrchr(args.interface, '-');
4425 if (temppos)
4426 *temppos = '\0';
4429 if (!ast_strlen_zero(args.penalty)) {
4430 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
4431 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
4432 penalty = 0;
4436 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
4437 case RES_OKAY:
4438 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
4439 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
4440 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
4441 res = 0;
4442 break;
4443 case RES_EXISTS:
4444 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
4445 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
4446 res = 0;
4447 break;
4448 case RES_NOSUCHQUEUE:
4449 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
4450 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
4451 res = 0;
4452 break;
4453 case RES_OUTOFMEMORY:
4454 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
4455 break;
4458 return res;
4461 /*! \brief QueueLog application */
4462 static int ql_exec(struct ast_channel *chan, void *data)
4464 char *parse;
4466 AST_DECLARE_APP_ARGS(args,
4467 AST_APP_ARG(queuename);
4468 AST_APP_ARG(uniqueid);
4469 AST_APP_ARG(membername);
4470 AST_APP_ARG(event);
4471 AST_APP_ARG(params);
4474 if (ast_strlen_zero(data)) {
4475 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
4476 return -1;
4479 parse = ast_strdupa(data);
4481 AST_STANDARD_APP_ARGS(args, parse);
4483 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
4484 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
4485 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
4486 return -1;
4489 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
4490 "%s", args.params ? args.params : "");
4492 return 0;
4495 /*! \brief Copy rule from global list into specified queue */
4496 static void copy_rules(struct queue_ent *qe, const char *rulename)
4498 struct penalty_rule *pr_iter;
4499 struct rule_list *rl_iter;
4500 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
4501 AST_LIST_LOCK(&rule_lists);
4502 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
4503 if (!strcasecmp(rl_iter->name, tmp))
4504 break;
4506 if (rl_iter) {
4507 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
4508 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
4509 if (!new_pr) {
4510 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
4511 AST_LIST_UNLOCK(&rule_lists);
4512 break;
4514 new_pr->time = pr_iter->time;
4515 new_pr->max_value = pr_iter->max_value;
4516 new_pr->min_value = pr_iter->min_value;
4517 new_pr->max_relative = pr_iter->max_relative;
4518 new_pr->min_relative = pr_iter->min_relative;
4519 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
4522 AST_LIST_UNLOCK(&rule_lists);
4525 /*!\brief The starting point for all queue calls
4527 * The process involved here is to
4528 * 1. Parse the options specified in the call to Queue()
4529 * 2. Join the queue
4530 * 3. Wait in a loop until it is our turn to try calling a queue member
4531 * 4. Attempt to call a queue member
4532 * 5. If 4. did not result in a bridged call, then check for between
4533 * call options such as periodic announcements etc.
4534 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
4535 * exit the queue.
4537 static int queue_exec(struct ast_channel *chan, void *data)
4539 int res=-1;
4540 int ringing=0;
4541 const char *user_priority;
4542 const char *max_penalty_str;
4543 const char *min_penalty_str;
4544 int prio;
4545 int qcontinue = 0;
4546 int max_penalty, min_penalty;
4547 enum queue_result reason = QUEUE_UNKNOWN;
4548 /* whether to exit Queue application after the timeout hits */
4549 int tries = 0;
4550 int noption = 0;
4551 char *parse;
4552 int makeannouncement = 0;
4553 AST_DECLARE_APP_ARGS(args,
4554 AST_APP_ARG(queuename);
4555 AST_APP_ARG(options);
4556 AST_APP_ARG(url);
4557 AST_APP_ARG(announceoverride);
4558 AST_APP_ARG(queuetimeoutstr);
4559 AST_APP_ARG(agi);
4560 AST_APP_ARG(macro);
4561 AST_APP_ARG(gosub);
4562 AST_APP_ARG(rule);
4564 /* Our queue entry */
4565 struct queue_ent qe;
4567 if (ast_strlen_zero(data)) {
4568 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
4569 return -1;
4572 parse = ast_strdupa(data);
4573 AST_STANDARD_APP_ARGS(args, parse);
4575 /* Setup our queue entry */
4576 memset(&qe, 0, sizeof(qe));
4577 qe.start = time(NULL);
4579 /* set the expire time based on the supplied timeout; */
4580 if (!ast_strlen_zero(args.queuetimeoutstr))
4581 qe.expire = qe.start + atoi(args.queuetimeoutstr);
4582 else
4583 qe.expire = 0;
4585 /* Get the priority from the variable ${QUEUE_PRIO} */
4586 ast_channel_lock(chan);
4587 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
4588 if (user_priority) {
4589 if (sscanf(user_priority, "%d", &prio) == 1) {
4590 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
4591 } else {
4592 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
4593 user_priority, chan->name);
4594 prio = 0;
4596 } else {
4597 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
4598 prio = 0;
4601 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
4603 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
4604 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
4605 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
4606 } else {
4607 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
4608 max_penalty_str, chan->name);
4609 max_penalty = 0;
4611 } else {
4612 max_penalty = 0;
4615 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
4616 if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
4617 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
4618 } else {
4619 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
4620 min_penalty_str, chan->name);
4621 min_penalty = 0;
4623 } else {
4624 min_penalty = 0;
4626 ast_channel_unlock(chan);
4628 if (args.options && (strchr(args.options, 'r')))
4629 ringing = 1;
4631 if (args.options && (strchr(args.options, 'c')))
4632 qcontinue = 1;
4634 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
4635 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
4637 qe.chan = chan;
4638 qe.prio = prio;
4639 qe.max_penalty = max_penalty;
4640 qe.min_penalty = min_penalty;
4641 qe.last_pos_said = 0;
4642 qe.last_pos = 0;
4643 qe.last_periodic_announce_time = time(NULL);
4644 qe.last_periodic_announce_sound = 0;
4645 qe.valid_digits = 0;
4646 if (join_queue(args.queuename, &qe, &reason)) {
4647 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
4648 set_queue_result(chan, reason);
4649 return 0;
4651 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
4652 S_OR(chan->cid.cid_num, ""));
4653 copy_rules(&qe, args.rule);
4654 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
4655 check_turns:
4656 if (ringing) {
4657 ast_indicate(chan, AST_CONTROL_RINGING);
4658 } else {
4659 ast_moh_start(chan, qe.moh, NULL);
4662 /* This is the wait loop for callers 2 through maxlen */
4663 res = wait_our_turn(&qe, ringing, &reason);
4664 if (res) {
4665 goto stop;
4668 makeannouncement = 0;
4670 for (;;) {
4671 /* This is the wait loop for the head caller*/
4672 /* To exit, they may get their call answered; */
4673 /* they may dial a digit from the queue context; */
4674 /* or, they may timeout. */
4676 enum queue_member_status stat;
4678 /* Leave if we have exceeded our queuetimeout */
4679 if (qe.expire && (time(NULL) > qe.expire)) {
4680 record_abandoned(&qe);
4681 reason = QUEUE_TIMEOUT;
4682 res = 0;
4683 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
4684 qe.pos, qe.opos, (long) time(NULL) - qe.start);
4685 break;
4688 if (makeannouncement) {
4689 /* Make a position announcement, if enabled */
4690 if (qe.parent->announcefrequency)
4691 if ((res = say_position(&qe,ringing)))
4692 goto stop;
4694 makeannouncement = 1;
4696 /* Make a periodic announcement, if enabled */
4697 if (qe.parent->periodicannouncefrequency)
4698 if ((res = say_periodic_announcement(&qe,ringing)))
4699 goto stop;
4701 /* see if we need to move to the next penalty level for this queue */
4702 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
4703 update_qe_rule(&qe);
4706 /* Try calling all queue members for 'timeout' seconds */
4707 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
4708 if (res) {
4709 goto stop;
4712 stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
4714 /* exit after 'timeout' cycle if 'n' option enabled */
4715 if (noption && tries >= qe.parent->membercount) {
4716 ast_verb(3, "Exiting on time-out cycle\n");
4717 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
4718 record_abandoned(&qe);
4719 reason = QUEUE_TIMEOUT;
4720 res = 0;
4721 break;
4724 /* leave the queue if no agents, if enabled */
4725 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
4726 record_abandoned(&qe);
4727 reason = QUEUE_LEAVEEMPTY;
4728 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4729 res = 0;
4730 break;
4733 /* leave the queue if no reachable agents, if enabled */
4734 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
4735 record_abandoned(&qe);
4736 reason = QUEUE_LEAVEUNAVAIL;
4737 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4738 res = 0;
4739 break;
4741 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
4742 record_abandoned(&qe);
4743 reason = QUEUE_LEAVEUNAVAIL;
4744 res = 0;
4745 break;
4748 /* Leave if we have exceeded our queuetimeout */
4749 if (qe.expire && (time(NULL) > qe.expire)) {
4750 record_abandoned(&qe);
4751 reason = QUEUE_TIMEOUT;
4752 res = 0;
4753 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
4754 break;
4757 /* If using dynamic realtime members, we should regenerate the member list for this queue */
4758 update_realtime_members(qe.parent);
4760 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
4761 res = wait_a_bit(&qe);
4762 if (res)
4763 goto stop;
4765 /* Since this is a priority queue and
4766 * it is not sure that we are still at the head
4767 * of the queue, go and check for our turn again.
4769 if (!is_our_turn(&qe)) {
4770 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
4771 goto check_turns;
4775 stop:
4776 if (res) {
4777 if (res < 0) {
4778 if (!qe.handled) {
4779 record_abandoned(&qe);
4780 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
4781 "%d|%d|%ld", qe.pos, qe.opos,
4782 (long) time(NULL) - qe.start);
4783 res = -1;
4784 } else if (qcontinue) {
4785 reason = QUEUE_CONTINUE;
4786 res = 0;
4788 } else if (qe.valid_digits) {
4789 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
4790 "%s|%d", qe.digits, qe.pos);
4794 /* Don't allow return code > 0 */
4795 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
4796 res = 0;
4797 if (ringing) {
4798 ast_indicate(chan, -1);
4799 } else {
4800 ast_moh_stop(chan);
4802 ast_stopstream(chan);
4805 set_queue_variables(&qe);
4807 leave_queue(&qe);
4808 if (reason != QUEUE_UNKNOWN)
4809 set_queue_result(chan, reason);
4811 return res;
4815 * \brief create interface var with all queue details.
4816 * \retval 0 on success
4817 * \retval -1 on error
4819 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4821 int res = -1;
4822 struct call_queue *q, tmpq = {
4823 .name = data,
4826 char interfacevar[256] = "";
4827 float sl = 0;
4829 if (ast_strlen_zero(data)) {
4830 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4831 return -1;
4834 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4835 ao2_lock(q);
4836 if (q->setqueuevar) {
4837 sl = 0;
4838 res = 0;
4840 if (q->callscompleted > 0) {
4841 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4844 snprintf(interfacevar, sizeof(interfacevar),
4845 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
4846 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
4848 pbx_builtin_setvar_multiple(chan, interfacevar);
4851 ao2_unlock(q);
4852 queue_unref(q);
4853 } else {
4854 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4857 snprintf(buf, len, "%d", res);
4859 return 0;
4862 /*!
4863 * \brief Get number either busy / free or total members of a specific queue
4864 * \retval number of members (busy / free / total)
4865 * \retval -1 on error
4867 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4869 int count = 0;
4870 struct member *m;
4871 struct ao2_iterator mem_iter;
4872 struct call_queue *q;
4873 char *option;
4875 if (ast_strlen_zero(data)) {
4876 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4877 return -1;
4880 if ((option = strchr(data, ',')))
4881 *option++ = '\0';
4882 else
4883 option = "logged";
4884 if ((q = load_realtime_queue(data))) {
4885 ao2_lock(q);
4886 if (!strcasecmp(option, "logged")) {
4887 mem_iter = ao2_iterator_init(q->members, 0);
4888 while ((m = ao2_iterator_next(&mem_iter))) {
4889 /* Count the agents who are logged in and presently answering calls */
4890 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4891 count++;
4893 ao2_ref(m, -1);
4895 } else if (!strcasecmp(option, "free")) {
4896 mem_iter = ao2_iterator_init(q->members, 0);
4897 while ((m = ao2_iterator_next(&mem_iter))) {
4898 /* Count the agents who are logged in and presently answering calls */
4899 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
4900 count++;
4902 ao2_ref(m, -1);
4904 } else /* must be "count" */
4905 count = q->membercount;
4906 ao2_unlock(q);
4907 queue_unref(q);
4908 } else
4909 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4911 snprintf(buf, len, "%d", count);
4913 return 0;
4916 /*!
4917 * \brief Get the total number of members in a specific queue (Deprecated)
4918 * \retval number of members
4919 * \retval -1 on error
4921 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4923 int count = 0;
4924 struct member *m;
4925 struct call_queue *q;
4926 struct ao2_iterator mem_iter;
4927 static int depflag = 1;
4929 if (depflag) {
4930 depflag = 0;
4931 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");
4934 if (ast_strlen_zero(data)) {
4935 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4936 return -1;
4939 if ((q = load_realtime_queue(data))) {
4940 ao2_lock(q);
4941 mem_iter = ao2_iterator_init(q->members, 0);
4942 while ((m = ao2_iterator_next(&mem_iter))) {
4943 /* Count the agents who are logged in and presently answering calls */
4944 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4945 count++;
4947 ao2_ref(m, -1);
4949 ao2_unlock(q);
4950 queue_unref(q);
4951 } else
4952 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4954 snprintf(buf, len, "%d", count);
4956 return 0;
4959 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
4960 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4962 int count = 0;
4963 struct call_queue *q, tmpq = {
4964 .name = data,
4966 struct ast_variable *var = NULL;
4968 buf[0] = '\0';
4970 if (ast_strlen_zero(data)) {
4971 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
4972 return -1;
4975 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4976 ao2_lock(q);
4977 count = q->count;
4978 ao2_unlock(q);
4979 queue_unref(q);
4980 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
4981 /* if the queue is realtime but was not found in memory, this
4982 * means that the queue had been deleted from memory since it was
4983 * "dead." This means it has a 0 waiting count
4985 count = 0;
4986 ast_variables_destroy(var);
4987 } else
4988 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4990 snprintf(buf, len, "%d", count);
4992 return 0;
4995 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
4996 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4998 struct call_queue *q, tmpq = {
4999 .name = data,
5001 struct member *m;
5003 /* Ensure an otherwise empty list doesn't return garbage */
5004 buf[0] = '\0';
5006 if (ast_strlen_zero(data)) {
5007 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
5008 return -1;
5011 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
5012 int buflen = 0, count = 0;
5013 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
5015 ao2_lock(q);
5016 while ((m = ao2_iterator_next(&mem_iter))) {
5017 /* strcat() is always faster than printf() */
5018 if (count++) {
5019 strncat(buf + buflen, ",", len - buflen - 1);
5020 buflen++;
5022 strncat(buf + buflen, m->membername, len - buflen - 1);
5023 buflen += strlen(m->membername);
5024 /* Safeguard against overflow (negative length) */
5025 if (buflen >= len - 2) {
5026 ao2_ref(m, -1);
5027 ast_log(LOG_WARNING, "Truncating list\n");
5028 break;
5030 ao2_ref(m, -1);
5032 ao2_unlock(q);
5033 queue_unref(q);
5034 } else
5035 ast_log(LOG_WARNING, "queue %s was not found\n", data);
5037 /* We should already be terminated, but let's make sure. */
5038 buf[len - 1] = '\0';
5040 return 0;
5043 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
5044 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
5046 int penalty;
5047 AST_DECLARE_APP_ARGS(args,
5048 AST_APP_ARG(queuename);
5049 AST_APP_ARG(interface);
5051 /* Make sure the returned value on error is NULL. */
5052 buf[0] = '\0';
5054 if (ast_strlen_zero(data)) {
5055 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5056 return -1;
5059 AST_STANDARD_APP_ARGS(args, data);
5061 if (args.argc < 2) {
5062 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5063 return -1;
5066 penalty = get_member_penalty (args.queuename, args.interface);
5068 if (penalty >= 0) /* remember that buf is already '\0' */
5069 snprintf (buf, len, "%d", penalty);
5071 return 0;
5074 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
5075 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
5077 int penalty;
5078 AST_DECLARE_APP_ARGS(args,
5079 AST_APP_ARG(queuename);
5080 AST_APP_ARG(interface);
5083 if (ast_strlen_zero(data)) {
5084 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5085 return -1;
5088 AST_STANDARD_APP_ARGS(args, data);
5090 if (args.argc < 2) {
5091 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5092 return -1;
5095 penalty = atoi(value);
5097 if (ast_strlen_zero(args.interface)) {
5098 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
5099 return -1;
5102 /* if queuename = NULL then penalty will be set for interface in all the queues. */
5103 if (set_member_penalty(args.queuename, args.interface, penalty)) {
5104 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
5105 return -1;
5108 return 0;
5111 static struct ast_custom_function queuevar_function = {
5112 .name = "QUEUE_VARIABLES",
5113 .synopsis = "Return Queue information in variables",
5114 .syntax = "QUEUE_VARIABLES(<queuename>)",
5115 .desc =
5116 "Makes the following queue variables available.\n"
5117 "QUEUEMAX maxmimum number of calls allowed\n"
5118 "QUEUESTRATEGY the strategy of the queue\n"
5119 "QUEUECALLS number of calls currently in the queue\n"
5120 "QUEUEHOLDTIME current average hold time\n"
5121 "QUEUECOMPLETED number of completed calls for the queue\n"
5122 "QUEUEABANDONED number of abandoned calls\n"
5123 "QUEUESRVLEVEL queue service level\n"
5124 "QUEUESRVLEVELPERF current service level performance\n"
5125 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
5126 .read = queue_function_var,
5129 static struct ast_custom_function queuemembercount_function = {
5130 .name = "QUEUE_MEMBER",
5131 .synopsis = "Count number of members answering a queue",
5132 .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
5133 .desc =
5134 "Returns the number of members currently associated with the specified queue.\n"
5135 "One of three options may be passed to determine the count returned:\n"
5136 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
5137 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
5138 "\"count\" - Returns the total number of members for the specified queue\n",
5139 .read = queue_function_qac,
5142 static struct ast_custom_function queuemembercount_dep = {
5143 .name = "QUEUE_MEMBER_COUNT",
5144 .synopsis = "Count number of members answering a queue",
5145 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
5146 .desc =
5147 "Returns the number of members currently associated with the specified queue.\n\n"
5148 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
5149 .read = queue_function_qac_dep,
5152 static struct ast_custom_function queuewaitingcount_function = {
5153 .name = "QUEUE_WAITING_COUNT",
5154 .synopsis = "Count number of calls currently waiting in a queue",
5155 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
5156 .desc =
5157 "Returns the number of callers currently waiting in the specified queue.\n",
5158 .read = queue_function_queuewaitingcount,
5161 static struct ast_custom_function queuememberlist_function = {
5162 .name = "QUEUE_MEMBER_LIST",
5163 .synopsis = "Returns a list of interfaces on a queue",
5164 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
5165 .desc =
5166 "Returns a comma-separated list of members associated with the specified queue.\n",
5167 .read = queue_function_queuememberlist,
5170 static struct ast_custom_function queuememberpenalty_function = {
5171 .name = "QUEUE_MEMBER_PENALTY",
5172 .synopsis = "Gets or sets queue members penalty.",
5173 .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
5174 .desc =
5175 "Gets or sets queue members penalty\n",
5176 .read = queue_function_memberpenalty_read,
5177 .write = queue_function_memberpenalty_write,
5180 static int reload_queue_rules(int reload)
5182 struct ast_config *cfg;
5183 struct rule_list *rl_iter, *new_rl;
5184 struct penalty_rule *pr_iter;
5185 char *rulecat = NULL;
5186 struct ast_variable *rulevar = NULL;
5187 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5189 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
5190 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
5191 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
5192 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
5193 return AST_MODULE_LOAD_SUCCESS;
5194 } else {
5195 AST_LIST_LOCK(&rule_lists);
5196 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
5197 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
5198 ast_free(pr_iter);
5199 ast_free(rl_iter);
5201 while ((rulecat = ast_category_browse(cfg, rulecat))) {
5202 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
5203 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
5204 AST_LIST_UNLOCK(&rule_lists);
5205 return AST_MODULE_LOAD_FAILURE;
5206 } else {
5207 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
5208 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
5209 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
5210 if(!strcasecmp(rulevar->name, "penaltychange"))
5211 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
5212 else
5213 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
5216 AST_LIST_UNLOCK(&rule_lists);
5219 ast_config_destroy(cfg);
5221 return AST_MODULE_LOAD_SUCCESS;
5225 static int reload_queues(int reload)
5227 struct call_queue *q;
5228 struct ast_config *cfg;
5229 char *cat, *tmp;
5230 struct ast_variable *var;
5231 struct member *cur, *newm;
5232 struct ao2_iterator mem_iter;
5233 int new;
5234 const char *general_val = NULL;
5235 char parse[80];
5236 char *interface, *state_interface;
5237 char *membername = NULL;
5238 int penalty;
5239 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5240 struct ao2_iterator queue_iter;
5241 AST_DECLARE_APP_ARGS(args,
5242 AST_APP_ARG(interface);
5243 AST_APP_ARG(penalty);
5244 AST_APP_ARG(membername);
5245 AST_APP_ARG(state_interface);
5248 /*First things first. Let's load queuerules.conf*/
5249 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
5250 return AST_MODULE_LOAD_FAILURE;
5252 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
5253 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
5254 return 0;
5255 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
5256 return 0;
5257 ao2_lock(queues);
5258 use_weight=0;
5259 /* Mark all queues as dead for the moment */
5260 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
5261 while ((q = ao2_iterator_next(&queue_iter))) {
5262 if (!q->realtime) {
5263 q->dead = 1;
5264 q->found = 0;
5266 queue_unref(q);
5269 /* Chug through config file */
5270 cat = NULL;
5271 while ((cat = ast_category_browse(cfg, cat)) ) {
5272 if (!strcasecmp(cat, "general")) {
5273 /* Initialize global settings */
5274 queue_keep_stats = 0;
5275 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
5276 queue_keep_stats = ast_true(general_val);
5277 queue_persistent_members = 0;
5278 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
5279 queue_persistent_members = ast_true(general_val);
5280 autofill_default = 0;
5281 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
5282 autofill_default = ast_true(general_val);
5283 montype_default = 0;
5284 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
5285 if (!strcasecmp(general_val, "mixmonitor"))
5286 montype_default = 1;
5288 update_cdr = 0;
5289 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
5290 update_cdr = ast_true(general_val);
5291 shared_lastcall = 0;
5292 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
5293 shared_lastcall = ast_true(general_val);
5294 } else { /* Define queue */
5295 /* Look for an existing one */
5296 struct call_queue tmpq = {
5297 .name = cat,
5299 if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
5300 /* Make one then */
5301 if (!(q = alloc_queue(cat))) {
5302 /* TODO: Handle memory allocation failure */
5304 new = 1;
5305 } else
5306 new = 0;
5307 if (q) {
5308 const char *tmpvar = NULL;
5309 if (!new)
5310 ao2_lock(q);
5311 /* Check if a queue with this name already exists */
5312 if (q->found) {
5313 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
5314 if (!new) {
5315 ao2_unlock(q);
5316 queue_unref(q);
5318 continue;
5320 /* Due to the fact that the "linear" strategy will have a different allocation
5321 * scheme for queue members, we must devise the queue's strategy before other initializations
5323 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
5324 q->strategy = strat2int(tmpvar);
5325 if (q->strategy < 0) {
5326 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
5327 tmpvar, q->name);
5328 q->strategy = QUEUE_STRATEGY_RINGALL;
5330 } else
5331 q->strategy = QUEUE_STRATEGY_RINGALL;
5332 /* Re-initialize the queue, and clear statistics */
5333 init_queue(q);
5334 if (!queue_keep_stats)
5335 clear_queue(q);
5336 mem_iter = ao2_iterator_init(q->members, 0);
5337 while ((cur = ao2_iterator_next(&mem_iter))) {
5338 if (!cur->dynamic) {
5339 cur->delme = 1;
5341 ao2_ref(cur, -1);
5343 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
5344 if (!strcasecmp(var->name, "member")) {
5345 struct member tmpmem;
5346 membername = NULL;
5348 /* Add a new member */
5349 ast_copy_string(parse, var->value, sizeof(parse));
5351 AST_STANDARD_APP_ARGS(args, parse);
5353 interface = args.interface;
5354 if (!ast_strlen_zero(args.penalty)) {
5355 tmp = args.penalty;
5356 while (*tmp && *tmp < 33) tmp++;
5357 penalty = atoi(tmp);
5358 if (penalty < 0) {
5359 penalty = 0;
5361 } else
5362 penalty = 0;
5364 if (!ast_strlen_zero(args.membername)) {
5365 membername = args.membername;
5366 while (*membername && *membername < 33) membername++;
5369 if (!ast_strlen_zero(args.state_interface)) {
5370 state_interface = args.state_interface;
5371 while (*state_interface && *state_interface < 33) state_interface++;
5372 } else
5373 state_interface = interface;
5375 /* Find the old position in the list */
5376 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
5377 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
5378 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
5379 if (cur && strcasecmp(cur->state_interface, state_interface)) {
5380 remove_from_interfaces(cur->state_interface);
5382 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
5383 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
5384 add_to_interfaces(state_interface);
5385 ao2_link(q->members, newm);
5386 ao2_ref(newm, -1);
5387 newm = NULL;
5389 if (cur)
5390 ao2_ref(cur, -1);
5391 else {
5392 q->membercount++;
5394 } else {
5395 queue_set_param(q, var->name, var->value, var->lineno, 1);
5399 /* Free remaining members marked as delme */
5400 mem_iter = ao2_iterator_init(q->members, 0);
5401 while ((cur = ao2_iterator_next(&mem_iter))) {
5402 if (! cur->delme) {
5403 ao2_ref(cur, -1);
5404 continue;
5406 q->membercount--;
5407 ao2_unlink(q->members, cur);
5408 remove_from_interfaces(cur->interface);
5409 ao2_ref(cur, -1);
5412 if (new) {
5413 ao2_link(queues, q);
5414 } else
5415 ao2_unlock(q);
5416 queue_unref(q);
5420 ast_config_destroy(cfg);
5421 queue_iter = ao2_iterator_init(queues, 0);
5422 while ((q = ao2_iterator_next(&queue_iter))) {
5423 if (q->dead) {
5424 ao2_unlink(queues, q);
5425 } else {
5426 ao2_lock(q);
5427 mem_iter = ao2_iterator_init(q->members, 0);
5428 while ((cur = ao2_iterator_next(&mem_iter))) {
5429 if (cur->dynamic)
5430 q->membercount++;
5431 cur->status = ast_device_state(cur->interface);
5432 ao2_ref(cur, -1);
5434 ao2_unlock(q);
5436 queue_unref(q);
5438 ao2_unlock(queues);
5439 return 1;
5442 /*! \brief direct ouput to manager or cli with proper terminator */
5443 static void do_print(struct mansession *s, int fd, const char *str)
5445 if (s)
5446 astman_append(s, "%s\r\n", str);
5447 else
5448 ast_cli(fd, "%s\n", str);
5451 /*!
5452 * \brief Show queue(s) status and statistics
5454 * List the queues strategy, calls processed, members logged in,
5455 * other queue statistics such as avg hold time.
5457 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
5459 struct call_queue *q;
5460 struct ast_str *out = ast_str_alloca(240);
5461 int found = 0;
5462 time_t now = time(NULL);
5463 struct ao2_iterator queue_iter;
5464 struct ao2_iterator mem_iter;
5466 if (argc != 2 && argc != 3)
5467 return CLI_SHOWUSAGE;
5469 if (argc == 3) { /* specific queue */
5470 load_realtime_queue(argv[2]);
5472 else if (ast_check_realtime("queues")) {
5473 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
5474 char *queuename;
5475 if (cfg) {
5476 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
5477 load_realtime_queue(queuename);
5479 ast_config_destroy(cfg);
5483 queue_iter = ao2_iterator_init(queues, 0);
5484 while ((q = ao2_iterator_next(&queue_iter))) {
5485 float sl;
5487 ao2_lock(q);
5488 if (argc == 3 && strcasecmp(q->name, argv[2])) {
5489 ao2_unlock(q);
5490 continue;
5492 found = 1;
5494 ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
5495 if (q->maxlen)
5496 ast_str_append(&out, 0, "%d", q->maxlen);
5497 else
5498 ast_str_append(&out, 0, "unlimited");
5499 sl = 0;
5500 if (q->callscompleted > 0)
5501 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
5502 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
5503 int2strat(q->strategy), q->holdtime, q->weight,
5504 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
5505 do_print(s, fd, out->str);
5506 if (!ao2_container_count(q->members))
5507 do_print(s, fd, " No Members");
5508 else {
5509 struct member *mem;
5511 do_print(s, fd, " Members: ");
5512 mem_iter = ao2_iterator_init(q->members, 0);
5513 while ((mem = ao2_iterator_next(&mem_iter))) {
5514 ast_str_set(&out, 0, " %s", mem->membername);
5515 if (strcasecmp(mem->membername, mem->interface)) {
5516 ast_str_append(&out, 0, " (%s)", mem->interface);
5518 if (mem->penalty)
5519 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
5520 ast_str_append(&out, 0, "%s%s%s (%s)",
5521 mem->dynamic ? " (dynamic)" : "",
5522 mem->realtime ? " (realtime)" : "",
5523 mem->paused ? " (paused)" : "",
5524 devstate2str(mem->status));
5525 if (mem->calls)
5526 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
5527 mem->calls, (long) (time(NULL) - mem->lastcall));
5528 else
5529 ast_str_append(&out, 0, " has taken no calls yet");
5530 do_print(s, fd, out->str);
5531 ao2_ref(mem, -1);
5534 if (!q->head)
5535 do_print(s, fd, " No Callers");
5536 else {
5537 struct queue_ent *qe;
5538 int pos = 1;
5540 do_print(s, fd, " Callers: ");
5541 for (qe = q->head; qe; qe = qe->next) {
5542 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
5543 pos++, qe->chan->name, (long) (now - qe->start) / 60,
5544 (long) (now - qe->start) % 60, qe->prio);
5545 do_print(s, fd, out->str);
5548 do_print(s, fd, ""); /* blank line between entries */
5549 ao2_unlock(q);
5550 if (q->realtime || argc == 3) {
5551 /* If a queue is realtime, then that means we used load_realtime_queue() above
5552 * to get its information. This means we have an extra reference we need to
5553 * remove at this point. If a specific queue was requested, then it also needs
5554 * to be unreffed here even if it is not a realtime queue.
5556 queue_unref(q);
5558 queue_unref(q); /* Unref the iterator's reference */
5560 if (!found) {
5561 if (argc == 3)
5562 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
5563 else
5564 ast_str_set(&out, 0, "No queues.");
5565 do_print(s, fd, out->str);
5567 return CLI_SUCCESS;
5570 static char *complete_queue(const char *line, const char *word, int pos, int state)
5572 struct call_queue *q;
5573 char *ret = NULL;
5574 int which = 0;
5575 int wordlen = strlen(word);
5576 struct ao2_iterator queue_iter;
5578 queue_iter = ao2_iterator_init(queues, 0);
5579 while ((q = ao2_iterator_next(&queue_iter))) {
5580 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
5581 ret = ast_strdup(q->name);
5582 queue_unref(q);
5583 break;
5585 queue_unref(q);
5588 return ret;
5591 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
5593 if (pos == 2)
5594 return complete_queue(line, word, pos, state);
5595 return NULL;
5598 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5600 switch ( cmd ) {
5601 case CLI_INIT:
5602 e->command = "queue show";
5603 e->usage =
5604 "Usage: queue show\n"
5605 " Provides summary information on a specified queue.\n";
5606 return NULL;
5607 case CLI_GENERATE:
5608 return complete_queue_show(a->line, a->word, a->pos, a->n);
5611 return __queues_show(NULL, a->fd, a->argc, a->argv);
5614 /*!\brief callback to display queues status in manager
5615 \addtogroup Group_AMI
5617 static int manager_queues_show(struct mansession *s, const struct message *m)
5619 char *a[] = { "queue", "show" };
5621 __queues_show(s, -1, 2, a);
5622 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
5624 return RESULT_SUCCESS;
5627 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
5629 const char *rule = astman_get_header(m, "Rule");
5630 struct rule_list *rl_iter;
5631 struct penalty_rule *pr_iter;
5633 AST_LIST_LOCK(&rule_lists);
5634 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
5635 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
5636 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
5637 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
5638 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 );
5640 if (!ast_strlen_zero(rule))
5641 break;
5644 AST_LIST_UNLOCK(&rule_lists);
5646 astman_append(s, "\r\n\r\n");
5648 return RESULT_SUCCESS;
5651 /*! \brief Summary of queue info via the AMI */
5652 static int manager_queues_summary(struct mansession *s, const struct message *m)
5654 time_t now;
5655 int qmemcount = 0;
5656 int qmemavail = 0;
5657 int qchancount = 0;
5658 int qlongestholdtime = 0;
5659 const char *id = astman_get_header(m, "ActionID");
5660 const char *queuefilter = astman_get_header(m, "Queue");
5661 char idText[256] = "";
5662 struct call_queue *q;
5663 struct queue_ent *qe;
5664 struct member *mem;
5665 struct ao2_iterator queue_iter;
5666 struct ao2_iterator mem_iter;
5668 astman_send_ack(s, m, "Queue summary will follow");
5669 time(&now);
5670 if (!ast_strlen_zero(id))
5671 snprintf(idText, 256, "ActionID: %s\r\n", id);
5672 queue_iter = ao2_iterator_init(queues, 0);
5673 while ((q = ao2_iterator_next(&queue_iter))) {
5674 ao2_lock(q);
5676 /* List queue properties */
5677 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5678 /* Reset the necessary local variables if no queuefilter is set*/
5679 qmemcount = 0;
5680 qmemavail = 0;
5681 qchancount = 0;
5682 qlongestholdtime = 0;
5684 /* List Queue Members */
5685 mem_iter = ao2_iterator_init(q->members, 0);
5686 while ((mem = ao2_iterator_next(&mem_iter))) {
5687 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
5688 ++qmemcount;
5689 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
5690 ++qmemavail;
5693 ao2_ref(mem, -1);
5695 for (qe = q->head; qe; qe = qe->next) {
5696 if ((now - qe->start) > qlongestholdtime) {
5697 qlongestholdtime = now - qe->start;
5699 ++qchancount;
5701 astman_append(s, "Event: QueueSummary\r\n"
5702 "Queue: %s\r\n"
5703 "LoggedIn: %d\r\n"
5704 "Available: %d\r\n"
5705 "Callers: %d\r\n"
5706 "HoldTime: %d\r\n"
5707 "LongestHoldTime: %d\r\n"
5708 "%s"
5709 "\r\n",
5710 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
5712 ao2_unlock(q);
5713 queue_unref(q);
5715 astman_append(s,
5716 "Event: QueueSummaryComplete\r\n"
5717 "%s"
5718 "\r\n", idText);
5720 return RESULT_SUCCESS;
5723 /*! \brief Queue status info via AMI */
5724 static int manager_queues_status(struct mansession *s, const struct message *m)
5726 time_t now;
5727 int pos;
5728 const char *id = astman_get_header(m,"ActionID");
5729 const char *queuefilter = astman_get_header(m,"Queue");
5730 const char *memberfilter = astman_get_header(m,"Member");
5731 char idText[256] = "";
5732 struct call_queue *q;
5733 struct queue_ent *qe;
5734 float sl = 0;
5735 struct member *mem;
5736 struct ao2_iterator queue_iter;
5737 struct ao2_iterator mem_iter;
5739 astman_send_ack(s, m, "Queue status will follow");
5740 time(&now);
5741 if (!ast_strlen_zero(id))
5742 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
5744 queue_iter = ao2_iterator_init(queues, 0);
5745 while ((q = ao2_iterator_next(&queue_iter))) {
5746 ao2_lock(q);
5748 /* List queue properties */
5749 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5750 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
5751 astman_append(s, "Event: QueueParams\r\n"
5752 "Queue: %s\r\n"
5753 "Max: %d\r\n"
5754 "Strategy: %s\r\n"
5755 "Calls: %d\r\n"
5756 "Holdtime: %d\r\n"
5757 "Completed: %d\r\n"
5758 "Abandoned: %d\r\n"
5759 "ServiceLevel: %d\r\n"
5760 "ServicelevelPerf: %2.1f\r\n"
5761 "Weight: %d\r\n"
5762 "%s"
5763 "\r\n",
5764 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
5765 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
5766 /* List Queue Members */
5767 mem_iter = ao2_iterator_init(q->members, 0);
5768 while ((mem = ao2_iterator_next(&mem_iter))) {
5769 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
5770 astman_append(s, "Event: QueueMember\r\n"
5771 "Queue: %s\r\n"
5772 "Name: %s\r\n"
5773 "Location: %s\r\n"
5774 "Membership: %s\r\n"
5775 "Penalty: %d\r\n"
5776 "CallsTaken: %d\r\n"
5777 "LastCall: %d\r\n"
5778 "Status: %d\r\n"
5779 "Paused: %d\r\n"
5780 "%s"
5781 "\r\n",
5782 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
5783 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
5785 ao2_ref(mem, -1);
5787 /* List Queue Entries */
5788 pos = 1;
5789 for (qe = q->head; qe; qe = qe->next) {
5790 astman_append(s, "Event: QueueEntry\r\n"
5791 "Queue: %s\r\n"
5792 "Position: %d\r\n"
5793 "Channel: %s\r\n"
5794 "CallerIDNum: %s\r\n"
5795 "CallerIDName: %s\r\n"
5796 "Wait: %ld\r\n"
5797 "%s"
5798 "\r\n",
5799 q->name, pos++, qe->chan->name,
5800 S_OR(qe->chan->cid.cid_num, "unknown"),
5801 S_OR(qe->chan->cid.cid_name, "unknown"),
5802 (long) (now - qe->start), idText);
5805 ao2_unlock(q);
5806 queue_unref(q);
5809 astman_append(s,
5810 "Event: QueueStatusComplete\r\n"
5811 "%s"
5812 "\r\n",idText);
5814 return RESULT_SUCCESS;
5817 static int manager_add_queue_member(struct mansession *s, const struct message *m)
5819 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
5820 int paused, penalty = 0;
5822 queuename = astman_get_header(m, "Queue");
5823 interface = astman_get_header(m, "Interface");
5824 penalty_s = astman_get_header(m, "Penalty");
5825 paused_s = astman_get_header(m, "Paused");
5826 membername = astman_get_header(m, "MemberName");
5827 state_interface = astman_get_header(m, "StateInterface");
5829 if (ast_strlen_zero(queuename)) {
5830 astman_send_error(s, m, "'Queue' not specified.");
5831 return 0;
5834 if (ast_strlen_zero(interface)) {
5835 astman_send_error(s, m, "'Interface' not specified.");
5836 return 0;
5839 if (ast_strlen_zero(penalty_s))
5840 penalty = 0;
5841 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
5842 penalty = 0;
5844 if (ast_strlen_zero(paused_s))
5845 paused = 0;
5846 else
5847 paused = abs(ast_true(paused_s));
5849 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
5850 case RES_OKAY:
5851 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
5852 astman_send_ack(s, m, "Added interface to queue");
5853 break;
5854 case RES_EXISTS:
5855 astman_send_error(s, m, "Unable to add interface: Already there");
5856 break;
5857 case RES_NOSUCHQUEUE:
5858 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
5859 break;
5860 case RES_OUTOFMEMORY:
5861 astman_send_error(s, m, "Out of memory");
5862 break;
5865 return 0;
5868 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
5870 const char *queuename, *interface;
5872 queuename = astman_get_header(m, "Queue");
5873 interface = astman_get_header(m, "Interface");
5875 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
5876 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
5877 return 0;
5880 switch (remove_from_queue(queuename, interface)) {
5881 case RES_OKAY:
5882 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
5883 astman_send_ack(s, m, "Removed interface from queue");
5884 break;
5885 case RES_EXISTS:
5886 astman_send_error(s, m, "Unable to remove interface: Not there");
5887 break;
5888 case RES_NOSUCHQUEUE:
5889 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
5890 break;
5891 case RES_OUTOFMEMORY:
5892 astman_send_error(s, m, "Out of memory");
5893 break;
5894 case RES_NOT_DYNAMIC:
5895 astman_send_error(s, m, "Member not dynamic");
5896 break;
5899 return 0;
5902 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
5904 const char *queuename, *interface, *paused_s, *reason;
5905 int paused;
5907 interface = astman_get_header(m, "Interface");
5908 paused_s = astman_get_header(m, "Paused");
5909 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
5910 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */
5912 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
5913 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
5914 return 0;
5917 paused = abs(ast_true(paused_s));
5919 if (set_member_paused(queuename, interface, reason, paused))
5920 astman_send_error(s, m, "Interface not found");
5921 else
5922 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
5923 return 0;
5926 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
5928 const char *queuename, *event, *message, *interface, *uniqueid;
5930 queuename = astman_get_header(m, "Queue");
5931 uniqueid = astman_get_header(m, "UniqueId");
5932 interface = astman_get_header(m, "Interface");
5933 event = astman_get_header(m, "Event");
5934 message = astman_get_header(m, "Message");
5936 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
5937 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
5938 return 0;
5941 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
5942 astman_send_ack(s, m, "Event added successfully");
5944 return 0;
5947 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
5949 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
5950 switch (pos) {
5951 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
5952 return NULL;
5953 case 4: /* only one possible match, "to" */
5954 return state == 0 ? ast_strdup("to") : NULL;
5955 case 5: /* <queue> */
5956 return complete_queue(line, word, pos, state);
5957 case 6: /* only one possible match, "penalty" */
5958 return state == 0 ? ast_strdup("penalty") : NULL;
5959 case 7:
5960 if (state < 100) { /* 0-99 */
5961 char *num;
5962 if ((num = ast_malloc(3))) {
5963 sprintf(num, "%d", state);
5965 return num;
5966 } else {
5967 return NULL;
5969 case 8: /* only one possible match, "as" */
5970 return state == 0 ? ast_strdup("as") : NULL;
5971 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
5972 return NULL;
5973 default:
5974 return NULL;
5978 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
5980 const char *queuename, *interface, *penalty_s;
5981 int penalty;
5983 interface = astman_get_header(m, "Interface");
5984 penalty_s = astman_get_header(m, "Penalty");
5985 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
5986 queuename = astman_get_header(m, "Queue");
5988 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
5989 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
5990 return 0;
5993 penalty = atoi(penalty_s);
5995 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
5996 astman_send_error(s, m, "Invalid interface, queuename or penalty");
5997 else
5998 astman_send_ack(s, m, "Interface penalty set successfully");
6000 return 0;
6003 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6005 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
6006 int penalty;
6008 switch ( cmd ) {
6009 case CLI_INIT:
6010 e->command = "queue add member";
6011 e->usage =
6012 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
6013 return NULL;
6014 case CLI_GENERATE:
6015 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
6018 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
6019 return CLI_SHOWUSAGE;
6020 } else if (strcmp(a->argv[4], "to")) {
6021 return CLI_SHOWUSAGE;
6022 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
6023 return CLI_SHOWUSAGE;
6024 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
6025 return CLI_SHOWUSAGE;
6026 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
6027 return CLI_SHOWUSAGE;
6030 queuename = a->argv[5];
6031 interface = a->argv[3];
6032 if (a->argc >= 8) {
6033 if (sscanf(a->argv[7], "%d", &penalty) == 1) {
6034 if (penalty < 0) {
6035 ast_cli(a->fd, "Penalty must be >= 0\n");
6036 penalty = 0;
6038 } else {
6039 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
6040 penalty = 0;
6042 } else {
6043 penalty = 0;
6046 if (a->argc >= 10) {
6047 membername = a->argv[9];
6050 if (a->argc >= 12) {
6051 state_interface = a->argv[11];
6054 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
6055 case RES_OKAY:
6056 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
6057 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
6058 return CLI_SUCCESS;
6059 case RES_EXISTS:
6060 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
6061 return CLI_FAILURE;
6062 case RES_NOSUCHQUEUE:
6063 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
6064 return CLI_FAILURE;
6065 case RES_OUTOFMEMORY:
6066 ast_cli(a->fd, "Out of memory\n");
6067 return CLI_FAILURE;
6068 case RES_NOT_DYNAMIC:
6069 ast_cli(a->fd, "Member not dynamic\n");
6070 return CLI_FAILURE;
6071 default:
6072 return CLI_FAILURE;
6076 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
6078 int which = 0;
6079 struct call_queue *q;
6080 struct member *m;
6081 struct ao2_iterator queue_iter;
6082 struct ao2_iterator mem_iter;
6083 int wordlen = strlen(word);
6085 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
6086 if (pos > 5 || pos < 3)
6087 return NULL;
6088 if (pos == 4) /* only one possible match, 'from' */
6089 return (state == 0 ? ast_strdup("from") : NULL);
6091 if (pos == 5) /* No need to duplicate code */
6092 return complete_queue(line, word, pos, state);
6094 /* here is the case for 3, <member> */
6095 queue_iter = ao2_iterator_init(queues, 0);
6096 while ((q = ao2_iterator_next(&queue_iter))) {
6097 ao2_lock(q);
6098 mem_iter = ao2_iterator_init(q->members, 0);
6099 while ((m = ao2_iterator_next(&mem_iter))) {
6100 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
6101 char *tmp;
6102 ao2_unlock(q);
6103 tmp = ast_strdup(m->interface);
6104 ao2_ref(m, -1);
6105 queue_unref(q);
6106 return tmp;
6108 ao2_ref(m, -1);
6110 ao2_unlock(q);
6111 queue_unref(q);
6114 return NULL;
6117 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6119 char *queuename, *interface;
6121 switch (cmd) {
6122 case CLI_INIT:
6123 e->command = "queue remove member";
6124 e->usage = "Usage: queue remove member <channel> from <queue>\n";
6125 return NULL;
6126 case CLI_GENERATE:
6127 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
6130 if (a->argc != 6) {
6131 return CLI_SHOWUSAGE;
6132 } else if (strcmp(a->argv[4], "from")) {
6133 return CLI_SHOWUSAGE;
6136 queuename = a->argv[5];
6137 interface = a->argv[3];
6139 switch (remove_from_queue(queuename, interface)) {
6140 case RES_OKAY:
6141 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
6142 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
6143 return CLI_SUCCESS;
6144 case RES_EXISTS:
6145 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
6146 return CLI_FAILURE;
6147 case RES_NOSUCHQUEUE:
6148 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
6149 return CLI_FAILURE;
6150 case RES_OUTOFMEMORY:
6151 ast_cli(a->fd, "Out of memory\n");
6152 return CLI_FAILURE;
6153 default:
6154 return CLI_FAILURE;
6158 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
6160 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
6161 switch (pos) {
6162 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
6163 return NULL;
6164 case 4: /* only one possible match, "queue" */
6165 return state == 0 ? ast_strdup("queue") : NULL;
6166 case 5: /* <queue> */
6167 return complete_queue(line, word, pos, state);
6168 case 6: /* "reason" */
6169 return state == 0 ? ast_strdup("reason") : NULL;
6170 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
6171 return NULL;
6172 default:
6173 return NULL;
6177 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6179 char *queuename, *interface, *reason;
6180 int paused;
6182 switch (cmd) {
6183 case CLI_INIT:
6184 e->command = "queue {pause|unpause} member";
6185 e->usage =
6186 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
6187 " Pause or unpause a queue member. Not specifying a particular queue\n"
6188 " will pause or unpause a member across all queues to which the member\n"
6189 " belongs.\n";
6190 return NULL;
6191 case CLI_GENERATE:
6192 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
6195 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
6196 return CLI_SHOWUSAGE;
6197 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
6198 return CLI_SHOWUSAGE;
6199 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
6200 return CLI_SHOWUSAGE;
6204 interface = a->argv[3];
6205 queuename = a->argc >= 6 ? a->argv[5] : NULL;
6206 reason = a->argc == 8 ? a->argv[7] : NULL;
6207 paused = !strcasecmp(a->argv[1], "pause");
6209 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
6210 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
6211 if (!ast_strlen_zero(queuename))
6212 ast_cli(a->fd, " in queue '%s'", queuename);
6213 if (!ast_strlen_zero(reason))
6214 ast_cli(a->fd, " for reason '%s'", reason);
6215 ast_cli(a->fd, "\n");
6216 return CLI_SUCCESS;
6217 } else {
6218 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
6219 if (!ast_strlen_zero(queuename))
6220 ast_cli(a->fd, " in queue '%s'", queuename);
6221 if (!ast_strlen_zero(reason))
6222 ast_cli(a->fd, " for reason '%s'", reason);
6223 ast_cli(a->fd, "\n");
6224 return CLI_FAILURE;
6228 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
6230 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
6231 switch (pos) {
6232 case 4:
6233 if (state == 0) {
6234 return ast_strdup("on");
6235 } else {
6236 return NULL;
6238 case 6:
6239 if (state == 0) {
6240 return ast_strdup("in");
6241 } else {
6242 return NULL;
6244 case 7:
6245 return complete_queue(line, word, pos, state);
6246 default:
6247 return NULL;
6251 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6253 char *queuename = NULL, *interface;
6254 int penalty = 0;
6256 switch (cmd) {
6257 case CLI_INIT:
6258 e->command = "queue set penalty";
6259 e->usage =
6260 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
6261 "Set a member's penalty in the queue specified. If no queue is specified\n"
6262 "then that interface's penalty is set in all queues to which that interface is a member\n";
6263 return NULL;
6264 case CLI_GENERATE:
6265 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
6268 if (a->argc != 6 && a->argc != 8) {
6269 return CLI_SHOWUSAGE;
6270 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
6271 return CLI_SHOWUSAGE;
6274 if (a->argc == 8)
6275 queuename = a->argv[7];
6276 interface = a->argv[5];
6277 penalty = atoi(a->argv[3]);
6279 switch (set_member_penalty(queuename, interface, penalty)) {
6280 case RESULT_SUCCESS:
6281 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6282 return CLI_SUCCESS;
6283 case RESULT_FAILURE:
6284 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6285 return CLI_FAILURE;
6286 default:
6287 return CLI_FAILURE;
6291 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
6293 int which = 0;
6294 struct rule_list *rl_iter;
6295 int wordlen = strlen(word);
6296 char *ret = NULL;
6297 if (pos != 3) /* Wha? */ {
6298 ast_log(LOG_DEBUG, "Hitting this???, pos is %d\n", pos);
6299 return NULL;
6302 AST_LIST_LOCK(&rule_lists);
6303 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6304 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
6305 ret = ast_strdup(rl_iter->name);
6306 break;
6309 AST_LIST_UNLOCK(&rule_lists);
6311 return ret;
6314 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6316 char *rule;
6317 struct rule_list *rl_iter;
6318 struct penalty_rule *pr_iter;
6319 switch (cmd) {
6320 case CLI_INIT:
6321 e->command = "queue rules show";
6322 e->usage =
6323 "Usage: queue rules show [rulename]\n"
6324 "Show the list of rules associated with rulename. If no\n"
6325 "rulename is specified, list all rules defined in queuerules.conf\n";
6326 return NULL;
6327 case CLI_GENERATE:
6328 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
6331 if (a->argc != 3 && a->argc != 4)
6332 return CLI_SHOWUSAGE;
6334 rule = a->argc == 4 ? a->argv[3] : "";
6335 AST_LIST_LOCK(&rule_lists);
6336 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6337 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
6338 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
6339 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
6340 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);
6344 AST_LIST_UNLOCK(&rule_lists);
6345 return CLI_SUCCESS;
6348 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6350 switch (cmd) {
6351 case CLI_INIT:
6352 e->command = "queue rules reload";
6353 e->usage =
6354 "Usage: queue rules reload\n"
6355 "Reloads rules defined in queuerules.conf\n";
6356 return NULL;
6357 case CLI_GENERATE:
6358 return NULL;
6360 reload_queue_rules(1);
6361 return CLI_SUCCESS;
6364 static const char qpm_cmd_usage[] =
6365 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
6367 static const char qum_cmd_usage[] =
6368 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
6370 static const char qsmp_cmd_usage[] =
6371 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
6373 static struct ast_cli_entry cli_queue[] = {
6374 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
6375 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
6376 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
6377 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
6378 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
6379 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
6380 AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
6383 static int unload_module(void)
6385 int res;
6386 struct ast_context *con;
6387 struct ao2_iterator q_iter;
6388 struct call_queue *q = NULL;
6390 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6391 res = ast_manager_unregister("QueueStatus");
6392 res |= ast_manager_unregister("Queues");
6393 res |= ast_manager_unregister("QueueRule");
6394 res |= ast_manager_unregister("QueueSummary");
6395 res |= ast_manager_unregister("QueueAdd");
6396 res |= ast_manager_unregister("QueueRemove");
6397 res |= ast_manager_unregister("QueuePause");
6398 res |= ast_manager_unregister("QueueLog");
6399 res |= ast_manager_unregister("QueuePenalty");
6400 res |= ast_unregister_application(app_aqm);
6401 res |= ast_unregister_application(app_rqm);
6402 res |= ast_unregister_application(app_pqm);
6403 res |= ast_unregister_application(app_upqm);
6404 res |= ast_unregister_application(app_ql);
6405 res |= ast_unregister_application(app);
6406 res |= ast_custom_function_unregister(&queuevar_function);
6407 res |= ast_custom_function_unregister(&queuemembercount_function);
6408 res |= ast_custom_function_unregister(&queuemembercount_dep);
6409 res |= ast_custom_function_unregister(&queuememberlist_function);
6410 res |= ast_custom_function_unregister(&queuewaitingcount_function);
6411 res |= ast_custom_function_unregister(&queuememberpenalty_function);
6413 if (device_state_sub)
6414 ast_event_unsubscribe(device_state_sub);
6416 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
6417 ast_context_remove_extension2(con, "s", 1, NULL, 0);
6418 ast_context_destroy(con, "app_queue"); /* leave no trace */
6421 clear_and_free_interfaces();
6423 q_iter = ao2_iterator_init(queues, 0);
6424 while ((q = ao2_iterator_next(&q_iter))) {
6425 ao2_unlink(queues, q);
6426 queue_unref(q);
6428 ao2_ref(queues, -1);
6429 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
6430 ast_unload_realtime("queue_members");
6431 return res;
6434 static int load_module(void)
6436 int res;
6437 struct ast_context *con;
6439 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
6441 if (!reload_queues(0))
6442 return AST_MODULE_LOAD_DECLINE;
6444 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
6445 if (!con)
6446 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
6447 else
6448 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_queue");
6450 if (queue_persistent_members)
6451 reload_queue_members();
6453 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6454 res = ast_register_application(app, queue_exec, synopsis, descrip);
6455 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
6456 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
6457 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
6458 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
6459 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
6460 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
6461 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
6462 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
6463 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
6464 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
6465 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
6466 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
6467 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
6468 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
6469 res |= ast_custom_function_register(&queuevar_function);
6470 res |= ast_custom_function_register(&queuemembercount_function);
6471 res |= ast_custom_function_register(&queuemembercount_dep);
6472 res |= ast_custom_function_register(&queuememberlist_function);
6473 res |= ast_custom_function_register(&queuewaitingcount_function);
6474 res |= ast_custom_function_register(&queuememberpenalty_function);
6476 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
6477 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
6480 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE, device_state_cb, NULL, AST_EVENT_IE_END))) {
6481 res = -1;
6484 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
6486 return res ? AST_MODULE_LOAD_DECLINE : 0;
6489 static int reload(void)
6491 ast_unload_realtime("queue_members");
6492 reload_queues(1);
6493 return 0;
6496 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
6497 .load = load_module,
6498 .unload = unload_module,
6499 .reload = reload,