Add a function, CHANNELS(), which retrieves a list of all active channels.
[asterisk-bristuff.git] / apps / app_queue.c
blobfe9be2c143133479545b63e2c397260336255ccf
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief True call queues with optional send URL on answer
23 * \author Mark Spencer <markster@digium.com>
25 * \arg Config in \ref Config_qu queues.conf
27 * \par Development notes
28 * \note 2004-11-25: Persistent Dynamic Members added by:
29 * NetNation Communications (www.netnation.com)
30 * Kevin Lindsay <kevinl@netnation.com>
32 * Each dynamic agent in each queue is now stored in the astdb.
33 * When asterisk is restarted, each agent will be automatically
34 * readded into their recorded queues. This feature can be
35 * configured with the 'persistent_members=<1|0>' setting in the
36 * '[general]' category in queues.conf. The default is on.
38 * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
40 * \note These features added by David C. Troy <dave@toad.net>:
41 * - Per-queue holdtime calculation
42 * - Estimated holdtime announcement
43 * - Position announcement
44 * - Abandoned/completed call counters
45 * - Failout timer passed as optional app parameter
46 * - Optional monitoring of calls, started when call is answered
48 * Patch Version 1.07 2003-12-24 01
50 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
51 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
53 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
54 * by Matthew Enger <m.enger@xi.com.au>
56 * \ingroup applications
59 /*** MODULEINFO
60 <depend>res_monitor</depend>
61 ***/
63 #include "asterisk.h"
65 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
67 #include <sys/time.h>
68 #include <sys/signal.h>
69 #include <netinet/in.h>
71 #include "asterisk/lock.h"
72 #include "asterisk/file.h"
73 #include "asterisk/channel.h"
74 #include "asterisk/pbx.h"
75 #include "asterisk/app.h"
76 #include "asterisk/linkedlists.h"
77 #include "asterisk/module.h"
78 #include "asterisk/translate.h"
79 #include "asterisk/say.h"
80 #include "asterisk/features.h"
81 #include "asterisk/musiconhold.h"
82 #include "asterisk/cli.h"
83 #include "asterisk/manager.h"
84 #include "asterisk/config.h"
85 #include "asterisk/monitor.h"
86 #include "asterisk/utils.h"
87 #include "asterisk/causes.h"
88 #include "asterisk/astdb.h"
89 #include "asterisk/devicestate.h"
90 #include "asterisk/stringfields.h"
91 #include "asterisk/event.h"
92 #include "asterisk/astobj2.h"
93 #include "asterisk/strings.h"
94 #include "asterisk/global_datastores.h"
95 #include "asterisk/taskprocessor.h"
97 /*!
98 * \par Please read before modifying this file.
99 * There are three locks which are regularly used
100 * throughout this file, the queue list lock, the lock
101 * for each individual queue, and the interface list lock.
102 * Please be extra careful to always lock in the following order
103 * 1) queue list lock
104 * 2) individual queue lock
105 * 3) interface list lock
106 * This order has sort of "evolved" over the lifetime of this
107 * application, but it is now in place this way, so please adhere
108 * to this order!
112 enum {
113 QUEUE_STRATEGY_RINGALL = 0,
114 QUEUE_STRATEGY_LEASTRECENT,
115 QUEUE_STRATEGY_FEWESTCALLS,
116 QUEUE_STRATEGY_RANDOM,
117 QUEUE_STRATEGY_RRMEMORY,
118 QUEUE_STRATEGY_LINEAR,
119 QUEUE_STRATEGY_WRANDOM
122 static const struct strategy {
123 int strategy;
124 const char *name;
125 } strategies[] = {
126 { QUEUE_STRATEGY_RINGALL, "ringall" },
127 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
128 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
129 { QUEUE_STRATEGY_RANDOM, "random" },
130 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
131 { QUEUE_STRATEGY_LINEAR, "linear" },
132 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
135 static struct ast_taskprocessor *devicestate_tps;
137 #define DEFAULT_RETRY 5
138 #define DEFAULT_TIMEOUT 15
139 #define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
140 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
141 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /*!< The minimum number of seconds between position announcements \
142 The default value of 15 provides backwards compatibility */
143 #define MAX_QUEUE_BUCKETS 53
145 #define RES_OKAY 0 /*!< Action completed */
146 #define RES_EXISTS (-1) /*!< Entry already exists */
147 #define RES_OUTOFMEMORY (-2) /*!< Out of memory */
148 #define RES_NOSUCHQUEUE (-3) /*!< No such queue */
149 #define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
151 static char *app = "Queue";
153 static char *synopsis = "Queue a call for a call queue";
155 static char *descrip =
156 " Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
157 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
158 "This application will return to the dialplan if the queue does not exist, or\n"
159 "any of the join options cause the caller to not enter the queue.\n"
160 "The option string may contain zero or more of the following characters:\n"
161 " 'c' -- continue in the dialplan if the callee hangs up.\n"
162 " 'd' -- data-quality (modem) call (minimum delay).\n"
163 " 'h' -- allow callee to hang up by pressing *.\n"
164 " 'H' -- allow caller to hang up by pressing *.\n"
165 " 'n' -- no retries on the timeout; will exit this application and \n"
166 " go to the next step.\n"
167 " 'i' -- ignore call forward requests from queue members and do nothing\n"
168 " when they are requested.\n"
169 " 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
170 " 't' -- allow the called user to transfer the calling user.\n"
171 " 'T' -- allow the calling user to transfer the call.\n"
172 " 'w' -- allow the called user to write the conversation to disk via Monitor.\n"
173 " 'W' -- allow the calling user to write the conversation to disk via Monitor.\n"
174 " 'k' -- Allow the called party to enable parking of the call by sending\n"
175 " the DTMF sequence defined for call parking in features.conf.\n"
176 " 'K' -- Allow the calling party to enable parking of the call by sending\n"
177 " the DTMF sequence defined for call parking in features.conf.\n"
178 " 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
179 " 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
181 " In addition to transferring the call, a call may be parked and then picked\n"
182 "up by another user.\n"
183 " The optional URL will be sent to the called party if the channel supports\n"
184 "it.\n"
185 " The optional AGI parameter will setup an AGI script to be executed on the \n"
186 "calling party's channel once they are connected to a queue member.\n"
187 " The optional macro parameter will run a macro on the \n"
188 "calling party's channel once they are connected to a queue member.\n"
189 " The optional gosub parameter will run a gosub on the \n"
190 "calling party's channel once they are connected to a queue member.\n"
191 " The optional rule parameter will cause the queue's defaultrule to be\n"
192 "overridden by the rule specified.\n"
193 " The timeout will cause the queue to fail out after a specified number of\n"
194 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
195 " This application sets the following channel variable upon completion:\n"
196 " QUEUESTATUS The status of the call as a text string, one of\n"
197 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
199 static char *app_aqm = "AddQueueMember" ;
200 static char *app_aqm_synopsis = "Dynamically adds queue members" ;
201 static char *app_aqm_descrip =
202 " AddQueueMember(queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]):\n"
203 "Dynamically adds interface to an existing queue.\n"
204 "If the interface is already in the queue it will return an error.\n"
205 " This application sets the following channel variable upon completion:\n"
206 " AQMSTATUS The status of the attempt to add a queue member as a \n"
207 " text string, one of\n"
208 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
209 "Example: AddQueueMember(techsupport,SIP/3000)\n"
212 static char *app_rqm = "RemoveQueueMember" ;
213 static char *app_rqm_synopsis = "Dynamically removes queue members" ;
214 static char *app_rqm_descrip =
215 " RemoveQueueMember(queuename[,interface[,options]]):\n"
216 "Dynamically removes interface to an existing queue\n"
217 "If the interface is NOT in the queue it will return an error.\n"
218 " This application sets the following channel variable upon completion:\n"
219 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
220 " text string, one of\n"
221 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
222 "Example: RemoveQueueMember(techsupport,SIP/3000)\n"
225 static char *app_pqm = "PauseQueueMember" ;
226 static char *app_pqm_synopsis = "Pauses a queue member" ;
227 static char *app_pqm_descrip =
228 " PauseQueueMember([queuename],interface[,options[,reason]]):\n"
229 "Pauses (blocks calls for) a queue member.\n"
230 "The given interface will be paused in the given queue. This prevents\n"
231 "any calls from being sent from the queue to the interface until it is\n"
232 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
233 "queuename is given, the interface is paused in every queue it is a\n"
234 "member of. The application will fail if the interface is not found.\n"
235 "The reason string is entirely optional and is used to add extra information\n"
236 "to the appropriate queue_log entries and manager events.\n"
237 " This application sets the following channel variable upon completion:\n"
238 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
239 " text string, one of\n"
240 " PAUSED | NOTFOUND\n"
241 "Example: PauseQueueMember(,SIP/3000)\n";
243 static char *app_upqm = "UnpauseQueueMember" ;
244 static char *app_upqm_synopsis = "Unpauses a queue member" ;
245 static char *app_upqm_descrip =
246 " UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
247 "Unpauses (resumes calls to) a queue member.\n"
248 "This is the counterpart to PauseQueueMember and operates exactly the\n"
249 "same way, except it unpauses instead of pausing the given interface.\n"
250 "The reason string is entirely optional and is used to add extra information\n"
251 "to the appropriate queue_log entries and manager events.\n"
252 " This application sets the following channel variable upon completion:\n"
253 " UPQMSTATUS The status of the attempt to unpause a queue \n"
254 " member as a text string, one of\n"
255 " UNPAUSED | NOTFOUND\n"
256 "Example: UnpauseQueueMember(,SIP/3000)\n";
258 static char *app_ql = "QueueLog" ;
259 static char *app_ql_synopsis = "Writes to the queue_log" ;
260 static char *app_ql_descrip =
261 " QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
262 "Allows you to write your own events into the queue log\n"
263 "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
265 /*! \brief Persistent Members astdb family */
266 static const char *pm_family = "Queue/PersistentMembers";
267 /* The maximum length of each persistent member queue database entry */
268 #define PM_MAX_LEN 8192
270 /*! \brief queues.conf [general] option */
271 static int queue_keep_stats = 0;
273 /*! \brief queues.conf [general] option */
274 static int queue_persistent_members = 0;
276 /*! \brief queues.conf per-queue weight option */
277 static int use_weight = 0;
279 /*! \brief queues.conf [general] option */
280 static int autofill_default = 0;
282 /*! \brief queues.conf [general] option */
283 static int montype_default = 0;
285 /*! \brief queues.conf [general] option */
286 static int shared_lastcall = 0;
288 /*! \brief Subscription to device state change events */
289 static struct ast_event_sub *device_state_sub;
291 /*! \brief queues.conf [general] option */
292 static int update_cdr = 0;
294 enum queue_result {
295 QUEUE_UNKNOWN = 0,
296 QUEUE_TIMEOUT = 1,
297 QUEUE_JOINEMPTY = 2,
298 QUEUE_LEAVEEMPTY = 3,
299 QUEUE_JOINUNAVAIL = 4,
300 QUEUE_LEAVEUNAVAIL = 5,
301 QUEUE_FULL = 6,
302 QUEUE_CONTINUE = 7,
305 const struct {
306 enum queue_result id;
307 char *text;
308 } queue_results[] = {
309 { QUEUE_UNKNOWN, "UNKNOWN" },
310 { QUEUE_TIMEOUT, "TIMEOUT" },
311 { QUEUE_JOINEMPTY,"JOINEMPTY" },
312 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
313 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
314 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
315 { QUEUE_FULL, "FULL" },
316 { QUEUE_CONTINUE, "CONTINUE" },
319 /*! \brief We define a custom "local user" structure because we
320 * use it not only for keeping track of what is in use but
321 * also for keeping track of who we're dialing.
323 * There are two "links" defined in this structure, q_next and call_next.
324 * q_next links ALL defined callattempt structures into a linked list. call_next is
325 * a link which allows for a subset of the callattempts to be traversed. This subset
326 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
327 * also is helpful so that queue logs are always accurate in the case where a call to
328 * a member times out, especially if using the ringall strategy.
331 struct callattempt {
332 struct callattempt *q_next;
333 struct callattempt *call_next;
334 struct ast_channel *chan;
335 char interface[256];
336 int stillgoing;
337 int metric;
338 int oldstatus;
339 time_t lastcall;
340 struct call_queue *lastqueue;
341 struct member *member;
345 struct queue_ent {
346 struct call_queue *parent; /*!< What queue is our parent */
347 char moh[80]; /*!< Name of musiconhold to be used */
348 char announce[80]; /*!< Announcement to play for member when call is answered */
349 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
350 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
351 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
352 int pos; /*!< Where we are in the queue */
353 int prio; /*!< Our priority */
354 int last_pos_said; /*!< Last position we told the user */
355 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
356 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
357 time_t last_pos; /*!< Last time we told the user their position */
358 int opos; /*!< Where we started in the queue */
359 int handled; /*!< Whether our call was handled */
360 int pending; /*!< Non-zero if we are attempting to call a member */
361 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
362 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
363 int linpos; /*!< If using linear strategy, what position are we at? */
364 int linwrapped; /*!< Is the linpos wrapped? */
365 time_t start; /*!< When we started holding */
366 time_t expire; /*!< When this entry should expire (time out of queue) */
367 struct ast_channel *chan; /*!< Our channel */
368 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
369 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
370 struct queue_ent *next; /*!< The next queue entry */
373 struct member {
374 char interface[80]; /*!< Technology/Location to dial to reach this member*/
375 char state_interface[80]; /*!< Technology/Location from which to read devicestate changes */
376 char membername[80]; /*!< Member name to use in queue logs */
377 int penalty; /*!< Are we a last resort? */
378 int calls; /*!< Number of calls serviced by this member */
379 int dynamic; /*!< Are we dynamically added? */
380 int realtime; /*!< Is this member realtime? */
381 int status; /*!< Status of queue member */
382 int paused; /*!< Are we paused (not accepting calls)? */
383 time_t lastcall; /*!< When last successful call was hungup */
384 struct call_queue *lastqueue; /*!< Last queue we received a call */
385 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
386 unsigned int delme:1; /*!< Flag to delete entry on reload */
387 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
390 struct member_interface {
391 char interface[80];
392 AST_LIST_ENTRY(member_interface) list; /*!< Next call queue */
395 static AST_LIST_HEAD_STATIC(interfaces, member_interface);
397 /* values used in multi-bit flags in call_queue */
398 #define QUEUE_EMPTY_NORMAL 1
399 #define QUEUE_EMPTY_STRICT 2
400 #define QUEUE_EMPTY_LOOSE 3
401 #define ANNOUNCEHOLDTIME_ALWAYS 1
402 #define ANNOUNCEHOLDTIME_ONCE 2
403 #define QUEUE_EVENT_VARIABLES 3
405 struct penalty_rule {
406 int time; /*!< Number of seconds that need to pass before applying this rule */
407 int max_value; /*!< The amount specified in the penalty rule for max penalty */
408 int min_value; /*!< The amount specified in the penalty rule for min penalty */
409 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
410 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
411 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
414 #define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
415 #define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
416 #define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
417 #define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
419 struct call_queue {
420 AST_DECLARE_STRING_FIELDS(
421 /*! Queue name */
422 AST_STRING_FIELD(name);
423 /*! Music on Hold class */
424 AST_STRING_FIELD(moh);
425 /*! Announcement to play when call is answered */
426 AST_STRING_FIELD(announce);
427 /*! Exit context */
428 AST_STRING_FIELD(context);
429 /*! Macro to run upon member connection */
430 AST_STRING_FIELD(membermacro);
431 /*! Gosub to run upon member connection */
432 AST_STRING_FIELD(membergosub);
433 /*! Default rule to use if none specified in call to Queue() */
434 AST_STRING_FIELD(defaultrule);
435 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
436 AST_STRING_FIELD(sound_next);
437 /*! Sound file: "There are currently" (def. queue-thereare) */
438 AST_STRING_FIELD(sound_thereare);
439 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
440 AST_STRING_FIELD(sound_calls);
441 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
442 AST_STRING_FIELD(queue_quantity1);
443 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
444 AST_STRING_FIELD(queue_quantity2);
445 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
446 AST_STRING_FIELD(sound_holdtime);
447 /*! Sound file: "minutes." (def. queue-minutes) */
448 AST_STRING_FIELD(sound_minutes);
449 /*! Sound file: "minute." (def. queue-minute) */
450 AST_STRING_FIELD(sound_minute);
451 /*! Sound file: "seconds." (def. queue-seconds) */
452 AST_STRING_FIELD(sound_seconds);
453 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
454 AST_STRING_FIELD(sound_thanks);
455 /*! Sound file: Custom announce for caller, no default */
456 AST_STRING_FIELD(sound_callerannounce);
457 /*! Sound file: "Hold time" (def. queue-reporthold) */
458 AST_STRING_FIELD(sound_reporthold);
460 /*! Sound files: Custom announce, no default */
461 struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
462 unsigned int dead:1;
463 unsigned int joinempty:2;
464 unsigned int eventwhencalled:2;
465 unsigned int leavewhenempty:2;
466 unsigned int ringinuse:1;
467 unsigned int setinterfacevar:1;
468 unsigned int setqueuevar:1;
469 unsigned int setqueueentryvar:1;
470 unsigned int reportholdtime:1;
471 unsigned int wrapped:1;
472 unsigned int timeoutrestart:1;
473 unsigned int announceholdtime:2;
474 unsigned int announceposition:3;
475 int strategy:4;
476 unsigned int maskmemberstatus:1;
477 unsigned int realtime:1;
478 unsigned int found:1;
479 int announcepositionlimit; /*!< How many positions we announce? */
480 int announcefrequency; /*!< How often to announce their position */
481 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
482 int periodicannouncefrequency; /*!< How often to play periodic announcement */
483 int numperiodicannounce; /*!< The number of periodic announcements configured */
484 int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
485 int roundingseconds; /*!< How many seconds do we round to? */
486 int holdtime; /*!< Current avg holdtime, based on recursive boxcar filter */
487 int callscompleted; /*!< Number of queue calls completed */
488 int callsabandoned; /*!< Number of queue calls abandoned */
489 int servicelevel; /*!< seconds setting for servicelevel*/
490 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
491 char monfmt[8]; /*!< Format to use when recording calls */
492 int montype; /*!< Monitor type Monitor vs. MixMonitor */
493 int count; /*!< How many entries */
494 int maxlen; /*!< Max number of entries */
495 int wrapuptime; /*!< Wrapup Time */
497 int retry; /*!< Retry calling everyone after this amount of time */
498 int timeout; /*!< How long to wait for an answer */
499 int weight; /*!< Respective weight */
500 int autopause; /*!< Auto pause queue members if they fail to answer */
502 /* Queue strategy things */
503 int rrpos; /*!< Round Robin - position */
504 int memberdelay; /*!< Seconds to delay connecting member to caller */
505 int autofill; /*!< Ignore the head call status and ring an available agent */
507 struct ao2_container *members; /*!< Head of the list of members */
508 /*!
509 * \brief Number of members _logged in_
510 * \note There will be members in the members container that are not logged
511 * in, so this can not simply be replaced with ao2_container_count().
513 int membercount;
514 struct queue_ent *head; /*!< Head of the list of callers */
515 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
516 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
519 struct rule_list {
520 char name[80];
521 AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
522 AST_LIST_ENTRY(rule_list) list;
525 AST_LIST_HEAD_STATIC(rule_lists, rule_list);
527 static struct ao2_container *queues;
529 static void update_realtime_members(struct call_queue *q);
530 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
532 /*! \brief sets the QUEUESTATUS channel variable */
533 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
535 int i;
537 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
538 if (queue_results[i].id == res) {
539 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
540 return;
545 static const char *int2strat(int strategy)
547 int x;
549 for (x = 0; x < ARRAY_LEN(strategies); x++) {
550 if (strategy == strategies[x].strategy)
551 return strategies[x].name;
554 return "<unknown>";
557 static int strat2int(const char *strategy)
559 int x;
561 for (x = 0; x < ARRAY_LEN(strategies); x++) {
562 if (!strcasecmp(strategy, strategies[x].name))
563 return strategies[x].strategy;
566 return -1;
569 static int queue_hash_cb(const void *obj, const int flags)
571 const struct call_queue *q = obj;
572 return ast_str_hash(q->name);
575 static int queue_cmp_cb(void *obj, void *arg, int flags)
577 struct call_queue *q = obj, *q2 = arg;
578 return !strcasecmp(q->name, q2->name) ? CMP_MATCH : 0;
581 static inline struct call_queue *queue_ref(struct call_queue *q)
583 ao2_ref(q, 1);
584 return q;
587 static inline struct call_queue *queue_unref(struct call_queue *q)
589 ao2_ref(q, -1);
590 return q;
593 /*! \brief Set variables of queue */
594 static void set_queue_variables(struct queue_ent *qe)
596 char interfacevar[256]="";
597 float sl = 0;
599 if (qe->parent->setqueuevar) {
600 sl = 0;
601 if (qe->parent->callscompleted > 0)
602 sl = 100 * ((float) qe->parent->callscompletedinsl / (float) qe->parent->callscompleted);
604 snprintf(interfacevar, sizeof(interfacevar),
605 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
606 qe->parent->name, qe->parent->maxlen, int2strat(qe->parent->strategy), qe->parent->count, qe->parent->holdtime, qe->parent->callscompleted,
607 qe->parent->callsabandoned, qe->parent->servicelevel, sl);
609 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
613 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
614 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
616 struct queue_ent *cur;
618 if (!q || !new)
619 return;
620 if (prev) {
621 cur = prev->next;
622 prev->next = new;
623 } else {
624 cur = q->head;
625 q->head = new;
627 new->next = cur;
628 new->parent = q;
629 new->pos = ++(*pos);
630 new->opos = *pos;
633 enum queue_member_status {
634 QUEUE_NO_MEMBERS,
635 QUEUE_NO_REACHABLE_MEMBERS,
636 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS,
637 QUEUE_NORMAL
640 /*! \brief Check if members are available
642 * This function checks to see if members are available to be called. If any member
643 * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
644 * the appropriate reason why is returned
646 static enum queue_member_status get_member_status(struct call_queue *q, int max_penalty, int min_penalty)
648 struct member *member;
649 struct ao2_iterator mem_iter;
650 enum queue_member_status result = QUEUE_NO_MEMBERS;
652 ao2_lock(q);
653 mem_iter = ao2_iterator_init(q->members, 0);
654 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
655 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
656 continue;
658 switch (member->status) {
659 case AST_DEVICE_INVALID:
660 /* nothing to do */
661 break;
662 case AST_DEVICE_UNAVAILABLE:
663 if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)
664 result = QUEUE_NO_REACHABLE_MEMBERS;
665 break;
666 default:
667 if (member->paused) {
668 result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
669 } else {
670 ao2_unlock(q);
671 ao2_ref(member, -1);
672 return QUEUE_NORMAL;
674 break;
678 ao2_unlock(q);
679 return result;
682 struct statechange {
683 AST_LIST_ENTRY(statechange) entry;
684 int state;
685 char dev[0];
688 /*! \brief set a member's status based on device state of that member's state_interface.
690 * Lock interface list find sc, iterate through each queues queue_member list for member to
691 * update state inside queues
693 static int update_status(const char *interface, const int status)
695 struct member *cur;
696 struct ao2_iterator mem_iter, queue_iter;
697 struct call_queue *q;
699 queue_iter = ao2_iterator_init(queues, 0);
700 while ((q = ao2_iterator_next(&queue_iter))) {
701 ao2_lock(q);
702 mem_iter = ao2_iterator_init(q->members, 0);
703 while ((cur = ao2_iterator_next(&mem_iter))) {
704 char *tmp_interface;
705 char *slash_pos;
706 tmp_interface = ast_strdupa(cur->state_interface);
707 if ((slash_pos = strchr(interface, '/')))
708 if ((slash_pos = strchr(slash_pos + 1, '/')))
709 *slash_pos = '\0';
711 if (strcasecmp(interface, tmp_interface)) {
712 ao2_ref(cur, -1);
713 continue;
716 if (cur->status != status) {
717 cur->status = status;
718 if (q->maskmemberstatus) {
719 ao2_ref(cur, -1);
720 continue;
723 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
724 "Queue: %s\r\n"
725 "Location: %s\r\n"
726 "MemberName: %s\r\n"
727 "Membership: %s\r\n"
728 "Penalty: %d\r\n"
729 "CallsTaken: %d\r\n"
730 "LastCall: %d\r\n"
731 "Status: %d\r\n"
732 "Paused: %d\r\n",
733 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
734 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
736 ao2_ref(cur, -1);
738 queue_unref(q);
739 ao2_unlock(q);
742 return 0;
745 /*! \brief set a member's status based on device state of that member's interface*/
746 static int handle_statechange(void *datap)
748 struct member_interface *curint;
749 char *loc;
750 char *technology;
751 struct statechange *sc = datap;
753 technology = ast_strdupa(sc->dev);
754 loc = strchr(technology, '/');
755 if (loc) {
756 *loc++ = '\0';
757 } else {
758 ast_free(sc);
759 return 0;
762 AST_LIST_LOCK(&interfaces);
763 AST_LIST_TRAVERSE(&interfaces, curint, list) {
764 char *interface;
765 char *slash_pos;
766 interface = ast_strdupa(curint->interface);
767 if ((slash_pos = strchr(interface, '/')))
768 if ((slash_pos = strchr(slash_pos + 1, '/')))
769 *slash_pos = '\0';
771 if (!strcasecmp(interface, sc->dev))
772 break;
774 AST_LIST_UNLOCK(&interfaces);
776 if (!curint) {
777 if (option_debug > 2)
778 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
779 return 0;
782 if (option_debug)
783 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
785 update_status(sc->dev, sc->state);
786 ast_free(sc);
787 return 0;
790 static void device_state_cb(const struct ast_event *event, void *unused)
792 enum ast_device_state state;
793 const char *device;
794 struct statechange *sc;
795 size_t datapsize;
797 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
798 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
800 if (ast_strlen_zero(device)) {
801 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
802 return;
804 datapsize = sizeof(*sc) + strlen(device) + 1;
805 if (!(sc = ast_calloc(1, datapsize))) {
806 ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
807 return;
809 sc->state = state;
810 strcpy(sc->dev, device);
811 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
812 ast_free(sc);
816 /*! \brief allocate space for new queue member and set fields based on parameters passed */
817 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
819 struct member *cur;
821 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
822 cur->penalty = penalty;
823 cur->paused = paused;
824 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
825 if (!ast_strlen_zero(state_interface))
826 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
827 else
828 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
829 if (!ast_strlen_zero(membername))
830 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
831 else
832 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
833 if (!strchr(cur->interface, '/'))
834 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
835 cur->status = ast_device_state(cur->state_interface);
838 return cur;
842 static int compress_char(const char c)
844 if (c < 32)
845 return 0;
846 else if (c > 96)
847 return c - 64;
848 else
849 return c - 32;
852 static int member_hash_fn(const void *obj, const int flags)
854 const struct member *mem = obj;
855 const char *chname = strchr(mem->interface, '/');
856 int ret = 0, i;
857 if (!chname)
858 chname = mem->interface;
859 for (i = 0; i < 5 && chname[i]; i++)
860 ret += compress_char(chname[i]) << (i * 6);
861 return ret;
864 static int member_cmp_fn(void *obj1, void *obj2, int flags)
866 struct member *mem1 = obj1, *mem2 = obj2;
867 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
870 /*!
871 * \brief Initialize Queue default values.
872 * \note the queue's lock must be held before executing this function
874 static void init_queue(struct call_queue *q)
876 int i;
877 struct penalty_rule *pr_iter;
879 q->dead = 0;
880 q->retry = DEFAULT_RETRY;
881 q->timeout = -1;
882 q->maxlen = 0;
883 q->announcefrequency = 0;
884 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
885 q->announceholdtime = 1;
886 q->announcepositionlimit = 10; /* Default 10 positions */
887 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
888 q->roundingseconds = 0; /* Default - don't announce seconds */
889 q->servicelevel = 0;
890 q->ringinuse = 1;
891 q->setinterfacevar = 0;
892 q->setqueuevar = 0;
893 q->setqueueentryvar = 0;
894 q->autofill = autofill_default;
895 q->montype = montype_default;
896 q->monfmt[0] = '\0';
897 q->reportholdtime = 0;
898 q->wrapuptime = 0;
899 q->joinempty = 0;
900 q->leavewhenempty = 0;
901 q->memberdelay = 0;
902 q->maskmemberstatus = 0;
903 q->eventwhencalled = 0;
904 q->weight = 0;
905 q->timeoutrestart = 0;
906 q->periodicannouncefrequency = 0;
907 q->randomperiodicannounce = 0;
908 q->numperiodicannounce = 0;
909 if (!q->members) {
910 if (q->strategy == QUEUE_STRATEGY_LINEAR)
911 /* linear strategy depends on order, so we have to place all members in a single bucket */
912 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
913 else
914 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
916 q->membercount = 0;
917 q->found = 1;
919 ast_string_field_set(q, sound_next, "queue-youarenext");
920 ast_string_field_set(q, sound_thereare, "queue-thereare");
921 ast_string_field_set(q, sound_calls, "queue-callswaiting");
922 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
923 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
924 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
925 ast_string_field_set(q, sound_minutes, "queue-minutes");
926 ast_string_field_set(q, sound_minute, "queue-minute");
927 ast_string_field_set(q, sound_seconds, "queue-seconds");
928 ast_string_field_set(q, sound_thanks, "queue-thankyou");
929 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
931 if ((q->sound_periodicannounce[0] = ast_str_create(32)))
932 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
934 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
935 if (q->sound_periodicannounce[i])
936 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
939 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
940 ast_free(pr_iter);
943 static void clear_queue(struct call_queue *q)
945 q->holdtime = 0;
946 q->callscompleted = 0;
947 q->callsabandoned = 0;
948 q->callscompletedinsl = 0;
949 q->wrapuptime = 0;
952 static int add_to_interfaces(const char *interface)
954 struct member_interface *curint;
956 AST_LIST_LOCK(&interfaces);
957 AST_LIST_TRAVERSE(&interfaces, curint, list) {
958 if (!strcasecmp(curint->interface, interface))
959 break;
962 if (curint) {
963 AST_LIST_UNLOCK(&interfaces);
964 return 0;
967 ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
969 if ((curint = ast_calloc(1, sizeof(*curint)))) {
970 ast_copy_string(curint->interface, interface, sizeof(curint->interface));
971 AST_LIST_INSERT_HEAD(&interfaces, curint, list);
973 AST_LIST_UNLOCK(&interfaces);
975 return 0;
978 static int interface_exists_global(const char *interface)
980 struct call_queue *q;
981 struct member *mem, tmpmem;
982 struct ao2_iterator queue_iter, mem_iter;
983 int ret = 0;
985 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
986 queue_iter = ao2_iterator_init(queues, 0);
987 while ((q = ao2_iterator_next(&queue_iter))) {
988 ao2_lock(q);
989 mem_iter = ao2_iterator_init(q->members, 0);
990 while ((mem = ao2_iterator_next(&mem_iter))) {
991 if (!strcasecmp(mem->state_interface, interface)) {
992 ao2_ref(mem, -1);
993 ret = 1;
994 break;
997 ao2_unlock(q);
998 queue_unref(q);
1001 return ret;
1004 static int remove_from_interfaces(const char *interface)
1006 struct member_interface *curint;
1008 if (interface_exists_global(interface))
1009 return 0;
1011 AST_LIST_LOCK(&interfaces);
1012 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
1013 if (!strcasecmp(curint->interface, interface)) {
1014 ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
1015 AST_LIST_REMOVE_CURRENT(list);
1016 ast_free(curint);
1017 break;
1020 AST_LIST_TRAVERSE_SAFE_END;
1021 AST_LIST_UNLOCK(&interfaces);
1023 return 0;
1026 static void clear_and_free_interfaces(void)
1028 struct member_interface *curint;
1030 AST_LIST_LOCK(&interfaces);
1031 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
1032 ast_free(curint);
1033 AST_LIST_UNLOCK(&interfaces);
1036 /*!
1037 * \brief Change queue penalty by adding rule.
1039 * Check rule for errors with time or fomatting, see if rule is relative to rest
1040 * of queue, iterate list of rules to find correct insertion point, insert and return.
1041 * \retval -1 on failure
1042 * \retval 0 on success
1043 * \note Call this with the rule_lists locked
1045 static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
1047 char *timestr, *maxstr, *minstr, *contentdup;
1048 struct penalty_rule *rule = NULL, *rule_iter;
1049 struct rule_list *rl_iter;
1050 int time, inserted = 0;
1052 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
1053 ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
1054 return -1;
1057 contentdup = ast_strdupa(content);
1059 if (!(maxstr = strchr(contentdup, ','))) {
1060 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
1061 ast_free(rule);
1062 return -1;
1065 *maxstr++ = '\0';
1066 timestr = contentdup;
1068 if ((time = atoi(timestr)) < 0) {
1069 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
1070 ast_free(rule);
1071 return -1;
1074 rule->time = time;
1076 if ((minstr = strchr(maxstr,',')))
1077 *minstr++ = '\0';
1079 /* The last check will evaluate true if either no penalty change is indicated for a given rule
1080 * OR if a min penalty change is indicated but no max penalty change is */
1081 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
1082 rule->max_relative = 1;
1085 rule->max_value = atoi(maxstr);
1087 if (!ast_strlen_zero(minstr)) {
1088 if (*minstr == '+' || *minstr == '-')
1089 rule->min_relative = 1;
1090 rule->min_value = atoi(minstr);
1091 } else /*there was no minimum specified, so assume this means no change*/
1092 rule->min_relative = 1;
1094 /*We have the rule made, now we need to insert it where it belongs*/
1095 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
1096 if (strcasecmp(rl_iter->name, list_name))
1097 continue;
1099 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
1100 if (rule->time < rule_iter->time) {
1101 AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
1102 inserted = 1;
1103 break;
1106 AST_LIST_TRAVERSE_SAFE_END;
1108 if (!inserted) {
1109 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
1113 return 0;
1116 /*! \brief Configure a queue parameter.
1118 * The failunknown flag is set for config files (and static realtime) to show
1119 * errors for unknown parameters. It is cleared for dynamic realtime to allow
1120 * extra fields in the tables.
1121 * \note For error reporting, line number is passed for .conf static configuration,
1122 * for Realtime queues, linenum is -1.
1124 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
1126 if (!strcasecmp(param, "musicclass") ||
1127 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
1128 ast_string_field_set(q, moh, val);
1129 } else if (!strcasecmp(param, "announce")) {
1130 ast_string_field_set(q, announce, val);
1131 } else if (!strcasecmp(param, "context")) {
1132 ast_string_field_set(q, context, val);
1133 } else if (!strcasecmp(param, "timeout")) {
1134 q->timeout = atoi(val);
1135 if (q->timeout < 0)
1136 q->timeout = DEFAULT_TIMEOUT;
1137 } else if (!strcasecmp(param, "ringinuse")) {
1138 q->ringinuse = ast_true(val);
1139 } else if (!strcasecmp(param, "setinterfacevar")) {
1140 q->setinterfacevar = ast_true(val);
1141 } else if (!strcasecmp(param, "setqueuevar")) {
1142 q->setqueuevar = ast_true(val);
1143 } else if (!strcasecmp(param, "setqueueentryvar")) {
1144 q->setqueueentryvar = ast_true(val);
1145 } else if (!strcasecmp(param, "monitor-format")) {
1146 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
1147 } else if (!strcasecmp(param, "membermacro")) {
1148 ast_string_field_set(q, membermacro, val);
1149 } else if (!strcasecmp(param, "membergosub")) {
1150 ast_string_field_set(q, membergosub, val);
1151 } else if (!strcasecmp(param, "queue-youarenext")) {
1152 ast_string_field_set(q, sound_next, val);
1153 } else if (!strcasecmp(param, "queue-thereare")) {
1154 ast_string_field_set(q, sound_thereare, val);
1155 } else if (!strcasecmp(param, "queue-callswaiting")) {
1156 ast_string_field_set(q, sound_calls, val);
1157 } else if (!strcasecmp(param, "queue-quantity1")) {
1158 ast_string_field_set(q, queue_quantity1, val);
1159 } else if (!strcasecmp(param, "queue-quantity2")) {
1160 ast_string_field_set(q, queue_quantity2, val);
1161 } else if (!strcasecmp(param, "queue-holdtime")) {
1162 ast_string_field_set(q, sound_holdtime, val);
1163 } else if (!strcasecmp(param, "queue-minutes")) {
1164 ast_string_field_set(q, sound_minutes, val);
1165 } else if (!strcasecmp(param, "queue-minute")) {
1166 ast_string_field_set(q, sound_minute, val);
1167 } else if (!strcasecmp(param, "queue-seconds")) {
1168 ast_string_field_set(q, sound_seconds, val);
1169 } else if (!strcasecmp(param, "queue-thankyou")) {
1170 ast_string_field_set(q, sound_thanks, val);
1171 } else if (!strcasecmp(param, "queue-callerannounce")) {
1172 ast_string_field_set(q, sound_callerannounce, val);
1173 } else if (!strcasecmp(param, "queue-reporthold")) {
1174 ast_string_field_set(q, sound_reporthold, val);
1175 } else if (!strcasecmp(param, "announce-frequency")) {
1176 q->announcefrequency = atoi(val);
1177 } else if (!strcasecmp(param, "min-announce-frequency")) {
1178 q->minannouncefrequency = atoi(val);
1179 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
1180 } else if (!strcasecmp(param, "announce-round-seconds")) {
1181 q->roundingseconds = atoi(val);
1182 /* Rounding to any other values just doesn't make sense... */
1183 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
1184 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
1185 if (linenum >= 0) {
1186 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1187 "using 0 instead for queue '%s' at line %d of queues.conf\n",
1188 val, param, q->name, linenum);
1189 } else {
1190 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1191 "using 0 instead for queue '%s'\n", val, param, q->name);
1193 q->roundingseconds=0;
1195 } else if (!strcasecmp(param, "announce-holdtime")) {
1196 if (!strcasecmp(val, "once"))
1197 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
1198 else if (ast_true(val))
1199 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
1200 else
1201 q->announceholdtime = 0;
1202 } else if (!strcasecmp(param, "announce-position")) {
1203 if (!strcasecmp(val, "limit"))
1204 q->announceposition = ANNOUNCEPOSITION_LIMIT;
1205 else if (!strcasecmp(val, "more"))
1206 q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
1207 else if (ast_true(val))
1208 q->announceposition = ANNOUNCEPOSITION_YES;
1209 else
1210 q->announceposition = ANNOUNCEPOSITION_NO;
1211 } else if (!strcasecmp(param, "announce-position-limit")) {
1212 q->announcepositionlimit = atoi(val);
1213 } else if (!strcasecmp(param, "periodic-announce")) {
1214 if (strchr(val, ',')) {
1215 char *s, *buf = ast_strdupa(val);
1216 unsigned int i = 0;
1218 while ((s = strsep(&buf, ",|"))) {
1219 if (!q->sound_periodicannounce[i])
1220 q->sound_periodicannounce[i] = ast_str_create(16);
1221 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
1222 i++;
1223 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
1224 break;
1226 q->numperiodicannounce = i;
1227 } else {
1228 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
1229 q->numperiodicannounce = 1;
1231 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
1232 q->periodicannouncefrequency = atoi(val);
1233 } else if (!strcasecmp(param, "random-periodic-announce")) {
1234 q->randomperiodicannounce = ast_true(val);
1235 } else if (!strcasecmp(param, "retry")) {
1236 q->retry = atoi(val);
1237 if (q->retry <= 0)
1238 q->retry = DEFAULT_RETRY;
1239 } else if (!strcasecmp(param, "wrapuptime")) {
1240 q->wrapuptime = atoi(val);
1241 } else if (!strcasecmp(param, "autofill")) {
1242 q->autofill = ast_true(val);
1243 } else if (!strcasecmp(param, "monitor-type")) {
1244 if (!strcasecmp(val, "mixmonitor"))
1245 q->montype = 1;
1246 } else if (!strcasecmp(param, "autopause")) {
1247 q->autopause = ast_true(val);
1248 } else if (!strcasecmp(param, "maxlen")) {
1249 q->maxlen = atoi(val);
1250 if (q->maxlen < 0)
1251 q->maxlen = 0;
1252 } else if (!strcasecmp(param, "servicelevel")) {
1253 q->servicelevel= atoi(val);
1254 } else if (!strcasecmp(param, "strategy")) {
1255 /* We already have set this, no need to do it again */
1256 return;
1257 } else if (!strcasecmp(param, "joinempty")) {
1258 if (!strcasecmp(val, "loose"))
1259 q->joinempty = QUEUE_EMPTY_LOOSE;
1260 else if (!strcasecmp(val, "strict"))
1261 q->joinempty = QUEUE_EMPTY_STRICT;
1262 else if (ast_true(val))
1263 q->joinempty = QUEUE_EMPTY_NORMAL;
1264 else
1265 q->joinempty = 0;
1266 } else if (!strcasecmp(param, "leavewhenempty")) {
1267 if (!strcasecmp(val, "loose"))
1268 q->leavewhenempty = QUEUE_EMPTY_LOOSE;
1269 else if (!strcasecmp(val, "strict"))
1270 q->leavewhenempty = QUEUE_EMPTY_STRICT;
1271 else if (ast_true(val))
1272 q->leavewhenempty = QUEUE_EMPTY_NORMAL;
1273 else
1274 q->leavewhenempty = 0;
1275 } else if (!strcasecmp(param, "eventmemberstatus")) {
1276 q->maskmemberstatus = !ast_true(val);
1277 } else if (!strcasecmp(param, "eventwhencalled")) {
1278 if (!strcasecmp(val, "vars")) {
1279 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
1280 } else {
1281 q->eventwhencalled = ast_true(val) ? 1 : 0;
1283 } else if (!strcasecmp(param, "reportholdtime")) {
1284 q->reportholdtime = ast_true(val);
1285 } else if (!strcasecmp(param, "memberdelay")) {
1286 q->memberdelay = atoi(val);
1287 } else if (!strcasecmp(param, "weight")) {
1288 q->weight = atoi(val);
1289 if (q->weight)
1290 use_weight++;
1291 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1292 we will not see any effect on use_weight until next reload. */
1293 } else if (!strcasecmp(param, "timeoutrestart")) {
1294 q->timeoutrestart = ast_true(val);
1295 } else if (!strcasecmp(param, "defaultrule")) {
1296 ast_string_field_set(q, defaultrule, val);
1297 } else if (failunknown) {
1298 if (linenum >= 0) {
1299 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1300 q->name, param, linenum);
1301 } else {
1302 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
1308 * \brief Find rt member record to update otherwise create one.
1310 * Search for member in queue, if found update penalty/paused state,
1311 * if no memeber exists create one flag it as a RT member and add to queue member list.
1313 static void rt_handle_member_record(struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char* state_interface)
1315 struct member *m;
1316 struct ao2_iterator mem_iter;
1317 int penalty = 0;
1318 int paused = 0;
1319 int found = 0;
1321 if (penalty_str) {
1322 penalty = atoi(penalty_str);
1323 if (penalty < 0)
1324 penalty = 0;
1327 if (paused_str) {
1328 paused = atoi(paused_str);
1329 if (paused < 0)
1330 paused = 0;
1333 /* Find member by realtime uniqueid and update */
1334 mem_iter = ao2_iterator_init(q->members, 0);
1335 while ((m = ao2_iterator_next(&mem_iter))) {
1336 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
1337 m->dead = 0; /* Do not delete this one. */
1338 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1339 if (paused_str)
1340 m->paused = paused;
1341 if (strcasecmp(state_interface, m->state_interface)) {
1342 remove_from_interfaces(m->state_interface);
1343 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
1344 add_to_interfaces(m->state_interface);
1346 m->penalty = penalty;
1347 found = 1;
1348 ao2_ref(m, -1);
1349 break;
1351 ao2_ref(m, -1);
1354 /* Create a new member */
1355 if (!found) {
1356 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
1357 m->dead = 0;
1358 m->realtime = 1;
1359 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1360 add_to_interfaces(m->state_interface);
1361 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
1362 ao2_link(q->members, m);
1363 ao2_ref(m, -1);
1364 m = NULL;
1365 q->membercount++;
1370 /*! \brief Iterate through queue's member list and delete them */
1371 static void free_members(struct call_queue *q, int all)
1373 /* Free non-dynamic members */
1374 struct member *cur;
1375 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
1377 while ((cur = ao2_iterator_next(&mem_iter))) {
1378 if (all || !cur->dynamic) {
1379 ao2_unlink(q->members, cur);
1380 remove_from_interfaces(cur->state_interface);
1381 q->membercount--;
1383 ao2_ref(cur, -1);
1387 /*! \brief Free queue's member list then its string fields */
1388 static void destroy_queue(void *obj)
1390 struct call_queue *q = obj;
1391 int i;
1393 free_members(q, 1);
1394 ast_string_field_free_memory(q);
1395 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
1396 if (q->sound_periodicannounce[i])
1397 free(q->sound_periodicannounce[i]);
1399 ao2_ref(q->members, -1);
1402 static struct call_queue *alloc_queue(const char *queuename)
1404 struct call_queue *q;
1406 if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
1407 if (ast_string_field_init(q, 64)) {
1408 free(q);
1409 return NULL;
1411 ast_string_field_set(q, name, queuename);
1413 return q;
1417 * \brief Reload a single queue via realtime.
1419 * Check for statically defined queue first, check if deleted RT queue,
1420 * check for new RT queue, if queue vars are not defined init them with defaults.
1421 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
1422 * \retval the queue,
1423 * \retval NULL if it doesn't exist.
1424 * \note Should be called with the "queues" container locked.
1426 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
1428 struct ast_variable *v;
1429 struct call_queue *q, tmpq = {
1430 .name = queuename,
1432 struct member *m;
1433 struct ao2_iterator mem_iter;
1434 char *interface = NULL;
1435 const char *tmp_name;
1436 char *tmp;
1437 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
1439 /* Static queues override realtime. */
1440 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
1441 ao2_lock(q);
1442 if (!q->realtime) {
1443 if (q->dead) {
1444 ao2_unlock(q);
1445 queue_unref(q);
1446 return NULL;
1447 } else {
1448 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
1449 ao2_unlock(q);
1450 return q;
1453 queue_unref(q);
1454 } else if (!member_config)
1455 /* Not found in the list, and it's not realtime ... */
1456 return NULL;
1458 /* Check if queue is defined in realtime. */
1459 if (!queue_vars) {
1460 /* Delete queue from in-core list if it has been deleted in realtime. */
1461 if (q) {
1462 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1463 found condition... So we might delete an in-core queue
1464 in case of DB failure. */
1465 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
1467 q->dead = 1;
1468 /* Delete if unused (else will be deleted when last caller leaves). */
1469 ao2_unlink(queues, q);
1470 ao2_unlock(q);
1471 queue_unref(q);
1473 return NULL;
1476 /* Create a new queue if an in-core entry does not exist yet. */
1477 if (!q) {
1478 struct ast_variable *tmpvar = NULL;
1479 if (!(q = alloc_queue(queuename)))
1480 return NULL;
1481 ao2_lock(q);
1482 clear_queue(q);
1483 q->realtime = 1;
1484 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
1485 * will allocate the members properly
1487 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
1488 if (!strcasecmp(tmpvar->name, "strategy")) {
1489 q->strategy = strat2int(tmpvar->value);
1490 if (q->strategy < 0) {
1491 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1492 tmpvar->value, q->name);
1493 q->strategy = QUEUE_STRATEGY_RINGALL;
1495 break;
1498 /* We traversed all variables and didn't find a strategy */
1499 if (!tmpvar)
1500 q->strategy = QUEUE_STRATEGY_RINGALL;
1501 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
1502 ao2_link(queues, q);
1505 memset(tmpbuf, 0, sizeof(tmpbuf));
1506 for (v = queue_vars; v; v = v->next) {
1507 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1508 if ((tmp = strchr(v->name, '_'))) {
1509 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
1510 tmp_name = tmpbuf;
1511 tmp = tmpbuf;
1512 while ((tmp = strchr(tmp, '_')))
1513 *tmp++ = '-';
1514 } else
1515 tmp_name = v->name;
1517 if (!ast_strlen_zero(v->value)) {
1518 /* Don't want to try to set the option if the value is empty */
1519 queue_set_param(q, tmp_name, v->value, -1, 0);
1523 /* Temporarily set realtime members dead so we can detect deleted ones.
1524 * Also set the membercount correctly for realtime*/
1525 mem_iter = ao2_iterator_init(q->members, 0);
1526 while ((m = ao2_iterator_next(&mem_iter))) {
1527 q->membercount++;
1528 if (m->realtime)
1529 m->dead = 1;
1530 ao2_ref(m, -1);
1533 while ((interface = ast_category_browse(member_config, interface))) {
1534 rt_handle_member_record(q, interface,
1535 ast_variable_retrieve(member_config, interface, "uniqueid"),
1536 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
1537 ast_variable_retrieve(member_config, interface, "penalty"),
1538 ast_variable_retrieve(member_config, interface, "paused"),
1539 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
1542 /* Delete all realtime members that have been deleted in DB. */
1543 mem_iter = ao2_iterator_init(q->members, 0);
1544 while ((m = ao2_iterator_next(&mem_iter))) {
1545 if (m->dead) {
1546 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
1547 ao2_unlink(q->members, m);
1548 remove_from_interfaces(m->state_interface);
1549 q->membercount--;
1551 ao2_ref(m, -1);
1554 ao2_unlock(q);
1556 return q;
1559 static struct call_queue *load_realtime_queue(const char *queuename)
1561 struct ast_variable *queue_vars;
1562 struct ast_config *member_config = NULL;
1563 struct call_queue *q = NULL, tmpq = {
1564 .name = queuename,
1567 /* Find the queue in the in-core list first. */
1568 q = ao2_find(queues, &tmpq, OBJ_POINTER);
1570 if (!q || q->realtime) {
1571 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
1572 queue operations while waiting for the DB.
1574 This will be two separate database transactions, so we might
1575 see queue parameters as they were before another process
1576 changed the queue and member list as it was after the change.
1577 Thus we might see an empty member list when a queue is
1578 deleted. In practise, this is unlikely to cause a problem. */
1580 queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
1581 if (queue_vars) {
1582 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
1583 if (!member_config) {
1584 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
1585 ast_variables_destroy(queue_vars);
1586 return NULL;
1590 ao2_lock(queues);
1591 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
1592 if (member_config)
1593 ast_config_destroy(member_config);
1594 if (queue_vars)
1595 ast_variables_destroy(queue_vars);
1596 ao2_unlock(queues);
1598 } else {
1599 update_realtime_members(q);
1601 return q;
1604 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
1606 int ret = -1;
1608 if (ast_strlen_zero(mem->rt_uniqueid))
1609 return ret;
1611 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, NULL)) > 0)
1612 ret = 0;
1614 return ret;
1618 static void update_realtime_members(struct call_queue *q)
1620 struct ast_config *member_config = NULL;
1621 struct member *m;
1622 char *interface = NULL;
1623 struct ao2_iterator mem_iter;
1625 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
1626 /*This queue doesn't have realtime members*/
1627 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
1628 return;
1631 ao2_lock(q);
1633 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1634 mem_iter = ao2_iterator_init(q->members, 0);
1635 while ((m = ao2_iterator_next(&mem_iter))) {
1636 if (m->realtime)
1637 m->dead = 1;
1638 ao2_ref(m, -1);
1641 while ((interface = ast_category_browse(member_config, interface))) {
1642 rt_handle_member_record(q, interface,
1643 ast_variable_retrieve(member_config, interface, "uniqueid"),
1644 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
1645 ast_variable_retrieve(member_config, interface, "penalty"),
1646 ast_variable_retrieve(member_config, interface, "paused"),
1647 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
1650 /* Delete all realtime members that have been deleted in DB. */
1651 mem_iter = ao2_iterator_init(q->members, 0);
1652 while ((m = ao2_iterator_next(&mem_iter))) {
1653 if (m->dead) {
1654 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
1655 ao2_unlink(q->members, m);
1656 remove_from_interfaces(m->state_interface);
1657 q->membercount--;
1659 ao2_ref(m, -1);
1661 ao2_unlock(q);
1662 ast_config_destroy(member_config);
1665 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
1667 struct call_queue *q;
1668 struct queue_ent *cur, *prev = NULL;
1669 int res = -1;
1670 int pos = 0;
1671 int inserted = 0;
1672 enum queue_member_status stat;
1674 if (!(q = load_realtime_queue(queuename)))
1675 return res;
1677 ao2_lock(queues);
1678 ao2_lock(q);
1680 /* This is our one */
1681 stat = get_member_status(q, qe->max_penalty, qe->min_penalty);
1682 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
1683 *reason = QUEUE_JOINEMPTY;
1684 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
1685 *reason = QUEUE_JOINUNAVAIL;
1686 else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
1687 *reason = QUEUE_JOINUNAVAIL;
1688 else if (q->maxlen && (q->count >= q->maxlen))
1689 *reason = QUEUE_FULL;
1690 else {
1691 /* There's space for us, put us at the right position inside
1692 * the queue.
1693 * Take into account the priority of the calling user */
1694 inserted = 0;
1695 prev = NULL;
1696 cur = q->head;
1697 while (cur) {
1698 /* We have higher priority than the current user, enter
1699 * before him, after all the other users with priority
1700 * higher or equal to our priority. */
1701 if ((!inserted) && (qe->prio > cur->prio)) {
1702 insert_entry(q, prev, qe, &pos);
1703 inserted = 1;
1705 cur->pos = ++pos;
1706 prev = cur;
1707 cur = cur->next;
1709 /* No luck, join at the end of the queue */
1710 if (!inserted)
1711 insert_entry(q, prev, qe, &pos);
1712 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
1713 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
1714 ast_copy_string(qe->context, q->context, sizeof(qe->context));
1715 q->count++;
1716 res = 0;
1717 manager_event(EVENT_FLAG_CALL, "Join",
1718 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1719 qe->chan->name,
1720 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
1721 S_OR(qe->chan->cid.cid_name, "unknown"),
1722 q->name, qe->pos, q->count, qe->chan->uniqueid );
1723 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
1725 ao2_unlock(q);
1726 ao2_unlock(queues);
1728 return res;
1731 static int play_file(struct ast_channel *chan, const char *filename)
1733 int res;
1735 ast_stopstream(chan);
1737 res = ast_streamfile(chan, filename, chan->language);
1738 if (!res)
1739 res = ast_waitstream(chan, AST_DIGIT_ANY);
1741 ast_stopstream(chan);
1743 return res;
1747 * \brief Check for valid exit from queue via goto
1748 * \retval 0 if failure
1749 * \retval 1 if successful
1751 static int valid_exit(struct queue_ent *qe, char digit)
1753 int digitlen = strlen(qe->digits);
1755 /* Prevent possible buffer overflow */
1756 if (digitlen < sizeof(qe->digits) - 2) {
1757 qe->digits[digitlen] = digit;
1758 qe->digits[digitlen + 1] = '\0';
1759 } else {
1760 qe->digits[0] = '\0';
1761 return 0;
1764 /* If there's no context to goto, short-circuit */
1765 if (ast_strlen_zero(qe->context))
1766 return 0;
1768 /* If the extension is bad, then reset the digits to blank */
1769 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
1770 qe->digits[0] = '\0';
1771 return 0;
1774 /* We have an exact match */
1775 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
1776 qe->valid_digits = 1;
1777 /* Return 1 on a successful goto */
1778 return 1;
1781 return 0;
1784 static int say_position(struct queue_ent *qe, int ringing)
1786 int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
1787 time_t now;
1789 /* Let minannouncefrequency seconds pass between the start of each position announcement */
1790 time(&now);
1791 if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
1792 return 0;
1794 /* If either our position has changed, or we are over the freq timer, say position */
1795 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
1796 return 0;
1798 if (ringing) {
1799 ast_indicate(qe->chan,-1);
1800 } else {
1801 ast_moh_stop(qe->chan);
1804 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
1805 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
1806 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
1807 qe->pos <= qe->parent->announcepositionlimit))
1808 announceposition = 1;
1811 if (announceposition == 1) {
1812 /* Say we're next, if we are */
1813 if (qe->pos == 1) {
1814 res = play_file(qe->chan, qe->parent->sound_next);
1815 if (res)
1816 goto playout;
1817 else
1818 goto posout;
1819 } else {
1820 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
1821 /* More than Case*/
1822 res = play_file(qe->chan, qe->parent->queue_quantity1);
1823 if (res)
1824 goto playout;
1825 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
1826 if (res)
1827 goto playout;
1828 } else {
1829 /* Normal Case */
1830 res = play_file(qe->chan, qe->parent->sound_thereare);
1831 if (res)
1832 goto playout;
1833 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
1834 if (res)
1835 goto playout;
1837 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
1838 /* More than Case*/
1839 res = play_file(qe->chan, qe->parent->queue_quantity2);
1840 if (res)
1841 goto playout;
1842 } else {
1843 res = play_file(qe->chan, qe->parent->sound_calls);
1844 if (res)
1845 goto playout;
1849 /* Round hold time to nearest minute */
1850 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
1852 /* If they have specified a rounding then round the seconds as well */
1853 if (qe->parent->roundingseconds) {
1854 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
1855 avgholdsecs *= qe->parent->roundingseconds;
1856 } else {
1857 avgholdsecs = 0;
1860 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
1862 /* If the hold time is >1 min, if it's enabled, and if it's not
1863 supposed to be only once and we have already said it, say it */
1864 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
1865 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
1866 res = play_file(qe->chan, qe->parent->sound_holdtime);
1867 if (res)
1868 goto playout;
1870 if (avgholdmins > 1) {
1871 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
1872 if (res)
1873 goto playout;
1875 if (avgholdmins == 1) {
1876 res = play_file(qe->chan, qe->parent->sound_minute);
1877 if (res)
1878 goto playout;
1879 } else {
1880 res = play_file(qe->chan, qe->parent->sound_minutes);
1881 if (res)
1882 goto playout;
1885 if (avgholdsecs > 1) {
1886 res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
1887 if (res)
1888 goto playout;
1890 res = play_file(qe->chan, qe->parent->sound_seconds);
1891 if (res)
1892 goto playout;
1897 posout:
1898 if (announceposition == 1){
1899 if (qe->parent->announceposition) {
1900 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
1901 qe->chan->name, qe->parent->name, qe->pos);
1903 res = play_file(qe->chan, qe->parent->sound_thanks);
1905 playout:
1906 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
1907 res = 0;
1909 /* Set our last_pos indicators */
1910 qe->last_pos = now;
1911 qe->last_pos_said = qe->pos;
1913 /* Don't restart music on hold if we're about to exit the caller from the queue */
1914 if (!res) {
1915 if (ringing) {
1916 ast_indicate(qe->chan, AST_CONTROL_RINGING);
1917 } else {
1918 ast_moh_start(qe->chan, qe->moh, NULL);
1921 return res;
1924 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
1926 int oldvalue;
1928 /* Calculate holdtime using a recursive boxcar filter */
1929 /* Thanks to SRT for this contribution */
1930 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1932 ao2_lock(qe->parent);
1933 oldvalue = qe->parent->holdtime;
1934 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
1935 ao2_unlock(qe->parent);
1938 /*! \brief Caller leaving queue.
1940 * Search the queue to find the leaving client, if found remove from queue
1941 * create manager event, move others up the queue.
1943 static void leave_queue(struct queue_ent *qe)
1945 struct call_queue *q;
1946 struct queue_ent *cur, *prev = NULL;
1947 struct penalty_rule *pr_iter;
1948 int pos = 0;
1950 if (!(q = qe->parent))
1951 return;
1952 queue_ref(q);
1953 ao2_lock(q);
1955 prev = NULL;
1956 for (cur = q->head; cur; cur = cur->next) {
1957 if (cur == qe) {
1958 q->count--;
1960 /* Take us out of the queue */
1961 manager_event(EVENT_FLAG_CALL, "Leave",
1962 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1963 qe->chan->name, q->name, q->count, qe->chan->uniqueid);
1964 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
1965 /* Take us out of the queue */
1966 if (prev)
1967 prev->next = cur->next;
1968 else
1969 q->head = cur->next;
1970 /* Free penalty rules */
1971 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
1972 ast_free(pr_iter);
1973 } else {
1974 /* Renumber the people after us in the queue based on a new count */
1975 cur->pos = ++pos;
1976 prev = cur;
1979 ao2_unlock(q);
1981 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
1982 if (q->realtime) {
1983 if (!ast_load_realtime("queues", "name", q->name, NULL))
1984 q->dead = 1;
1987 if (q->dead) {
1988 /* It's dead and nobody is in it, so kill it */
1989 ao2_unlink(queues, q);
1990 /* unref the container's reference to the queue */
1991 queue_unref(q);
1993 /* unref the explicit ref earlier in the function */
1994 queue_unref(q);
1997 /*! \brief Hang up a list of outgoing calls */
1998 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception)
2000 struct callattempt *oo;
2002 while (outgoing) {
2003 /* Hangup any existing lines we have open */
2004 if (outgoing->chan && (outgoing->chan != exception))
2005 ast_hangup(outgoing->chan);
2006 oo = outgoing;
2007 outgoing = outgoing->q_next;
2008 if (oo->member)
2009 ao2_ref(oo->member, -1);
2010 ast_free(oo);
2014 /*!
2015 * \brief traverse all defined queues which have calls waiting and contain this member
2016 * \retval 0 if no other queue has precedence (higher weight)
2017 * \retval 1 if found
2019 static int compare_weight(struct call_queue *rq, struct member *member)
2021 struct call_queue *q;
2022 struct member *mem;
2023 int found = 0;
2024 struct ao2_iterator queue_iter;
2026 /* q's lock and rq's lock already set by try_calling()
2027 * to solve deadlock */
2028 queue_iter = ao2_iterator_init(queues, 0);
2029 while ((q = ao2_iterator_next(&queue_iter))) {
2030 if (q == rq) { /* don't check myself, could deadlock */
2031 queue_unref(q);
2032 continue;
2034 ao2_lock(q);
2035 if (q->count && q->members) {
2036 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
2037 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
2038 if (q->weight > rq->weight) {
2039 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);
2040 found = 1;
2042 ao2_ref(mem, -1);
2045 ao2_unlock(q);
2046 if (found) {
2047 queue_unref(q);
2048 break;
2050 queue_unref(q);
2052 return found;
2055 /*! \brief common hangup actions */
2056 static void do_hang(struct callattempt *o)
2058 o->stillgoing = 0;
2059 ast_hangup(o->chan);
2060 o->chan = NULL;
2063 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2064 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
2066 struct ast_str *buf = ast_str_alloca(len + 1);
2067 char *tmp;
2069 if (pbx_builtin_serialize_variables(chan, &buf)) {
2070 int i, j;
2072 /* convert "\n" to "\nVariable: " */
2073 strcpy(vars, "Variable: ");
2074 tmp = buf->str;
2076 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
2077 vars[j] = tmp[i];
2079 if (tmp[i + 1] == '\0')
2080 break;
2081 if (tmp[i] == '\n') {
2082 vars[j++] = '\r';
2083 vars[j++] = '\n';
2085 ast_copy_string(&(vars[j]), "Variable: ", len - j);
2086 j += 9;
2089 if (j > len - 3)
2090 j = len - 3;
2091 vars[j++] = '\r';
2092 vars[j++] = '\n';
2093 vars[j] = '\0';
2094 } else {
2095 /* there are no channel variables; leave it blank */
2096 *vars = '\0';
2098 return vars;
2101 /*!
2102 * \brief Part 2 of ring_one
2104 * Does error checking before attempting to request a channel and call a member.
2105 * This function is only called from ring_one().
2106 * Failure can occur if:
2107 * - Agent on call
2108 * - Agent is paused
2109 * - Wrapup time not expired
2110 * - Priority by another queue
2112 * \retval 1 on success to reach a free agent
2113 * \retval 0 on failure to get agent.
2115 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
2117 int res;
2118 int status;
2119 char tech[256];
2120 char *location;
2121 const char *macrocontext, *macroexten;
2123 /* on entry here, we know that tmp->chan == NULL */
2124 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
2125 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
2126 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
2127 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
2128 if (qe->chan->cdr)
2129 ast_cdr_busy(qe->chan->cdr);
2130 tmp->stillgoing = 0;
2131 (*busies)++;
2132 return 0;
2135 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
2136 ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
2137 if (qe->chan->cdr)
2138 ast_cdr_busy(qe->chan->cdr);
2139 tmp->stillgoing = 0;
2140 return 0;
2143 if (tmp->member->paused) {
2144 ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
2145 if (qe->chan->cdr)
2146 ast_cdr_busy(qe->chan->cdr);
2147 tmp->stillgoing = 0;
2148 return 0;
2150 if (use_weight && compare_weight(qe->parent,tmp->member)) {
2151 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
2152 if (qe->chan->cdr)
2153 ast_cdr_busy(qe->chan->cdr);
2154 tmp->stillgoing = 0;
2155 (*busies)++;
2156 return 0;
2159 ast_copy_string(tech, tmp->interface, sizeof(tech));
2160 if ((location = strchr(tech, '/')))
2161 *location++ = '\0';
2162 else
2163 location = "";
2165 /* Request the peer */
2166 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
2167 if (!tmp->chan) { /* If we can't, just go on to the next call */
2168 if (qe->chan->cdr)
2169 ast_cdr_busy(qe->chan->cdr);
2170 tmp->stillgoing = 0;
2172 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
2174 ao2_lock(qe->parent);
2175 qe->parent->rrpos++;
2176 qe->linpos++;
2177 ao2_unlock(qe->parent);
2180 (*busies)++;
2181 return 0;
2184 tmp->chan->appl = "AppQueue";
2185 tmp->chan->data = "(Outgoing Line)";
2186 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
2187 if (tmp->chan->cid.cid_num)
2188 ast_free(tmp->chan->cid.cid_num);
2189 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
2190 if (tmp->chan->cid.cid_name)
2191 ast_free(tmp->chan->cid.cid_name);
2192 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
2193 if (tmp->chan->cid.cid_ani)
2194 ast_free(tmp->chan->cid.cid_ani);
2195 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
2197 /* Inherit specially named variables from parent channel */
2198 ast_channel_inherit_variables(qe->chan, tmp->chan);
2200 /* Presense of ADSI CPE on outgoing channel follows ours */
2201 tmp->chan->adsicpe = qe->chan->adsicpe;
2203 /* Inherit context and extension */
2204 ast_channel_lock(qe->chan);
2205 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
2206 if (!ast_strlen_zero(macrocontext))
2207 ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
2208 else
2209 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
2210 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
2211 if (!ast_strlen_zero(macroexten))
2212 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
2213 else
2214 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
2215 ast_channel_unlock(qe->chan);
2217 /* Place the call, but don't wait on the answer */
2218 if ((res = ast_call(tmp->chan, location, 0))) {
2219 /* Again, keep going even if there's an error */
2220 ast_debug(1, "ast call on peer returned %d\n", res);
2221 ast_verb(3, "Couldn't call %s\n", tmp->interface);
2222 do_hang(tmp);
2223 (*busies)++;
2224 return 0;
2225 } else if (qe->parent->eventwhencalled) {
2226 char vars[2048];
2228 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
2229 "Queue: %s\r\n"
2230 "AgentCalled: %s\r\n"
2231 "AgentName: %s\r\n"
2232 "ChannelCalling: %s\r\n"
2233 "DestinationChannel: %s\r\n"
2234 "CallerIDNum: %s\r\n"
2235 "CallerIDName: %s\r\n"
2236 "Context: %s\r\n"
2237 "Extension: %s\r\n"
2238 "Priority: %d\r\n"
2239 "Uniqueid: %s\r\n"
2240 "%s",
2241 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
2242 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
2243 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
2244 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
2245 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2246 ast_verb(3, "Called %s\n", tmp->interface);
2249 return 1;
2252 /*! \brief find the entry with the best metric, or NULL */
2253 static struct callattempt *find_best(struct callattempt *outgoing)
2255 struct callattempt *best = NULL, *cur;
2257 for (cur = outgoing; cur; cur = cur->q_next) {
2258 if (cur->stillgoing && /* Not already done */
2259 !cur->chan && /* Isn't already going */
2260 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
2261 best = cur;
2265 return best;
2268 /*!
2269 * \brief Place a call to a queue member.
2271 * Once metrics have been calculated for each member, this function is used
2272 * to place a call to the appropriate member (or members). The low-level
2273 * channel-handling and error detection is handled in ring_entry
2275 * \retval 1 if a member was called successfully
2276 * \retval 0 otherwise
2278 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
2280 int ret = 0;
2282 while (ret == 0) {
2283 struct callattempt *best = find_best(outgoing);
2284 if (!best) {
2285 ast_debug(1, "Nobody left to try ringing in queue\n");
2286 break;
2288 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2289 struct callattempt *cur;
2290 /* Ring everyone who shares this best metric (for ringall) */
2291 for (cur = outgoing; cur; cur = cur->q_next) {
2292 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
2293 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
2294 ret |= ring_entry(qe, cur, busies);
2297 } else {
2298 /* Ring just the best channel */
2299 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
2300 ret = ring_entry(qe, best, busies);
2304 return ret;
2307 /*! \brief Search for best metric and add to Round Robbin queue */
2308 static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
2310 struct callattempt *best = find_best(outgoing);
2312 if (best) {
2313 /* Ring just the best channel */
2314 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
2315 qe->parent->rrpos = best->metric % 1000;
2316 } else {
2317 /* Just increment rrpos */
2318 if (qe->parent->wrapped) {
2319 /* No more channels, start over */
2320 qe->parent->rrpos = 0;
2321 } else {
2322 /* Prioritize next entry */
2323 qe->parent->rrpos++;
2326 qe->parent->wrapped = 0;
2328 return 0;
2331 /*! \brief Search for best metric and add to Linear queue */
2332 static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
2334 struct callattempt *best = find_best(outgoing);
2336 if (best) {
2337 /* Ring just the best channel */
2338 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
2339 qe->linpos = best->metric % 1000;
2340 } else {
2341 /* Just increment rrpos */
2342 if (qe->linwrapped) {
2343 /* No more channels, start over */
2344 qe->linpos = 0;
2345 } else {
2346 /* Prioritize next entry */
2347 qe->linpos++;
2350 qe->linwrapped = 0;
2352 return 0;
2355 /*! \brief Playback announcement to queued members if peroid has elapsed */
2356 static int say_periodic_announcement(struct queue_ent *qe, int ringing)
2358 int res = 0;
2359 time_t now;
2361 /* Get the current time */
2362 time(&now);
2364 /* Check to see if it is time to announce */
2365 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
2366 return 0;
2368 /* Stop the music on hold so we can play our own file */
2369 if (ringing)
2370 ast_indicate(qe->chan,-1);
2371 else
2372 ast_moh_stop(qe->chan);
2374 ast_verb(3, "Playing periodic announcement\n");
2376 if (qe->parent->randomperiodicannounce) {
2377 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
2378 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
2379 ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
2380 qe->last_periodic_announce_sound = 0;
2383 /* play the announcement */
2384 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
2386 if ((res > 0 && !valid_exit(qe, res)) || res < 0)
2387 res = 0;
2389 /* Resume Music on Hold if the caller is going to stay in the queue */
2390 if (!res) {
2391 if (ringing)
2392 ast_indicate(qe->chan, AST_CONTROL_RINGING);
2393 else
2394 ast_moh_start(qe->chan, qe->moh, NULL);
2397 /* update last_periodic_announce_time */
2398 qe->last_periodic_announce_time = now;
2400 /* Update the current periodic announcement to the next announcement */
2401 if (!qe->parent->randomperiodicannounce) {
2402 qe->last_periodic_announce_sound++;
2405 return res;
2408 /*! \brief Record that a caller gave up on waiting in queue */
2409 static void record_abandoned(struct queue_ent *qe)
2411 ao2_lock(qe->parent);
2412 set_queue_variables(qe);
2413 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
2414 "Queue: %s\r\n"
2415 "Uniqueid: %s\r\n"
2416 "Position: %d\r\n"
2417 "OriginalPosition: %d\r\n"
2418 "HoldTime: %d\r\n",
2419 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
2421 qe->parent->callsabandoned++;
2422 ao2_unlock(qe->parent);
2425 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2426 static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
2428 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
2429 if (qe->parent->eventwhencalled)
2430 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
2431 "Queue: %s\r\n"
2432 "Uniqueid: %s\r\n"
2433 "Channel: %s\r\n"
2434 "Member: %s\r\n"
2435 "MemberName: %s\r\n"
2436 "Ringtime: %d\r\n",
2437 qe->parent->name,
2438 qe->chan->uniqueid,
2439 qe->chan->name,
2440 interface,
2441 membername,
2442 rnatime);
2443 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
2444 if (qe->parent->autopause) {
2445 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
2446 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
2447 } else {
2448 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
2451 return;
2454 #define AST_MAX_WATCHERS 256
2455 /*! \brief Wait for a member to answer the call
2457 * \param[in] qe the queue_ent corresponding to the caller in the queue
2458 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
2459 * \param[in] to the amount of time (in milliseconds) to wait for a response
2460 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
2461 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
2462 * \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
2463 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
2465 static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2467 const char *queue = qe->parent->name;
2468 struct callattempt *o, *start = NULL, *prev = NULL;
2469 int status;
2470 int numbusies = prebusies;
2471 int numnochan = 0;
2472 int stillgoing = 0;
2473 int orig = *to;
2474 struct ast_frame *f;
2475 struct callattempt *peer = NULL;
2476 struct ast_channel *winner;
2477 struct ast_channel *in = qe->chan;
2478 char on[80] = "";
2479 char membername[80] = "";
2480 long starttime = 0;
2481 long endtime = 0;
2482 #ifdef HAVE_EPOLL
2483 struct callattempt *epollo;
2484 #endif
2486 starttime = (long) time(NULL);
2487 #ifdef HAVE_EPOLL
2488 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
2489 if (epollo->chan)
2490 ast_poll_channel_add(in, epollo->chan);
2492 #endif
2494 while (*to && !peer) {
2495 int numlines, retry, pos = 1;
2496 struct ast_channel *watchers[AST_MAX_WATCHERS];
2497 watchers[0] = in;
2498 start = NULL;
2500 for (retry = 0; retry < 2; retry++) {
2501 numlines = 0;
2502 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
2503 if (o->stillgoing) { /* Keep track of important channels */
2504 stillgoing = 1;
2505 if (o->chan) {
2506 watchers[pos++] = o->chan;
2507 if (!start)
2508 start = o;
2509 else
2510 prev->call_next = o;
2511 prev = o;
2514 numlines++;
2516 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
2517 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
2518 break;
2519 /* On "ringall" strategy we only move to the next penalty level
2520 when *all* ringing phones are done in the current penalty level */
2521 ring_one(qe, outgoing, &numbusies);
2522 /* and retry... */
2524 if (pos == 1 /* not found */) {
2525 if (numlines == (numbusies + numnochan)) {
2526 ast_debug(1, "Everyone is busy at this time\n");
2527 } else {
2528 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
2530 *to = 0;
2531 return NULL;
2533 winner = ast_waitfor_n(watchers, pos, to);
2534 for (o = start; o; o = o->call_next) {
2535 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) {
2536 if (!peer) {
2537 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
2538 peer = o;
2540 } else if (o->chan && (o->chan == winner)) {
2542 ast_copy_string(on, o->member->interface, sizeof(on));
2543 ast_copy_string(membername, o->member->membername, sizeof(membername));
2545 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
2546 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
2547 numnochan++;
2548 do_hang(o);
2549 winner = NULL;
2550 continue;
2551 } else if (!ast_strlen_zero(o->chan->call_forward)) {
2552 char tmpchan[256];
2553 char *stuff;
2554 char *tech;
2556 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
2557 if ((stuff = strchr(tmpchan, '/'))) {
2558 *stuff++ = '\0';
2559 tech = tmpchan;
2560 } else {
2561 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
2562 stuff = tmpchan;
2563 tech = "Local";
2565 /* Before processing channel, go ahead and check for forwarding */
2566 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
2567 /* Setup parameters */
2568 o->chan = ast_request(tech, in->nativeformats, stuff, &status);
2569 if (!o->chan) {
2570 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
2571 o->stillgoing = 0;
2572 numnochan++;
2573 } else {
2574 ast_channel_inherit_variables(in, o->chan);
2575 ast_channel_datastore_inherit(in, o->chan);
2576 if (o->chan->cid.cid_num)
2577 ast_free(o->chan->cid.cid_num);
2578 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
2580 if (o->chan->cid.cid_name)
2581 ast_free(o->chan->cid.cid_name);
2582 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
2584 ast_string_field_set(o->chan, accountcode, in->accountcode);
2585 o->chan->cdrflags = in->cdrflags;
2587 if (in->cid.cid_ani) {
2588 if (o->chan->cid.cid_ani)
2589 ast_free(o->chan->cid.cid_ani);
2590 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
2592 if (o->chan->cid.cid_rdnis)
2593 ast_free(o->chan->cid.cid_rdnis);
2594 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
2595 if (ast_call(o->chan, tmpchan, 0)) {
2596 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
2597 do_hang(o);
2598 numnochan++;
2601 /* Hangup the original channel now, in case we needed it */
2602 ast_hangup(winner);
2603 continue;
2605 f = ast_read(winner);
2606 if (f) {
2607 if (f->frametype == AST_FRAME_CONTROL) {
2608 switch (f->subclass) {
2609 case AST_CONTROL_ANSWER:
2610 /* This is our guy if someone answered. */
2611 if (!peer) {
2612 ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
2613 peer = o;
2615 break;
2616 case AST_CONTROL_BUSY:
2617 ast_verb(3, "%s is busy\n", o->chan->name);
2618 if (in->cdr)
2619 ast_cdr_busy(in->cdr);
2620 do_hang(o);
2621 endtime = (long) time(NULL);
2622 endtime -= starttime;
2623 rna(endtime*1000, qe, on, membername);
2624 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2625 if (qe->parent->timeoutrestart)
2626 *to = orig;
2627 ring_one(qe, outgoing, &numbusies);
2629 numbusies++;
2630 break;
2631 case AST_CONTROL_CONGESTION:
2632 ast_verb(3, "%s is circuit-busy\n", o->chan->name);
2633 if (in->cdr)
2634 ast_cdr_busy(in->cdr);
2635 endtime = (long) time(NULL);
2636 endtime -= starttime;
2637 rna(endtime*1000, qe, on, membername);
2638 do_hang(o);
2639 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2640 if (qe->parent->timeoutrestart)
2641 *to = orig;
2642 ring_one(qe, outgoing, &numbusies);
2644 numbusies++;
2645 break;
2646 case AST_CONTROL_RINGING:
2647 ast_verb(3, "%s is ringing\n", o->chan->name);
2648 break;
2649 case AST_CONTROL_OFFHOOK:
2650 /* Ignore going off hook */
2651 break;
2652 default:
2653 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
2656 ast_frfree(f);
2657 } else {
2658 endtime = (long) time(NULL) - starttime;
2659 rna(endtime * 1000, qe, on, membername);
2660 do_hang(o);
2661 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2662 if (qe->parent->timeoutrestart)
2663 *to = orig;
2664 ring_one(qe, outgoing, &numbusies);
2669 if (winner == in) {
2670 f = ast_read(in);
2671 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
2672 /* Got hung up */
2673 *to = -1;
2674 if (f) {
2675 if (f->data.uint32) {
2676 in->hangupcause = f->data.uint32;
2678 ast_frfree(f);
2680 return NULL;
2682 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
2683 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
2684 *to = 0;
2685 ast_frfree(f);
2686 return NULL;
2688 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
2689 ast_verb(3, "User pressed digit: %c\n", f->subclass);
2690 *to = 0;
2691 *digit = f->subclass;
2692 ast_frfree(f);
2693 return NULL;
2695 ast_frfree(f);
2697 if (!*to) {
2698 for (o = start; o; o = o->call_next)
2699 rna(orig, qe, o->interface, o->member->membername);
2703 #ifdef HAVE_EPOLL
2704 for (epollo = outgoing; epollo; epollo = epollo->q_next) {
2705 if (epollo->chan)
2706 ast_poll_channel_del(in, epollo->chan);
2708 #endif
2710 return peer;
2713 /*!
2714 * \brief Check if we should start attempting to call queue members.
2716 * The behavior of this function is dependent first on whether autofill is enabled
2717 * and second on whether the ring strategy is ringall. If autofill is not enabled,
2718 * then return true if we're the head of the queue. If autofill is enabled, then
2719 * we count the available members and see if the number of available members is enough
2720 * that given our position in the queue, we would theoretically be able to connect to
2721 * one of those available members
2723 static int is_our_turn(struct queue_ent *qe)
2725 struct queue_ent *ch;
2726 struct member *cur;
2727 int avl = 0;
2728 int idx = 0;
2729 int res;
2731 if (!qe->parent->autofill) {
2732 /* Atomically read the parent head -- does not need a lock */
2733 ch = qe->parent->head;
2734 /* If we are now at the top of the head, break out */
2735 if (ch == qe) {
2736 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
2737 res = 1;
2738 } else {
2739 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
2740 res = 0;
2743 } else {
2744 /* This needs a lock. How many members are available to be served? */
2745 ao2_lock(qe->parent);
2747 ch = qe->parent->head;
2749 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
2750 ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
2751 avl = 1;
2752 } else {
2753 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
2754 while ((cur = ao2_iterator_next(&mem_iter))) {
2755 switch (cur->status) {
2756 case AST_DEVICE_INUSE:
2757 if (!qe->parent->ringinuse)
2758 break;
2759 /* else fall through */
2760 case AST_DEVICE_NOT_INUSE:
2761 case AST_DEVICE_UNKNOWN:
2762 if (!cur->paused)
2763 avl++;
2764 break;
2766 ao2_ref(cur, -1);
2770 ast_debug(1, "There are %d available members.\n", avl);
2772 while ((idx < avl) && (ch) && (ch != qe)) {
2773 if (!ch->pending)
2774 idx++;
2775 ch = ch->next;
2778 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2779 if (ch && idx < avl) {
2780 ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
2781 res = 1;
2782 } else {
2783 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
2784 res = 0;
2787 ao2_unlock(qe->parent);
2790 return res;
2794 * \brief update rules for queues
2796 * Calculate min/max penalties making sure if relative they stay within bounds.
2797 * Update queues penalty and set dialplan vars, goto next list entry.
2799 static void update_qe_rule(struct queue_ent *qe)
2801 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
2802 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
2803 char max_penalty_str[20], min_penalty_str[20];
2804 /* a relative change to the penalty could put it below 0 */
2805 if (max_penalty < 0)
2806 max_penalty = 0;
2807 if (min_penalty < 0)
2808 min_penalty = 0;
2809 if (min_penalty > max_penalty)
2810 min_penalty = max_penalty;
2811 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
2812 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
2813 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
2814 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
2815 qe->max_penalty = max_penalty;
2816 qe->min_penalty = min_penalty;
2817 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);
2818 qe->pr = AST_LIST_NEXT(qe->pr, list);
2821 /*! \brief The waiting areas for callers who are not actively calling members
2823 * This function is one large loop. This function will return if a caller
2824 * either exits the queue or it becomes that caller's turn to attempt calling
2825 * queue members. Inside the loop, we service the caller with periodic announcements,
2826 * holdtime announcements, etc. as configured in queues.conf
2828 * \retval 0 if the caller's turn has arrived
2829 * \retval -1 if the caller should exit the queue.
2831 static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
2833 int res = 0;
2835 /* This is the holding pen for callers 2 through maxlen */
2836 for (;;) {
2837 enum queue_member_status stat;
2839 if (is_our_turn(qe))
2840 break;
2842 /* If we have timed out, break out */
2843 if (qe->expire && (time(NULL) > qe->expire)) {
2844 *reason = QUEUE_TIMEOUT;
2845 break;
2848 stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
2850 /* leave the queue if no agents, if enabled */
2851 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
2852 *reason = QUEUE_LEAVEEMPTY;
2853 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2854 leave_queue(qe);
2855 break;
2858 /* leave the queue if no reachable agents, if enabled */
2859 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
2860 *reason = QUEUE_LEAVEUNAVAIL;
2861 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2862 leave_queue(qe);
2863 break;
2865 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
2866 *reason = QUEUE_LEAVEUNAVAIL;
2867 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
2868 leave_queue(qe);
2869 break;
2872 /* Make a position announcement, if enabled */
2873 if (qe->parent->announcefrequency &&
2874 (res = say_position(qe,ringing)))
2875 break;
2877 /* Make a periodic announcement, if enabled */
2878 if (qe->parent->periodicannouncefrequency &&
2879 (res = say_periodic_announcement(qe,ringing)))
2880 break;
2882 /* see if we need to move to the next penalty level for this queue */
2883 while (qe->pr && ((time(NULL) - qe->start) > qe->pr->time)) {
2884 update_qe_rule(qe);
2887 /* Wait a second before checking again */
2888 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
2889 if (res > 0 && !valid_exit(qe, res))
2890 res = 0;
2891 else
2892 break;
2896 return res;
2900 * \brief update the queue status
2901 * \retval Always 0
2903 static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl)
2905 struct member *mem;
2906 struct call_queue *qtmp;
2907 struct ao2_iterator queue_iter;
2909 if (shared_lastcall) {
2910 queue_iter = ao2_iterator_init(queues, 0);
2911 while ((qtmp = ao2_iterator_next(&queue_iter))) {
2912 ao2_lock(qtmp);
2913 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
2914 time(&mem->lastcall);
2915 mem->calls++;
2916 mem->lastqueue = q;
2917 ao2_ref(mem, -1);
2919 ao2_unlock(qtmp);
2920 ao2_ref(qtmp, -1);
2922 } else {
2923 ao2_lock(q);
2924 time(&member->lastcall);
2925 member->calls++;
2926 member->lastqueue = q;
2927 ao2_unlock(q);
2929 ao2_lock(q);
2930 q->callscompleted++;
2931 if (callcompletedinsl)
2932 q->callscompletedinsl++;
2933 ao2_unlock(q);
2934 return 0;
2937 /*! \brief Calculate the metric of each member in the outgoing callattempts
2939 * A numeric metric is given to each member depending on the ring strategy used
2940 * by the queue. Members with lower metrics will be called before members with
2941 * higher metrics
2942 * \retval -1 if penalties are exceeded
2943 * \retval 0 otherwise
2945 static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
2947 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
2948 return -1;
2950 switch (q->strategy) {
2951 case QUEUE_STRATEGY_RINGALL:
2952 /* Everyone equal, except for penalty */
2953 tmp->metric = mem->penalty * 1000000;
2954 break;
2955 case QUEUE_STRATEGY_LINEAR:
2956 if (pos < qe->linpos) {
2957 tmp->metric = 1000 + pos;
2958 } else {
2959 if (pos > qe->linpos)
2960 /* Indicate there is another priority */
2961 qe->linwrapped = 1;
2962 tmp->metric = pos;
2964 tmp->metric += mem->penalty * 1000000;
2965 break;
2966 case QUEUE_STRATEGY_RRMEMORY:
2967 if (pos < q->rrpos) {
2968 tmp->metric = 1000 + pos;
2969 } else {
2970 if (pos > q->rrpos)
2971 /* Indicate there is another priority */
2972 q->wrapped = 1;
2973 tmp->metric = pos;
2975 tmp->metric += mem->penalty * 1000000;
2976 break;
2977 case QUEUE_STRATEGY_RANDOM:
2978 tmp->metric = ast_random() % 1000;
2979 tmp->metric += mem->penalty * 1000000;
2980 break;
2981 case QUEUE_STRATEGY_WRANDOM:
2982 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
2983 break;
2984 case QUEUE_STRATEGY_FEWESTCALLS:
2985 tmp->metric = mem->calls;
2986 tmp->metric += mem->penalty * 1000000;
2987 break;
2988 case QUEUE_STRATEGY_LEASTRECENT:
2989 if (!mem->lastcall)
2990 tmp->metric = 0;
2991 else
2992 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
2993 tmp->metric += mem->penalty * 1000000;
2994 break;
2995 default:
2996 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
2997 break;
2999 return 0;
3002 enum agent_complete_reason {
3003 CALLER,
3004 AGENT,
3005 TRANSFER
3008 /*! \brief Send out AMI message with member call completion status information */
3009 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
3010 const struct ast_channel *peer, const struct member *member, time_t callstart,
3011 char *vars, size_t vars_len, enum agent_complete_reason rsn)
3013 const char *reason = NULL; /* silence dumb compilers */
3015 if (!qe->parent->eventwhencalled)
3016 return;
3018 switch (rsn) {
3019 case CALLER:
3020 reason = "caller";
3021 break;
3022 case AGENT:
3023 reason = "agent";
3024 break;
3025 case TRANSFER:
3026 reason = "transfer";
3027 break;
3030 manager_event(EVENT_FLAG_AGENT, "AgentComplete",
3031 "Queue: %s\r\n"
3032 "Uniqueid: %s\r\n"
3033 "Channel: %s\r\n"
3034 "Member: %s\r\n"
3035 "MemberName: %s\r\n"
3036 "HoldTime: %ld\r\n"
3037 "TalkTime: %ld\r\n"
3038 "Reason: %s\r\n"
3039 "%s",
3040 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3041 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
3042 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
3045 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
3047 * Here is the process of this function
3048 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
3049 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
3050 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
3051 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
3052 * during each iteration, we call calc_metric to determine which members should be rung when.
3053 * 3. Call ring_one to place a call to the appropriate member(s)
3054 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
3055 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
3056 * 6. Start the monitor or mixmonitor if the option is set
3057 * 7. Remove the caller from the queue to allow other callers to advance
3058 * 8. Bridge the call.
3059 * 9. Do any post processing after the call has disconnected.
3061 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
3062 * \param[in] options the options passed as the third parameter to the Queue() application
3063 * \param[in] announceoverride filename to play to user when waiting
3064 * \param[in] url the url passed as the fourth parameter to the Queue() application
3065 * \param[in,out] tries the number of times we have tried calling queue members
3066 * \param[out] noption set if the call to Queue() has the 'n' option set.
3067 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
3068 * \param[in] macro the macro passed as the sixth parameter to the Queue() application
3069 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
3070 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
3072 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)
3074 struct member *cur;
3075 struct callattempt *outgoing = NULL; /* the list of calls we are building */
3076 int to, orig;
3077 char oldexten[AST_MAX_EXTENSION]="";
3078 char oldcontext[AST_MAX_CONTEXT]="";
3079 char queuename[256]="";
3080 char interfacevar[256]="";
3081 struct ast_channel *peer;
3082 struct ast_channel *which;
3083 struct callattempt *lpeer;
3084 struct member *member;
3085 struct ast_app *app;
3086 int res = 0, bridge = 0;
3087 int numbusies = 0;
3088 int x=0;
3089 char *announce = NULL;
3090 char digit = 0;
3091 time_t callstart;
3092 time_t now = time(NULL);
3093 struct ast_bridge_config bridge_config;
3094 char nondataquality = 1;
3095 char *agiexec = NULL;
3096 char *macroexec = NULL;
3097 char *gosubexec = NULL;
3098 int ret = 0;
3099 const char *monitorfilename;
3100 const char *monitor_exec;
3101 const char *monitor_options;
3102 char tmpid[256], tmpid2[256];
3103 char meid[1024], meid2[1024];
3104 char mixmonargs[1512];
3105 struct ast_app *mixmonapp = NULL;
3106 char *p;
3107 char vars[2048];
3108 int forwardsallowed = 1;
3109 int callcompletedinsl;
3110 struct ao2_iterator memi;
3111 struct ast_datastore *datastore;
3113 ast_channel_lock(qe->chan);
3114 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
3115 ast_channel_unlock(qe->chan);
3117 memset(&bridge_config, 0, sizeof(bridge_config));
3118 tmpid[0] = 0;
3119 meid[0] = 0;
3120 time(&now);
3122 for (; options && *options; options++)
3123 switch (*options) {
3124 case 't':
3125 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
3126 break;
3127 case 'T':
3128 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
3129 break;
3130 case 'w':
3131 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
3132 break;
3133 case 'W':
3134 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
3135 break;
3136 case 'd':
3137 nondataquality = 0;
3138 break;
3139 case 'h':
3140 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
3141 break;
3142 case 'H':
3143 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
3144 break;
3145 case 'k':
3146 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
3147 break;
3148 case 'K':
3149 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
3150 break;
3151 case 'n':
3152 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
3153 (*tries)++;
3154 else
3155 *tries = qe->parent->membercount;
3156 *noption = 1;
3157 break;
3158 case 'i':
3159 forwardsallowed = 0;
3160 break;
3161 case 'x':
3162 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
3163 break;
3164 case 'X':
3165 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
3166 break;
3170 /* Hold the lock while we setup the outgoing calls */
3171 if (use_weight)
3172 ao2_lock(queues);
3173 ao2_lock(qe->parent);
3174 ast_debug(1, "%s is trying to call a queue member.\n",
3175 qe->chan->name);
3176 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
3177 if (!ast_strlen_zero(qe->announce))
3178 announce = qe->announce;
3179 if (!ast_strlen_zero(announceoverride))
3180 announce = announceoverride;
3182 memi = ao2_iterator_init(qe->parent->members, 0);
3183 while ((cur = ao2_iterator_next(&memi))) {
3184 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
3185 struct ast_dialed_interface *di;
3186 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
3187 if (!tmp) {
3188 ao2_ref(cur, -1);
3189 ao2_unlock(qe->parent);
3190 if (use_weight)
3191 ao2_unlock(queues);
3192 goto out;
3194 if (!datastore) {
3195 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
3196 ao2_ref(cur, -1);
3197 ao2_unlock(qe->parent);
3198 if (use_weight)
3199 ao2_unlock(queues);
3200 free(tmp);
3201 goto out;
3203 datastore->inheritance = DATASTORE_INHERIT_FOREVER;
3204 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
3205 ao2_ref(cur, -1);
3206 ao2_unlock(&qe->parent);
3207 if (use_weight)
3208 ao2_unlock(queues);
3209 free(tmp);
3210 goto out;
3212 datastore->data = dialed_interfaces;
3213 AST_LIST_HEAD_INIT(dialed_interfaces);
3215 ast_channel_lock(qe->chan);
3216 ast_channel_datastore_add(qe->chan, datastore);
3217 ast_channel_unlock(qe->chan);
3218 } else
3219 dialed_interfaces = datastore->data;
3221 AST_LIST_LOCK(dialed_interfaces);
3222 AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
3223 if (!strcasecmp(cur->interface, di->interface)) {
3224 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n",
3225 di->interface);
3226 break;
3229 AST_LIST_UNLOCK(dialed_interfaces);
3231 if (di) {
3232 free(tmp);
3233 continue;
3236 /* It is always ok to dial a Local interface. We only keep track of
3237 * which "real" interfaces have been dialed. The Local channel will
3238 * inherit this list so that if it ends up dialing a real interface,
3239 * it won't call one that has already been called. */
3240 if (strncasecmp(cur->interface, "Local/", 6)) {
3241 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
3242 ao2_ref(cur, -1);
3243 ao2_unlock(qe->parent);
3244 if (use_weight)
3245 ao2_unlock(queues);
3246 free(tmp);
3247 goto out;
3249 strcpy(di->interface, cur->interface);
3251 AST_LIST_LOCK(dialed_interfaces);
3252 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
3253 AST_LIST_UNLOCK(dialed_interfaces);
3256 tmp->stillgoing = -1;
3257 tmp->member = cur;
3258 tmp->oldstatus = cur->status;
3259 tmp->lastcall = cur->lastcall;
3260 tmp->lastqueue = cur->lastqueue;
3261 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
3262 /* Special case: If we ring everyone, go ahead and ring them, otherwise
3263 just calculate their metric for the appropriate strategy */
3264 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
3265 /* Put them in the list of outgoing thingies... We're ready now.
3266 XXX If we're forcibly removed, these outgoing calls won't get
3267 hung up XXX */
3268 tmp->q_next = outgoing;
3269 outgoing = tmp;
3270 /* If this line is up, don't try anybody else */
3271 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
3272 break;
3273 } else {
3274 ao2_ref(cur, -1);
3275 ast_free(tmp);
3278 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
3279 to = (qe->expire - now) * 1000;
3280 else
3281 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
3282 orig = to;
3283 ++qe->pending;
3284 ao2_unlock(qe->parent);
3285 ring_one(qe, outgoing, &numbusies);
3286 if (use_weight)
3287 ao2_unlock(queues);
3288 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
3289 /* The ast_channel_datastore_remove() function could fail here if the
3290 * datastore was moved to another channel during a masquerade. If this is
3291 * the case, don't free the datastore here because later, when the channel
3292 * to which the datastore was moved hangs up, it will attempt to free this
3293 * datastore again, causing a crash
3295 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
3296 ast_channel_datastore_free(datastore);
3298 ao2_lock(qe->parent);
3299 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
3300 store_next_rr(qe, outgoing);
3302 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
3303 store_next_lin(qe, outgoing);
3305 ao2_unlock(qe->parent);
3306 peer = lpeer ? lpeer->chan : NULL;
3307 if (!peer) {
3308 qe->pending = 0;
3309 if (to) {
3310 /* Must gotten hung up */
3311 res = -1;
3312 } else {
3313 /* User exited by pressing a digit */
3314 res = digit;
3316 if (res == -1)
3317 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
3318 } else { /* peer is valid */
3319 /* Ah ha! Someone answered within the desired timeframe. Of course after this
3320 we will always return with -1 so that it is hung up properly after the
3321 conversation. */
3322 if (!strcmp(qe->chan->tech->type, "Zap"))
3323 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
3324 if (!strcmp(peer->tech->type, "Zap"))
3325 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
3326 /* Update parameters for the queue */
3327 time(&now);
3328 recalc_holdtime(qe, (now - qe->start));
3329 ao2_lock(qe->parent);
3330 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
3331 ao2_unlock(qe->parent);
3332 member = lpeer->member;
3333 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
3334 ao2_ref(member, 1);
3335 hangupcalls(outgoing, peer);
3336 outgoing = NULL;
3337 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
3338 int res2;
3340 res2 = ast_autoservice_start(qe->chan);
3341 if (!res2) {
3342 if (qe->parent->memberdelay) {
3343 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
3344 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
3346 if (!res2 && announce) {
3347 play_file(peer, announce);
3349 if (!res2 && qe->parent->reportholdtime) {
3350 if (!play_file(peer, qe->parent->sound_reporthold)) {
3351 int holdtime, holdtimesecs;
3353 time(&now);
3354 holdtime = abs((now - qe->start) / 60);
3355 holdtimesecs = abs((now - qe->start));
3356 if (holdtime == 1) {
3357 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
3358 play_file(peer, qe->parent->sound_minute);
3359 } else {
3360 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
3361 play_file(peer, qe->parent->sound_minutes);
3363 if (holdtimesecs > 1) {
3364 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
3365 play_file(peer, qe->parent->sound_seconds);
3370 res2 |= ast_autoservice_stop(qe->chan);
3371 if (ast_check_hangup(peer)) {
3372 /* Agent must have hung up */
3373 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
3374 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
3375 if (qe->parent->eventwhencalled)
3376 manager_event(EVENT_FLAG_AGENT, "AgentDump",
3377 "Queue: %s\r\n"
3378 "Uniqueid: %s\r\n"
3379 "Channel: %s\r\n"
3380 "Member: %s\r\n"
3381 "MemberName: %s\r\n"
3382 "%s",
3383 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3384 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3385 ast_hangup(peer);
3386 ao2_ref(member, -1);
3387 goto out;
3388 } else if (res2) {
3389 /* Caller must have hung up just before being connected*/
3390 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
3391 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
3392 record_abandoned(qe);
3393 ast_hangup(peer);
3394 ao2_ref(member, -1);
3395 return -1;
3398 /* Stop music on hold */
3399 if (ringing)
3400 ast_indicate(qe->chan,-1);
3401 else
3402 ast_moh_stop(qe->chan);
3403 /* If appropriate, log that we have a destination channel */
3404 if (qe->chan->cdr)
3405 ast_cdr_setdestchan(qe->chan->cdr, peer->name);
3406 /* Make sure channels are compatible */
3407 res = ast_channel_make_compatible(qe->chan, peer);
3408 if (res < 0) {
3409 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
3410 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
3411 record_abandoned(qe);
3412 ast_hangup(peer);
3413 ao2_ref(member, -1);
3414 return -1;
3417 /* Play announcement to the caller telling it's his turn if defined */
3418 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
3419 if (play_file(qe->chan, qe->parent->sound_callerannounce))
3420 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
3423 ao2_lock(qe->parent);
3424 /* if setinterfacevar is defined, make member variables available to the channel */
3425 /* use pbx_builtin_setvar to set a load of variables with one call */
3426 if (qe->parent->setinterfacevar) {
3427 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
3428 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
3429 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
3432 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
3433 /* use pbx_builtin_setvar to set a load of variables with one call */
3434 if (qe->parent->setqueueentryvar) {
3435 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
3436 (long) time(NULL) - qe->start, qe->opos);
3437 pbx_builtin_setvar_multiple(qe->chan, interfacevar);
3440 /* try to set queue variables if configured to do so*/
3441 set_queue_variables(qe);
3442 ao2_unlock(qe->parent);
3444 ast_channel_lock(qe->chan);
3445 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
3446 monitorfilename = ast_strdupa(monitorfilename);
3448 ast_channel_unlock(qe->chan);
3449 /* Begin Monitoring */
3450 if (qe->parent->monfmt && *qe->parent->monfmt) {
3451 if (!qe->parent->montype) {
3452 ast_debug(1, "Starting Monitor as requested.\n");
3453 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
3454 which = qe->chan;
3455 else
3456 which = peer;
3457 if (monitorfilename)
3458 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
3459 else if (qe->chan->cdr)
3460 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
3461 else {
3462 /* Last ditch effort -- no CDR, make up something */
3463 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3464 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
3466 } else {
3467 mixmonapp = pbx_findapp("MixMonitor");
3469 if (mixmonapp) {
3470 ast_debug(1, "Starting MixMonitor as requested.\n");
3471 if (!monitorfilename) {
3472 if (qe->chan->cdr)
3473 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
3474 else
3475 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
3476 } else {
3477 const char *m = monitorfilename;
3478 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
3479 switch (*m) {
3480 case '^':
3481 if (*(m + 1) == '{')
3482 *p = '$';
3483 break;
3484 case ',':
3485 *p++ = '\\';
3486 /* Fall through */
3487 default:
3488 *p = *m;
3490 if (*m == '\0')
3491 break;
3493 if (p == tmpid2 + sizeof(tmpid2))
3494 tmpid2[sizeof(tmpid2) - 1] = '\0';
3496 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
3499 ast_channel_lock(qe->chan);
3500 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
3501 monitor_exec = ast_strdupa(monitor_exec);
3503 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
3504 monitor_options = ast_strdupa(monitor_options);
3505 } else {
3506 monitor_options = "";
3508 ast_channel_unlock(qe->chan);
3510 if (monitor_exec) {
3511 const char *m = monitor_exec;
3512 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
3513 switch (*m) {
3514 case '^':
3515 if (*(m + 1) == '{')
3516 *p = '$';
3517 break;
3518 case ',':
3519 *p++ = '\\';
3520 /* Fall through */
3521 default:
3522 *p = *m;
3524 if (*m == '\0')
3525 break;
3527 if (p == meid2 + sizeof(meid2))
3528 meid2[sizeof(meid2) - 1] = '\0';
3530 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
3533 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
3535 if (!ast_strlen_zero(monitor_exec))
3536 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
3537 else
3538 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
3540 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
3541 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3542 if (qe->chan->cdr)
3543 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3544 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
3545 if (qe->chan->cdr)
3546 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
3548 } else {
3549 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3553 /* Drop out of the queue at this point, to prepare for next caller */
3554 leave_queue(qe);
3555 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
3556 ast_debug(1, "app_queue: sendurl=%s.\n", url);
3557 ast_channel_sendurl(peer, url);
3560 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
3561 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
3562 if (!ast_strlen_zero(macro)) {
3563 macroexec = ast_strdupa(macro);
3564 } else {
3565 if (qe->parent->membermacro)
3566 macroexec = ast_strdupa(qe->parent->membermacro);
3569 if (!ast_strlen_zero(macroexec)) {
3570 ast_debug(1, "app_queue: macro=%s.\n", macroexec);
3572 res = ast_autoservice_start(qe->chan);
3573 if (res) {
3574 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3575 res = -1;
3578 app = pbx_findapp("Macro");
3580 if (app) {
3581 res = pbx_exec(qe->chan, app, macroexec);
3582 ast_debug(1, "Macro exited with status %d\n", res);
3583 res = 0;
3584 } else {
3585 ast_log(LOG_ERROR, "Could not find application Macro\n");
3586 res = -1;
3589 if (ast_autoservice_stop(qe->chan) < 0) {
3590 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3591 res = -1;
3595 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
3596 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
3597 if (!ast_strlen_zero(gosub)) {
3598 gosubexec = ast_strdupa(gosub);
3599 } else {
3600 if (qe->parent->membergosub)
3601 gosubexec = ast_strdupa(qe->parent->membergosub);
3604 if (!ast_strlen_zero(gosubexec)) {
3605 if (option_debug)
3606 ast_log(LOG_DEBUG, "app_queue: gosub=%s.\n", gosubexec);
3608 res = ast_autoservice_start(qe->chan);
3609 if (res) {
3610 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
3611 res = -1;
3614 app = pbx_findapp("Gosub");
3616 if (app) {
3617 char *gosub_args, *gosub_argstart;
3619 /* Set where we came from */
3620 ast_copy_string(qe->chan->context, "app_dial_gosub_virtual_context", sizeof(qe->chan->context));
3621 ast_copy_string(qe->chan->exten, "s", sizeof(qe->chan->exten));
3622 qe->chan->priority = 0;
3624 gosub_argstart = strchr(gosubexec, ',');
3625 if (gosub_argstart) {
3626 *gosub_argstart = 0;
3627 asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1);
3628 *gosub_argstart = '|';
3629 } else {
3630 asprintf(&gosub_args, "%s,s,1", gosubexec);
3632 if (gosub_args) {
3633 res = pbx_exec(qe->chan, app, gosub_args);
3634 ast_pbx_run(qe->chan);
3635 free(gosub_args);
3636 if (option_debug)
3637 ast_log(LOG_DEBUG, "Gosub exited with status %d\n", res);
3638 } else
3639 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
3641 res = 0;
3642 } else {
3643 ast_log(LOG_ERROR, "Could not find application Gosub\n");
3644 res = -1;
3647 if (ast_autoservice_stop(qe->chan) < 0) {
3648 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
3649 res = -1;
3653 if (!ast_strlen_zero(agi)) {
3654 ast_debug(1, "app_queue: agi=%s.\n", agi);
3655 app = pbx_findapp("agi");
3656 if (app) {
3657 agiexec = ast_strdupa(agi);
3658 ret = pbx_exec(qe->chan, app, agiexec);
3659 } else
3660 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3662 qe->handled++;
3663 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
3664 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
3665 if (update_cdr && qe->chan->cdr)
3666 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
3667 if (qe->parent->eventwhencalled)
3668 manager_event(EVENT_FLAG_AGENT, "AgentConnect",
3669 "Queue: %s\r\n"
3670 "Uniqueid: %s\r\n"
3671 "Channel: %s\r\n"
3672 "Member: %s\r\n"
3673 "MemberName: %s\r\n"
3674 "Holdtime: %ld\r\n"
3675 "BridgedChannel: %s\r\n"
3676 "Ringtime: %ld\r\n"
3677 "%s",
3678 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
3679 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
3680 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3681 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
3682 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
3683 time(&callstart);
3685 bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3687 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3688 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
3689 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
3690 (long) (time(NULL) - callstart));
3691 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
3692 } else if (ast_check_hangup(qe->chan)) {
3693 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
3694 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3695 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
3696 } else {
3697 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
3698 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
3699 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
3702 if (bridge != AST_PBX_NO_HANGUP_PEER)
3703 ast_hangup(peer);
3704 update_queue(qe->parent, member, callcompletedinsl);
3705 res = bridge ? bridge : 1;
3706 ao2_ref(member, -1);
3708 out:
3709 hangupcalls(outgoing, NULL);
3711 return res;
3714 static int wait_a_bit(struct queue_ent *qe)
3716 /* Don't need to hold the lock while we setup the outgoing calls */
3717 int retrywait = qe->parent->retry * 1000;
3719 int res = ast_waitfordigit(qe->chan, retrywait);
3720 if (res > 0 && !valid_exit(qe, res))
3721 res = 0;
3723 return res;
3726 static struct member *interface_exists(struct call_queue *q, const char *interface)
3728 struct member *mem;
3729 struct ao2_iterator mem_iter;
3731 if (!q)
3732 return NULL;
3734 mem_iter = ao2_iterator_init(q->members, 0);
3735 while ((mem = ao2_iterator_next(&mem_iter))) {
3736 if (!strcasecmp(interface, mem->interface))
3737 return mem;
3738 ao2_ref(mem, -1);
3741 return NULL;
3745 /*! \brief Dump all members in a specific queue to the database
3747 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
3749 static void dump_queue_members(struct call_queue *pm_queue)
3751 struct member *cur_member;
3752 char value[PM_MAX_LEN];
3753 int value_len = 0;
3754 int res;
3755 struct ao2_iterator mem_iter;
3757 memset(value, 0, sizeof(value));
3759 if (!pm_queue)
3760 return;
3762 mem_iter = ao2_iterator_init(pm_queue->members, 0);
3763 while ((cur_member = ao2_iterator_next(&mem_iter))) {
3764 if (!cur_member->dynamic) {
3765 ao2_ref(cur_member, -1);
3766 continue;
3769 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
3770 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
3772 ao2_ref(cur_member, -1);
3774 if (res != strlen(value + value_len)) {
3775 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
3776 break;
3778 value_len += res;
3781 if (value_len && !cur_member) {
3782 if (ast_db_put(pm_family, pm_queue->name, value))
3783 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
3784 } else
3785 /* Delete the entry if the queue is empty or there is an error */
3786 ast_db_del(pm_family, pm_queue->name);
3789 /*! \brief Remove member from queue
3790 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3791 * \retval RES_NOSUCHQUEUE queue does not exist
3792 * \retval RES_OKAY removed member from queue
3793 * \retval RES_EXISTS queue exists but no members
3795 static int remove_from_queue(const char *queuename, const char *interface)
3797 struct call_queue *q, tmpq = {
3798 .name = queuename,
3800 struct member *mem, tmpmem;
3801 int res = RES_NOSUCHQUEUE;
3803 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
3804 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
3805 ao2_lock(q);
3806 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
3807 /* XXX future changes should beware of this assumption!! */
3808 if (!mem->dynamic) {
3809 ao2_ref(mem, -1);
3810 ao2_unlock(q);
3811 return RES_NOT_DYNAMIC;
3813 q->membercount--;
3814 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
3815 "Queue: %s\r\n"
3816 "Location: %s\r\n"
3817 "MemberName: %s\r\n",
3818 q->name, mem->interface, mem->membername);
3819 ao2_unlink(q->members, mem);
3820 remove_from_interfaces(mem->state_interface);
3821 ao2_ref(mem, -1);
3823 if (queue_persistent_members)
3824 dump_queue_members(q);
3826 res = RES_OKAY;
3827 } else {
3828 res = RES_EXISTS;
3830 ao2_unlock(q);
3831 queue_unref(q);
3834 return res;
3837 /*! \brief Add member to queue
3838 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3839 * \retval RES_NOSUCHQUEUE queue does not exist
3840 * \retval RES_OKAY added member from queue
3841 * \retval RES_EXISTS queue exists but no members
3842 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
3844 static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
3846 struct call_queue *q;
3847 struct member *new_member, *old_member;
3848 int res = RES_NOSUCHQUEUE;
3850 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
3851 * short-circuits if the queue is already in memory. */
3852 if (!(q = load_realtime_queue(queuename)))
3853 return res;
3855 ao2_lock(queues);
3857 ao2_lock(q);
3858 if ((old_member = interface_exists(q, interface)) == NULL) {
3859 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
3860 add_to_interfaces(new_member->state_interface);
3861 new_member->dynamic = 1;
3862 ao2_link(q->members, new_member);
3863 q->membercount++;
3864 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
3865 "Queue: %s\r\n"
3866 "Location: %s\r\n"
3867 "MemberName: %s\r\n"
3868 "Membership: %s\r\n"
3869 "Penalty: %d\r\n"
3870 "CallsTaken: %d\r\n"
3871 "LastCall: %d\r\n"
3872 "Status: %d\r\n"
3873 "Paused: %d\r\n",
3874 q->name, new_member->interface, new_member->membername,
3875 "dynamic",
3876 new_member->penalty, new_member->calls, (int) new_member->lastcall,
3877 new_member->status, new_member->paused);
3879 ao2_ref(new_member, -1);
3880 new_member = NULL;
3882 if (dump)
3883 dump_queue_members(q);
3885 res = RES_OKAY;
3886 } else {
3887 res = RES_OUTOFMEMORY;
3889 } else {
3890 ao2_ref(old_member, -1);
3891 res = RES_EXISTS;
3893 ao2_unlock(q);
3894 ao2_unlock(queues);
3896 return res;
3899 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
3901 int found = 0;
3902 struct call_queue *q;
3903 struct member *mem;
3904 struct ao2_iterator queue_iter;
3905 int failed;
3907 /* Special event for when all queues are paused - individual events still generated */
3908 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3909 if (ast_strlen_zero(queuename))
3910 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3912 queue_iter = ao2_iterator_init(queues, 0);
3913 while ((q = ao2_iterator_next(&queue_iter))) {
3914 ao2_lock(q);
3915 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3916 if ((mem = interface_exists(q, interface))) {
3917 if (mem->paused == paused) {
3918 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
3921 failed = 0;
3922 if (mem->realtime) {
3923 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
3926 if (failed) {
3927 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
3928 ao2_ref(mem, -1);
3929 continue;
3931 found++;
3932 mem->paused = paused;
3934 if (queue_persistent_members)
3935 dump_queue_members(q);
3937 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
3939 if (!ast_strlen_zero(reason)) {
3940 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3941 "Queue: %s\r\n"
3942 "Location: %s\r\n"
3943 "MemberName: %s\r\n"
3944 "Paused: %d\r\n"
3945 "Reason: %s\r\n",
3946 q->name, mem->interface, mem->membername, paused, reason);
3947 } else {
3948 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
3949 "Queue: %s\r\n"
3950 "Location: %s\r\n"
3951 "MemberName: %s\r\n"
3952 "Paused: %d\r\n",
3953 q->name, mem->interface, mem->membername, paused);
3955 ao2_ref(mem, -1);
3959 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
3960 ao2_unlock(q);
3961 queue_unref(q);
3962 break;
3965 ao2_unlock(q);
3966 queue_unref(q);
3969 return found ? RESULT_SUCCESS : RESULT_FAILURE;
3972 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
3973 static int set_member_penalty(char *queuename, char *interface, int penalty)
3975 int foundinterface = 0, foundqueue = 0;
3976 struct call_queue *q;
3977 struct member *mem;
3978 struct ao2_iterator queue_iter;
3980 if (penalty < 0) {
3981 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
3982 return RESULT_FAILURE;
3985 queue_iter = ao2_iterator_init(queues, 0);
3986 while ((q = ao2_iterator_next(&queue_iter))) {
3987 ao2_lock(q);
3988 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
3989 foundqueue++;
3990 if ((mem = interface_exists(q, interface))) {
3991 foundinterface++;
3992 mem->penalty = penalty;
3994 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
3995 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
3996 "Queue: %s\r\n"
3997 "Location: %s\r\n"
3998 "Penalty: %d\r\n",
3999 q->name, mem->interface, penalty);
4003 ao2_unlock(q);
4004 queue_unref(q);
4007 if (foundinterface) {
4008 return RESULT_SUCCESS;
4009 } else if (!foundqueue) {
4010 ast_log (LOG_ERROR, "Invalid queuename\n");
4011 } else {
4012 ast_log (LOG_ERROR, "Invalid interface\n");
4015 return RESULT_FAILURE;
4018 /* \brief Gets members penalty.
4019 * \return Return the members penalty or RESULT_FAILURE on error.
4021 static int get_member_penalty(char *queuename, char *interface)
4023 int foundqueue = 0, penalty;
4024 struct call_queue *q, tmpq = {
4025 .name = queuename,
4027 struct member *mem;
4029 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4030 foundqueue = 1;
4031 ao2_lock(q);
4032 if ((mem = interface_exists(q, interface))) {
4033 penalty = mem->penalty;
4034 ao2_unlock(q);
4035 queue_unref(q);
4036 return penalty;
4038 ao2_unlock(q);
4039 queue_unref(q);
4042 /* some useful debuging */
4043 if (foundqueue)
4044 ast_log (LOG_ERROR, "Invalid queuename\n");
4045 else
4046 ast_log (LOG_ERROR, "Invalid interface\n");
4048 return RESULT_FAILURE;
4051 /*! \brief Reload dynamic queue members persisted into the astdb */
4052 static void reload_queue_members(void)
4054 char *cur_ptr;
4055 const char *queue_name;
4056 char *member;
4057 char *interface;
4058 char *membername = NULL;
4059 char *state_interface;
4060 char *penalty_tok;
4061 int penalty = 0;
4062 char *paused_tok;
4063 int paused = 0;
4064 struct ast_db_entry *db_tree;
4065 struct ast_db_entry *entry;
4066 struct call_queue *cur_queue;
4067 char queue_data[PM_MAX_LEN];
4069 ao2_lock(queues);
4071 /* Each key in 'pm_family' is the name of a queue */
4072 db_tree = ast_db_gettree(pm_family, NULL);
4073 for (entry = db_tree; entry; entry = entry->next) {
4075 queue_name = entry->key + strlen(pm_family) + 2;
4078 struct call_queue tmpq = {
4079 .name = queue_name,
4081 cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
4084 if (!cur_queue)
4085 cur_queue = load_realtime_queue(queue_name);
4087 if (!cur_queue) {
4088 /* If the queue no longer exists, remove it from the
4089 * database */
4090 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
4091 ast_db_del(pm_family, queue_name);
4092 continue;
4095 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
4096 queue_unref(cur_queue);
4097 continue;
4100 cur_ptr = queue_data;
4101 while ((member = strsep(&cur_ptr, ",|"))) {
4102 if (ast_strlen_zero(member))
4103 continue;
4105 interface = strsep(&member, ";");
4106 penalty_tok = strsep(&member, ";");
4107 paused_tok = strsep(&member, ";");
4108 membername = strsep(&member, ";");
4109 state_interface = strsep(&member, ";");
4111 if (!penalty_tok) {
4112 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
4113 break;
4115 penalty = strtol(penalty_tok, NULL, 10);
4116 if (errno == ERANGE) {
4117 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
4118 break;
4121 if (!paused_tok) {
4122 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
4123 break;
4125 paused = strtol(paused_tok, NULL, 10);
4126 if ((errno == ERANGE) || paused < 0 || paused > 1) {
4127 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
4128 break;
4131 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused);
4133 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
4134 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
4135 break;
4138 queue_unref(cur_queue);
4141 ao2_unlock(queues);
4142 if (db_tree) {
4143 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
4144 ast_db_freetree(db_tree);
4148 /*! \brief PauseQueueMember application */
4149 static int pqm_exec(struct ast_channel *chan, void *data)
4151 char *parse;
4152 AST_DECLARE_APP_ARGS(args,
4153 AST_APP_ARG(queuename);
4154 AST_APP_ARG(interface);
4155 AST_APP_ARG(options);
4156 AST_APP_ARG(reason);
4159 if (ast_strlen_zero(data)) {
4160 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
4161 return -1;
4164 parse = ast_strdupa(data);
4166 AST_STANDARD_APP_ARGS(args, parse);
4168 if (ast_strlen_zero(args.interface)) {
4169 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4170 return -1;
4173 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
4174 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
4175 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
4176 return 0;
4179 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
4181 return 0;
4184 /*! \brief UnPauseQueueMember application */
4185 static int upqm_exec(struct ast_channel *chan, void *data)
4187 char *parse;
4188 AST_DECLARE_APP_ARGS(args,
4189 AST_APP_ARG(queuename);
4190 AST_APP_ARG(interface);
4191 AST_APP_ARG(options);
4192 AST_APP_ARG(reason);
4195 if (ast_strlen_zero(data)) {
4196 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
4197 return -1;
4200 parse = ast_strdupa(data);
4202 AST_STANDARD_APP_ARGS(args, parse);
4204 if (ast_strlen_zero(args.interface)) {
4205 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4206 return -1;
4209 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
4210 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
4211 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
4212 return 0;
4215 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
4217 return 0;
4220 /*! \brief RemoveQueueMember application */
4221 static int rqm_exec(struct ast_channel *chan, void *data)
4223 int res=-1;
4224 char *parse, *temppos = NULL;
4225 AST_DECLARE_APP_ARGS(args,
4226 AST_APP_ARG(queuename);
4227 AST_APP_ARG(interface);
4228 AST_APP_ARG(options);
4232 if (ast_strlen_zero(data)) {
4233 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
4234 return -1;
4237 parse = ast_strdupa(data);
4239 AST_STANDARD_APP_ARGS(args, parse);
4241 if (ast_strlen_zero(args.interface)) {
4242 args.interface = ast_strdupa(chan->name);
4243 temppos = strrchr(args.interface, '-');
4244 if (temppos)
4245 *temppos = '\0';
4248 switch (remove_from_queue(args.queuename, args.interface)) {
4249 case RES_OKAY:
4250 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
4251 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
4252 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
4253 res = 0;
4254 break;
4255 case RES_EXISTS:
4256 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
4257 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
4258 res = 0;
4259 break;
4260 case RES_NOSUCHQUEUE:
4261 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
4262 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
4263 res = 0;
4264 break;
4265 case RES_NOT_DYNAMIC:
4266 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
4267 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
4268 res = 0;
4269 break;
4272 return res;
4275 /*! \brief AddQueueMember application */
4276 static int aqm_exec(struct ast_channel *chan, void *data)
4278 int res=-1;
4279 char *parse, *temppos = NULL;
4280 AST_DECLARE_APP_ARGS(args,
4281 AST_APP_ARG(queuename);
4282 AST_APP_ARG(interface);
4283 AST_APP_ARG(penalty);
4284 AST_APP_ARG(options);
4285 AST_APP_ARG(membername);
4286 AST_APP_ARG(state_interface);
4288 int penalty = 0;
4290 if (ast_strlen_zero(data)) {
4291 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
4292 return -1;
4295 parse = ast_strdupa(data);
4297 AST_STANDARD_APP_ARGS(args, parse);
4299 if (ast_strlen_zero(args.interface)) {
4300 args.interface = ast_strdupa(chan->name);
4301 temppos = strrchr(args.interface, '-');
4302 if (temppos)
4303 *temppos = '\0';
4306 if (!ast_strlen_zero(args.penalty)) {
4307 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
4308 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
4309 penalty = 0;
4313 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
4314 case RES_OKAY:
4315 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
4316 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
4317 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
4318 res = 0;
4319 break;
4320 case RES_EXISTS:
4321 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
4322 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
4323 res = 0;
4324 break;
4325 case RES_NOSUCHQUEUE:
4326 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
4327 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
4328 res = 0;
4329 break;
4330 case RES_OUTOFMEMORY:
4331 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
4332 break;
4335 return res;
4338 /*! \brief QueueLog application */
4339 static int ql_exec(struct ast_channel *chan, void *data)
4341 char *parse;
4343 AST_DECLARE_APP_ARGS(args,
4344 AST_APP_ARG(queuename);
4345 AST_APP_ARG(uniqueid);
4346 AST_APP_ARG(membername);
4347 AST_APP_ARG(event);
4348 AST_APP_ARG(params);
4351 if (ast_strlen_zero(data)) {
4352 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
4353 return -1;
4356 parse = ast_strdupa(data);
4358 AST_STANDARD_APP_ARGS(args, parse);
4360 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
4361 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
4362 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
4363 return -1;
4366 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
4367 "%s", args.params ? args.params : "");
4369 return 0;
4372 /*! \brief Copy rule from global list into specified queue */
4373 static void copy_rules(struct queue_ent *qe, const char *rulename)
4375 struct penalty_rule *pr_iter;
4376 struct rule_list *rl_iter;
4377 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
4378 AST_LIST_LOCK(&rule_lists);
4379 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
4380 if (!strcasecmp(rl_iter->name, tmp))
4381 break;
4383 if (rl_iter) {
4384 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
4385 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
4386 if (!new_pr) {
4387 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
4388 AST_LIST_UNLOCK(&rule_lists);
4389 break;
4391 new_pr->time = pr_iter->time;
4392 new_pr->max_value = pr_iter->max_value;
4393 new_pr->min_value = pr_iter->min_value;
4394 new_pr->max_relative = pr_iter->max_relative;
4395 new_pr->min_relative = pr_iter->min_relative;
4396 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
4399 AST_LIST_UNLOCK(&rule_lists);
4402 /*!\brief The starting point for all queue calls
4404 * The process involved here is to
4405 * 1. Parse the options specified in the call to Queue()
4406 * 2. Join the queue
4407 * 3. Wait in a loop until it is our turn to try calling a queue member
4408 * 4. Attempt to call a queue member
4409 * 5. If 4. did not result in a bridged call, then check for between
4410 * call options such as periodic announcements etc.
4411 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
4412 * exit the queue.
4414 static int queue_exec(struct ast_channel *chan, void *data)
4416 int res=-1;
4417 int ringing=0;
4418 const char *user_priority;
4419 const char *max_penalty_str;
4420 const char *min_penalty_str;
4421 int prio;
4422 int qcontinue = 0;
4423 int max_penalty, min_penalty;
4424 enum queue_result reason = QUEUE_UNKNOWN;
4425 /* whether to exit Queue application after the timeout hits */
4426 int tries = 0;
4427 int noption = 0;
4428 char *parse;
4429 int makeannouncement = 0;
4430 AST_DECLARE_APP_ARGS(args,
4431 AST_APP_ARG(queuename);
4432 AST_APP_ARG(options);
4433 AST_APP_ARG(url);
4434 AST_APP_ARG(announceoverride);
4435 AST_APP_ARG(queuetimeoutstr);
4436 AST_APP_ARG(agi);
4437 AST_APP_ARG(macro);
4438 AST_APP_ARG(gosub);
4439 AST_APP_ARG(rule);
4441 /* Our queue entry */
4442 struct queue_ent qe;
4444 if (ast_strlen_zero(data)) {
4445 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
4446 return -1;
4449 parse = ast_strdupa(data);
4450 AST_STANDARD_APP_ARGS(args, parse);
4452 /* Setup our queue entry */
4453 memset(&qe, 0, sizeof(qe));
4454 qe.start = time(NULL);
4456 /* set the expire time based on the supplied timeout; */
4457 if (!ast_strlen_zero(args.queuetimeoutstr))
4458 qe.expire = qe.start + atoi(args.queuetimeoutstr);
4459 else
4460 qe.expire = 0;
4462 /* Get the priority from the variable ${QUEUE_PRIO} */
4463 ast_channel_lock(chan);
4464 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
4465 if (user_priority) {
4466 if (sscanf(user_priority, "%d", &prio) == 1) {
4467 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
4468 } else {
4469 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
4470 user_priority, chan->name);
4471 prio = 0;
4473 } else {
4474 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
4475 prio = 0;
4478 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
4480 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
4481 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
4482 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
4483 } else {
4484 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
4485 max_penalty_str, chan->name);
4486 max_penalty = 0;
4488 } else {
4489 max_penalty = 0;
4492 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
4493 if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
4494 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
4495 } else {
4496 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
4497 min_penalty_str, chan->name);
4498 min_penalty = 0;
4500 } else {
4501 min_penalty = 0;
4503 ast_channel_unlock(chan);
4505 if (args.options && (strchr(args.options, 'r')))
4506 ringing = 1;
4508 if (args.options && (strchr(args.options, 'c')))
4509 qcontinue = 1;
4511 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
4512 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
4514 qe.chan = chan;
4515 qe.prio = prio;
4516 qe.max_penalty = max_penalty;
4517 qe.min_penalty = min_penalty;
4518 qe.last_pos_said = 0;
4519 qe.last_pos = 0;
4520 qe.last_periodic_announce_time = time(NULL);
4521 qe.last_periodic_announce_sound = 0;
4522 qe.valid_digits = 0;
4523 if (join_queue(args.queuename, &qe, &reason)) {
4524 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
4525 set_queue_result(chan, reason);
4526 return 0;
4528 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
4529 S_OR(chan->cid.cid_num, ""));
4530 copy_rules(&qe, args.rule);
4531 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
4532 check_turns:
4533 if (ringing) {
4534 ast_indicate(chan, AST_CONTROL_RINGING);
4535 } else {
4536 ast_moh_start(chan, qe.moh, NULL);
4539 /* This is the wait loop for callers 2 through maxlen */
4540 res = wait_our_turn(&qe, ringing, &reason);
4541 if (res) {
4542 goto stop;
4545 makeannouncement = 0;
4547 for (;;) {
4548 /* This is the wait loop for the head caller*/
4549 /* To exit, they may get their call answered; */
4550 /* they may dial a digit from the queue context; */
4551 /* or, they may timeout. */
4553 enum queue_member_status stat;
4555 /* Leave if we have exceeded our queuetimeout */
4556 if (qe.expire && (time(NULL) > qe.expire)) {
4557 record_abandoned(&qe);
4558 reason = QUEUE_TIMEOUT;
4559 res = 0;
4560 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
4561 qe.pos, qe.opos, (long) time(NULL) - qe.start);
4562 break;
4565 if (makeannouncement) {
4566 /* Make a position announcement, if enabled */
4567 if (qe.parent->announcefrequency)
4568 if ((res = say_position(&qe,ringing)))
4569 goto stop;
4571 makeannouncement = 1;
4573 /* Make a periodic announcement, if enabled */
4574 if (qe.parent->periodicannouncefrequency)
4575 if ((res = say_periodic_announcement(&qe,ringing)))
4576 goto stop;
4578 /* see if we need to move to the next penalty level for this queue */
4579 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
4580 update_qe_rule(&qe);
4583 /* Try calling all queue members for 'timeout' seconds */
4584 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
4585 if (res) {
4586 goto stop;
4589 stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
4591 /* exit after 'timeout' cycle if 'n' option enabled */
4592 if (noption && tries >= qe.parent->membercount) {
4593 ast_verb(3, "Exiting on time-out cycle\n");
4594 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
4595 record_abandoned(&qe);
4596 reason = QUEUE_TIMEOUT;
4597 res = 0;
4598 break;
4601 /* leave the queue if no agents, if enabled */
4602 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
4603 record_abandoned(&qe);
4604 reason = QUEUE_LEAVEEMPTY;
4605 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4606 res = 0;
4607 break;
4610 /* leave the queue if no reachable agents, if enabled */
4611 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
4612 record_abandoned(&qe);
4613 reason = QUEUE_LEAVEUNAVAIL;
4614 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
4615 res = 0;
4616 break;
4618 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
4619 record_abandoned(&qe);
4620 reason = QUEUE_LEAVEUNAVAIL;
4621 res = 0;
4622 break;
4625 /* Leave if we have exceeded our queuetimeout */
4626 if (qe.expire && (time(NULL) > qe.expire)) {
4627 record_abandoned(&qe);
4628 reason = QUEUE_TIMEOUT;
4629 res = 0;
4630 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
4631 break;
4634 /* If using dynamic realtime members, we should regenerate the member list for this queue */
4635 update_realtime_members(qe.parent);
4637 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
4638 res = wait_a_bit(&qe);
4639 if (res)
4640 goto stop;
4642 /* Since this is a priority queue and
4643 * it is not sure that we are still at the head
4644 * of the queue, go and check for our turn again.
4646 if (!is_our_turn(&qe)) {
4647 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
4648 goto check_turns;
4652 stop:
4653 if (res) {
4654 if (res < 0) {
4655 if (!qe.handled) {
4656 record_abandoned(&qe);
4657 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
4658 "%d|%d|%ld", qe.pos, qe.opos,
4659 (long) time(NULL) - qe.start);
4661 res = -1;
4662 } else if (qe.valid_digits) {
4663 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
4664 "%s|%d", qe.digits, qe.pos);
4668 /* Don't allow return code > 0 */
4669 if (res >= 0 && res != AST_PBX_KEEPALIVE) {
4670 res = 0;
4671 if (ringing) {
4672 ast_indicate(chan, -1);
4673 } else {
4674 ast_moh_stop(chan);
4676 ast_stopstream(chan);
4679 set_queue_variables(&qe);
4681 leave_queue(&qe);
4682 if (reason != QUEUE_UNKNOWN)
4683 set_queue_result(chan, reason);
4685 return res;
4689 * \brief create interface var with all queue details.
4690 * \retval 0 on success
4691 * \retval -1 on error
4693 static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4695 int res = -1;
4696 struct call_queue *q, tmpq = {
4697 .name = data,
4700 char interfacevar[256] = "";
4701 float sl = 0;
4703 if (ast_strlen_zero(data)) {
4704 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4705 return -1;
4708 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4709 ao2_lock(q);
4710 if (q->setqueuevar) {
4711 sl = 0;
4712 res = 0;
4714 if (q->callscompleted > 0) {
4715 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
4718 snprintf(interfacevar, sizeof(interfacevar),
4719 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
4720 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
4722 pbx_builtin_setvar_multiple(chan, interfacevar);
4725 ao2_unlock(q);
4726 queue_unref(q);
4727 } else {
4728 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4731 snprintf(buf, len, "%d", res);
4733 return 0;
4736 /*!
4737 * \brief Get number either busy / free or total members of a specific queue
4738 * \retval number of members (busy / free / total)
4739 * \retval -1 on error
4741 static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4743 int count = 0;
4744 struct member *m;
4745 struct ao2_iterator mem_iter;
4746 struct call_queue *q;
4747 char *option;
4749 if (ast_strlen_zero(data)) {
4750 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4751 return -1;
4754 if ((option = strchr(data, ',')))
4755 *option++ = '\0';
4756 else
4757 option = "logged";
4758 if ((q = load_realtime_queue(data))) {
4759 ao2_lock(q);
4760 if (!strcasecmp(option, "logged")) {
4761 mem_iter = ao2_iterator_init(q->members, 0);
4762 while ((m = ao2_iterator_next(&mem_iter))) {
4763 /* Count the agents who are logged in and presently answering calls */
4764 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4765 count++;
4767 ao2_ref(m, -1);
4769 } else if (!strcasecmp(option, "free")) {
4770 mem_iter = ao2_iterator_init(q->members, 0);
4771 while ((m = ao2_iterator_next(&mem_iter))) {
4772 /* Count the agents who are logged in and presently answering calls */
4773 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
4774 count++;
4776 ao2_ref(m, -1);
4778 } else /* must be "count" */
4779 count = q->membercount;
4780 ao2_unlock(q);
4781 queue_unref(q);
4782 } else
4783 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4785 snprintf(buf, len, "%d", count);
4787 return 0;
4790 /*!
4791 * \brief Get the total number of members in a specific queue (Deprecated)
4792 * \retval number of members
4793 * \retval -1 on error
4795 static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4797 int count = 0;
4798 struct member *m;
4799 struct call_queue *q;
4800 struct ao2_iterator mem_iter;
4801 static int depflag = 1;
4803 if (depflag) {
4804 depflag = 0;
4805 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");
4808 if (ast_strlen_zero(data)) {
4809 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
4810 return -1;
4813 if ((q = load_realtime_queue(data))) {
4814 ao2_lock(q);
4815 mem_iter = ao2_iterator_init(q->members, 0);
4816 while ((m = ao2_iterator_next(&mem_iter))) {
4817 /* Count the agents who are logged in and presently answering calls */
4818 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
4819 count++;
4821 ao2_ref(m, -1);
4823 ao2_unlock(q);
4824 queue_unref(q);
4825 } else
4826 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4828 snprintf(buf, len, "%d", count);
4830 return 0;
4833 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
4834 static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4836 int count = 0;
4837 struct call_queue *q, tmpq = {
4838 .name = data,
4840 struct ast_variable *var = NULL;
4842 buf[0] = '\0';
4844 if (ast_strlen_zero(data)) {
4845 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
4846 return -1;
4849 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4850 ao2_lock(q);
4851 count = q->count;
4852 ao2_unlock(q);
4853 queue_unref(q);
4854 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
4855 /* if the queue is realtime but was not found in memory, this
4856 * means that the queue had been deleted from memory since it was
4857 * "dead." This means it has a 0 waiting count
4859 count = 0;
4860 ast_variables_destroy(var);
4861 } else
4862 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4864 snprintf(buf, len, "%d", count);
4866 return 0;
4869 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
4870 static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4872 struct call_queue *q, tmpq = {
4873 .name = data,
4875 struct member *m;
4877 /* Ensure an otherwise empty list doesn't return garbage */
4878 buf[0] = '\0';
4880 if (ast_strlen_zero(data)) {
4881 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
4882 return -1;
4885 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
4886 int buflen = 0, count = 0;
4887 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
4889 ao2_lock(q);
4890 while ((m = ao2_iterator_next(&mem_iter))) {
4891 /* strcat() is always faster than printf() */
4892 if (count++) {
4893 strncat(buf + buflen, ",", len - buflen - 1);
4894 buflen++;
4896 strncat(buf + buflen, m->membername, len - buflen - 1);
4897 buflen += strlen(m->membername);
4898 /* Safeguard against overflow (negative length) */
4899 if (buflen >= len - 2) {
4900 ao2_ref(m, -1);
4901 ast_log(LOG_WARNING, "Truncating list\n");
4902 break;
4904 ao2_ref(m, -1);
4906 ao2_unlock(q);
4907 queue_unref(q);
4908 } else
4909 ast_log(LOG_WARNING, "queue %s was not found\n", data);
4911 /* We should already be terminated, but let's make sure. */
4912 buf[len - 1] = '\0';
4914 return 0;
4917 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
4918 static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4920 int penalty;
4921 AST_DECLARE_APP_ARGS(args,
4922 AST_APP_ARG(queuename);
4923 AST_APP_ARG(interface);
4925 /* Make sure the returned value on error is NULL. */
4926 buf[0] = '\0';
4928 if (ast_strlen_zero(data)) {
4929 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4930 return -1;
4933 AST_STANDARD_APP_ARGS(args, data);
4935 if (args.argc < 2) {
4936 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4937 return -1;
4940 penalty = get_member_penalty (args.queuename, args.interface);
4942 if (penalty >= 0) /* remember that buf is already '\0' */
4943 snprintf (buf, len, "%d", penalty);
4945 return 0;
4948 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
4949 static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
4951 int penalty;
4952 AST_DECLARE_APP_ARGS(args,
4953 AST_APP_ARG(queuename);
4954 AST_APP_ARG(interface);
4957 if (ast_strlen_zero(data)) {
4958 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4959 return -1;
4962 AST_STANDARD_APP_ARGS(args, data);
4964 if (args.argc < 2) {
4965 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4966 return -1;
4969 penalty = atoi(value);
4971 if (ast_strlen_zero(args.interface)) {
4972 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
4973 return -1;
4976 /* if queuename = NULL then penalty will be set for interface in all the queues. */
4977 if (set_member_penalty(args.queuename, args.interface, penalty)) {
4978 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
4979 return -1;
4982 return 0;
4985 static struct ast_custom_function queuevar_function = {
4986 .name = "QUEUE_VARIABLES",
4987 .synopsis = "Return Queue information in variables",
4988 .syntax = "QUEUE_VARIABLES(<queuename>)",
4989 .desc =
4990 "Makes the following queue variables available.\n"
4991 "QUEUEMAX maxmimum number of calls allowed\n"
4992 "QUEUESTRATEGY the strategy of the queue\n"
4993 "QUEUECALLS number of calls currently in the queue\n"
4994 "QUEUEHOLDTIME current average hold time\n"
4995 "QUEUECOMPLETED number of completed calls for the queue\n"
4996 "QUEUEABANDONED number of abandoned calls\n"
4997 "QUEUESRVLEVEL queue service level\n"
4998 "QUEUESRVLEVELPERF current service level performance\n"
4999 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
5000 .read = queue_function_var,
5003 static struct ast_custom_function queuemembercount_function = {
5004 .name = "QUEUE_MEMBER",
5005 .synopsis = "Count number of members answering a queue",
5006 .syntax = "QUEUE_MEMBER(<queuename>, <option>)",
5007 .desc =
5008 "Returns the number of members currently associated with the specified queue.\n"
5009 "One of three options may be passed to determine the count returned:\n"
5010 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
5011 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
5012 "\"count\" - Returns the total number of members for the specified queue\n",
5013 .read = queue_function_qac,
5016 static struct ast_custom_function queuemembercount_dep = {
5017 .name = "QUEUE_MEMBER_COUNT",
5018 .synopsis = "Count number of members answering a queue",
5019 .syntax = "QUEUE_MEMBER_COUNT(<queuename>)",
5020 .desc =
5021 "Returns the number of members currently associated with the specified queue.\n\n"
5022 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
5023 .read = queue_function_qac_dep,
5026 static struct ast_custom_function queuewaitingcount_function = {
5027 .name = "QUEUE_WAITING_COUNT",
5028 .synopsis = "Count number of calls currently waiting in a queue",
5029 .syntax = "QUEUE_WAITING_COUNT(<queuename>)",
5030 .desc =
5031 "Returns the number of callers currently waiting in the specified queue.\n",
5032 .read = queue_function_queuewaitingcount,
5035 static struct ast_custom_function queuememberlist_function = {
5036 .name = "QUEUE_MEMBER_LIST",
5037 .synopsis = "Returns a list of interfaces on a queue",
5038 .syntax = "QUEUE_MEMBER_LIST(<queuename>)",
5039 .desc =
5040 "Returns a comma-separated list of members associated with the specified queue.\n",
5041 .read = queue_function_queuememberlist,
5044 static struct ast_custom_function queuememberpenalty_function = {
5045 .name = "QUEUE_MEMBER_PENALTY",
5046 .synopsis = "Gets or sets queue members penalty.",
5047 .syntax = "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
5048 .desc =
5049 "Gets or sets queue members penalty\n",
5050 .read = queue_function_memberpenalty_read,
5051 .write = queue_function_memberpenalty_write,
5054 static int reload_queue_rules(int reload)
5056 struct ast_config *cfg;
5057 struct rule_list *rl_iter, *new_rl;
5058 struct penalty_rule *pr_iter;
5059 char *rulecat = NULL;
5060 struct ast_variable *rulevar = NULL;
5061 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5063 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
5064 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
5065 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
5066 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
5067 return AST_MODULE_LOAD_SUCCESS;
5068 } else {
5069 AST_LIST_LOCK(&rule_lists);
5070 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
5071 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
5072 ast_free(pr_iter);
5073 ast_free(rl_iter);
5075 while ((rulecat = ast_category_browse(cfg, rulecat))) {
5076 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
5077 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
5078 AST_LIST_UNLOCK(&rule_lists);
5079 return AST_MODULE_LOAD_FAILURE;
5080 } else {
5081 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
5082 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
5083 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
5084 if(!strcasecmp(rulevar->name, "penaltychange"))
5085 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
5086 else
5087 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
5090 AST_LIST_UNLOCK(&rule_lists);
5093 ast_config_destroy(cfg);
5095 return AST_MODULE_LOAD_SUCCESS;
5099 static int reload_queues(int reload)
5101 struct call_queue *q;
5102 struct ast_config *cfg;
5103 char *cat, *tmp;
5104 struct ast_variable *var;
5105 struct member *cur, *newm;
5106 struct ao2_iterator mem_iter;
5107 int new;
5108 const char *general_val = NULL;
5109 char parse[80];
5110 char *interface, *state_interface;
5111 char *membername = NULL;
5112 int penalty;
5113 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
5114 struct ao2_iterator queue_iter;
5115 AST_DECLARE_APP_ARGS(args,
5116 AST_APP_ARG(interface);
5117 AST_APP_ARG(penalty);
5118 AST_APP_ARG(membername);
5119 AST_APP_ARG(state_interface);
5122 /*First things first. Let's load queuerules.conf*/
5123 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
5124 return AST_MODULE_LOAD_FAILURE;
5126 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
5127 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
5128 return 0;
5129 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
5130 return 0;
5131 ao2_lock(queues);
5132 use_weight=0;
5133 /* Mark all queues as dead for the moment */
5134 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
5135 while ((q = ao2_iterator_next(&queue_iter))) {
5136 if (!q->realtime) {
5137 q->dead = 1;
5138 q->found = 0;
5140 queue_unref(q);
5143 /* Chug through config file */
5144 cat = NULL;
5145 while ((cat = ast_category_browse(cfg, cat)) ) {
5146 if (!strcasecmp(cat, "general")) {
5147 /* Initialize global settings */
5148 queue_keep_stats = 0;
5149 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
5150 queue_keep_stats = ast_true(general_val);
5151 queue_persistent_members = 0;
5152 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
5153 queue_persistent_members = ast_true(general_val);
5154 autofill_default = 0;
5155 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
5156 autofill_default = ast_true(general_val);
5157 montype_default = 0;
5158 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
5159 if (!strcasecmp(general_val, "mixmonitor"))
5160 montype_default = 1;
5162 update_cdr = 0;
5163 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
5164 update_cdr = ast_true(general_val);
5165 shared_lastcall = 0;
5166 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
5167 shared_lastcall = ast_true(general_val);
5168 } else { /* Define queue */
5169 /* Look for an existing one */
5170 struct call_queue tmpq = {
5171 .name = cat,
5173 if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
5174 /* Make one then */
5175 if (!(q = alloc_queue(cat))) {
5176 /* TODO: Handle memory allocation failure */
5178 new = 1;
5179 } else
5180 new = 0;
5181 if (q) {
5182 const char *tmpvar = NULL;
5183 if (!new)
5184 ao2_lock(q);
5185 /* Check if a queue with this name already exists */
5186 if (q->found) {
5187 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
5188 if (!new) {
5189 ao2_unlock(q);
5190 queue_unref(q);
5192 continue;
5194 /* Due to the fact that the "linear" strategy will have a different allocation
5195 * scheme for queue members, we must devise the queue's strategy before other initializations
5197 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
5198 q->strategy = strat2int(tmpvar);
5199 if (q->strategy < 0) {
5200 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
5201 tmpvar, q->name);
5202 q->strategy = QUEUE_STRATEGY_RINGALL;
5204 } else
5205 q->strategy = QUEUE_STRATEGY_RINGALL;
5206 /* Re-initialize the queue, and clear statistics */
5207 init_queue(q);
5208 if (!queue_keep_stats)
5209 clear_queue(q);
5210 mem_iter = ao2_iterator_init(q->members, 0);
5211 while ((cur = ao2_iterator_next(&mem_iter))) {
5212 if (!cur->dynamic) {
5213 cur->delme = 1;
5215 ao2_ref(cur, -1);
5217 for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
5218 if (!strcasecmp(var->name, "member")) {
5219 struct member tmpmem;
5220 membername = NULL;
5222 /* Add a new member */
5223 ast_copy_string(parse, var->value, sizeof(parse));
5225 AST_STANDARD_APP_ARGS(args, parse);
5227 interface = args.interface;
5228 if (!ast_strlen_zero(args.penalty)) {
5229 tmp = args.penalty;
5230 while (*tmp && *tmp < 33) tmp++;
5231 penalty = atoi(tmp);
5232 if (penalty < 0) {
5233 penalty = 0;
5235 } else
5236 penalty = 0;
5238 if (!ast_strlen_zero(args.membername)) {
5239 membername = args.membername;
5240 while (*membername && *membername < 33) membername++;
5243 if (!ast_strlen_zero(args.state_interface)) {
5244 state_interface = args.state_interface;
5245 while (*state_interface && *state_interface < 33) state_interface++;
5246 } else
5247 state_interface = interface;
5249 /* Find the old position in the list */
5250 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
5251 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
5252 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
5253 if (cur && strcasecmp(cur->state_interface, state_interface)) {
5254 remove_from_interfaces(cur->state_interface);
5256 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
5257 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
5258 add_to_interfaces(state_interface);
5259 ao2_link(q->members, newm);
5260 ao2_ref(newm, -1);
5261 newm = NULL;
5263 if (cur)
5264 ao2_ref(cur, -1);
5265 else {
5266 q->membercount++;
5268 } else {
5269 queue_set_param(q, var->name, var->value, var->lineno, 1);
5273 /* Free remaining members marked as delme */
5274 mem_iter = ao2_iterator_init(q->members, 0);
5275 while ((cur = ao2_iterator_next(&mem_iter))) {
5276 if (! cur->delme) {
5277 ao2_ref(cur, -1);
5278 continue;
5280 q->membercount--;
5281 ao2_unlink(q->members, cur);
5282 remove_from_interfaces(cur->interface);
5283 ao2_ref(cur, -1);
5286 if (new) {
5287 ao2_link(queues, q);
5288 } else
5289 ao2_unlock(q);
5290 queue_unref(q);
5294 ast_config_destroy(cfg);
5295 queue_iter = ao2_iterator_init(queues, 0);
5296 while ((q = ao2_iterator_next(&queue_iter))) {
5297 if (q->dead) {
5298 ao2_unlink(queues, q);
5299 } else {
5300 ao2_lock(q);
5301 mem_iter = ao2_iterator_init(q->members, 0);
5302 while ((cur = ao2_iterator_next(&mem_iter))) {
5303 if (cur->dynamic)
5304 q->membercount++;
5305 cur->status = ast_device_state(cur->interface);
5306 ao2_ref(cur, -1);
5308 ao2_unlock(q);
5310 queue_unref(q);
5312 ao2_unlock(queues);
5313 return 1;
5316 /*! \brief direct ouput to manager or cli with proper terminator */
5317 static void do_print(struct mansession *s, int fd, const char *str)
5319 if (s)
5320 astman_append(s, "%s\r\n", str);
5321 else
5322 ast_cli(fd, "%s\n", str);
5325 /*!
5326 * \brief Show queue(s) status and statistics
5328 * List the queues strategy, calls processed, members logged in,
5329 * other queue statistics such as avg hold time.
5331 static char *__queues_show(struct mansession *s, int fd, int argc, char **argv)
5333 struct call_queue *q;
5334 struct ast_str *out = ast_str_alloca(240);
5335 int found = 0;
5336 time_t now = time(NULL);
5337 struct ao2_iterator queue_iter;
5338 struct ao2_iterator mem_iter;
5340 if (argc != 2 && argc != 3)
5341 return CLI_SHOWUSAGE;
5343 /* We only want to load realtime queues when a specific queue is asked for. */
5344 if (argc == 3) /* specific queue */
5345 load_realtime_queue(argv[2]);
5347 queue_iter = ao2_iterator_init(queues, 0);
5348 while ((q = ao2_iterator_next(&queue_iter))) {
5349 float sl;
5351 ao2_lock(q);
5352 if (argc == 3 && strcasecmp(q->name, argv[2])) {
5353 ao2_unlock(q);
5354 continue;
5356 found = 1;
5358 ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
5359 if (q->maxlen)
5360 ast_str_append(&out, 0, "%d", q->maxlen);
5361 else
5362 ast_str_append(&out, 0, "unlimited");
5363 sl = 0;
5364 if (q->callscompleted > 0)
5365 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
5366 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
5367 int2strat(q->strategy), q->holdtime, q->weight,
5368 q->callscompleted, q->callsabandoned,sl,q->servicelevel);
5369 do_print(s, fd, out->str);
5370 if (!ao2_container_count(q->members))
5371 do_print(s, fd, " No Members");
5372 else {
5373 struct member *mem;
5375 do_print(s, fd, " Members: ");
5376 mem_iter = ao2_iterator_init(q->members, 0);
5377 while ((mem = ao2_iterator_next(&mem_iter))) {
5378 ast_str_set(&out, 0, " %s", mem->membername);
5379 if (mem->penalty)
5380 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
5381 ast_str_append(&out, 0, "%s%s%s (%s)",
5382 mem->dynamic ? " (dynamic)" : "",
5383 mem->realtime ? " (realtime)" : "",
5384 mem->paused ? " (paused)" : "",
5385 devstate2str(mem->status));
5386 if (mem->calls)
5387 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
5388 mem->calls, (long) (time(NULL) - mem->lastcall));
5389 else
5390 ast_str_append(&out, 0, " has taken no calls yet");
5391 do_print(s, fd, out->str);
5392 ao2_ref(mem, -1);
5395 if (!q->head)
5396 do_print(s, fd, " No Callers");
5397 else {
5398 struct queue_ent *qe;
5399 int pos = 1;
5401 do_print(s, fd, " Callers: ");
5402 for (qe = q->head; qe; qe = qe->next) {
5403 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
5404 pos++, qe->chan->name, (long) (now - qe->start) / 60,
5405 (long) (now - qe->start) % 60, qe->prio);
5406 do_print(s, fd, out->str);
5409 do_print(s, fd, ""); /* blank line between entries */
5410 ao2_unlock(q);
5411 if (argc == 3) { /* print a specific entry */
5412 queue_unref(q);
5413 break;
5415 queue_unref(q);
5417 if (!found) {
5418 if (argc == 3)
5419 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
5420 else
5421 ast_str_set(&out, 0, "No queues.");
5422 do_print(s, fd, out->str);
5424 return CLI_SUCCESS;
5427 static char *complete_queue(const char *line, const char *word, int pos, int state)
5429 struct call_queue *q;
5430 char *ret = NULL;
5431 int which = 0;
5432 int wordlen = strlen(word);
5433 struct ao2_iterator queue_iter;
5435 queue_iter = ao2_iterator_init(queues, 0);
5436 while ((q = ao2_iterator_next(&queue_iter))) {
5437 if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
5438 ret = ast_strdup(q->name);
5439 queue_unref(q);
5440 break;
5442 queue_unref(q);
5445 return ret;
5448 static char *complete_queue_show(const char *line, const char *word, int pos, int state)
5450 if (pos == 2)
5451 return complete_queue(line, word, pos, state);
5452 return NULL;
5455 static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5457 switch ( cmd ) {
5458 case CLI_INIT:
5459 e->command = "queue show";
5460 e->usage =
5461 "Usage: queue show\n"
5462 " Provides summary information on a specified queue.\n";
5463 return NULL;
5464 case CLI_GENERATE:
5465 return complete_queue_show(a->line, a->word, a->pos, a->n);
5468 return __queues_show(NULL, a->fd, a->argc, a->argv);
5471 /*!\brief callback to display queues status in manager
5472 \addtogroup Group_AMI
5474 static int manager_queues_show(struct mansession *s, const struct message *m)
5476 char *a[] = { "queue", "show" };
5478 __queues_show(s, -1, 2, a);
5479 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
5481 return RESULT_SUCCESS;
5484 static int manager_queue_rule_show(struct mansession *s, const struct message *m)
5486 const char *rule = astman_get_header(m, "Rule");
5487 struct rule_list *rl_iter;
5488 struct penalty_rule *pr_iter;
5490 AST_LIST_LOCK(&rule_lists);
5491 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
5492 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
5493 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
5494 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
5495 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 );
5497 if (!ast_strlen_zero(rule))
5498 break;
5501 AST_LIST_UNLOCK(&rule_lists);
5503 astman_append(s, "\r\n\r\n");
5505 return RESULT_SUCCESS;
5508 /*! \brief Summary of queue info via the AMI */
5509 static int manager_queues_summary(struct mansession *s, const struct message *m)
5511 time_t now;
5512 int qmemcount = 0;
5513 int qmemavail = 0;
5514 int qchancount = 0;
5515 int qlongestholdtime = 0;
5516 const char *id = astman_get_header(m, "ActionID");
5517 const char *queuefilter = astman_get_header(m, "Queue");
5518 char idText[256] = "";
5519 struct call_queue *q;
5520 struct queue_ent *qe;
5521 struct member *mem;
5522 struct ao2_iterator queue_iter;
5523 struct ao2_iterator mem_iter;
5525 astman_send_ack(s, m, "Queue summary will follow");
5526 time(&now);
5527 if (!ast_strlen_zero(id))
5528 snprintf(idText, 256, "ActionID: %s\r\n", id);
5529 queue_iter = ao2_iterator_init(queues, 0);
5530 while ((q = ao2_iterator_next(&queue_iter))) {
5531 ao2_lock(q);
5533 /* List queue properties */
5534 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5535 /* Reset the necessary local variables if no queuefilter is set*/
5536 qmemcount = 0;
5537 qmemavail = 0;
5538 qchancount = 0;
5539 qlongestholdtime = 0;
5541 /* List Queue Members */
5542 mem_iter = ao2_iterator_init(q->members, 0);
5543 while ((mem = ao2_iterator_next(&mem_iter))) {
5544 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
5545 ++qmemcount;
5546 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
5547 ++qmemavail;
5550 ao2_ref(mem, -1);
5552 for (qe = q->head; qe; qe = qe->next) {
5553 if ((now - qe->start) > qlongestholdtime) {
5554 qlongestholdtime = now - qe->start;
5556 ++qchancount;
5558 astman_append(s, "Event: QueueSummary\r\n"
5559 "Queue: %s\r\n"
5560 "LoggedIn: %d\r\n"
5561 "Available: %d\r\n"
5562 "Callers: %d\r\n"
5563 "HoldTime: %d\r\n"
5564 "LongestHoldTime: %d\r\n"
5565 "%s"
5566 "\r\n",
5567 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
5569 ao2_unlock(q);
5570 queue_unref(q);
5572 astman_append(s,
5573 "Event: QueueSummaryComplete\r\n"
5574 "%s"
5575 "\r\n", idText);
5577 return RESULT_SUCCESS;
5580 /*! \brief Queue status info via AMI */
5581 static int manager_queues_status(struct mansession *s, const struct message *m)
5583 time_t now;
5584 int pos;
5585 const char *id = astman_get_header(m,"ActionID");
5586 const char *queuefilter = astman_get_header(m,"Queue");
5587 const char *memberfilter = astman_get_header(m,"Member");
5588 char idText[256] = "";
5589 struct call_queue *q;
5590 struct queue_ent *qe;
5591 float sl = 0;
5592 struct member *mem;
5593 struct ao2_iterator queue_iter;
5594 struct ao2_iterator mem_iter;
5596 astman_send_ack(s, m, "Queue status will follow");
5597 time(&now);
5598 if (!ast_strlen_zero(id))
5599 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
5601 queue_iter = ao2_iterator_init(queues, 0);
5602 while ((q = ao2_iterator_next(&queue_iter))) {
5603 ao2_lock(q);
5605 /* List queue properties */
5606 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
5607 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
5608 astman_append(s, "Event: QueueParams\r\n"
5609 "Queue: %s\r\n"
5610 "Max: %d\r\n"
5611 "Strategy: %s\r\n"
5612 "Calls: %d\r\n"
5613 "Holdtime: %d\r\n"
5614 "Completed: %d\r\n"
5615 "Abandoned: %d\r\n"
5616 "ServiceLevel: %d\r\n"
5617 "ServicelevelPerf: %2.1f\r\n"
5618 "Weight: %d\r\n"
5619 "%s"
5620 "\r\n",
5621 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
5622 q->callsabandoned, q->servicelevel, sl, q->weight, idText);
5623 /* List Queue Members */
5624 mem_iter = ao2_iterator_init(q->members, 0);
5625 while ((mem = ao2_iterator_next(&mem_iter))) {
5626 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
5627 astman_append(s, "Event: QueueMember\r\n"
5628 "Queue: %s\r\n"
5629 "Name: %s\r\n"
5630 "Location: %s\r\n"
5631 "Membership: %s\r\n"
5632 "Penalty: %d\r\n"
5633 "CallsTaken: %d\r\n"
5634 "LastCall: %d\r\n"
5635 "Status: %d\r\n"
5636 "Paused: %d\r\n"
5637 "%s"
5638 "\r\n",
5639 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
5640 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
5642 ao2_ref(mem, -1);
5644 /* List Queue Entries */
5645 pos = 1;
5646 for (qe = q->head; qe; qe = qe->next) {
5647 astman_append(s, "Event: QueueEntry\r\n"
5648 "Queue: %s\r\n"
5649 "Position: %d\r\n"
5650 "Channel: %s\r\n"
5651 "CallerIDNum: %s\r\n"
5652 "CallerIDName: %s\r\n"
5653 "Wait: %ld\r\n"
5654 "%s"
5655 "\r\n",
5656 q->name, pos++, qe->chan->name,
5657 S_OR(qe->chan->cid.cid_num, "unknown"),
5658 S_OR(qe->chan->cid.cid_name, "unknown"),
5659 (long) (now - qe->start), idText);
5662 ao2_unlock(q);
5663 queue_unref(q);
5666 astman_append(s,
5667 "Event: QueueStatusComplete\r\n"
5668 "%s"
5669 "\r\n",idText);
5671 return RESULT_SUCCESS;
5674 static int manager_add_queue_member(struct mansession *s, const struct message *m)
5676 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
5677 int paused, penalty = 0;
5679 queuename = astman_get_header(m, "Queue");
5680 interface = astman_get_header(m, "Interface");
5681 penalty_s = astman_get_header(m, "Penalty");
5682 paused_s = astman_get_header(m, "Paused");
5683 membername = astman_get_header(m, "MemberName");
5684 state_interface = astman_get_header(m, "StateInterface");
5686 if (ast_strlen_zero(queuename)) {
5687 astman_send_error(s, m, "'Queue' not specified.");
5688 return 0;
5691 if (ast_strlen_zero(interface)) {
5692 astman_send_error(s, m, "'Interface' not specified.");
5693 return 0;
5696 if (ast_strlen_zero(penalty_s))
5697 penalty = 0;
5698 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
5699 penalty = 0;
5701 if (ast_strlen_zero(paused_s))
5702 paused = 0;
5703 else
5704 paused = abs(ast_true(paused_s));
5706 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
5707 case RES_OKAY:
5708 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
5709 astman_send_ack(s, m, "Added interface to queue");
5710 break;
5711 case RES_EXISTS:
5712 astman_send_error(s, m, "Unable to add interface: Already there");
5713 break;
5714 case RES_NOSUCHQUEUE:
5715 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
5716 break;
5717 case RES_OUTOFMEMORY:
5718 astman_send_error(s, m, "Out of memory");
5719 break;
5722 return 0;
5725 static int manager_remove_queue_member(struct mansession *s, const struct message *m)
5727 const char *queuename, *interface;
5729 queuename = astman_get_header(m, "Queue");
5730 interface = astman_get_header(m, "Interface");
5732 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
5733 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
5734 return 0;
5737 switch (remove_from_queue(queuename, interface)) {
5738 case RES_OKAY:
5739 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
5740 astman_send_ack(s, m, "Removed interface from queue");
5741 break;
5742 case RES_EXISTS:
5743 astman_send_error(s, m, "Unable to remove interface: Not there");
5744 break;
5745 case RES_NOSUCHQUEUE:
5746 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
5747 break;
5748 case RES_OUTOFMEMORY:
5749 astman_send_error(s, m, "Out of memory");
5750 break;
5751 case RES_NOT_DYNAMIC:
5752 astman_send_error(s, m, "Member not dynamic");
5753 break;
5756 return 0;
5759 static int manager_pause_queue_member(struct mansession *s, const struct message *m)
5761 const char *queuename, *interface, *paused_s, *reason;
5762 int paused;
5764 interface = astman_get_header(m, "Interface");
5765 paused_s = astman_get_header(m, "Paused");
5766 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
5767 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */
5769 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
5770 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
5771 return 0;
5774 paused = abs(ast_true(paused_s));
5776 if (set_member_paused(queuename, interface, reason, paused))
5777 astman_send_error(s, m, "Interface not found");
5778 else
5779 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
5780 return 0;
5783 static int manager_queue_log_custom(struct mansession *s, const struct message *m)
5785 const char *queuename, *event, *message, *interface, *uniqueid;
5787 queuename = astman_get_header(m, "Queue");
5788 uniqueid = astman_get_header(m, "UniqueId");
5789 interface = astman_get_header(m, "Interface");
5790 event = astman_get_header(m, "Event");
5791 message = astman_get_header(m, "Message");
5793 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
5794 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
5795 return 0;
5798 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
5799 astman_send_ack(s, m, "Event added successfully");
5801 return 0;
5804 static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
5806 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
5807 switch (pos) {
5808 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
5809 return NULL;
5810 case 4: /* only one possible match, "to" */
5811 return state == 0 ? ast_strdup("to") : NULL;
5812 case 5: /* <queue> */
5813 return complete_queue(line, word, pos, state);
5814 case 6: /* only one possible match, "penalty" */
5815 return state == 0 ? ast_strdup("penalty") : NULL;
5816 case 7:
5817 if (state < 100) { /* 0-99 */
5818 char *num;
5819 if ((num = ast_malloc(3))) {
5820 sprintf(num, "%d", state);
5822 return num;
5823 } else {
5824 return NULL;
5826 case 8: /* only one possible match, "as" */
5827 return state == 0 ? ast_strdup("as") : NULL;
5828 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
5829 return NULL;
5830 default:
5831 return NULL;
5835 static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
5837 const char *queuename, *interface, *penalty_s;
5838 int penalty;
5840 interface = astman_get_header(m, "Interface");
5841 penalty_s = astman_get_header(m, "Penalty");
5842 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
5843 queuename = astman_get_header(m, "Queue");
5845 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
5846 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
5847 return 0;
5850 penalty = atoi(penalty_s);
5852 if (set_member_penalty((char *)queuename, (char *)interface, penalty))
5853 astman_send_error(s, m, "Invalid interface, queuename or penalty");
5854 else
5855 astman_send_ack(s, m, "Interface penalty set successfully");
5857 return 0;
5860 static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5862 char *queuename, *interface, *membername = NULL, *state_interface = NULL;
5863 int penalty;
5865 switch ( cmd ) {
5866 case CLI_INIT:
5867 e->command = "queue add member";
5868 e->usage =
5869 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
5870 return NULL;
5871 case CLI_GENERATE:
5872 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
5875 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
5876 return CLI_SHOWUSAGE;
5877 } else if (strcmp(a->argv[4], "to")) {
5878 return CLI_SHOWUSAGE;
5879 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
5880 return CLI_SHOWUSAGE;
5881 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
5882 return CLI_SHOWUSAGE;
5883 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
5884 return CLI_SHOWUSAGE;
5887 queuename = a->argv[5];
5888 interface = a->argv[3];
5889 if (a->argc >= 8) {
5890 if (sscanf(a->argv[7], "%d", &penalty) == 1) {
5891 if (penalty < 0) {
5892 ast_cli(a->fd, "Penalty must be >= 0\n");
5893 penalty = 0;
5895 } else {
5896 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
5897 penalty = 0;
5899 } else {
5900 penalty = 0;
5903 if (a->argc >= 10) {
5904 membername = a->argv[9];
5907 if (a->argc >= 12) {
5908 state_interface = a->argv[11];
5911 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
5912 case RES_OKAY:
5913 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
5914 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
5915 return CLI_SUCCESS;
5916 case RES_EXISTS:
5917 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
5918 return CLI_FAILURE;
5919 case RES_NOSUCHQUEUE:
5920 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
5921 return CLI_FAILURE;
5922 case RES_OUTOFMEMORY:
5923 ast_cli(a->fd, "Out of memory\n");
5924 return CLI_FAILURE;
5925 case RES_NOT_DYNAMIC:
5926 ast_cli(a->fd, "Member not dynamic\n");
5927 return CLI_FAILURE;
5928 default:
5929 return CLI_FAILURE;
5933 static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
5935 int which = 0;
5936 struct call_queue *q;
5937 struct member *m;
5938 struct ao2_iterator queue_iter;
5939 struct ao2_iterator mem_iter;
5940 int wordlen = strlen(word);
5942 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
5943 if (pos > 5 || pos < 3)
5944 return NULL;
5945 if (pos == 4) /* only one possible match, 'from' */
5946 return (state == 0 ? ast_strdup("from") : NULL);
5948 if (pos == 5) /* No need to duplicate code */
5949 return complete_queue(line, word, pos, state);
5951 /* here is the case for 3, <member> */
5952 queue_iter = ao2_iterator_init(queues, 0);
5953 while ((q = ao2_iterator_next(&queue_iter))) {
5954 ao2_lock(q);
5955 mem_iter = ao2_iterator_init(q->members, 0);
5956 while ((m = ao2_iterator_next(&mem_iter))) {
5957 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
5958 char *tmp;
5959 ao2_unlock(q);
5960 tmp = m->membername;
5961 ao2_ref(m, -1);
5962 queue_unref(q);
5963 return ast_strdup(tmp);
5965 ao2_ref(m, -1);
5967 ao2_unlock(q);
5968 queue_unref(q);
5971 return NULL;
5974 static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
5976 char *queuename, *interface;
5978 switch (cmd) {
5979 case CLI_INIT:
5980 e->command = "queue remove member";
5981 e->usage = "Usage: queue remove member <channel> from <queue>\n";
5982 return NULL;
5983 case CLI_GENERATE:
5984 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
5987 if (a->argc != 6) {
5988 return CLI_SHOWUSAGE;
5989 } else if (strcmp(a->argv[4], "from")) {
5990 return CLI_SHOWUSAGE;
5993 queuename = a->argv[5];
5994 interface = a->argv[3];
5996 switch (remove_from_queue(queuename, interface)) {
5997 case RES_OKAY:
5998 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
5999 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
6000 return CLI_SUCCESS;
6001 case RES_EXISTS:
6002 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
6003 return CLI_FAILURE;
6004 case RES_NOSUCHQUEUE:
6005 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
6006 return CLI_FAILURE;
6007 case RES_OUTOFMEMORY:
6008 ast_cli(a->fd, "Out of memory\n");
6009 return CLI_FAILURE;
6010 default:
6011 return CLI_FAILURE;
6015 static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
6017 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
6018 switch (pos) {
6019 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
6020 return NULL;
6021 case 4: /* only one possible match, "queue" */
6022 return state == 0 ? ast_strdup("queue") : NULL;
6023 case 5: /* <queue> */
6024 return complete_queue(line, word, pos, state);
6025 case 6: /* "reason" */
6026 return state == 0 ? ast_strdup("reason") : NULL;
6027 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
6028 return NULL;
6029 default:
6030 return NULL;
6034 static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6036 char *queuename, *interface, *reason;
6037 int paused;
6039 switch (cmd) {
6040 case CLI_INIT:
6041 e->command = "queue {pause|unpause} member";
6042 e->usage =
6043 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
6044 " Pause or unpause a queue member. Not specifying a particular queue\n"
6045 " will pause or unpause a member across all queues to which the member\n"
6046 " belongs.\n";
6047 return NULL;
6048 case CLI_GENERATE:
6049 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
6052 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
6053 return CLI_SHOWUSAGE;
6054 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
6055 return CLI_SHOWUSAGE;
6056 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
6057 return CLI_SHOWUSAGE;
6061 interface = a->argv[3];
6062 queuename = a->argc >= 6 ? a->argv[5] : NULL;
6063 reason = a->argc == 8 ? a->argv[7] : NULL;
6064 paused = !strcasecmp(a->argv[1], "pause");
6066 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
6067 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
6068 if (!ast_strlen_zero(queuename))
6069 ast_cli(a->fd, " in queue '%s'", queuename);
6070 if (!ast_strlen_zero(reason))
6071 ast_cli(a->fd, " for reason '%s'", reason);
6072 ast_cli(a->fd, "\n");
6073 return CLI_SUCCESS;
6074 } else {
6075 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
6076 if (!ast_strlen_zero(queuename))
6077 ast_cli(a->fd, " in queue '%s'", queuename);
6078 if (!ast_strlen_zero(reason))
6079 ast_cli(a->fd, " for reason '%s'", reason);
6080 ast_cli(a->fd, "\n");
6081 return CLI_FAILURE;
6085 static char *complete_queue_set_member_penalty(const char *line, const char *word, int pos, int state)
6087 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
6088 switch (pos) {
6089 case 4:
6090 if (state == 0) {
6091 return ast_strdup("on");
6092 } else {
6093 return NULL;
6095 case 6:
6096 if (state == 0) {
6097 return ast_strdup("in");
6098 } else {
6099 return NULL;
6101 case 7:
6102 return complete_queue(line, word, pos, state);
6103 default:
6104 return NULL;
6108 static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6110 char *queuename = NULL, *interface;
6111 int penalty = 0;
6113 switch (cmd) {
6114 case CLI_INIT:
6115 e->command = "queue set penalty";
6116 e->usage =
6117 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
6118 "Set a member's penalty in the queue specified. If no queue is specified\n"
6119 "then that interface's penalty is set in all queues to which that interface is a member\n";
6120 return NULL;
6121 case CLI_GENERATE:
6122 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
6125 if (a->argc != 6 && a->argc != 8) {
6126 return CLI_SHOWUSAGE;
6127 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
6128 return CLI_SHOWUSAGE;
6131 if (a->argc == 8)
6132 queuename = a->argv[7];
6133 interface = a->argv[5];
6134 penalty = atoi(a->argv[3]);
6136 switch (set_member_penalty(queuename, interface, penalty)) {
6137 case RESULT_SUCCESS:
6138 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6139 return CLI_SUCCESS;
6140 case RESULT_FAILURE:
6141 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
6142 return CLI_FAILURE;
6143 default:
6144 return CLI_FAILURE;
6148 static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
6150 int which = 0;
6151 struct rule_list *rl_iter;
6152 int wordlen = strlen(word);
6153 char *ret = NULL;
6154 if (pos != 3) /* Wha? */ {
6155 ast_log(LOG_DEBUG, "Hitting this???, pos is %d\n", pos);
6156 return NULL;
6159 AST_LIST_LOCK(&rule_lists);
6160 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6161 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
6162 ret = ast_strdup(rl_iter->name);
6163 break;
6166 AST_LIST_UNLOCK(&rule_lists);
6168 return ret;
6171 static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6173 char *rule;
6174 struct rule_list *rl_iter;
6175 struct penalty_rule *pr_iter;
6176 switch (cmd) {
6177 case CLI_INIT:
6178 e->command = "queue rules show";
6179 e->usage =
6180 "Usage: queue rules show [rulename]\n"
6181 "Show the list of rules associated with rulename. If no\n"
6182 "rulename is specified, list all rules defined in queuerules.conf\n";
6183 return NULL;
6184 case CLI_GENERATE:
6185 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
6188 if (a->argc != 3 && a->argc != 4)
6189 return CLI_SHOWUSAGE;
6191 rule = a->argc == 4 ? a->argv[3] : "";
6192 AST_LIST_LOCK(&rule_lists);
6193 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
6194 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
6195 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
6196 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
6197 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);
6201 AST_LIST_UNLOCK(&rule_lists);
6202 return CLI_SUCCESS;
6205 static char *handle_queue_rule_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
6207 switch (cmd) {
6208 case CLI_INIT:
6209 e->command = "queue rules reload";
6210 e->usage =
6211 "Usage: queue rules reload\n"
6212 "Reloads rules defined in queuerules.conf\n";
6213 return NULL;
6214 case CLI_GENERATE:
6215 return NULL;
6217 reload_queue_rules(1);
6218 return CLI_SUCCESS;
6221 static const char qpm_cmd_usage[] =
6222 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
6224 static const char qum_cmd_usage[] =
6225 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
6227 static const char qsmp_cmd_usage[] =
6228 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
6230 static struct ast_cli_entry cli_queue[] = {
6231 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
6232 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
6233 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
6234 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
6235 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
6236 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
6237 AST_CLI_DEFINE(handle_queue_rule_reload, "Reload the rules defined in queuerules.conf"),
6240 static int unload_module(void)
6242 int res;
6243 struct ast_context *con;
6244 struct ao2_iterator q_iter;
6245 struct call_queue *q = NULL;
6247 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6248 res = ast_manager_unregister("QueueStatus");
6249 res |= ast_manager_unregister("Queues");
6250 res |= ast_manager_unregister("QueueRule");
6251 res |= ast_manager_unregister("QueueSummary");
6252 res |= ast_manager_unregister("QueueAdd");
6253 res |= ast_manager_unregister("QueueRemove");
6254 res |= ast_manager_unregister("QueuePause");
6255 res |= ast_manager_unregister("QueueLog");
6256 res |= ast_manager_unregister("QueuePenalty");
6257 res |= ast_unregister_application(app_aqm);
6258 res |= ast_unregister_application(app_rqm);
6259 res |= ast_unregister_application(app_pqm);
6260 res |= ast_unregister_application(app_upqm);
6261 res |= ast_unregister_application(app_ql);
6262 res |= ast_unregister_application(app);
6263 res |= ast_custom_function_unregister(&queuevar_function);
6264 res |= ast_custom_function_unregister(&queuemembercount_function);
6265 res |= ast_custom_function_unregister(&queuemembercount_dep);
6266 res |= ast_custom_function_unregister(&queuememberlist_function);
6267 res |= ast_custom_function_unregister(&queuewaitingcount_function);
6268 res |= ast_custom_function_unregister(&queuememberpenalty_function);
6270 if (device_state_sub)
6271 ast_event_unsubscribe(device_state_sub);
6273 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
6274 ast_context_remove_extension2(con, "s", 1, NULL);
6275 ast_context_destroy(con, "app_queue"); /* leave no trace */
6278 clear_and_free_interfaces();
6280 q_iter = ao2_iterator_init(queues, 0);
6281 while ((q = ao2_iterator_next(&q_iter))) {
6282 ao2_unlink(queues, q);
6283 queue_unref(q);
6285 ao2_ref(queues, -1);
6286 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
6287 return res;
6290 static int load_module(void)
6292 int res;
6293 struct ast_context *con;
6295 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
6297 if (!reload_queues(0))
6298 return AST_MODULE_LOAD_DECLINE;
6300 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
6301 if (!con)
6302 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
6303 else
6304 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_queue");
6306 if (queue_persistent_members)
6307 reload_queue_members();
6309 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
6310 res = ast_register_application(app, queue_exec, synopsis, descrip);
6311 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
6312 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
6313 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
6314 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
6315 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
6316 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
6317 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
6318 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
6319 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
6320 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
6321 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
6322 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
6323 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member");
6324 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
6325 res |= ast_custom_function_register(&queuevar_function);
6326 res |= ast_custom_function_register(&queuemembercount_function);
6327 res |= ast_custom_function_register(&queuemembercount_dep);
6328 res |= ast_custom_function_register(&queuememberlist_function);
6329 res |= ast_custom_function_register(&queuewaitingcount_function);
6330 res |= ast_custom_function_register(&queuememberpenalty_function);
6332 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
6333 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
6336 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END)))
6337 res = -1;
6339 return res ? AST_MODULE_LOAD_DECLINE : 0;
6342 static int reload(void)
6344 reload_queues(1);
6345 return 0;
6348 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "True Call Queueing",
6349 .load = load_module,
6350 .unload = unload_module,
6351 .reload = reload,