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.
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
60 <depend>res_monitor</depend>
65 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
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"
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
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
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
{
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"
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;
298 QUEUE_LEAVEEMPTY
= 3,
299 QUEUE_JOINUNAVAIL
= 4,
300 QUEUE_LEAVEUNAVAIL
= 5,
306 enum queue_result id
;
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.
332 struct callattempt
*q_next
;
333 struct callattempt
*call_next
;
334 struct ast_channel
*chan
;
340 struct call_queue
*lastqueue
;
341 struct member
*member
;
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 */
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
{
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> */
420 AST_DECLARE_STRING_FIELDS(
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
);
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
];
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;
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 */
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().
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 */
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
)
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
);
545 static const char *int2strat(int strategy
)
549 for (x
= 0; x
< ARRAY_LEN(strategies
); x
++) {
550 if (strategy
== strategies
[x
].strategy
)
551 return strategies
[x
].name
;
557 static int strat2int(const char *strategy
)
561 for (x
= 0; x
< ARRAY_LEN(strategies
); x
++) {
562 if (!strcasecmp(strategy
, strategies
[x
].name
))
563 return strategies
[x
].strategy
;
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
)
587 static inline struct call_queue
*queue_unref(struct call_queue
*q
)
593 /*! \brief Set variables of queue */
594 static void set_queue_variables(struct queue_ent
*qe
)
596 char interfacevar
[256]="";
599 if (qe
->parent
->setqueuevar
) {
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
;
633 enum queue_member_status
{
635 QUEUE_NO_REACHABLE_MEMBERS
,
636 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
,
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
;
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
)))
658 switch (member
->status
) {
659 case AST_DEVICE_INVALID
:
662 case AST_DEVICE_UNAVAILABLE
:
663 if (result
!= QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)
664 result
= QUEUE_NO_REACHABLE_MEMBERS
;
667 if (member
->paused
) {
668 result
= QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
;
683 AST_LIST_ENTRY(statechange
) entry
;
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
)
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
))) {
702 mem_iter
= ao2_iterator_init(q
->members
, 0);
703 while ((cur
= ao2_iterator_next(&mem_iter
))) {
706 tmp_interface
= ast_strdupa(cur
->state_interface
);
707 if ((slash_pos
= strchr(interface
, '/')))
708 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
711 if (strcasecmp(interface
, tmp_interface
)) {
716 if (cur
->status
!= status
) {
717 cur
->status
= status
;
718 if (q
->maskmemberstatus
) {
723 manager_event(EVENT_FLAG_AGENT
, "QueueMemberStatus",
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
);
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
;
751 struct statechange
*sc
= datap
;
753 technology
= ast_strdupa(sc
->dev
);
754 loc
= strchr(technology
, '/');
762 AST_LIST_LOCK(&interfaces
);
763 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
766 interface
= ast_strdupa(curint
->interface
);
767 if ((slash_pos
= strchr(interface
, '/')))
768 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
771 if (!strcasecmp(interface
, sc
->dev
))
774 AST_LIST_UNLOCK(&interfaces
);
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
));
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
);
790 static void device_state_cb(const struct ast_event
*event
, void *unused
)
792 enum ast_device_state state
;
794 struct statechange
*sc
;
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");
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");
810 strcpy(sc
->dev
, device
);
811 if (ast_taskprocessor_push(devicestate_tps
, handle_statechange
, sc
) < 0) {
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
)
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
));
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
));
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
);
842 static int compress_char(const char c
)
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
, '/');
858 chname
= mem
->interface
;
859 for (i
= 0; i
< 5 && chname
[i
]; i
++)
860 ret
+= compress_char(chname
[i
]) << (i
* 6);
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
;
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
)
877 struct penalty_rule
*pr_iter
;
880 q
->retry
= DEFAULT_RETRY
;
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 */
891 q
->setinterfacevar
= 0;
893 q
->setqueueentryvar
= 0;
894 q
->autofill
= autofill_default
;
895 q
->montype
= montype_default
;
897 q
->reportholdtime
= 0;
900 q
->leavewhenempty
= 0;
902 q
->maskmemberstatus
= 0;
903 q
->eventwhencalled
= 0;
905 q
->timeoutrestart
= 0;
906 q
->periodicannouncefrequency
= 0;
907 q
->randomperiodicannounce
= 0;
908 q
->numperiodicannounce
= 0;
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
);
914 q
->members
= ao2_container_alloc(37, member_hash_fn
, member_cmp_fn
);
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
)))
943 static void clear_queue(struct call_queue
*q
)
946 q
->callscompleted
= 0;
947 q
->callsabandoned
= 0;
948 q
->callscompletedinsl
= 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
))
963 AST_LIST_UNLOCK(&interfaces
);
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
);
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
;
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
))) {
989 mem_iter
= ao2_iterator_init(q
->members
, 0);
990 while ((mem
= ao2_iterator_next(&mem_iter
))) {
991 if (!strcasecmp(mem
->state_interface
, interface
)) {
1004 static int remove_from_interfaces(const char *interface
)
1006 struct member_interface
*curint
;
1008 if (interface_exists_global(interface
))
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
);
1020 AST_LIST_TRAVERSE_SAFE_END
;
1021 AST_LIST_UNLOCK(&interfaces
);
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
)))
1033 AST_LIST_UNLOCK(&interfaces
);
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
);
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
);
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
);
1076 if ((minstr
= strchr(maxstr
,',')))
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
))
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
);
1106 AST_LIST_TRAVERSE_SAFE_END
;
1109 AST_LIST_INSERT_TAIL(&rl_iter
->rules
, rule
, list
);
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
);
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)) {
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
);
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
;
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
;
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
);
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
);
1223 if (i
== MAX_PERIODIC_ANNOUNCEMENTS
)
1226 q
->numperiodicannounce
= i
;
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
);
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"))
1246 } else if (!strcasecmp(param
, "autopause")) {
1247 q
->autopause
= ast_true(val
);
1248 } else if (!strcasecmp(param
, "maxlen")) {
1249 q
->maxlen
= atoi(val
);
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 */
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
;
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
;
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
;
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
);
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
) {
1299 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1300 q
->name
, param
, linenum
);
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
)
1316 struct ao2_iterator mem_iter
;
1322 penalty
= atoi(penalty_str
);
1328 paused
= atoi(paused_str
);
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
));
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
;
1354 /* Create a new member */
1356 if ((m
= create_queue_member(interface
, membername
, penalty
, paused
, state_interface
))) {
1359 ast_copy_string(m
->rt_uniqueid
, rt_uniqueid
, sizeof(m
->rt_uniqueid
));
1360 add_to_interfaces(m
->state_interface
);
1361 ao2_link(q
->members
, m
);
1369 /*! \brief Iterate through queue's member list and delete them */
1370 static void free_members(struct call_queue
*q
, int all
)
1372 /* Free non-dynamic members */
1374 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
1376 while ((cur
= ao2_iterator_next(&mem_iter
))) {
1377 if (all
|| !cur
->dynamic
) {
1378 ao2_unlink(q
->members
, cur
);
1379 remove_from_interfaces(cur
->state_interface
);
1386 /*! \brief Free queue's member list then its string fields */
1387 static void destroy_queue(void *obj
)
1389 struct call_queue
*q
= obj
;
1393 ast_string_field_free_memory(q
);
1394 for (i
= 0; i
< MAX_PERIODIC_ANNOUNCEMENTS
; i
++) {
1395 if (q
->sound_periodicannounce
[i
])
1396 free(q
->sound_periodicannounce
[i
]);
1398 ao2_ref(q
->members
, -1);
1401 static struct call_queue
*alloc_queue(const char *queuename
)
1403 struct call_queue
*q
;
1405 if ((q
= ao2_alloc(sizeof(*q
), destroy_queue
))) {
1406 if (ast_string_field_init(q
, 64)) {
1410 ast_string_field_set(q
, name
, queuename
);
1416 * \brief Reload a single queue via realtime.
1418 * Check for statically defined queue first, check if deleted RT queue,
1419 * check for new RT queue, if queue vars are not defined init them with defaults.
1420 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
1421 * \retval the queue,
1422 * \retval NULL if it doesn't exist.
1423 * \note Should be called with the "queues" container locked.
1425 static struct call_queue
*find_queue_by_name_rt(const char *queuename
, struct ast_variable
*queue_vars
, struct ast_config
*member_config
)
1427 struct ast_variable
*v
;
1428 struct call_queue
*q
, tmpq
= {
1432 struct ao2_iterator mem_iter
;
1433 char *interface
= NULL
;
1434 const char *tmp_name
;
1436 char tmpbuf
[64]; /* Must be longer than the longest queue param name. */
1438 /* Static queues override realtime. */
1439 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
1447 ast_log(LOG_WARNING
, "Static queue '%s' already exists. Not loading from realtime\n", q
->name
);
1453 } else if (!member_config
)
1454 /* Not found in the list, and it's not realtime ... */
1457 /* Check if queue is defined in realtime. */
1459 /* Delete queue from in-core list if it has been deleted in realtime. */
1461 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1462 found condition... So we might delete an in-core queue
1463 in case of DB failure. */
1464 ast_debug(1, "Queue %s not found in realtime.\n", queuename
);
1467 /* Delete if unused (else will be deleted when last caller leaves). */
1468 ao2_unlink(queues
, q
);
1475 /* Create a new queue if an in-core entry does not exist yet. */
1477 struct ast_variable
*tmpvar
= NULL
;
1478 if (!(q
= alloc_queue(queuename
)))
1483 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
1484 * will allocate the members properly
1486 for (tmpvar
= queue_vars
; tmpvar
; tmpvar
= tmpvar
->next
) {
1487 if (!strcasecmp(tmpvar
->name
, "strategy")) {
1488 q
->strategy
= strat2int(tmpvar
->value
);
1489 if (q
->strategy
< 0) {
1490 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1491 tmpvar
->value
, q
->name
);
1492 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
1497 /* We traversed all variables and didn't find a strategy */
1499 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
1500 init_queue(q
); /* Ensure defaults for all parameters not set explicitly. */
1501 ao2_link(queues
, q
);
1504 memset(tmpbuf
, 0, sizeof(tmpbuf
));
1505 for (v
= queue_vars
; v
; v
= v
->next
) {
1506 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1507 if ((tmp
= strchr(v
->name
, '_'))) {
1508 ast_copy_string(tmpbuf
, v
->name
, sizeof(tmpbuf
));
1511 while ((tmp
= strchr(tmp
, '_')))
1516 if (!ast_strlen_zero(v
->value
)) {
1517 /* Don't want to try to set the option if the value is empty */
1518 queue_set_param(q
, tmp_name
, v
->value
, -1, 0);
1522 /* Temporarily set realtime members dead so we can detect deleted ones.
1523 * Also set the membercount correctly for realtime*/
1524 mem_iter
= ao2_iterator_init(q
->members
, 0);
1525 while ((m
= ao2_iterator_next(&mem_iter
))) {
1532 while ((interface
= ast_category_browse(member_config
, interface
))) {
1533 rt_handle_member_record(q
, interface
,
1534 ast_variable_retrieve(member_config
, interface
, "uniqueid"),
1535 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"),interface
),
1536 ast_variable_retrieve(member_config
, interface
, "penalty"),
1537 ast_variable_retrieve(member_config
, interface
, "paused"),
1538 S_OR(ast_variable_retrieve(member_config
, interface
, "state_interface"),interface
));
1541 /* Delete all realtime members that have been deleted in DB. */
1542 mem_iter
= ao2_iterator_init(q
->members
, 0);
1543 while ((m
= ao2_iterator_next(&mem_iter
))) {
1545 ao2_unlink(q
->members
, m
);
1546 remove_from_interfaces(m
->state_interface
);
1557 static struct call_queue
*load_realtime_queue(const char *queuename
)
1559 struct ast_variable
*queue_vars
;
1560 struct ast_config
*member_config
= NULL
;
1561 struct call_queue
*q
= NULL
, tmpq
= {
1565 /* Find the queue in the in-core list first. */
1566 q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
);
1568 if (!q
|| q
->realtime
) {
1569 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
1570 queue operations while waiting for the DB.
1572 This will be two separate database transactions, so we might
1573 see queue parameters as they were before another process
1574 changed the queue and member list as it was after the change.
1575 Thus we might see an empty member list when a queue is
1576 deleted. In practise, this is unlikely to cause a problem. */
1578 queue_vars
= ast_load_realtime("queues", "name", queuename
, NULL
);
1580 member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename
, NULL
);
1581 if (!member_config
) {
1582 ast_log(LOG_ERROR
, "no queue_members defined in your config (extconfig.conf).\n");
1583 ast_variables_destroy(queue_vars
);
1589 q
= find_queue_by_name_rt(queuename
, queue_vars
, member_config
);
1591 ast_config_destroy(member_config
);
1593 ast_variables_destroy(queue_vars
);
1597 update_realtime_members(q
);
1602 static int update_realtime_member_field(struct member
*mem
, const char *queue_name
, const char *field
, const char *value
)
1606 if (ast_strlen_zero(mem
->rt_uniqueid
))
1609 if ((ast_update_realtime("queue_members", "uniqueid", mem
->rt_uniqueid
, field
, value
, NULL
)) > 0)
1616 static void update_realtime_members(struct call_queue
*q
)
1618 struct ast_config
*member_config
= NULL
;
1620 char *interface
= NULL
;
1621 struct ao2_iterator mem_iter
;
1623 if (!(member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q
->name
, NULL
))) {
1624 /*This queue doesn't have realtime members*/
1625 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q
->name
);
1631 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1632 mem_iter
= ao2_iterator_init(q
->members
, 0);
1633 while ((m
= ao2_iterator_next(&mem_iter
))) {
1639 while ((interface
= ast_category_browse(member_config
, interface
))) {
1640 rt_handle_member_record(q
, interface
,
1641 ast_variable_retrieve(member_config
, interface
, "uniqueid"),
1642 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"), interface
),
1643 ast_variable_retrieve(member_config
, interface
, "penalty"),
1644 ast_variable_retrieve(member_config
, interface
, "paused"),
1645 S_OR(ast_variable_retrieve(member_config
, interface
, "state_interface"), interface
));
1648 /* Delete all realtime members that have been deleted in DB. */
1649 mem_iter
= ao2_iterator_init(q
->members
, 0);
1650 while ((m
= ao2_iterator_next(&mem_iter
))) {
1652 ao2_unlink(q
->members
, m
);
1653 remove_from_interfaces(m
->state_interface
);
1659 ast_config_destroy(member_config
);
1662 static int join_queue(char *queuename
, struct queue_ent
*qe
, enum queue_result
*reason
)
1664 struct call_queue
*q
;
1665 struct queue_ent
*cur
, *prev
= NULL
;
1669 enum queue_member_status stat
;
1671 if (!(q
= load_realtime_queue(queuename
)))
1677 /* This is our one */
1678 stat
= get_member_status(q
, qe
->max_penalty
, qe
->min_penalty
);
1679 if (!q
->joinempty
&& (stat
== QUEUE_NO_MEMBERS
))
1680 *reason
= QUEUE_JOINEMPTY
;
1681 else if ((q
->joinempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
))
1682 *reason
= QUEUE_JOINUNAVAIL
;
1683 else if ((q
->joinempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
))
1684 *reason
= QUEUE_JOINUNAVAIL
;
1685 else if (q
->maxlen
&& (q
->count
>= q
->maxlen
))
1686 *reason
= QUEUE_FULL
;
1688 /* There's space for us, put us at the right position inside
1690 * Take into account the priority of the calling user */
1695 /* We have higher priority than the current user, enter
1696 * before him, after all the other users with priority
1697 * higher or equal to our priority. */
1698 if ((!inserted
) && (qe
->prio
> cur
->prio
)) {
1699 insert_entry(q
, prev
, qe
, &pos
);
1706 /* No luck, join at the end of the queue */
1708 insert_entry(q
, prev
, qe
, &pos
);
1709 ast_copy_string(qe
->moh
, q
->moh
, sizeof(qe
->moh
));
1710 ast_copy_string(qe
->announce
, q
->announce
, sizeof(qe
->announce
));
1711 ast_copy_string(qe
->context
, q
->context
, sizeof(qe
->context
));
1714 manager_event(EVENT_FLAG_CALL
, "Join",
1715 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1717 S_OR(qe
->chan
->cid
.cid_num
, "unknown"), /* XXX somewhere else it is <unknown> */
1718 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
1719 q
->name
, qe
->pos
, q
->count
, qe
->chan
->uniqueid
);
1720 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q
->name
, qe
->chan
->name
, qe
->pos
);
1728 static int play_file(struct ast_channel
*chan
, const char *filename
)
1732 ast_stopstream(chan
);
1734 res
= ast_streamfile(chan
, filename
, chan
->language
);
1736 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
1738 ast_stopstream(chan
);
1744 * \brief Check for valid exit from queue via goto
1745 * \retval 0 if failure
1746 * \retval 1 if successful
1748 static int valid_exit(struct queue_ent
*qe
, char digit
)
1750 int digitlen
= strlen(qe
->digits
);
1752 /* Prevent possible buffer overflow */
1753 if (digitlen
< sizeof(qe
->digits
) - 2) {
1754 qe
->digits
[digitlen
] = digit
;
1755 qe
->digits
[digitlen
+ 1] = '\0';
1757 qe
->digits
[0] = '\0';
1761 /* If there's no context to goto, short-circuit */
1762 if (ast_strlen_zero(qe
->context
))
1765 /* If the extension is bad, then reset the digits to blank */
1766 if (!ast_canmatch_extension(qe
->chan
, qe
->context
, qe
->digits
, 1, qe
->chan
->cid
.cid_num
)) {
1767 qe
->digits
[0] = '\0';
1771 /* We have an exact match */
1772 if (!ast_goto_if_exists(qe
->chan
, qe
->context
, qe
->digits
, 1)) {
1773 qe
->valid_digits
= 1;
1774 /* Return 1 on a successful goto */
1781 static int say_position(struct queue_ent
*qe
, int ringing
)
1783 int res
= 0, avgholdmins
, avgholdsecs
, announceposition
= 0;
1786 /* Let minannouncefrequency seconds pass between the start of each position announcement */
1788 if ((now
- qe
->last_pos
) < qe
->parent
->minannouncefrequency
)
1791 /* If either our position has changed, or we are over the freq timer, say position */
1792 if ((qe
->last_pos_said
== qe
->pos
) && ((now
- qe
->last_pos
) < qe
->parent
->announcefrequency
))
1796 ast_indicate(qe
->chan
,-1);
1798 ast_moh_stop(qe
->chan
);
1801 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_YES
||
1802 qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
||
1803 (qe
->parent
->announceposition
== ANNOUNCEPOSITION_LIMIT
&&
1804 qe
->pos
<= qe
->parent
->announcepositionlimit
))
1805 announceposition
= 1;
1808 if (announceposition
== 1) {
1809 /* Say we're next, if we are */
1811 res
= play_file(qe
->chan
, qe
->parent
->sound_next
);
1817 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
&& qe
->pos
> qe
->parent
->announcepositionlimit
){
1819 res
= play_file(qe
->chan
, qe
->parent
->queue_quantity1
);
1822 res
= ast_say_number(qe
->chan
, qe
->parent
->announcepositionlimit
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
); /* Needs gender */
1827 res
= play_file(qe
->chan
, qe
->parent
->sound_thereare
);
1830 res
= ast_say_number(qe
->chan
, qe
->pos
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
); /* Needs gender */
1834 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
&& qe
->pos
> qe
->parent
->announcepositionlimit
){
1836 res
= play_file(qe
->chan
, qe
->parent
->queue_quantity2
);
1840 res
= play_file(qe
->chan
, qe
->parent
->sound_calls
);
1846 /* Round hold time to nearest minute */
1847 avgholdmins
= abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
)) / 60);
1849 /* If they have specified a rounding then round the seconds as well */
1850 if (qe
->parent
->roundingseconds
) {
1851 avgholdsecs
= (abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
))) - 60 * avgholdmins
) / qe
->parent
->roundingseconds
;
1852 avgholdsecs
*= qe
->parent
->roundingseconds
;
1857 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe
->parent
->name
, avgholdmins
, avgholdsecs
);
1859 /* If the hold time is >1 min, if it's enabled, and if it's not
1860 supposed to be only once and we have already said it, say it */
1861 if ((avgholdmins
+avgholdsecs
) > 0 && (qe
->parent
->announceholdtime
) &&
1862 (!(qe
->parent
->announceholdtime
== ANNOUNCEHOLDTIME_ONCE
) && qe
->last_pos
)) {
1863 res
= play_file(qe
->chan
, qe
->parent
->sound_holdtime
);
1867 if (avgholdmins
> 1) {
1868 res
= ast_say_number(qe
->chan
, avgholdmins
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1872 if (avgholdmins
== 1) {
1873 res
= play_file(qe
->chan
, qe
->parent
->sound_minute
);
1877 res
= play_file(qe
->chan
, qe
->parent
->sound_minutes
);
1882 if (avgholdsecs
> 1) {
1883 res
= ast_say_number(qe
->chan
, avgholdmins
> 1 ? avgholdsecs
: avgholdmins
* 60 + avgholdsecs
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1887 res
= play_file(qe
->chan
, qe
->parent
->sound_seconds
);
1895 if (announceposition
== 1){
1896 if (qe
->parent
->announceposition
) {
1897 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
1898 qe
->chan
->name
, qe
->parent
->name
, qe
->pos
);
1900 res
= play_file(qe
->chan
, qe
->parent
->sound_thanks
);
1903 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
1906 /* Set our last_pos indicators */
1908 qe
->last_pos_said
= qe
->pos
;
1910 /* Don't restart music on hold if we're about to exit the caller from the queue */
1913 ast_indicate(qe
->chan
, AST_CONTROL_RINGING
);
1915 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
1921 static void recalc_holdtime(struct queue_ent
*qe
, int newholdtime
)
1925 /* Calculate holdtime using a recursive boxcar filter */
1926 /* Thanks to SRT for this contribution */
1927 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1929 ao2_lock(qe
->parent
);
1930 oldvalue
= qe
->parent
->holdtime
;
1931 qe
->parent
->holdtime
= (((oldvalue
<< 2) - oldvalue
) + newholdtime
) >> 2;
1932 ao2_unlock(qe
->parent
);
1935 /*! \brief Caller leaving queue.
1937 * Search the queue to find the leaving client, if found remove from queue
1938 * create manager event, move others up the queue.
1940 static void leave_queue(struct queue_ent
*qe
)
1942 struct call_queue
*q
;
1943 struct queue_ent
*cur
, *prev
= NULL
;
1944 struct penalty_rule
*pr_iter
;
1947 if (!(q
= qe
->parent
))
1953 for (cur
= q
->head
; cur
; cur
= cur
->next
) {
1957 /* Take us out of the queue */
1958 manager_event(EVENT_FLAG_CALL
, "Leave",
1959 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1960 qe
->chan
->name
, q
->name
, q
->count
, qe
->chan
->uniqueid
);
1961 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q
->name
, qe
->chan
->name
);
1962 /* Take us out of the queue */
1964 prev
->next
= cur
->next
;
1966 q
->head
= cur
->next
;
1967 /* Free penalty rules */
1968 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&qe
->qe_rules
, list
)))
1971 /* Renumber the people after us in the queue based on a new count */
1978 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
1980 if (!ast_load_realtime("queues", "name", q
->name
, NULL
))
1985 /* It's dead and nobody is in it, so kill it */
1986 ao2_unlink(queues
, q
);
1987 /* unref the container's reference to the queue */
1990 /* unref the explicit ref earlier in the function */
1994 /*! \brief Hang up a list of outgoing calls */
1995 static void hangupcalls(struct callattempt
*outgoing
, struct ast_channel
*exception
)
1997 struct callattempt
*oo
;
2000 /* Hangup any existing lines we have open */
2001 if (outgoing
->chan
&& (outgoing
->chan
!= exception
))
2002 ast_hangup(outgoing
->chan
);
2004 outgoing
= outgoing
->q_next
;
2006 ao2_ref(oo
->member
, -1);
2012 * \brief traverse all defined queues which have calls waiting and contain this member
2013 * \retval 0 if no other queue has precedence (higher weight)
2014 * \retval 1 if found
2016 static int compare_weight(struct call_queue
*rq
, struct member
*member
)
2018 struct call_queue
*q
;
2021 struct ao2_iterator queue_iter
;
2023 /* q's lock and rq's lock already set by try_calling()
2024 * to solve deadlock */
2025 queue_iter
= ao2_iterator_init(queues
, 0);
2026 while ((q
= ao2_iterator_next(&queue_iter
))) {
2027 if (q
== rq
) { /* don't check myself, could deadlock */
2032 if (q
->count
&& q
->members
) {
2033 if ((mem
= ao2_find(q
->members
, member
, OBJ_POINTER
))) {
2034 ast_debug(1, "Found matching member %s in queue '%s'\n", mem
->interface
, q
->name
);
2035 if (q
->weight
> rq
->weight
) {
2036 ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q
->name
, q
->weight
, q
->count
, rq
->name
, rq
->weight
, rq
->count
);
2052 /*! \brief common hangup actions */
2053 static void do_hang(struct callattempt
*o
)
2056 ast_hangup(o
->chan
);
2060 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2061 static char *vars2manager(struct ast_channel
*chan
, char *vars
, size_t len
)
2063 struct ast_str
*buf
= ast_str_alloca(len
+ 1);
2066 if (pbx_builtin_serialize_variables(chan
, &buf
)) {
2069 /* convert "\n" to "\nVariable: " */
2070 strcpy(vars
, "Variable: ");
2073 for (i
= 0, j
= 10; (i
< len
- 1) && (j
< len
- 1); i
++, j
++) {
2076 if (tmp
[i
+ 1] == '\0')
2078 if (tmp
[i
] == '\n') {
2082 ast_copy_string(&(vars
[j
]), "Variable: ", len
- j
);
2092 /* there are no channel variables; leave it blank */
2099 * \brief Part 2 of ring_one
2101 * Does error checking before attempting to request a channel and call a member.
2102 * This function is only called from ring_one().
2103 * Failure can occur if:
2106 * - Wrapup time not expired
2107 * - Priority by another queue
2109 * \retval 1 on success to reach a free agent
2110 * \retval 0 on failure to get agent.
2112 static int ring_entry(struct queue_ent
*qe
, struct callattempt
*tmp
, int *busies
)
2118 const char *macrocontext
, *macroexten
;
2120 /* on entry here, we know that tmp->chan == NULL */
2121 if ((tmp
->lastqueue
&& tmp
->lastqueue
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< tmp
->lastqueue
->wrapuptime
)) ||
2122 (!tmp
->lastqueue
&& qe
->parent
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< qe
->parent
->wrapuptime
))) {
2123 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
2124 (tmp
->lastqueue
? tmp
->lastqueue
->name
: qe
->parent
->name
), tmp
->interface
);
2126 ast_cdr_busy(qe
->chan
->cdr
);
2127 tmp
->stillgoing
= 0;
2132 if (!qe
->parent
->ringinuse
&& (tmp
->member
->status
!= AST_DEVICE_NOT_INUSE
) && (tmp
->member
->status
!= AST_DEVICE_UNKNOWN
)) {
2133 ast_debug(1, "%s in use, can't receive call\n", tmp
->interface
);
2135 ast_cdr_busy(qe
->chan
->cdr
);
2136 tmp
->stillgoing
= 0;
2140 if (tmp
->member
->paused
) {
2141 ast_debug(1, "%s paused, can't receive call\n", tmp
->interface
);
2143 ast_cdr_busy(qe
->chan
->cdr
);
2144 tmp
->stillgoing
= 0;
2147 if (use_weight
&& compare_weight(qe
->parent
,tmp
->member
)) {
2148 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe
->parent
->name
, tmp
->interface
);
2150 ast_cdr_busy(qe
->chan
->cdr
);
2151 tmp
->stillgoing
= 0;
2156 ast_copy_string(tech
, tmp
->interface
, sizeof(tech
));
2157 if ((location
= strchr(tech
, '/')))
2162 /* Request the peer */
2163 tmp
->chan
= ast_request(tech
, qe
->chan
->nativeformats
, location
, &status
);
2164 if (!tmp
->chan
) { /* If we can't, just go on to the next call */
2166 ast_cdr_busy(qe
->chan
->cdr
);
2167 tmp
->stillgoing
= 0;
2169 update_status(tmp
->member
->state_interface
, ast_device_state(tmp
->member
->state_interface
));
2171 ao2_lock(qe
->parent
);
2172 qe
->parent
->rrpos
++;
2174 ao2_unlock(qe
->parent
);
2181 tmp
->chan
->appl
= "AppQueue";
2182 tmp
->chan
->data
= "(Outgoing Line)";
2183 memset(&tmp
->chan
->whentohangup
, 0, sizeof(tmp
->chan
->whentohangup
));
2184 if (tmp
->chan
->cid
.cid_num
)
2185 ast_free(tmp
->chan
->cid
.cid_num
);
2186 tmp
->chan
->cid
.cid_num
= ast_strdup(qe
->chan
->cid
.cid_num
);
2187 if (tmp
->chan
->cid
.cid_name
)
2188 ast_free(tmp
->chan
->cid
.cid_name
);
2189 tmp
->chan
->cid
.cid_name
= ast_strdup(qe
->chan
->cid
.cid_name
);
2190 if (tmp
->chan
->cid
.cid_ani
)
2191 ast_free(tmp
->chan
->cid
.cid_ani
);
2192 tmp
->chan
->cid
.cid_ani
= ast_strdup(qe
->chan
->cid
.cid_ani
);
2194 /* Inherit specially named variables from parent channel */
2195 ast_channel_inherit_variables(qe
->chan
, tmp
->chan
);
2197 /* Presense of ADSI CPE on outgoing channel follows ours */
2198 tmp
->chan
->adsicpe
= qe
->chan
->adsicpe
;
2200 /* Inherit context and extension */
2201 ast_channel_lock(qe
->chan
);
2202 macrocontext
= pbx_builtin_getvar_helper(qe
->chan
, "MACRO_CONTEXT");
2203 if (!ast_strlen_zero(macrocontext
))
2204 ast_copy_string(tmp
->chan
->dialcontext
, macrocontext
, sizeof(tmp
->chan
->dialcontext
));
2206 ast_copy_string(tmp
->chan
->dialcontext
, qe
->chan
->context
, sizeof(tmp
->chan
->dialcontext
));
2207 macroexten
= pbx_builtin_getvar_helper(qe
->chan
, "MACRO_EXTEN");
2208 if (!ast_strlen_zero(macroexten
))
2209 ast_copy_string(tmp
->chan
->exten
, macroexten
, sizeof(tmp
->chan
->exten
));
2211 ast_copy_string(tmp
->chan
->exten
, qe
->chan
->exten
, sizeof(tmp
->chan
->exten
));
2212 ast_channel_unlock(qe
->chan
);
2214 /* Place the call, but don't wait on the answer */
2215 if ((res
= ast_call(tmp
->chan
, location
, 0))) {
2216 /* Again, keep going even if there's an error */
2217 ast_debug(1, "ast call on peer returned %d\n", res
);
2218 ast_verb(3, "Couldn't call %s\n", tmp
->interface
);
2222 } else if (qe
->parent
->eventwhencalled
) {
2225 manager_event(EVENT_FLAG_AGENT
, "AgentCalled",
2227 "AgentCalled: %s\r\n"
2229 "ChannelCalling: %s\r\n"
2230 "DestinationChannel: %s\r\n"
2231 "CallerIDNum: %s\r\n"
2232 "CallerIDName: %s\r\n"
2238 qe
->parent
->name
, tmp
->interface
, tmp
->member
->membername
, qe
->chan
->name
, tmp
->chan
->name
,
2239 tmp
->chan
->cid
.cid_num
? tmp
->chan
->cid
.cid_num
: "unknown",
2240 tmp
->chan
->cid
.cid_name
? tmp
->chan
->cid
.cid_name
: "unknown",
2241 qe
->chan
->context
, qe
->chan
->exten
, qe
->chan
->priority
, qe
->chan
->uniqueid
,
2242 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2243 ast_verb(3, "Called %s\n", tmp
->interface
);
2249 /*! \brief find the entry with the best metric, or NULL */
2250 static struct callattempt
*find_best(struct callattempt
*outgoing
)
2252 struct callattempt
*best
= NULL
, *cur
;
2254 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
2255 if (cur
->stillgoing
&& /* Not already done */
2256 !cur
->chan
&& /* Isn't already going */
2257 (!best
|| cur
->metric
< best
->metric
)) { /* We haven't found one yet, or it's better */
2266 * \brief Place a call to a queue member.
2268 * Once metrics have been calculated for each member, this function is used
2269 * to place a call to the appropriate member (or members). The low-level
2270 * channel-handling and error detection is handled in ring_entry
2272 * \retval 1 if a member was called successfully
2273 * \retval 0 otherwise
2275 static int ring_one(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *busies
)
2280 struct callattempt
*best
= find_best(outgoing
);
2282 ast_debug(1, "Nobody left to try ringing in queue\n");
2285 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2286 struct callattempt
*cur
;
2287 /* Ring everyone who shares this best metric (for ringall) */
2288 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
2289 if (cur
->stillgoing
&& !cur
->chan
&& cur
->metric
<= best
->metric
) {
2290 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur
->interface
, cur
->metric
);
2291 ret
|= ring_entry(qe
, cur
, busies
);
2295 /* Ring just the best channel */
2296 ast_debug(1, "Trying '%s' with metric %d\n", best
->interface
, best
->metric
);
2297 ret
= ring_entry(qe
, best
, busies
);
2304 /*! \brief Search for best metric and add to Round Robbin queue */
2305 static int store_next_rr(struct queue_ent
*qe
, struct callattempt
*outgoing
)
2307 struct callattempt
*best
= find_best(outgoing
);
2310 /* Ring just the best channel */
2311 ast_debug(1, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
2312 qe
->parent
->rrpos
= best
->metric
% 1000;
2314 /* Just increment rrpos */
2315 if (qe
->parent
->wrapped
) {
2316 /* No more channels, start over */
2317 qe
->parent
->rrpos
= 0;
2319 /* Prioritize next entry */
2320 qe
->parent
->rrpos
++;
2323 qe
->parent
->wrapped
= 0;
2328 /*! \brief Search for best metric and add to Linear queue */
2329 static int store_next_lin(struct queue_ent
*qe
, struct callattempt
*outgoing
)
2331 struct callattempt
*best
= find_best(outgoing
);
2334 /* Ring just the best channel */
2335 ast_debug(1, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
2336 qe
->linpos
= best
->metric
% 1000;
2338 /* Just increment rrpos */
2339 if (qe
->linwrapped
) {
2340 /* No more channels, start over */
2343 /* Prioritize next entry */
2352 /*! \brief Playback announcement to queued members if peroid has elapsed */
2353 static int say_periodic_announcement(struct queue_ent
*qe
, int ringing
)
2358 /* Get the current time */
2361 /* Check to see if it is time to announce */
2362 if ((now
- qe
->last_periodic_announce_time
) < qe
->parent
->periodicannouncefrequency
)
2365 /* Stop the music on hold so we can play our own file */
2367 ast_indicate(qe
->chan
,-1);
2369 ast_moh_stop(qe
->chan
);
2371 ast_verb(3, "Playing periodic announcement\n");
2373 if (qe
->parent
->randomperiodicannounce
) {
2374 qe
->last_periodic_announce_sound
= ((unsigned long) ast_random()) % qe
->parent
->numperiodicannounce
;
2375 } else if (qe
->last_periodic_announce_sound
>= qe
->parent
->numperiodicannounce
||
2376 ast_strlen_zero(qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]->str
)) {
2377 qe
->last_periodic_announce_sound
= 0;
2380 /* play the announcement */
2381 res
= play_file(qe
->chan
, qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]->str
);
2383 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
2386 /* Resume Music on Hold if the caller is going to stay in the queue */
2389 ast_indicate(qe
->chan
, AST_CONTROL_RINGING
);
2391 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
2394 /* update last_periodic_announce_time */
2395 qe
->last_periodic_announce_time
= now
;
2397 /* Update the current periodic announcement to the next announcement */
2398 if (!qe
->parent
->randomperiodicannounce
) {
2399 qe
->last_periodic_announce_sound
++;
2405 /*! \brief Record that a caller gave up on waiting in queue */
2406 static void record_abandoned(struct queue_ent
*qe
)
2408 ao2_lock(qe
->parent
);
2409 set_queue_variables(qe
);
2410 manager_event(EVENT_FLAG_AGENT
, "QueueCallerAbandon",
2414 "OriginalPosition: %d\r\n"
2416 qe
->parent
->name
, qe
->chan
->uniqueid
, qe
->pos
, qe
->opos
, (int)(time(NULL
) - qe
->start
));
2418 qe
->parent
->callsabandoned
++;
2419 ao2_unlock(qe
->parent
);
2422 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2423 static void rna(int rnatime
, struct queue_ent
*qe
, char *interface
, char *membername
)
2425 ast_verb(3, "Nobody picked up in %d ms\n", rnatime
);
2426 if (qe
->parent
->eventwhencalled
)
2427 manager_event(EVENT_FLAG_AGENT
, "AgentRingNoAnswer",
2432 "MemberName: %s\r\n"
2440 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, membername
, "RINGNOANSWER", "%d", rnatime
);
2441 if (qe
->parent
->autopause
) {
2442 if (!set_member_paused(qe
->parent
->name
, interface
, "Auto-Pause", 1)) {
2443 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface
, qe
->parent
->name
);
2445 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface
, qe
->parent
->name
);
2451 #define AST_MAX_WATCHERS 256
2452 /*! \brief Wait for a member to answer the call
2454 * \param[in] qe the queue_ent corresponding to the caller in the queue
2455 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
2456 * \param[in] to the amount of time (in milliseconds) to wait for a response
2457 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
2458 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
2459 * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
2460 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
2462 static struct callattempt
*wait_for_answer(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *to
, char *digit
, int prebusies
, int caller_disconnect
, int forwardsallowed
)
2464 const char *queue
= qe
->parent
->name
;
2465 struct callattempt
*o
, *start
= NULL
, *prev
= NULL
;
2467 int numbusies
= prebusies
;
2471 struct ast_frame
*f
;
2472 struct callattempt
*peer
= NULL
;
2473 struct ast_channel
*winner
;
2474 struct ast_channel
*in
= qe
->chan
;
2476 char membername
[80] = "";
2480 struct callattempt
*epollo
;
2483 starttime
= (long) time(NULL
);
2485 for (epollo
= outgoing
; epollo
; epollo
= epollo
->q_next
) {
2487 ast_poll_channel_add(in
, epollo
->chan
);
2491 while (*to
&& !peer
) {
2492 int numlines
, retry
, pos
= 1;
2493 struct ast_channel
*watchers
[AST_MAX_WATCHERS
];
2497 for (retry
= 0; retry
< 2; retry
++) {
2499 for (o
= outgoing
; o
; o
= o
->q_next
) { /* Keep track of important channels */
2500 if (o
->stillgoing
) { /* Keep track of important channels */
2503 watchers
[pos
++] = o
->chan
;
2507 prev
->call_next
= o
;
2513 if (pos
> 1 /* found */ || !stillgoing
/* nobody listening */ ||
2514 (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) /* ring would not be delivered */)
2516 /* On "ringall" strategy we only move to the next penalty level
2517 when *all* ringing phones are done in the current penalty level */
2518 ring_one(qe
, outgoing
, &numbusies
);
2521 if (pos
== 1 /* not found */) {
2522 if (numlines
== (numbusies
+ numnochan
)) {
2523 ast_debug(1, "Everyone is busy at this time\n");
2525 ast_log(LOG_NOTICE
, "No one is answering queue '%s' (%d/%d/%d)\n", queue
, numlines
, numbusies
, numnochan
);
2530 winner
= ast_waitfor_n(watchers
, pos
, to
);
2531 for (o
= start
; o
; o
= o
->call_next
) {
2532 if (o
->stillgoing
&& (o
->chan
) && (o
->chan
->_state
== AST_STATE_UP
)) {
2534 ast_verb(3, "%s answered %s\n", o
->chan
->name
, in
->name
);
2537 } else if (o
->chan
&& (o
->chan
== winner
)) {
2539 ast_copy_string(on
, o
->member
->interface
, sizeof(on
));
2540 ast_copy_string(membername
, o
->member
->membername
, sizeof(membername
));
2542 if (!ast_strlen_zero(o
->chan
->call_forward
) && !forwardsallowed
) {
2543 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in
->name
, o
->chan
->call_forward
);
2548 } else if (!ast_strlen_zero(o
->chan
->call_forward
)) {
2553 ast_copy_string(tmpchan
, o
->chan
->call_forward
, sizeof(tmpchan
));
2554 if ((stuff
= strchr(tmpchan
, '/'))) {
2558 snprintf(tmpchan
, sizeof(tmpchan
), "%s@%s", o
->chan
->call_forward
, o
->chan
->context
);
2562 /* Before processing channel, go ahead and check for forwarding */
2563 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in
->name
, tech
, stuff
, o
->chan
->name
);
2564 /* Setup parameters */
2565 o
->chan
= ast_request(tech
, in
->nativeformats
, stuff
, &status
);
2567 ast_log(LOG_NOTICE
, "Unable to create local channel for call forward to '%s/%s'\n", tech
, stuff
);
2571 ast_channel_inherit_variables(in
, o
->chan
);
2572 ast_channel_datastore_inherit(in
, o
->chan
);
2573 if (o
->chan
->cid
.cid_num
)
2574 ast_free(o
->chan
->cid
.cid_num
);
2575 o
->chan
->cid
.cid_num
= ast_strdup(in
->cid
.cid_num
);
2577 if (o
->chan
->cid
.cid_name
)
2578 ast_free(o
->chan
->cid
.cid_name
);
2579 o
->chan
->cid
.cid_name
= ast_strdup(in
->cid
.cid_name
);
2581 ast_string_field_set(o
->chan
, accountcode
, in
->accountcode
);
2582 o
->chan
->cdrflags
= in
->cdrflags
;
2584 if (in
->cid
.cid_ani
) {
2585 if (o
->chan
->cid
.cid_ani
)
2586 ast_free(o
->chan
->cid
.cid_ani
);
2587 o
->chan
->cid
.cid_ani
= ast_strdup(in
->cid
.cid_ani
);
2589 if (o
->chan
->cid
.cid_rdnis
)
2590 ast_free(o
->chan
->cid
.cid_rdnis
);
2591 o
->chan
->cid
.cid_rdnis
= ast_strdup(S_OR(in
->macroexten
, in
->exten
));
2592 if (ast_call(o
->chan
, tmpchan
, 0)) {
2593 ast_log(LOG_NOTICE
, "Failed to dial on local channel for call forward to '%s'\n", tmpchan
);
2598 /* Hangup the original channel now, in case we needed it */
2602 f
= ast_read(winner
);
2604 if (f
->frametype
== AST_FRAME_CONTROL
) {
2605 switch (f
->subclass
) {
2606 case AST_CONTROL_ANSWER
:
2607 /* This is our guy if someone answered. */
2609 ast_verb(3, "%s answered %s\n", o
->chan
->name
, in
->name
);
2613 case AST_CONTROL_BUSY
:
2614 ast_verb(3, "%s is busy\n", o
->chan
->name
);
2616 ast_cdr_busy(in
->cdr
);
2618 endtime
= (long) time(NULL
);
2619 endtime
-= starttime
;
2620 rna(endtime
*1000, qe
, on
, membername
);
2621 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2622 if (qe
->parent
->timeoutrestart
)
2624 ring_one(qe
, outgoing
, &numbusies
);
2628 case AST_CONTROL_CONGESTION
:
2629 ast_verb(3, "%s is circuit-busy\n", o
->chan
->name
);
2631 ast_cdr_busy(in
->cdr
);
2632 endtime
= (long) time(NULL
);
2633 endtime
-= starttime
;
2634 rna(endtime
*1000, qe
, on
, membername
);
2636 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2637 if (qe
->parent
->timeoutrestart
)
2639 ring_one(qe
, outgoing
, &numbusies
);
2643 case AST_CONTROL_RINGING
:
2644 ast_verb(3, "%s is ringing\n", o
->chan
->name
);
2646 case AST_CONTROL_OFFHOOK
:
2647 /* Ignore going off hook */
2650 ast_debug(1, "Dunno what to do with control type %d\n", f
->subclass
);
2655 endtime
= (long) time(NULL
) - starttime
;
2656 rna(endtime
* 1000, qe
, on
, membername
);
2658 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2659 if (qe
->parent
->timeoutrestart
)
2661 ring_one(qe
, outgoing
, &numbusies
);
2668 if (!f
|| ((f
->frametype
== AST_FRAME_CONTROL
) && (f
->subclass
== AST_CONTROL_HANGUP
))) {
2672 if (f
->data
.uint32
) {
2673 in
->hangupcause
= f
->data
.uint32
;
2679 if ((f
->frametype
== AST_FRAME_DTMF
) && caller_disconnect
&& (f
->subclass
== '*')) {
2680 ast_verb(3, "User hit %c to disconnect call.\n", f
->subclass
);
2685 if ((f
->frametype
== AST_FRAME_DTMF
) && valid_exit(qe
, f
->subclass
)) {
2686 ast_verb(3, "User pressed digit: %c\n", f
->subclass
);
2688 *digit
= f
->subclass
;
2695 for (o
= start
; o
; o
= o
->call_next
)
2696 rna(orig
, qe
, o
->interface
, o
->member
->membername
);
2701 for (epollo
= outgoing
; epollo
; epollo
= epollo
->q_next
) {
2703 ast_poll_channel_del(in
, epollo
->chan
);
2711 * \brief Check if we should start attempting to call queue members.
2713 * The behavior of this function is dependent first on whether autofill is enabled
2714 * and second on whether the ring strategy is ringall. If autofill is not enabled,
2715 * then return true if we're the head of the queue. If autofill is enabled, then
2716 * we count the available members and see if the number of available members is enough
2717 * that given our position in the queue, we would theoretically be able to connect to
2718 * one of those available members
2720 static int is_our_turn(struct queue_ent
*qe
)
2722 struct queue_ent
*ch
;
2728 if (!qe
->parent
->autofill
) {
2729 /* Atomically read the parent head -- does not need a lock */
2730 ch
= qe
->parent
->head
;
2731 /* If we are now at the top of the head, break out */
2733 ast_debug(1, "It's our turn (%s).\n", qe
->chan
->name
);
2736 ast_debug(1, "It's not our turn (%s).\n", qe
->chan
->name
);
2741 /* This needs a lock. How many members are available to be served? */
2742 ao2_lock(qe
->parent
);
2744 ch
= qe
->parent
->head
;
2746 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2747 ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
2750 struct ao2_iterator mem_iter
= ao2_iterator_init(qe
->parent
->members
, 0);
2751 while ((cur
= ao2_iterator_next(&mem_iter
))) {
2752 switch (cur
->status
) {
2753 case AST_DEVICE_INUSE
:
2754 if (!qe
->parent
->ringinuse
)
2756 /* else fall through */
2757 case AST_DEVICE_NOT_INUSE
:
2758 case AST_DEVICE_UNKNOWN
:
2767 ast_debug(1, "There are %d available members.\n", avl
);
2769 while ((idx
< avl
) && (ch
) && (ch
!= qe
)) {
2775 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2776 if (ch
&& idx
< avl
) {
2777 ast_debug(1, "It's our turn (%s).\n", qe
->chan
->name
);
2780 ast_debug(1, "It's not our turn (%s).\n", qe
->chan
->name
);
2784 ao2_unlock(qe
->parent
);
2791 * \brief update rules for queues
2793 * Calculate min/max penalties making sure if relative they stay within bounds.
2794 * Update queues penalty and set dialplan vars, goto next list entry.
2796 static void update_qe_rule(struct queue_ent
*qe
)
2798 int max_penalty
= qe
->pr
->max_relative
? qe
->max_penalty
+ qe
->pr
->max_value
: qe
->pr
->max_value
;
2799 int min_penalty
= qe
->pr
->min_relative
? qe
->min_penalty
+ qe
->pr
->min_value
: qe
->pr
->min_value
;
2800 char max_penalty_str
[20], min_penalty_str
[20];
2801 /* a relative change to the penalty could put it below 0 */
2802 if (max_penalty
< 0)
2804 if (min_penalty
< 0)
2806 if (min_penalty
> max_penalty
)
2807 min_penalty
= max_penalty
;
2808 snprintf(max_penalty_str
, sizeof(max_penalty_str
), "%d", max_penalty
);
2809 snprintf(min_penalty_str
, sizeof(min_penalty_str
), "%d", min_penalty
);
2810 pbx_builtin_setvar_helper(qe
->chan
, "QUEUE_MAX_PENALTY", max_penalty_str
);
2811 pbx_builtin_setvar_helper(qe
->chan
, "QUEUE_MIN_PENALTY", min_penalty_str
);
2812 qe
->max_penalty
= max_penalty
;
2813 qe
->min_penalty
= min_penalty
;
2814 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe
->max_penalty
, qe
->min_penalty
, qe
->chan
->name
, qe
->pr
->time
);
2815 qe
->pr
= AST_LIST_NEXT(qe
->pr
, list
);
2818 /*! \brief The waiting areas for callers who are not actively calling members
2820 * This function is one large loop. This function will return if a caller
2821 * either exits the queue or it becomes that caller's turn to attempt calling
2822 * queue members. Inside the loop, we service the caller with periodic announcements,
2823 * holdtime announcements, etc. as configured in queues.conf
2825 * \retval 0 if the caller's turn has arrived
2826 * \retval -1 if the caller should exit the queue.
2828 static int wait_our_turn(struct queue_ent
*qe
, int ringing
, enum queue_result
*reason
)
2832 /* This is the holding pen for callers 2 through maxlen */
2834 enum queue_member_status stat
;
2836 if (is_our_turn(qe
))
2839 /* If we have timed out, break out */
2840 if (qe
->expire
&& (time(NULL
) > qe
->expire
)) {
2841 *reason
= QUEUE_TIMEOUT
;
2845 stat
= get_member_status(qe
->parent
, qe
->max_penalty
, qe
->min_penalty
);
2847 /* leave the queue if no agents, if enabled */
2848 if (qe
->parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
2849 *reason
= QUEUE_LEAVEEMPTY
;
2850 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2855 /* leave the queue if no reachable agents, if enabled */
2856 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)) {
2857 *reason
= QUEUE_LEAVEUNAVAIL
;
2858 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2862 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
2863 *reason
= QUEUE_LEAVEUNAVAIL
;
2864 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2869 /* Make a position announcement, if enabled */
2870 if (qe
->parent
->announcefrequency
&&
2871 (res
= say_position(qe
,ringing
)))
2874 /* Make a periodic announcement, if enabled */
2875 if (qe
->parent
->periodicannouncefrequency
&&
2876 (res
= say_periodic_announcement(qe
,ringing
)))
2879 /* see if we need to move to the next penalty level for this queue */
2880 while (qe
->pr
&& ((time(NULL
) - qe
->start
) > qe
->pr
->time
)) {
2884 /* Wait a second before checking again */
2885 if ((res
= ast_waitfordigit(qe
->chan
, RECHECK
* 1000))) {
2886 if (res
> 0 && !valid_exit(qe
, res
))
2897 * \brief update the queue status
2900 static int update_queue(struct call_queue
*q
, struct member
*member
, int callcompletedinsl
)
2903 struct call_queue
*qtmp
;
2904 struct ao2_iterator queue_iter
;
2906 if (shared_lastcall
) {
2907 queue_iter
= ao2_iterator_init(queues
, 0);
2908 while ((qtmp
= ao2_iterator_next(&queue_iter
))) {
2910 if ((mem
= ao2_find(qtmp
->members
, member
, OBJ_POINTER
))) {
2911 time(&mem
->lastcall
);
2921 time(&member
->lastcall
);
2923 member
->lastqueue
= q
;
2927 q
->callscompleted
++;
2928 if (callcompletedinsl
)
2929 q
->callscompletedinsl
++;
2934 /*! \brief Calculate the metric of each member in the outgoing callattempts
2936 * A numeric metric is given to each member depending on the ring strategy used
2937 * by the queue. Members with lower metrics will be called before members with
2939 * \retval -1 if penalties are exceeded
2940 * \retval 0 otherwise
2942 static int calc_metric(struct call_queue
*q
, struct member
*mem
, int pos
, struct queue_ent
*qe
, struct callattempt
*tmp
)
2944 if ((qe
->max_penalty
&& (mem
->penalty
> qe
->max_penalty
)) || (qe
->min_penalty
&& (mem
->penalty
< qe
->min_penalty
)))
2947 switch (q
->strategy
) {
2948 case QUEUE_STRATEGY_RINGALL
:
2949 /* Everyone equal, except for penalty */
2950 tmp
->metric
= mem
->penalty
* 1000000;
2952 case QUEUE_STRATEGY_LINEAR
:
2953 if (pos
< qe
->linpos
) {
2954 tmp
->metric
= 1000 + pos
;
2956 if (pos
> qe
->linpos
)
2957 /* Indicate there is another priority */
2961 tmp
->metric
+= mem
->penalty
* 1000000;
2963 case QUEUE_STRATEGY_RRMEMORY
:
2964 if (pos
< q
->rrpos
) {
2965 tmp
->metric
= 1000 + pos
;
2968 /* Indicate there is another priority */
2972 tmp
->metric
+= mem
->penalty
* 1000000;
2974 case QUEUE_STRATEGY_RANDOM
:
2975 tmp
->metric
= ast_random() % 1000;
2976 tmp
->metric
+= mem
->penalty
* 1000000;
2978 case QUEUE_STRATEGY_WRANDOM
:
2979 tmp
->metric
= ast_random() % ((1 + mem
->penalty
) * 1000);
2981 case QUEUE_STRATEGY_FEWESTCALLS
:
2982 tmp
->metric
= mem
->calls
;
2983 tmp
->metric
+= mem
->penalty
* 1000000;
2985 case QUEUE_STRATEGY_LEASTRECENT
:
2989 tmp
->metric
= 1000000 - (time(NULL
) - mem
->lastcall
);
2990 tmp
->metric
+= mem
->penalty
* 1000000;
2993 ast_log(LOG_WARNING
, "Can't calculate metric for unknown strategy %d\n", q
->strategy
);
2999 enum agent_complete_reason
{
3005 /*! \brief Send out AMI message with member call completion status information */
3006 static void send_agent_complete(const struct queue_ent
*qe
, const char *queuename
,
3007 const struct ast_channel
*peer
, const struct member
*member
, time_t callstart
,
3008 char *vars
, size_t vars_len
, enum agent_complete_reason rsn
)
3010 const char *reason
= NULL
; /* silence dumb compilers */
3012 if (!qe
->parent
->eventwhencalled
)
3023 reason
= "transfer";
3027 manager_event(EVENT_FLAG_AGENT
, "AgentComplete",
3032 "MemberName: %s\r\n"
3037 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3038 (long)(callstart
- qe
->start
), (long)(time(NULL
) - callstart
), reason
,
3039 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, vars_len
) : "");
3042 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
3044 * Here is the process of this function
3045 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
3046 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
3047 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
3048 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
3049 * during each iteration, we call calc_metric to determine which members should be rung when.
3050 * 3. Call ring_one to place a call to the appropriate member(s)
3051 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
3052 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
3053 * 6. Start the monitor or mixmonitor if the option is set
3054 * 7. Remove the caller from the queue to allow other callers to advance
3055 * 8. Bridge the call.
3056 * 9. Do any post processing after the call has disconnected.
3058 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
3059 * \param[in] options the options passed as the third parameter to the Queue() application
3060 * \param[in] announceoverride filename to play to user when waiting
3061 * \param[in] url the url passed as the fourth parameter to the Queue() application
3062 * \param[in,out] tries the number of times we have tried calling queue members
3063 * \param[out] noption set if the call to Queue() has the 'n' option set.
3064 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
3065 * \param[in] macro the macro passed as the sixth parameter to the Queue() application
3066 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
3067 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
3069 static int try_calling(struct queue_ent
*qe
, const char *options
, char *announceoverride
, const char *url
, int *tries
, int *noption
, const char *agi
, const char *macro
, const char *gosub
, int ringing
)
3072 struct callattempt
*outgoing
= NULL
; /* the list of calls we are building */
3074 char oldexten
[AST_MAX_EXTENSION
]="";
3075 char oldcontext
[AST_MAX_CONTEXT
]="";
3076 char queuename
[256]="";
3077 char interfacevar
[256]="";
3078 struct ast_channel
*peer
;
3079 struct ast_channel
*which
;
3080 struct callattempt
*lpeer
;
3081 struct member
*member
;
3082 struct ast_app
*app
;
3083 int res
= 0, bridge
= 0;
3086 char *announce
= NULL
;
3089 time_t now
= time(NULL
);
3090 struct ast_bridge_config bridge_config
;
3091 char nondataquality
= 1;
3092 char *agiexec
= NULL
;
3093 char *macroexec
= NULL
;
3094 char *gosubexec
= NULL
;
3096 const char *monitorfilename
;
3097 const char *monitor_exec
;
3098 const char *monitor_options
;
3099 char tmpid
[256], tmpid2
[256];
3100 char meid
[1024], meid2
[1024];
3101 char mixmonargs
[1512];
3102 struct ast_app
*mixmonapp
= NULL
;
3105 int forwardsallowed
= 1;
3106 int callcompletedinsl
;
3107 struct ao2_iterator memi
;
3108 struct ast_datastore
*datastore
;
3110 ast_channel_lock(qe
->chan
);
3111 datastore
= ast_channel_datastore_find(qe
->chan
, &dialed_interface_info
, NULL
);
3112 ast_channel_unlock(qe
->chan
);
3114 memset(&bridge_config
, 0, sizeof(bridge_config
));
3119 for (; options
&& *options
; options
++)
3122 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_REDIRECT
);
3125 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_REDIRECT
);
3128 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMON
);
3131 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMON
);
3137 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_DISCONNECT
);
3140 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
);
3143 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_PARKCALL
);
3146 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_PARKCALL
);
3149 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
|| qe
->parent
->strategy
== QUEUE_STRATEGY_LINEAR
)
3152 *tries
= qe
->parent
->membercount
;
3156 forwardsallowed
= 0;
3159 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMIXMON
);
3162 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMIXMON
);
3167 /* Hold the lock while we setup the outgoing calls */
3170 ao2_lock(qe
->parent
);
3171 ast_debug(1, "%s is trying to call a queue member.\n",
3173 ast_copy_string(queuename
, qe
->parent
->name
, sizeof(queuename
));
3174 if (!ast_strlen_zero(qe
->announce
))
3175 announce
= qe
->announce
;
3176 if (!ast_strlen_zero(announceoverride
))
3177 announce
= announceoverride
;
3179 memi
= ao2_iterator_init(qe
->parent
->members
, 0);
3180 while ((cur
= ao2_iterator_next(&memi
))) {
3181 struct callattempt
*tmp
= ast_calloc(1, sizeof(*tmp
));
3182 struct ast_dialed_interface
*di
;
3183 AST_LIST_HEAD(, ast_dialed_interface
) *dialed_interfaces
;
3186 ao2_unlock(qe
->parent
);
3192 if (!(datastore
= ast_channel_datastore_alloc(&dialed_interface_info
, NULL
))) {
3194 ao2_unlock(qe
->parent
);
3200 datastore
->inheritance
= DATASTORE_INHERIT_FOREVER
;
3201 if (!(dialed_interfaces
= ast_calloc(1, sizeof(*dialed_interfaces
)))) {
3203 ao2_unlock(&qe
->parent
);
3209 datastore
->data
= dialed_interfaces
;
3210 AST_LIST_HEAD_INIT(dialed_interfaces
);
3212 ast_channel_lock(qe
->chan
);
3213 ast_channel_datastore_add(qe
->chan
, datastore
);
3214 ast_channel_unlock(qe
->chan
);
3216 dialed_interfaces
= datastore
->data
;
3218 AST_LIST_LOCK(dialed_interfaces
);
3219 AST_LIST_TRAVERSE(dialed_interfaces
, di
, list
) {
3220 if (!strcasecmp(cur
->interface
, di
->interface
)) {
3221 ast_log(LOG_DEBUG
, "Skipping dialing interface '%s' since it has already been dialed\n",
3226 AST_LIST_UNLOCK(dialed_interfaces
);
3233 /* It is always ok to dial a Local interface. We only keep track of
3234 * which "real" interfaces have been dialed. The Local channel will
3235 * inherit this list so that if it ends up dialing a real interface,
3236 * it won't call one that has already been called. */
3237 if (strncasecmp(cur
->interface
, "Local/", 6)) {
3238 if (!(di
= ast_calloc(1, sizeof(*di
) + strlen(cur
->interface
)))) {
3240 ao2_unlock(qe
->parent
);
3246 strcpy(di
->interface
, cur
->interface
);
3248 AST_LIST_LOCK(dialed_interfaces
);
3249 AST_LIST_INSERT_TAIL(dialed_interfaces
, di
, list
);
3250 AST_LIST_UNLOCK(dialed_interfaces
);
3253 tmp
->stillgoing
= -1;
3255 tmp
->oldstatus
= cur
->status
;
3256 tmp
->lastcall
= cur
->lastcall
;
3257 tmp
->lastqueue
= cur
->lastqueue
;
3258 ast_copy_string(tmp
->interface
, cur
->interface
, sizeof(tmp
->interface
));
3259 /* Special case: If we ring everyone, go ahead and ring them, otherwise
3260 just calculate their metric for the appropriate strategy */
3261 if (!calc_metric(qe
->parent
, cur
, x
++, qe
, tmp
)) {
3262 /* Put them in the list of outgoing thingies... We're ready now.
3263 XXX If we're forcibly removed, these outgoing calls won't get
3265 tmp
->q_next
= outgoing
;
3267 /* If this line is up, don't try anybody else */
3268 if (outgoing
->chan
&& (outgoing
->chan
->_state
== AST_STATE_UP
))
3275 if (qe
->expire
&& (!qe
->parent
->timeout
|| (qe
->expire
- now
) <= qe
->parent
->timeout
))
3276 to
= (qe
->expire
- now
) * 1000;
3278 to
= (qe
->parent
->timeout
) ? qe
->parent
->timeout
* 1000 : -1;
3281 ao2_unlock(qe
->parent
);
3282 ring_one(qe
, outgoing
, &numbusies
);
3285 lpeer
= wait_for_answer(qe
, outgoing
, &to
, &digit
, numbusies
, ast_test_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
), forwardsallowed
);
3286 /* The ast_channel_datastore_remove() function could fail here if the
3287 * datastore was moved to another channel during a masquerade. If this is
3288 * the case, don't free the datastore here because later, when the channel
3289 * to which the datastore was moved hangs up, it will attempt to free this
3290 * datastore again, causing a crash
3292 if (datastore
&& !ast_channel_datastore_remove(qe
->chan
, datastore
)) {
3293 ast_channel_datastore_free(datastore
);
3295 ao2_lock(qe
->parent
);
3296 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
) {
3297 store_next_rr(qe
, outgoing
);
3299 if (qe
->parent
->strategy
== QUEUE_STRATEGY_LINEAR
) {
3300 store_next_lin(qe
, outgoing
);
3302 ao2_unlock(qe
->parent
);
3303 peer
= lpeer
? lpeer
->chan
: NULL
;
3307 /* Must gotten hung up */
3310 /* User exited by pressing a digit */
3314 ast_debug(1, "%s: Nobody answered.\n", qe
->chan
->name
);
3315 } else { /* peer is valid */
3316 /* Ah ha! Someone answered within the desired timeframe. Of course after this
3317 we will always return with -1 so that it is hung up properly after the
3319 if (!strcmp(qe
->chan
->tech
->type
, "Zap"))
3320 ast_channel_setoption(qe
->chan
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
3321 if (!strcmp(peer
->tech
->type
, "Zap"))
3322 ast_channel_setoption(peer
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
3323 /* Update parameters for the queue */
3325 recalc_holdtime(qe
, (now
- qe
->start
));
3326 ao2_lock(qe
->parent
);
3327 callcompletedinsl
= ((now
- qe
->start
) <= qe
->parent
->servicelevel
);
3328 ao2_unlock(qe
->parent
);
3329 member
= lpeer
->member
;
3330 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
3332 hangupcalls(outgoing
, peer
);
3334 if (announce
|| qe
->parent
->reportholdtime
|| qe
->parent
->memberdelay
) {
3337 res2
= ast_autoservice_start(qe
->chan
);
3339 if (qe
->parent
->memberdelay
) {
3340 ast_log(LOG_NOTICE
, "Delaying member connect for %d seconds\n", qe
->parent
->memberdelay
);
3341 res2
|= ast_safe_sleep(peer
, qe
->parent
->memberdelay
* 1000);
3343 if (!res2
&& announce
) {
3344 play_file(peer
, announce
);
3346 if (!res2
&& qe
->parent
->reportholdtime
) {
3347 if (!play_file(peer
, qe
->parent
->sound_reporthold
)) {
3348 int holdtime
, holdtimesecs
;
3351 holdtime
= abs((now
- qe
->start
) / 60);
3352 holdtimesecs
= abs((now
- qe
->start
));
3353 if (holdtime
== 1) {
3354 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3355 play_file(peer
, qe
->parent
->sound_minute
);
3357 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3358 play_file(peer
, qe
->parent
->sound_minutes
);
3360 if (holdtimesecs
> 1) {
3361 ast_say_number(peer
, holdtimesecs
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3362 play_file(peer
, qe
->parent
->sound_seconds
);
3367 res2
|= ast_autoservice_stop(qe
->chan
);
3368 if (ast_check_hangup(peer
)) {
3369 /* Agent must have hung up */
3370 ast_log(LOG_WARNING
, "Agent on %s hungup on the customer.\n", peer
->name
);
3371 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "AGENTDUMP", "%s", "");
3372 if (qe
->parent
->eventwhencalled
)
3373 manager_event(EVENT_FLAG_AGENT
, "AgentDump",
3378 "MemberName: %s\r\n"
3380 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3381 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
3383 ao2_ref(member
, -1);
3386 /* Caller must have hung up just before being connected*/
3387 ast_log(LOG_NOTICE
, "Caller was about to talk to agent on %s but the caller hungup.\n", peer
->name
);
3388 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "ABANDON", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
3389 record_abandoned(qe
);
3391 ao2_ref(member
, -1);
3395 /* Stop music on hold */
3397 ast_indicate(qe
->chan
,-1);
3399 ast_moh_stop(qe
->chan
);
3400 /* If appropriate, log that we have a destination channel */
3402 ast_cdr_setdestchan(qe
->chan
->cdr
, peer
->name
);
3403 /* Make sure channels are compatible */
3404 res
= ast_channel_make_compatible(qe
->chan
, peer
);
3406 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "SYSCOMPAT", "%s", "");
3407 ast_log(LOG_WARNING
, "Had to drop call because I couldn't make %s compatible with %s\n", qe
->chan
->name
, peer
->name
);
3408 record_abandoned(qe
);
3410 ao2_ref(member
, -1);
3414 /* Play announcement to the caller telling it's his turn if defined */
3415 if (!ast_strlen_zero(qe
->parent
->sound_callerannounce
)) {
3416 if (play_file(qe
->chan
, qe
->parent
->sound_callerannounce
))
3417 ast_log(LOG_WARNING
, "Announcement file '%s' is unavailable, continuing anyway...\n", qe
->parent
->sound_callerannounce
);
3420 ao2_lock(qe
->parent
);
3421 /* if setinterfacevar is defined, make member variables available to the channel */
3422 /* use pbx_builtin_setvar to set a load of variables with one call */
3423 if (qe
->parent
->setinterfacevar
) {
3424 snprintf(interfacevar
, sizeof(interfacevar
), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
3425 member
->interface
, member
->membername
, member
->calls
, (long)member
->lastcall
, member
->penalty
, member
->dynamic
, member
->realtime
);
3426 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
3429 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
3430 /* use pbx_builtin_setvar to set a load of variables with one call */
3431 if (qe
->parent
->setqueueentryvar
) {
3432 snprintf(interfacevar
, sizeof(interfacevar
), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
3433 (long) time(NULL
) - qe
->start
, qe
->opos
);
3434 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
3437 /* try to set queue variables if configured to do so*/
3438 set_queue_variables(qe
);
3439 ao2_unlock(qe
->parent
);
3441 /* Begin Monitoring */
3442 if (qe
->parent
->monfmt
&& *qe
->parent
->monfmt
) {
3443 if (!qe
->parent
->montype
) {
3444 ast_debug(1, "Starting Monitor as requested.\n");
3445 monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME");
3446 if (pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC_ARGS"))
3450 if (monitorfilename
)
3451 ast_monitor_start(which
, qe
->parent
->monfmt
, monitorfilename
, 1, X_REC_IN
| X_REC_OUT
);
3452 else if (qe
->chan
->cdr
)
3453 ast_monitor_start(which
, qe
->parent
->monfmt
, qe
->chan
->cdr
->uniqueid
, 1, X_REC_IN
| X_REC_OUT
);
3455 /* Last ditch effort -- no CDR, make up something */
3456 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
3457 ast_monitor_start(which
, qe
->parent
->monfmt
, tmpid
, 1, X_REC_IN
| X_REC_OUT
);
3460 ast_debug(1, "Starting MixMonitor as requested.\n");
3461 monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME");
3462 if (!monitorfilename
) {
3464 ast_copy_string(tmpid
, qe
->chan
->cdr
->uniqueid
, sizeof(tmpid
));
3466 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
3468 const char *m
= monitorfilename
;
3469 for (p
= tmpid2
; p
< tmpid2
+ sizeof(tmpid2
) - 1; p
++, m
++) {
3472 if (*(m
+ 1) == '{')
3484 if (p
== tmpid2
+ sizeof(tmpid2
))
3485 tmpid2
[sizeof(tmpid2
) - 1] = '\0';
3487 pbx_substitute_variables_helper(qe
->chan
, tmpid2
, tmpid
, sizeof(tmpid
) - 1);
3490 monitor_exec
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC");
3491 monitor_options
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_OPTIONS");
3494 const char *m
= monitor_exec
;
3495 for (p
= meid2
; p
< meid2
+ sizeof(meid2
) - 1; p
++, m
++) {
3498 if (*(m
+ 1) == '{')
3510 if (p
== meid2
+ sizeof(meid2
))
3511 meid2
[sizeof(meid2
) - 1] = '\0';
3513 pbx_substitute_variables_helper(qe
->chan
, meid2
, meid
, sizeof(meid
) - 1);
3516 snprintf(tmpid2
, sizeof(tmpid2
), "%s.%s", tmpid
, qe
->parent
->monfmt
);
3518 mixmonapp
= pbx_findapp("MixMonitor");
3520 if (!monitor_options
)
3521 monitor_options
= "";
3524 if (!ast_strlen_zero(monitor_exec
))
3525 snprintf(mixmonargs
, sizeof(mixmonargs
), "%s,b%s,%s", tmpid2
, monitor_options
, monitor_exec
);
3527 snprintf(mixmonargs
, sizeof(mixmonargs
), "%s,b%s", tmpid2
, monitor_options
);
3529 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs
);
3530 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3532 ast_set_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
3533 ret
= pbx_exec(qe
->chan
, mixmonapp
, mixmonargs
);
3535 ast_clear_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
3538 ast_log(LOG_WARNING
, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3542 /* Drop out of the queue at this point, to prepare for next caller */
3544 if (!ast_strlen_zero(url
) && ast_channel_supports_html(peer
)) {
3545 ast_debug(1, "app_queue: sendurl=%s.\n", url
);
3546 ast_channel_sendurl(peer
, url
);
3549 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
3550 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
3551 if (!ast_strlen_zero(macro
)) {
3552 macroexec
= ast_strdupa(macro
);
3554 if (qe
->parent
->membermacro
)
3555 macroexec
= ast_strdupa(qe
->parent
->membermacro
);
3558 if (!ast_strlen_zero(macroexec
)) {
3559 ast_debug(1, "app_queue: macro=%s.\n", macroexec
);
3561 res
= ast_autoservice_start(qe
->chan
);
3563 ast_log(LOG_ERROR
, "Unable to start autoservice on calling channel\n");
3567 app
= pbx_findapp("Macro");
3570 res
= pbx_exec(qe
->chan
, app
, macroexec
);
3571 ast_debug(1, "Macro exited with status %d\n", res
);
3574 ast_log(LOG_ERROR
, "Could not find application Macro\n");
3578 if (ast_autoservice_stop(qe
->chan
) < 0) {
3579 ast_log(LOG_ERROR
, "Could not stop autoservice on calling channel\n");
3584 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
3585 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
3586 if (!ast_strlen_zero(gosub
)) {
3587 gosubexec
= ast_strdupa(gosub
);
3589 if (qe
->parent
->membergosub
)
3590 gosubexec
= ast_strdupa(qe
->parent
->membergosub
);
3593 if (!ast_strlen_zero(gosubexec
)) {
3595 ast_log(LOG_DEBUG
, "app_queue: gosub=%s.\n", gosubexec
);
3597 res
= ast_autoservice_start(qe
->chan
);
3599 ast_log(LOG_ERROR
, "Unable to start autoservice on calling channel\n");
3603 app
= pbx_findapp("Gosub");
3606 char *gosub_args
, *gosub_argstart
;
3608 /* Set where we came from */
3609 ast_copy_string(qe
->chan
->context
, "app_dial_gosub_virtual_context", sizeof(qe
->chan
->context
));
3610 ast_copy_string(qe
->chan
->exten
, "s", sizeof(qe
->chan
->exten
));
3611 qe
->chan
->priority
= 0;
3613 gosub_argstart
= strchr(gosubexec
, ',');
3614 if (gosub_argstart
) {
3615 *gosub_argstart
= 0;
3616 asprintf(&gosub_args
, "%s,s,1(%s)", gosubexec
, gosub_argstart
+ 1);
3617 *gosub_argstart
= '|';
3619 asprintf(&gosub_args
, "%s,s,1", gosubexec
);
3622 res
= pbx_exec(qe
->chan
, app
, gosub_args
);
3623 ast_pbx_run(qe
->chan
);
3626 ast_log(LOG_DEBUG
, "Gosub exited with status %d\n", res
);
3628 ast_log(LOG_ERROR
, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
3632 ast_log(LOG_ERROR
, "Could not find application Gosub\n");
3636 if (ast_autoservice_stop(qe
->chan
) < 0) {
3637 ast_log(LOG_ERROR
, "Could not stop autoservice on calling channel\n");
3642 if (!ast_strlen_zero(agi
)) {
3643 ast_debug(1, "app_queue: agi=%s.\n", agi
);
3644 app
= pbx_findapp("agi");
3646 agiexec
= ast_strdupa(agi
);
3647 ret
= pbx_exec(qe
->chan
, app
, agiexec
);
3649 ast_log(LOG_WARNING
, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3652 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "CONNECT", "%ld|%s|%ld", (long) time(NULL
) - qe
->start
, peer
->uniqueid
,
3653 (long)(orig
- to
> 0 ? (orig
- to
) / 1000 : 0));
3654 if (update_cdr
&& qe
->chan
->cdr
)
3655 ast_copy_string(qe
->chan
->cdr
->dstchannel
, member
->membername
, sizeof(qe
->chan
->cdr
->dstchannel
));
3656 if (qe
->parent
->eventwhencalled
)
3657 manager_event(EVENT_FLAG_AGENT
, "AgentConnect",
3662 "MemberName: %s\r\n"
3664 "BridgedChannel: %s\r\n"
3667 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3668 (long) time(NULL
) - qe
->start
, peer
->uniqueid
, (long)(orig
- to
> 0 ? (orig
- to
) / 1000 : 0),
3669 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
3670 ast_copy_string(oldcontext
, qe
->chan
->context
, sizeof(oldcontext
));
3671 ast_copy_string(oldexten
, qe
->chan
->exten
, sizeof(oldexten
));
3674 bridge
= ast_bridge_call(qe
->chan
,peer
, &bridge_config
);
3676 if (strcasecmp(oldcontext
, qe
->chan
->context
) || strcasecmp(oldexten
, qe
->chan
->exten
)) {
3677 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "TRANSFER", "%s|%s|%ld|%ld",
3678 qe
->chan
->exten
, qe
->chan
->context
, (long) (callstart
- qe
->start
),
3679 (long) (time(NULL
) - callstart
));
3680 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), TRANSFER
);
3681 } else if (ast_check_hangup(qe
->chan
)) {
3682 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETECALLER", "%ld|%ld|%d",
3683 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
3684 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), CALLER
);
3686 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETEAGENT", "%ld|%ld|%d",
3687 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
3688 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), AGENT
);
3691 if (bridge
!= AST_PBX_NO_HANGUP_PEER
)
3693 update_queue(qe
->parent
, member
, callcompletedinsl
);
3694 res
= bridge
? bridge
: 1;
3695 ao2_ref(member
, -1);
3698 hangupcalls(outgoing
, NULL
);
3703 static int wait_a_bit(struct queue_ent
*qe
)
3705 /* Don't need to hold the lock while we setup the outgoing calls */
3706 int retrywait
= qe
->parent
->retry
* 1000;
3708 int res
= ast_waitfordigit(qe
->chan
, retrywait
);
3709 if (res
> 0 && !valid_exit(qe
, res
))
3715 static struct member
*interface_exists(struct call_queue
*q
, const char *interface
)
3718 struct ao2_iterator mem_iter
;
3723 mem_iter
= ao2_iterator_init(q
->members
, 0);
3724 while ((mem
= ao2_iterator_next(&mem_iter
))) {
3725 if (!strcasecmp(interface
, mem
->interface
))
3734 /*! \brief Dump all members in a specific queue to the database
3736 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
3738 static void dump_queue_members(struct call_queue
*pm_queue
)
3740 struct member
*cur_member
;
3741 char value
[PM_MAX_LEN
];
3744 struct ao2_iterator mem_iter
;
3746 memset(value
, 0, sizeof(value
));
3751 mem_iter
= ao2_iterator_init(pm_queue
->members
, 0);
3752 while ((cur_member
= ao2_iterator_next(&mem_iter
))) {
3753 if (!cur_member
->dynamic
) {
3754 ao2_ref(cur_member
, -1);
3758 res
= snprintf(value
+ value_len
, sizeof(value
) - value_len
, "%s%s;%d;%d;%s",
3759 value_len
? "|" : "", cur_member
->interface
, cur_member
->penalty
, cur_member
->paused
, cur_member
->membername
);
3761 ao2_ref(cur_member
, -1);
3763 if (res
!= strlen(value
+ value_len
)) {
3764 ast_log(LOG_WARNING
, "Could not create persistent member string, out of space\n");
3770 if (value_len
&& !cur_member
) {
3771 if (ast_db_put(pm_family
, pm_queue
->name
, value
))
3772 ast_log(LOG_WARNING
, "failed to create persistent dynamic entry!\n");
3774 /* Delete the entry if the queue is empty or there is an error */
3775 ast_db_del(pm_family
, pm_queue
->name
);
3778 /*! \brief Remove member from queue
3779 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3780 * \retval RES_NOSUCHQUEUE queue does not exist
3781 * \retval RES_OKAY removed member from queue
3782 * \retval RES_EXISTS queue exists but no members
3784 static int remove_from_queue(const char *queuename
, const char *interface
)
3786 struct call_queue
*q
, tmpq
= {
3789 struct member
*mem
, tmpmem
;
3790 int res
= RES_NOSUCHQUEUE
;
3792 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
3793 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
3795 if ((mem
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
))) {
3796 /* XXX future changes should beware of this assumption!! */
3797 if (!mem
->dynamic
) {
3800 return RES_NOT_DYNAMIC
;
3803 manager_event(EVENT_FLAG_AGENT
, "QueueMemberRemoved",
3806 "MemberName: %s\r\n",
3807 q
->name
, mem
->interface
, mem
->membername
);
3808 ao2_unlink(q
->members
, mem
);
3809 remove_from_interfaces(mem
->state_interface
);
3812 if (queue_persistent_members
)
3813 dump_queue_members(q
);
3826 /*! \brief Add member to queue
3827 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3828 * \retval RES_NOSUCHQUEUE queue does not exist
3829 * \retval RES_OKAY added member from queue
3830 * \retval RES_EXISTS queue exists but no members
3831 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
3833 static int add_to_queue(const char *queuename
, const char *interface
, const char *membername
, int penalty
, int paused
, int dump
, const char *state_interface
)
3835 struct call_queue
*q
;
3836 struct member
*new_member
, *old_member
;
3837 int res
= RES_NOSUCHQUEUE
;
3839 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
3840 * short-circuits if the queue is already in memory. */
3841 if (!(q
= load_realtime_queue(queuename
)))
3847 if ((old_member
= interface_exists(q
, interface
)) == NULL
) {
3848 if ((new_member
= create_queue_member(interface
, membername
, penalty
, paused
, state_interface
))) {
3849 add_to_interfaces(new_member
->state_interface
);
3850 new_member
->dynamic
= 1;
3851 ao2_link(q
->members
, new_member
);
3853 manager_event(EVENT_FLAG_AGENT
, "QueueMemberAdded",
3856 "MemberName: %s\r\n"
3857 "Membership: %s\r\n"
3859 "CallsTaken: %d\r\n"
3863 q
->name
, new_member
->interface
, new_member
->membername
,
3865 new_member
->penalty
, new_member
->calls
, (int) new_member
->lastcall
,
3866 new_member
->status
, new_member
->paused
);
3868 ao2_ref(new_member
, -1);
3872 dump_queue_members(q
);
3876 res
= RES_OUTOFMEMORY
;
3879 ao2_ref(old_member
, -1);
3888 static int set_member_paused(const char *queuename
, const char *interface
, const char *reason
, int paused
)
3891 struct call_queue
*q
;
3893 struct ao2_iterator queue_iter
;
3896 /* Special event for when all queues are paused - individual events still generated */
3897 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3898 if (ast_strlen_zero(queuename
))
3899 ast_queue_log("NONE", "NONE", interface
, (paused
? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3901 queue_iter
= ao2_iterator_init(queues
, 0);
3902 while ((q
= ao2_iterator_next(&queue_iter
))) {
3904 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
3905 if ((mem
= interface_exists(q
, interface
))) {
3906 if (mem
->paused
== paused
) {
3907 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused
? "" : "un"), (paused
? "" : "un"), q
->name
, interface
);
3911 if (mem
->realtime
) {
3912 failed
= update_realtime_member_field(mem
, q
->name
, "paused", paused
? "1" : "0");
3916 ast_log(LOG_WARNING
, "Failed %spausing realtime queue member %s:%s\n", (paused
? "" : "un"), q
->name
, interface
);
3921 mem
->paused
= paused
;
3923 if (queue_persistent_members
)
3924 dump_queue_members(q
);
3926 ast_queue_log(q
->name
, "NONE", mem
->membername
, (paused
? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason
, ""));
3928 if (!ast_strlen_zero(reason
)) {
3929 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
3932 "MemberName: %s\r\n"
3935 q
->name
, mem
->interface
, mem
->membername
, paused
, reason
);
3937 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
3940 "MemberName: %s\r\n"
3942 q
->name
, mem
->interface
, mem
->membername
, paused
);
3948 if (!ast_strlen_zero(queuename
) && !strcasecmp(queuename
, q
->name
)) {
3958 return found
? RESULT_SUCCESS
: RESULT_FAILURE
;
3961 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
3962 static int set_member_penalty(char *queuename
, char *interface
, int penalty
)
3964 int foundinterface
= 0, foundqueue
= 0;
3965 struct call_queue
*q
;
3967 struct ao2_iterator queue_iter
;
3970 ast_log(LOG_ERROR
, "Invalid penalty (%d)\n", penalty
);
3971 return RESULT_FAILURE
;
3974 queue_iter
= ao2_iterator_init(queues
, 0);
3975 while ((q
= ao2_iterator_next(&queue_iter
))) {
3977 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
3979 if ((mem
= interface_exists(q
, interface
))) {
3981 mem
->penalty
= penalty
;
3983 ast_queue_log(q
->name
, "NONE", interface
, "PENALTY", "%d", penalty
);
3984 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPenalty",
3988 q
->name
, mem
->interface
, penalty
);
3996 if (foundinterface
) {
3997 return RESULT_SUCCESS
;
3998 } else if (!foundqueue
) {
3999 ast_log (LOG_ERROR
, "Invalid queuename\n");
4001 ast_log (LOG_ERROR
, "Invalid interface\n");
4004 return RESULT_FAILURE
;
4007 /* \brief Gets members penalty.
4008 * \return Return the members penalty or RESULT_FAILURE on error.
4010 static int get_member_penalty(char *queuename
, char *interface
)
4012 int foundqueue
= 0, penalty
;
4013 struct call_queue
*q
, tmpq
= {
4018 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4021 if ((mem
= interface_exists(q
, interface
))) {
4022 penalty
= mem
->penalty
;
4031 /* some useful debuging */
4033 ast_log (LOG_ERROR
, "Invalid queuename\n");
4035 ast_log (LOG_ERROR
, "Invalid interface\n");
4037 return RESULT_FAILURE
;
4040 /*! \brief Reload dynamic queue members persisted into the astdb */
4041 static void reload_queue_members(void)
4044 const char *queue_name
;
4047 char *membername
= NULL
;
4048 char *state_interface
;
4053 struct ast_db_entry
*db_tree
;
4054 struct ast_db_entry
*entry
;
4055 struct call_queue
*cur_queue
;
4056 char queue_data
[PM_MAX_LEN
];
4060 /* Each key in 'pm_family' is the name of a queue */
4061 db_tree
= ast_db_gettree(pm_family
, NULL
);
4062 for (entry
= db_tree
; entry
; entry
= entry
->next
) {
4064 queue_name
= entry
->key
+ strlen(pm_family
) + 2;
4067 struct call_queue tmpq
= {
4070 cur_queue
= ao2_find(queues
, &tmpq
, OBJ_POINTER
);
4074 cur_queue
= load_realtime_queue(queue_name
);
4077 /* If the queue no longer exists, remove it from the
4079 ast_log(LOG_WARNING
, "Error loading persistent queue: '%s': it does not exist\n", queue_name
);
4080 ast_db_del(pm_family
, queue_name
);
4084 if (ast_db_get(pm_family
, queue_name
, queue_data
, PM_MAX_LEN
)) {
4085 queue_unref(cur_queue
);
4089 cur_ptr
= queue_data
;
4090 while ((member
= strsep(&cur_ptr
, ",|"))) {
4091 if (ast_strlen_zero(member
))
4094 interface
= strsep(&member
, ";");
4095 penalty_tok
= strsep(&member
, ";");
4096 paused_tok
= strsep(&member
, ";");
4097 membername
= strsep(&member
, ";");
4098 state_interface
= strsep(&member
, ";");
4101 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (penalty)\n", queue_name
);
4104 penalty
= strtol(penalty_tok
, NULL
, 10);
4105 if (errno
== ERANGE
) {
4106 ast_log(LOG_WARNING
, "Error converting penalty: %s: Out of range.\n", penalty_tok
);
4111 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (paused)\n", queue_name
);
4114 paused
= strtol(paused_tok
, NULL
, 10);
4115 if ((errno
== ERANGE
) || paused
< 0 || paused
> 1) {
4116 ast_log(LOG_WARNING
, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok
);
4120 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name
, interface
, membername
, penalty
, paused
);
4122 if (add_to_queue(queue_name
, interface
, membername
, penalty
, paused
, 0, state_interface
) == RES_OUTOFMEMORY
) {
4123 ast_log(LOG_ERROR
, "Out of Memory when reloading persistent queue member\n");
4127 queue_unref(cur_queue
);
4132 ast_log(LOG_NOTICE
, "Queue members successfully reloaded from database.\n");
4133 ast_db_freetree(db_tree
);
4137 /*! \brief PauseQueueMember application */
4138 static int pqm_exec(struct ast_channel
*chan
, void *data
)
4141 AST_DECLARE_APP_ARGS(args
,
4142 AST_APP_ARG(queuename
);
4143 AST_APP_ARG(interface
);
4144 AST_APP_ARG(options
);
4145 AST_APP_ARG(reason
);
4148 if (ast_strlen_zero(data
)) {
4149 ast_log(LOG_WARNING
, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
4153 parse
= ast_strdupa(data
);
4155 AST_STANDARD_APP_ARGS(args
, parse
);
4157 if (ast_strlen_zero(args
.interface
)) {
4158 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4162 if (set_member_paused(args
.queuename
, args
.interface
, args
.reason
, 1)) {
4163 ast_log(LOG_WARNING
, "Attempt to pause interface %s, not found\n", args
.interface
);
4164 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "NOTFOUND");
4168 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "PAUSED");
4173 /*! \brief UnPauseQueueMember application */
4174 static int upqm_exec(struct ast_channel
*chan
, void *data
)
4177 AST_DECLARE_APP_ARGS(args
,
4178 AST_APP_ARG(queuename
);
4179 AST_APP_ARG(interface
);
4180 AST_APP_ARG(options
);
4181 AST_APP_ARG(reason
);
4184 if (ast_strlen_zero(data
)) {
4185 ast_log(LOG_WARNING
, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
4189 parse
= ast_strdupa(data
);
4191 AST_STANDARD_APP_ARGS(args
, parse
);
4193 if (ast_strlen_zero(args
.interface
)) {
4194 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4198 if (set_member_paused(args
.queuename
, args
.interface
, args
.reason
, 0)) {
4199 ast_log(LOG_WARNING
, "Attempt to unpause interface %s, not found\n", args
.interface
);
4200 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "NOTFOUND");
4204 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "UNPAUSED");
4209 /*! \brief RemoveQueueMember application */
4210 static int rqm_exec(struct ast_channel
*chan
, void *data
)
4213 char *parse
, *temppos
= NULL
;
4214 AST_DECLARE_APP_ARGS(args
,
4215 AST_APP_ARG(queuename
);
4216 AST_APP_ARG(interface
);
4217 AST_APP_ARG(options
);
4221 if (ast_strlen_zero(data
)) {
4222 ast_log(LOG_WARNING
, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
4226 parse
= ast_strdupa(data
);
4228 AST_STANDARD_APP_ARGS(args
, parse
);
4230 if (ast_strlen_zero(args
.interface
)) {
4231 args
.interface
= ast_strdupa(chan
->name
);
4232 temppos
= strrchr(args
.interface
, '-');
4237 switch (remove_from_queue(args
.queuename
, args
.interface
)) {
4239 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "REMOVEMEMBER", "%s", "");
4240 ast_log(LOG_NOTICE
, "Removed interface '%s' from queue '%s'\n", args
.interface
, args
.queuename
);
4241 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "REMOVED");
4245 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args
.interface
, args
.queuename
);
4246 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTINQUEUE");
4249 case RES_NOSUCHQUEUE
:
4250 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': No such queue\n", args
.queuename
);
4251 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOSUCHQUEUE");
4254 case RES_NOT_DYNAMIC
:
4255 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args
.queuename
, args
.interface
);
4256 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTDYNAMIC");
4264 /*! \brief AddQueueMember application */
4265 static int aqm_exec(struct ast_channel
*chan
, void *data
)
4268 char *parse
, *temppos
= NULL
;
4269 AST_DECLARE_APP_ARGS(args
,
4270 AST_APP_ARG(queuename
);
4271 AST_APP_ARG(interface
);
4272 AST_APP_ARG(penalty
);
4273 AST_APP_ARG(options
);
4274 AST_APP_ARG(membername
);
4275 AST_APP_ARG(state_interface
);
4279 if (ast_strlen_zero(data
)) {
4280 ast_log(LOG_WARNING
, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
4284 parse
= ast_strdupa(data
);
4286 AST_STANDARD_APP_ARGS(args
, parse
);
4288 if (ast_strlen_zero(args
.interface
)) {
4289 args
.interface
= ast_strdupa(chan
->name
);
4290 temppos
= strrchr(args
.interface
, '-');
4295 if (!ast_strlen_zero(args
.penalty
)) {
4296 if ((sscanf(args
.penalty
, "%d", &penalty
) != 1) || penalty
< 0) {
4297 ast_log(LOG_WARNING
, "Penalty '%s' is invalid, must be an integer >= 0\n", args
.penalty
);
4302 switch (add_to_queue(args
.queuename
, args
.interface
, args
.membername
, penalty
, 0, queue_persistent_members
, args
.state_interface
)) {
4304 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "ADDMEMBER", "%s", "");
4305 ast_log(LOG_NOTICE
, "Added interface '%s' to queue '%s'\n", args
.interface
, args
.queuename
);
4306 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "ADDED");
4310 ast_log(LOG_WARNING
, "Unable to add interface '%s' to queue '%s': Already there\n", args
.interface
, args
.queuename
);
4311 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "MEMBERALREADY");
4314 case RES_NOSUCHQUEUE
:
4315 ast_log(LOG_WARNING
, "Unable to add interface to queue '%s': No such queue\n", args
.queuename
);
4316 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "NOSUCHQUEUE");
4319 case RES_OUTOFMEMORY
:
4320 ast_log(LOG_ERROR
, "Out of memory adding member %s to queue %s\n", args
.interface
, args
.queuename
);
4327 /*! \brief QueueLog application */
4328 static int ql_exec(struct ast_channel
*chan
, void *data
)
4332 AST_DECLARE_APP_ARGS(args
,
4333 AST_APP_ARG(queuename
);
4334 AST_APP_ARG(uniqueid
);
4335 AST_APP_ARG(membername
);
4337 AST_APP_ARG(params
);
4340 if (ast_strlen_zero(data
)) {
4341 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
4345 parse
= ast_strdupa(data
);
4347 AST_STANDARD_APP_ARGS(args
, parse
);
4349 if (ast_strlen_zero(args
.queuename
) || ast_strlen_zero(args
.uniqueid
)
4350 || ast_strlen_zero(args
.membername
) || ast_strlen_zero(args
.event
)) {
4351 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
4355 ast_queue_log(args
.queuename
, args
.uniqueid
, args
.membername
, args
.event
,
4356 "%s", args
.params
? args
.params
: "");
4361 /*! \brief Copy rule from global list into specified queue */
4362 static void copy_rules(struct queue_ent
*qe
, const char *rulename
)
4364 struct penalty_rule
*pr_iter
;
4365 struct rule_list
*rl_iter
;
4366 const char *tmp
= ast_strlen_zero(rulename
) ? qe
->parent
->defaultrule
: rulename
;
4367 AST_LIST_LOCK(&rule_lists
);
4368 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
4369 if (!strcasecmp(rl_iter
->name
, tmp
))
4373 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
4374 struct penalty_rule
*new_pr
= ast_calloc(1, sizeof(*new_pr
));
4376 ast_log(LOG_ERROR
, "Memory allocation error when copying penalty rules! Aborting!\n");
4377 AST_LIST_UNLOCK(&rule_lists
);
4380 new_pr
->time
= pr_iter
->time
;
4381 new_pr
->max_value
= pr_iter
->max_value
;
4382 new_pr
->min_value
= pr_iter
->min_value
;
4383 new_pr
->max_relative
= pr_iter
->max_relative
;
4384 new_pr
->min_relative
= pr_iter
->min_relative
;
4385 AST_LIST_INSERT_TAIL(&qe
->qe_rules
, new_pr
, list
);
4388 AST_LIST_UNLOCK(&rule_lists
);
4391 /*!\brief The starting point for all queue calls
4393 * The process involved here is to
4394 * 1. Parse the options specified in the call to Queue()
4396 * 3. Wait in a loop until it is our turn to try calling a queue member
4397 * 4. Attempt to call a queue member
4398 * 5. If 4. did not result in a bridged call, then check for between
4399 * call options such as periodic announcements etc.
4400 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
4403 static int queue_exec(struct ast_channel
*chan
, void *data
)
4407 const char *user_priority
;
4408 const char *max_penalty_str
;
4409 const char *min_penalty_str
;
4412 int max_penalty
, min_penalty
;
4413 enum queue_result reason
= QUEUE_UNKNOWN
;
4414 /* whether to exit Queue application after the timeout hits */
4418 int makeannouncement
= 0;
4419 AST_DECLARE_APP_ARGS(args
,
4420 AST_APP_ARG(queuename
);
4421 AST_APP_ARG(options
);
4423 AST_APP_ARG(announceoverride
);
4424 AST_APP_ARG(queuetimeoutstr
);
4430 /* Our queue entry */
4431 struct queue_ent qe
;
4433 if (ast_strlen_zero(data
)) {
4434 ast_log(LOG_WARNING
, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
4438 parse
= ast_strdupa(data
);
4439 AST_STANDARD_APP_ARGS(args
, parse
);
4441 /* Setup our queue entry */
4442 memset(&qe
, 0, sizeof(qe
));
4443 qe
.start
= time(NULL
);
4445 /* set the expire time based on the supplied timeout; */
4446 if (!ast_strlen_zero(args
.queuetimeoutstr
))
4447 qe
.expire
= qe
.start
+ atoi(args
.queuetimeoutstr
);
4451 /* Get the priority from the variable ${QUEUE_PRIO} */
4452 user_priority
= pbx_builtin_getvar_helper(chan
, "QUEUE_PRIO");
4453 if (user_priority
) {
4454 if (sscanf(user_priority
, "%d", &prio
) == 1) {
4455 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan
->name
, prio
);
4457 ast_log(LOG_WARNING
, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
4458 user_priority
, chan
->name
);
4462 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
4466 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
4468 if ((max_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MAX_PENALTY"))) {
4469 if (sscanf(max_penalty_str
, "%d", &max_penalty
) == 1) {
4470 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan
->name
, max_penalty
);
4472 ast_log(LOG_WARNING
, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
4473 max_penalty_str
, chan
->name
);
4480 if ((min_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MIN_PENALTY"))) {
4481 if (sscanf(min_penalty_str
, "%d", &min_penalty
) == 1) {
4482 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan
->name
, min_penalty
);
4484 ast_log(LOG_WARNING
, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
4485 min_penalty_str
, chan
->name
);
4492 if (args
.options
&& (strchr(args
.options
, 'r')))
4495 if (args
.options
&& (strchr(args
.options
, 'c')))
4498 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
4499 args
.queuename
, args
.options
, args
.url
, args
.announceoverride
, (long)qe
.expire
, prio
);
4503 qe
.max_penalty
= max_penalty
;
4504 qe
.min_penalty
= min_penalty
;
4505 qe
.last_pos_said
= 0;
4507 qe
.last_periodic_announce_time
= time(NULL
);
4508 qe
.last_periodic_announce_sound
= 0;
4509 qe
.valid_digits
= 0;
4510 if (join_queue(args
.queuename
, &qe
, &reason
)) {
4511 ast_log(LOG_WARNING
, "Unable to join queue '%s'\n", args
.queuename
);
4512 set_queue_result(chan
, reason
);
4515 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args
.url
, ""),
4516 S_OR(chan
->cid
.cid_num
, ""));
4517 copy_rules(&qe
, args
.rule
);
4518 qe
.pr
= AST_LIST_FIRST(&qe
.qe_rules
);
4521 ast_indicate(chan
, AST_CONTROL_RINGING
);
4523 ast_moh_start(chan
, qe
.moh
, NULL
);
4526 /* This is the wait loop for callers 2 through maxlen */
4527 res
= wait_our_turn(&qe
, ringing
, &reason
);
4532 makeannouncement
= 0;
4535 /* This is the wait loop for the head caller*/
4536 /* To exit, they may get their call answered; */
4537 /* they may dial a digit from the queue context; */
4538 /* or, they may timeout. */
4540 enum queue_member_status stat
;
4542 /* Leave if we have exceeded our queuetimeout */
4543 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
4544 record_abandoned(&qe
);
4545 reason
= QUEUE_TIMEOUT
;
4547 ast_queue_log(args
.queuename
, chan
->uniqueid
,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
4548 qe
.pos
, qe
.opos
, (long) time(NULL
) - qe
.start
);
4552 if (makeannouncement
) {
4553 /* Make a position announcement, if enabled */
4554 if (qe
.parent
->announcefrequency
)
4555 if ((res
= say_position(&qe
,ringing
)))
4558 makeannouncement
= 1;
4560 /* Make a periodic announcement, if enabled */
4561 if (qe
.parent
->periodicannouncefrequency
)
4562 if ((res
= say_periodic_announcement(&qe
,ringing
)))
4565 /* see if we need to move to the next penalty level for this queue */
4566 while (qe
.pr
&& ((time(NULL
) - qe
.start
) > qe
.pr
->time
)) {
4567 update_qe_rule(&qe
);
4570 /* Try calling all queue members for 'timeout' seconds */
4571 res
= try_calling(&qe
, args
.options
, args
.announceoverride
, args
.url
, &tries
, &noption
, args
.agi
, args
.macro
, args
.gosub
, ringing
);
4576 stat
= get_member_status(qe
.parent
, qe
.max_penalty
, qe
.min_penalty
);
4578 /* exit after 'timeout' cycle if 'n' option enabled */
4579 if (noption
&& tries
>= qe
.parent
->membercount
) {
4580 ast_verb(3, "Exiting on time-out cycle\n");
4581 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
4582 record_abandoned(&qe
);
4583 reason
= QUEUE_TIMEOUT
;
4588 /* leave the queue if no agents, if enabled */
4589 if (qe
.parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
4590 record_abandoned(&qe
);
4591 reason
= QUEUE_LEAVEEMPTY
;
4592 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
4597 /* leave the queue if no reachable agents, if enabled */
4598 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)) {
4599 record_abandoned(&qe
);
4600 reason
= QUEUE_LEAVEUNAVAIL
;
4601 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
4605 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
4606 record_abandoned(&qe
);
4607 reason
= QUEUE_LEAVEUNAVAIL
;
4612 /* Leave if we have exceeded our queuetimeout */
4613 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
4614 record_abandoned(&qe
);
4615 reason
= QUEUE_TIMEOUT
;
4617 ast_queue_log(qe
.parent
->name
, qe
.chan
->uniqueid
,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long) time(NULL
) - qe
.start
);
4621 /* If using dynamic realtime members, we should regenerate the member list for this queue */
4622 update_realtime_members(qe
.parent
);
4624 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
4625 res
= wait_a_bit(&qe
);
4629 /* Since this is a priority queue and
4630 * it is not sure that we are still at the head
4631 * of the queue, go and check for our turn again.
4633 if (!is_our_turn(&qe
)) {
4634 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe
.chan
->name
);
4643 record_abandoned(&qe
);
4644 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ABANDON",
4645 "%d|%d|%ld", qe
.pos
, qe
.opos
,
4646 (long) time(NULL
) - qe
.start
);
4649 } else if (qe
.valid_digits
) {
4650 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY",
4651 "%s|%d", qe
.digits
, qe
.pos
);
4655 /* Don't allow return code > 0 */
4656 if (res
>= 0 && res
!= AST_PBX_KEEPALIVE
) {
4659 ast_indicate(chan
, -1);
4663 ast_stopstream(chan
);
4666 set_queue_variables(&qe
);
4669 if (reason
!= QUEUE_UNKNOWN
)
4670 set_queue_result(chan
, reason
);
4676 * \brief create interface var with all queue details.
4677 * \retval 0 on success
4678 * \retval -1 on error
4680 static int queue_function_var(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4683 struct call_queue
*q
, tmpq
= {
4687 char interfacevar
[256] = "";
4690 if (ast_strlen_zero(data
)) {
4691 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4695 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4697 if (q
->setqueuevar
) {
4701 if (q
->callscompleted
> 0) {
4702 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
4705 snprintf(interfacevar
, sizeof(interfacevar
),
4706 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
4707 q
->maxlen
, int2strat(q
->strategy
), q
->count
, q
->holdtime
, q
->callscompleted
, q
->callsabandoned
, q
->servicelevel
, sl
);
4709 pbx_builtin_setvar_multiple(chan
, interfacevar
);
4715 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4718 snprintf(buf
, len
, "%d", res
);
4724 * \brief Get number either busy / free or total members of a specific queue
4725 * \retval number of members (busy / free / total)
4726 * \retval -1 on error
4728 static int queue_function_qac(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4732 struct ao2_iterator mem_iter
;
4733 struct call_queue
*q
;
4736 if (ast_strlen_zero(data
)) {
4737 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4741 if ((option
= strchr(data
, ',')))
4745 if ((q
= load_realtime_queue(data
))) {
4747 if (!strcasecmp(option
, "logged")) {
4748 mem_iter
= ao2_iterator_init(q
->members
, 0);
4749 while ((m
= ao2_iterator_next(&mem_iter
))) {
4750 /* Count the agents who are logged in and presently answering calls */
4751 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
4756 } else if (!strcasecmp(option
, "free")) {
4757 mem_iter
= ao2_iterator_init(q
->members
, 0);
4758 while ((m
= ao2_iterator_next(&mem_iter
))) {
4759 /* Count the agents who are logged in and presently answering calls */
4760 if ((m
->status
== AST_DEVICE_NOT_INUSE
) && (!m
->paused
)) {
4765 } else /* must be "count" */
4766 count
= q
->membercount
;
4770 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4772 snprintf(buf
, len
, "%d", count
);
4778 * \brief Get the total number of members in a specific queue (Deprecated)
4779 * \retval number of members
4780 * \retval -1 on error
4782 static int queue_function_qac_dep(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4786 struct call_queue
*q
;
4787 struct ao2_iterator mem_iter
;
4788 static int depflag
= 1;
4792 ast_log(LOG_NOTICE
, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
4795 if (ast_strlen_zero(data
)) {
4796 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4800 if ((q
= load_realtime_queue(data
))) {
4802 mem_iter
= ao2_iterator_init(q
->members
, 0);
4803 while ((m
= ao2_iterator_next(&mem_iter
))) {
4804 /* Count the agents who are logged in and presently answering calls */
4805 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
4813 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4815 snprintf(buf
, len
, "%d", count
);
4820 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
4821 static int queue_function_queuewaitingcount(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4824 struct call_queue
*q
, tmpq
= {
4827 struct ast_variable
*var
= NULL
;
4831 if (ast_strlen_zero(data
)) {
4832 ast_log(LOG_ERROR
, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
4836 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4841 } else if ((var
= ast_load_realtime("queues", "name", data
, NULL
))) {
4842 /* if the queue is realtime but was not found in memory, this
4843 * means that the queue had been deleted from memory since it was
4844 * "dead." This means it has a 0 waiting count
4847 ast_variables_destroy(var
);
4849 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4851 snprintf(buf
, len
, "%d", count
);
4856 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
4857 static int queue_function_queuememberlist(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4859 struct call_queue
*q
, tmpq
= {
4864 /* Ensure an otherwise empty list doesn't return garbage */
4867 if (ast_strlen_zero(data
)) {
4868 ast_log(LOG_ERROR
, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
4872 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4873 int buflen
= 0, count
= 0;
4874 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
4877 while ((m
= ao2_iterator_next(&mem_iter
))) {
4878 /* strcat() is always faster than printf() */
4880 strncat(buf
+ buflen
, ",", len
- buflen
- 1);
4883 strncat(buf
+ buflen
, m
->membername
, len
- buflen
- 1);
4884 buflen
+= strlen(m
->membername
);
4885 /* Safeguard against overflow (negative length) */
4886 if (buflen
>= len
- 2) {
4888 ast_log(LOG_WARNING
, "Truncating list\n");
4896 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4898 /* We should already be terminated, but let's make sure. */
4899 buf
[len
- 1] = '\0';
4904 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
4905 static int queue_function_memberpenalty_read(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4908 AST_DECLARE_APP_ARGS(args
,
4909 AST_APP_ARG(queuename
);
4910 AST_APP_ARG(interface
);
4912 /* Make sure the returned value on error is NULL. */
4915 if (ast_strlen_zero(data
)) {
4916 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4920 AST_STANDARD_APP_ARGS(args
, data
);
4922 if (args
.argc
< 2) {
4923 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4927 penalty
= get_member_penalty (args
.queuename
, args
.interface
);
4929 if (penalty
>= 0) /* remember that buf is already '\0' */
4930 snprintf (buf
, len
, "%d", penalty
);
4935 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
4936 static int queue_function_memberpenalty_write(struct ast_channel
*chan
, const char *cmd
, char *data
, const char *value
)
4939 AST_DECLARE_APP_ARGS(args
,
4940 AST_APP_ARG(queuename
);
4941 AST_APP_ARG(interface
);
4944 if (ast_strlen_zero(data
)) {
4945 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4949 AST_STANDARD_APP_ARGS(args
, data
);
4951 if (args
.argc
< 2) {
4952 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4956 penalty
= atoi(value
);
4958 if (ast_strlen_zero(args
.interface
)) {
4959 ast_log (LOG_ERROR
, "<interface> parameter can't be null\n");
4963 /* if queuename = NULL then penalty will be set for interface in all the queues. */
4964 if (set_member_penalty(args
.queuename
, args
.interface
, penalty
)) {
4965 ast_log(LOG_ERROR
, "Invalid interface, queue or penalty\n");
4972 static struct ast_custom_function queuevar_function
= {
4973 .name
= "QUEUE_VARIABLES",
4974 .synopsis
= "Return Queue information in variables",
4975 .syntax
= "QUEUE_VARIABLES(<queuename>)",
4977 "Makes the following queue variables available.\n"
4978 "QUEUEMAX maxmimum number of calls allowed\n"
4979 "QUEUESTRATEGY the strategy of the queue\n"
4980 "QUEUECALLS number of calls currently in the queue\n"
4981 "QUEUEHOLDTIME current average hold time\n"
4982 "QUEUECOMPLETED number of completed calls for the queue\n"
4983 "QUEUEABANDONED number of abandoned calls\n"
4984 "QUEUESRVLEVEL queue service level\n"
4985 "QUEUESRVLEVELPERF current service level performance\n"
4986 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
4987 .read
= queue_function_var
,
4990 static struct ast_custom_function queuemembercount_function
= {
4991 .name
= "QUEUE_MEMBER",
4992 .synopsis
= "Count number of members answering a queue",
4993 .syntax
= "QUEUE_MEMBER(<queuename>, <option>)",
4995 "Returns the number of members currently associated with the specified queue.\n"
4996 "One of three options may be passed to determine the count returned:\n"
4997 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
4998 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
4999 "\"count\" - Returns the total number of members for the specified queue\n",
5000 .read
= queue_function_qac
,
5003 static struct ast_custom_function queuemembercount_dep
= {
5004 .name
= "QUEUE_MEMBER_COUNT",
5005 .synopsis
= "Count number of members answering a queue",
5006 .syntax
= "QUEUE_MEMBER_COUNT(<queuename>)",
5008 "Returns the number of members currently associated with the specified queue.\n\n"
5009 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
5010 .read
= queue_function_qac_dep
,
5013 static struct ast_custom_function queuewaitingcount_function
= {
5014 .name
= "QUEUE_WAITING_COUNT",
5015 .synopsis
= "Count number of calls currently waiting in a queue",
5016 .syntax
= "QUEUE_WAITING_COUNT(<queuename>)",
5018 "Returns the number of callers currently waiting in the specified queue.\n",
5019 .read
= queue_function_queuewaitingcount
,
5022 static struct ast_custom_function queuememberlist_function
= {
5023 .name
= "QUEUE_MEMBER_LIST",
5024 .synopsis
= "Returns a list of interfaces on a queue",
5025 .syntax
= "QUEUE_MEMBER_LIST(<queuename>)",
5027 "Returns a comma-separated list of members associated with the specified queue.\n",
5028 .read
= queue_function_queuememberlist
,
5031 static struct ast_custom_function queuememberpenalty_function
= {
5032 .name
= "QUEUE_MEMBER_PENALTY",
5033 .synopsis
= "Gets or sets queue members penalty.",
5034 .syntax
= "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
5036 "Gets or sets queue members penalty\n",
5037 .read
= queue_function_memberpenalty_read
,
5038 .write
= queue_function_memberpenalty_write
,
5041 static int reload_queue_rules(int reload
)
5043 struct ast_config
*cfg
;
5044 struct rule_list
*rl_iter
, *new_rl
;
5045 struct penalty_rule
*pr_iter
;
5046 char *rulecat
= NULL
;
5047 struct ast_variable
*rulevar
= NULL
;
5048 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
5050 if (!(cfg
= ast_config_load("queuerules.conf", config_flags
))) {
5051 ast_log(LOG_NOTICE
, "No queuerules.conf file found, queues will not follow penalty rules\n");
5052 } else if (cfg
== CONFIG_STATUS_FILEUNCHANGED
) {
5053 ast_log(LOG_NOTICE
, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
5054 return AST_MODULE_LOAD_SUCCESS
;
5056 AST_LIST_LOCK(&rule_lists
);
5057 while ((rl_iter
= AST_LIST_REMOVE_HEAD(&rule_lists
, list
))) {
5058 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&rl_iter
->rules
, list
)))
5062 while ((rulecat
= ast_category_browse(cfg
, rulecat
))) {
5063 if (!(new_rl
= ast_calloc(1, sizeof(*new_rl
)))) {
5064 ast_log(LOG_ERROR
, "Memory allocation error while loading queuerules.conf! Aborting!\n");
5065 AST_LIST_UNLOCK(&rule_lists
);
5066 return AST_MODULE_LOAD_FAILURE
;
5068 ast_copy_string(new_rl
->name
, rulecat
, sizeof(new_rl
->name
));
5069 AST_LIST_INSERT_TAIL(&rule_lists
, new_rl
, list
);
5070 for (rulevar
= ast_variable_browse(cfg
, rulecat
); rulevar
; rulevar
= rulevar
->next
)
5071 if(!strcasecmp(rulevar
->name
, "penaltychange"))
5072 insert_penaltychange(new_rl
->name
, rulevar
->value
, rulevar
->lineno
);
5074 ast_log(LOG_WARNING
, "Don't know how to handle rule type '%s' on line %d\n", rulevar
->name
, rulevar
->lineno
);
5077 AST_LIST_UNLOCK(&rule_lists
);
5080 ast_config_destroy(cfg
);
5082 return AST_MODULE_LOAD_SUCCESS
;
5086 static int reload_queues(int reload
)
5088 struct call_queue
*q
;
5089 struct ast_config
*cfg
;
5091 struct ast_variable
*var
;
5092 struct member
*cur
, *newm
;
5093 struct ao2_iterator mem_iter
;
5095 const char *general_val
= NULL
;
5097 char *interface
, *state_interface
;
5098 char *membername
= NULL
;
5100 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
5101 struct ao2_iterator queue_iter
;
5102 AST_DECLARE_APP_ARGS(args
,
5103 AST_APP_ARG(interface
);
5104 AST_APP_ARG(penalty
);
5105 AST_APP_ARG(membername
);
5106 AST_APP_ARG(state_interface
);
5109 /*First things first. Let's load queuerules.conf*/
5110 if (reload_queue_rules(reload
) == AST_MODULE_LOAD_FAILURE
)
5111 return AST_MODULE_LOAD_FAILURE
;
5113 if (!(cfg
= ast_config_load("queues.conf", config_flags
))) {
5114 ast_log(LOG_NOTICE
, "No call queueing config file (queues.conf), so no call queues\n");
5116 } else if (cfg
== CONFIG_STATUS_FILEUNCHANGED
)
5120 /* Mark all queues as dead for the moment */
5121 queue_iter
= ao2_iterator_init(queues
, F_AO2I_DONTLOCK
);
5122 while ((q
= ao2_iterator_next(&queue_iter
))) {
5130 /* Chug through config file */
5132 while ((cat
= ast_category_browse(cfg
, cat
)) ) {
5133 if (!strcasecmp(cat
, "general")) {
5134 /* Initialize global settings */
5135 queue_keep_stats
= 0;
5136 if ((general_val
= ast_variable_retrieve(cfg
, "general", "keepstats")))
5137 queue_keep_stats
= ast_true(general_val
);
5138 queue_persistent_members
= 0;
5139 if ((general_val
= ast_variable_retrieve(cfg
, "general", "persistentmembers")))
5140 queue_persistent_members
= ast_true(general_val
);
5141 autofill_default
= 0;
5142 if ((general_val
= ast_variable_retrieve(cfg
, "general", "autofill")))
5143 autofill_default
= ast_true(general_val
);
5144 montype_default
= 0;
5145 if ((general_val
= ast_variable_retrieve(cfg
, "general", "monitor-type"))) {
5146 if (!strcasecmp(general_val
, "mixmonitor"))
5147 montype_default
= 1;
5150 if ((general_val
= ast_variable_retrieve(cfg
, "general", "updatecdr")))
5151 update_cdr
= ast_true(general_val
);
5152 shared_lastcall
= 0;
5153 if ((general_val
= ast_variable_retrieve(cfg
, "general", "shared_lastcall")))
5154 shared_lastcall
= ast_true(general_val
);
5155 } else { /* Define queue */
5156 /* Look for an existing one */
5157 struct call_queue tmpq
= {
5160 if (!(q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
5162 if (!(q
= alloc_queue(cat
))) {
5163 /* TODO: Handle memory allocation failure */
5169 const char *tmpvar
= NULL
;
5172 /* Check if a queue with this name already exists */
5174 ast_log(LOG_WARNING
, "Queue '%s' already defined! Skipping!\n", cat
);
5181 /* Due to the fact that the "linear" strategy will have a different allocation
5182 * scheme for queue members, we must devise the queue's strategy before other initializations
5184 if ((tmpvar
= ast_variable_retrieve(cfg
, cat
, "strategy"))) {
5185 q
->strategy
= strat2int(tmpvar
);
5186 if (q
->strategy
< 0) {
5187 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
5189 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
5192 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
5193 /* Re-initialize the queue, and clear statistics */
5195 if (!queue_keep_stats
)
5197 mem_iter
= ao2_iterator_init(q
->members
, 0);
5198 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5199 if (!cur
->dynamic
) {
5204 for (var
= ast_variable_browse(cfg
, cat
); var
; var
= var
->next
) {
5205 if (!strcasecmp(var
->name
, "member")) {
5206 struct member tmpmem
;
5209 /* Add a new member */
5210 ast_copy_string(parse
, var
->value
, sizeof(parse
));
5212 AST_STANDARD_APP_ARGS(args
, parse
);
5214 interface
= args
.interface
;
5215 if (!ast_strlen_zero(args
.penalty
)) {
5217 while (*tmp
&& *tmp
< 33) tmp
++;
5218 penalty
= atoi(tmp
);
5225 if (!ast_strlen_zero(args
.membername
)) {
5226 membername
= args
.membername
;
5227 while (*membername
&& *membername
< 33) membername
++;
5230 if (!ast_strlen_zero(args
.state_interface
)) {
5231 state_interface
= args
.state_interface
;
5232 while (*state_interface
&& *state_interface
< 33) state_interface
++;
5234 state_interface
= interface
;
5236 /* Find the old position in the list */
5237 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
5238 cur
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
| OBJ_UNLINK
);
5239 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
5240 if (cur
&& strcasecmp(cur
->state_interface
, state_interface
)) {
5241 remove_from_interfaces(cur
->state_interface
);
5243 newm
= create_queue_member(interface
, membername
, penalty
, cur
? cur
->paused
: 0, state_interface
);
5244 if (!cur
|| (cur
&& strcasecmp(cur
->state_interface
, state_interface
)))
5245 add_to_interfaces(state_interface
);
5246 ao2_link(q
->members
, newm
);
5256 queue_set_param(q
, var
->name
, var
->value
, var
->lineno
, 1);
5260 /* Free remaining members marked as delme */
5261 mem_iter
= ao2_iterator_init(q
->members
, 0);
5262 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5268 ao2_unlink(q
->members
, cur
);
5269 remove_from_interfaces(cur
->interface
);
5274 ao2_link(queues
, q
);
5281 ast_config_destroy(cfg
);
5282 queue_iter
= ao2_iterator_init(queues
, 0);
5283 while ((q
= ao2_iterator_next(&queue_iter
))) {
5285 ao2_unlink(queues
, q
);
5288 mem_iter
= ao2_iterator_init(q
->members
, 0);
5289 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5292 cur
->status
= ast_device_state(cur
->interface
);
5303 /*! \brief direct ouput to manager or cli with proper terminator */
5304 static void do_print(struct mansession
*s
, int fd
, const char *str
)
5307 astman_append(s
, "%s\r\n", str
);
5309 ast_cli(fd
, "%s\n", str
);
5313 * \brief Show queue(s) status and statistics
5315 * List the queues strategy, calls processed, members logged in,
5316 * other queue statistics such as avg hold time.
5318 static char *__queues_show(struct mansession
*s
, int fd
, int argc
, char **argv
)
5320 struct call_queue
*q
;
5321 struct ast_str
*out
= ast_str_alloca(240);
5323 time_t now
= time(NULL
);
5324 struct ao2_iterator queue_iter
;
5325 struct ao2_iterator mem_iter
;
5327 if (argc
!= 2 && argc
!= 3)
5328 return CLI_SHOWUSAGE
;
5330 /* We only want to load realtime queues when a specific queue is asked for. */
5331 if (argc
== 3) /* specific queue */
5332 load_realtime_queue(argv
[2]);
5334 queue_iter
= ao2_iterator_init(queues
, 0);
5335 while ((q
= ao2_iterator_next(&queue_iter
))) {
5339 if (argc
== 3 && strcasecmp(q
->name
, argv
[2])) {
5345 ast_str_set(&out
, 0, "%-12.12s has %d calls (max ", q
->name
, q
->count
);
5347 ast_str_append(&out
, 0, "%d", q
->maxlen
);
5349 ast_str_append(&out
, 0, "unlimited");
5351 if (q
->callscompleted
> 0)
5352 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
5353 ast_str_append(&out
, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
5354 int2strat(q
->strategy
), q
->holdtime
, q
->weight
,
5355 q
->callscompleted
, q
->callsabandoned
,sl
,q
->servicelevel
);
5356 do_print(s
, fd
, out
->str
);
5357 if (!ao2_container_count(q
->members
))
5358 do_print(s
, fd
, " No Members");
5362 do_print(s
, fd
, " Members: ");
5363 mem_iter
= ao2_iterator_init(q
->members
, 0);
5364 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5365 ast_str_set(&out
, 0, " %s", mem
->membername
);
5367 ast_str_append(&out
, 0, " with penalty %d", mem
->penalty
);
5368 ast_str_append(&out
, 0, "%s%s%s (%s)",
5369 mem
->dynamic
? " (dynamic)" : "",
5370 mem
->realtime
? " (realtime)" : "",
5371 mem
->paused
? " (paused)" : "",
5372 devstate2str(mem
->status
));
5374 ast_str_append(&out
, 0, " has taken %d calls (last was %ld secs ago)",
5375 mem
->calls
, (long) (time(NULL
) - mem
->lastcall
));
5377 ast_str_append(&out
, 0, " has taken no calls yet");
5378 do_print(s
, fd
, out
->str
);
5383 do_print(s
, fd
, " No Callers");
5385 struct queue_ent
*qe
;
5388 do_print(s
, fd
, " Callers: ");
5389 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5390 ast_str_set(&out
, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
5391 pos
++, qe
->chan
->name
, (long) (now
- qe
->start
) / 60,
5392 (long) (now
- qe
->start
) % 60, qe
->prio
);
5393 do_print(s
, fd
, out
->str
);
5396 do_print(s
, fd
, ""); /* blank line between entries */
5398 if (argc
== 3) { /* print a specific entry */
5406 ast_str_set(&out
, 0, "No such queue: %s.", argv
[2]);
5408 ast_str_set(&out
, 0, "No queues.");
5409 do_print(s
, fd
, out
->str
);
5414 static char *complete_queue(const char *line
, const char *word
, int pos
, int state
)
5416 struct call_queue
*q
;
5419 int wordlen
= strlen(word
);
5420 struct ao2_iterator queue_iter
;
5422 queue_iter
= ao2_iterator_init(queues
, 0);
5423 while ((q
= ao2_iterator_next(&queue_iter
))) {
5424 if (!strncasecmp(word
, q
->name
, wordlen
) && ++which
> state
) {
5425 ret
= ast_strdup(q
->name
);
5435 static char *complete_queue_show(const char *line
, const char *word
, int pos
, int state
)
5438 return complete_queue(line
, word
, pos
, state
);
5442 static char *queue_show(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
5446 e
->command
= "queue show";
5448 "Usage: queue show\n"
5449 " Provides summary information on a specified queue.\n";
5452 return complete_queue_show(a
->line
, a
->word
, a
->pos
, a
->n
);
5455 return __queues_show(NULL
, a
->fd
, a
->argc
, a
->argv
);
5458 /*!\brief callback to display queues status in manager
5459 \addtogroup Group_AMI
5461 static int manager_queues_show(struct mansession
*s
, const struct message
*m
)
5463 char *a
[] = { "queue", "show" };
5465 __queues_show(s
, -1, 2, a
);
5466 astman_append(s
, "\r\n\r\n"); /* Properly terminate Manager output */
5468 return RESULT_SUCCESS
;
5471 static int manager_queue_rule_show(struct mansession
*s
, const struct message
*m
)
5473 const char *rule
= astman_get_header(m
, "Rule");
5474 struct rule_list
*rl_iter
;
5475 struct penalty_rule
*pr_iter
;
5477 AST_LIST_LOCK(&rule_lists
);
5478 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
5479 if (ast_strlen_zero(rule
) || !strcasecmp(rule
, rl_iter
->name
)) {
5480 astman_append(s
, "RuleList: %s\r\n", rl_iter
->name
);
5481 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
5482 astman_append(s
, "Rule: %d,%s%d,%s%d\r\n", pr_iter
->time
, pr_iter
->max_relative
&& pr_iter
->max_value
>= 0 ? "+" : "", pr_iter
->max_value
, pr_iter
->min_relative
&& pr_iter
->min_value
>= 0 ? "+" : "", pr_iter
->min_value
);
5484 if (!ast_strlen_zero(rule
))
5488 AST_LIST_UNLOCK(&rule_lists
);
5490 astman_append(s
, "\r\n\r\n");
5492 return RESULT_SUCCESS
;
5495 /*! \brief Summary of queue info via the AMI */
5496 static int manager_queues_summary(struct mansession
*s
, const struct message
*m
)
5502 int qlongestholdtime
= 0;
5503 const char *id
= astman_get_header(m
, "ActionID");
5504 const char *queuefilter
= astman_get_header(m
, "Queue");
5505 char idText
[256] = "";
5506 struct call_queue
*q
;
5507 struct queue_ent
*qe
;
5509 struct ao2_iterator queue_iter
;
5510 struct ao2_iterator mem_iter
;
5512 astman_send_ack(s
, m
, "Queue summary will follow");
5514 if (!ast_strlen_zero(id
))
5515 snprintf(idText
, 256, "ActionID: %s\r\n", id
);
5516 queue_iter
= ao2_iterator_init(queues
, 0);
5517 while ((q
= ao2_iterator_next(&queue_iter
))) {
5520 /* List queue properties */
5521 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
5522 /* Reset the necessary local variables if no queuefilter is set*/
5526 qlongestholdtime
= 0;
5528 /* List Queue Members */
5529 mem_iter
= ao2_iterator_init(q
->members
, 0);
5530 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5531 if ((mem
->status
!= AST_DEVICE_UNAVAILABLE
) && (mem
->status
!= AST_DEVICE_INVALID
)) {
5533 if (((mem
->status
== AST_DEVICE_NOT_INUSE
) || (mem
->status
== AST_DEVICE_UNKNOWN
)) && !(mem
->paused
)) {
5539 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5540 if ((now
- qe
->start
) > qlongestholdtime
) {
5541 qlongestholdtime
= now
- qe
->start
;
5545 astman_append(s
, "Event: QueueSummary\r\n"
5551 "LongestHoldTime: %d\r\n"
5554 q
->name
, qmemcount
, qmemavail
, qchancount
, q
->holdtime
, qlongestholdtime
, idText
);
5560 "Event: QueueSummaryComplete\r\n"
5564 return RESULT_SUCCESS
;
5567 /*! \brief Queue status info via AMI */
5568 static int manager_queues_status(struct mansession
*s
, const struct message
*m
)
5572 const char *id
= astman_get_header(m
,"ActionID");
5573 const char *queuefilter
= astman_get_header(m
,"Queue");
5574 const char *memberfilter
= astman_get_header(m
,"Member");
5575 char idText
[256] = "";
5576 struct call_queue
*q
;
5577 struct queue_ent
*qe
;
5580 struct ao2_iterator queue_iter
;
5581 struct ao2_iterator mem_iter
;
5583 astman_send_ack(s
, m
, "Queue status will follow");
5585 if (!ast_strlen_zero(id
))
5586 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
5588 queue_iter
= ao2_iterator_init(queues
, 0);
5589 while ((q
= ao2_iterator_next(&queue_iter
))) {
5592 /* List queue properties */
5593 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
5594 sl
= ((q
->callscompleted
> 0) ? 100 * ((float)q
->callscompletedinsl
/ (float)q
->callscompleted
) : 0);
5595 astman_append(s
, "Event: QueueParams\r\n"
5603 "ServiceLevel: %d\r\n"
5604 "ServicelevelPerf: %2.1f\r\n"
5608 q
->name
, q
->maxlen
, int2strat(q
->strategy
), q
->count
, q
->holdtime
, q
->callscompleted
,
5609 q
->callsabandoned
, q
->servicelevel
, sl
, q
->weight
, idText
);
5610 /* List Queue Members */
5611 mem_iter
= ao2_iterator_init(q
->members
, 0);
5612 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5613 if (ast_strlen_zero(memberfilter
) || !strcmp(mem
->interface
, memberfilter
)) {
5614 astman_append(s
, "Event: QueueMember\r\n"
5618 "Membership: %s\r\n"
5620 "CallsTaken: %d\r\n"
5626 q
->name
, mem
->membername
, mem
->interface
, mem
->dynamic
? "dynamic" : "static",
5627 mem
->penalty
, mem
->calls
, (int)mem
->lastcall
, mem
->status
, mem
->paused
, idText
);
5631 /* List Queue Entries */
5633 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5634 astman_append(s
, "Event: QueueEntry\r\n"
5638 "CallerIDNum: %s\r\n"
5639 "CallerIDName: %s\r\n"
5643 q
->name
, pos
++, qe
->chan
->name
,
5644 S_OR(qe
->chan
->cid
.cid_num
, "unknown"),
5645 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
5646 (long) (now
- qe
->start
), idText
);
5654 "Event: QueueStatusComplete\r\n"
5658 return RESULT_SUCCESS
;
5661 static int manager_add_queue_member(struct mansession
*s
, const struct message
*m
)
5663 const char *queuename
, *interface
, *penalty_s
, *paused_s
, *membername
, *state_interface
;
5664 int paused
, penalty
= 0;
5666 queuename
= astman_get_header(m
, "Queue");
5667 interface
= astman_get_header(m
, "Interface");
5668 penalty_s
= astman_get_header(m
, "Penalty");
5669 paused_s
= astman_get_header(m
, "Paused");
5670 membername
= astman_get_header(m
, "MemberName");
5671 state_interface
= astman_get_header(m
, "StateInterface");
5673 if (ast_strlen_zero(queuename
)) {
5674 astman_send_error(s
, m
, "'Queue' not specified.");
5678 if (ast_strlen_zero(interface
)) {
5679 astman_send_error(s
, m
, "'Interface' not specified.");
5683 if (ast_strlen_zero(penalty_s
))
5685 else if (sscanf(penalty_s
, "%d", &penalty
) != 1 || penalty
< 0)
5688 if (ast_strlen_zero(paused_s
))
5691 paused
= abs(ast_true(paused_s
));
5693 switch (add_to_queue(queuename
, interface
, membername
, penalty
, paused
, queue_persistent_members
, state_interface
)) {
5695 ast_queue_log(queuename
, "MANAGER", interface
, "ADDMEMBER", "%s", "");
5696 astman_send_ack(s
, m
, "Added interface to queue");
5699 astman_send_error(s
, m
, "Unable to add interface: Already there");
5701 case RES_NOSUCHQUEUE
:
5702 astman_send_error(s
, m
, "Unable to add interface to queue: No such queue");
5704 case RES_OUTOFMEMORY
:
5705 astman_send_error(s
, m
, "Out of memory");
5712 static int manager_remove_queue_member(struct mansession
*s
, const struct message
*m
)
5714 const char *queuename
, *interface
;
5716 queuename
= astman_get_header(m
, "Queue");
5717 interface
= astman_get_header(m
, "Interface");
5719 if (ast_strlen_zero(queuename
) || ast_strlen_zero(interface
)) {
5720 astman_send_error(s
, m
, "Need 'Queue' and 'Interface' parameters.");
5724 switch (remove_from_queue(queuename
, interface
)) {
5726 ast_queue_log(queuename
, "MANAGER", interface
, "REMOVEMEMBER", "%s", "");
5727 astman_send_ack(s
, m
, "Removed interface from queue");
5730 astman_send_error(s
, m
, "Unable to remove interface: Not there");
5732 case RES_NOSUCHQUEUE
:
5733 astman_send_error(s
, m
, "Unable to remove interface from queue: No such queue");
5735 case RES_OUTOFMEMORY
:
5736 astman_send_error(s
, m
, "Out of memory");
5738 case RES_NOT_DYNAMIC
:
5739 astman_send_error(s
, m
, "Member not dynamic");
5746 static int manager_pause_queue_member(struct mansession
*s
, const struct message
*m
)
5748 const char *queuename
, *interface
, *paused_s
, *reason
;
5751 interface
= astman_get_header(m
, "Interface");
5752 paused_s
= astman_get_header(m
, "Paused");
5753 queuename
= astman_get_header(m
, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
5754 reason
= astman_get_header(m
, "Reason"); /* Optional - Only used for logging purposes */
5756 if (ast_strlen_zero(interface
) || ast_strlen_zero(paused_s
)) {
5757 astman_send_error(s
, m
, "Need 'Interface' and 'Paused' parameters.");
5761 paused
= abs(ast_true(paused_s
));
5763 if (set_member_paused(queuename
, interface
, reason
, paused
))
5764 astman_send_error(s
, m
, "Interface not found");
5766 astman_send_ack(s
, m
, paused
? "Interface paused successfully" : "Interface unpaused successfully");
5770 static int manager_queue_log_custom(struct mansession
*s
, const struct message
*m
)
5772 const char *queuename
, *event
, *message
, *interface
, *uniqueid
;
5774 queuename
= astman_get_header(m
, "Queue");
5775 uniqueid
= astman_get_header(m
, "UniqueId");
5776 interface
= astman_get_header(m
, "Interface");
5777 event
= astman_get_header(m
, "Event");
5778 message
= astman_get_header(m
, "Message");
5780 if (ast_strlen_zero(queuename
) || ast_strlen_zero(event
)) {
5781 astman_send_error(s
, m
, "Need 'Queue' and 'Event' parameters.");
5785 ast_queue_log(queuename
, S_OR(uniqueid
, "NONE"), interface
, event
, "%s", message
);
5786 astman_send_ack(s
, m
, "Event added successfully");
5791 static char *complete_queue_add_member(const char *line
, const char *word
, int pos
, int state
)
5793 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
5795 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
5797 case 4: /* only one possible match, "to" */
5798 return state
== 0 ? ast_strdup("to") : NULL
;
5799 case 5: /* <queue> */
5800 return complete_queue(line
, word
, pos
, state
);
5801 case 6: /* only one possible match, "penalty" */
5802 return state
== 0 ? ast_strdup("penalty") : NULL
;
5804 if (state
< 100) { /* 0-99 */
5806 if ((num
= ast_malloc(3))) {
5807 sprintf(num
, "%d", state
);
5813 case 8: /* only one possible match, "as" */
5814 return state
== 0 ? ast_strdup("as") : NULL
;
5815 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
5822 static int manager_queue_member_penalty(struct mansession
*s
, const struct message
*m
)
5824 const char *queuename
, *interface
, *penalty_s
;
5827 interface
= astman_get_header(m
, "Interface");
5828 penalty_s
= astman_get_header(m
, "Penalty");
5829 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
5830 queuename
= astman_get_header(m
, "Queue");
5832 if (ast_strlen_zero(interface
) || ast_strlen_zero(penalty_s
)) {
5833 astman_send_error(s
, m
, "Need 'Interface' and 'Penalty' parameters.");
5837 penalty
= atoi(penalty_s
);
5839 if (set_member_penalty((char *)queuename
, (char *)interface
, penalty
))
5840 astman_send_error(s
, m
, "Invalid interface, queuename or penalty");
5842 astman_send_ack(s
, m
, "Interface penalty set successfully");
5847 static char *handle_queue_add_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
5849 char *queuename
, *interface
, *membername
= NULL
, *state_interface
= NULL
;
5854 e
->command
= "queue add member";
5856 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
5859 return complete_queue_add_member(a
->line
, a
->word
, a
->pos
, a
->n
);
5862 if ((a
->argc
!= 6) && (a
->argc
!= 8) && (a
->argc
!= 10) && (a
->argc
!= 12)) {
5863 return CLI_SHOWUSAGE
;
5864 } else if (strcmp(a
->argv
[4], "to")) {
5865 return CLI_SHOWUSAGE
;
5866 } else if ((a
->argc
>= 8) && strcmp(a
->argv
[6], "penalty")) {
5867 return CLI_SHOWUSAGE
;
5868 } else if ((a
->argc
>= 10) && strcmp(a
->argv
[8], "as")) {
5869 return CLI_SHOWUSAGE
;
5870 } else if ((a
->argc
== 12) && strcmp(a
->argv
[10], "state_interface")) {
5871 return CLI_SHOWUSAGE
;
5874 queuename
= a
->argv
[5];
5875 interface
= a
->argv
[3];
5877 if (sscanf(a
->argv
[7], "%d", &penalty
) == 1) {
5879 ast_cli(a
->fd
, "Penalty must be >= 0\n");
5883 ast_cli(a
->fd
, "Penalty must be an integer >= 0\n");
5890 if (a
->argc
>= 10) {
5891 membername
= a
->argv
[9];
5894 if (a
->argc
>= 12) {
5895 state_interface
= a
->argv
[11];
5898 switch (add_to_queue(queuename
, interface
, membername
, penalty
, 0, queue_persistent_members
, state_interface
)) {
5900 ast_queue_log(queuename
, "CLI", interface
, "ADDMEMBER", "%s", "");
5901 ast_cli(a
->fd
, "Added interface '%s' to queue '%s'\n", interface
, queuename
);
5904 ast_cli(a
->fd
, "Unable to add interface '%s' to queue '%s': Already there\n", interface
, queuename
);
5906 case RES_NOSUCHQUEUE
:
5907 ast_cli(a
->fd
, "Unable to add interface to queue '%s': No such queue\n", queuename
);
5909 case RES_OUTOFMEMORY
:
5910 ast_cli(a
->fd
, "Out of memory\n");
5912 case RES_NOT_DYNAMIC
:
5913 ast_cli(a
->fd
, "Member not dynamic\n");
5920 static char *complete_queue_remove_member(const char *line
, const char *word
, int pos
, int state
)
5923 struct call_queue
*q
;
5925 struct ao2_iterator queue_iter
;
5926 struct ao2_iterator mem_iter
;
5927 int wordlen
= strlen(word
);
5929 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
5930 if (pos
> 5 || pos
< 3)
5932 if (pos
== 4) /* only one possible match, 'from' */
5933 return (state
== 0 ? ast_strdup("from") : NULL
);
5935 if (pos
== 5) /* No need to duplicate code */
5936 return complete_queue(line
, word
, pos
, state
);
5938 /* here is the case for 3, <member> */
5939 queue_iter
= ao2_iterator_init(queues
, 0);
5940 while ((q
= ao2_iterator_next(&queue_iter
))) {
5942 mem_iter
= ao2_iterator_init(q
->members
, 0);
5943 while ((m
= ao2_iterator_next(&mem_iter
))) {
5944 if (!strncasecmp(word
, m
->membername
, wordlen
) && ++which
> state
) {
5947 tmp
= m
->membername
;
5950 return ast_strdup(tmp
);
5961 static char *handle_queue_remove_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
5963 char *queuename
, *interface
;
5967 e
->command
= "queue remove member";
5968 e
->usage
= "Usage: queue remove member <channel> from <queue>\n";
5971 return complete_queue_remove_member(a
->line
, a
->word
, a
->pos
, a
->n
);
5975 return CLI_SHOWUSAGE
;
5976 } else if (strcmp(a
->argv
[4], "from")) {
5977 return CLI_SHOWUSAGE
;
5980 queuename
= a
->argv
[5];
5981 interface
= a
->argv
[3];
5983 switch (remove_from_queue(queuename
, interface
)) {
5985 ast_queue_log(queuename
, "CLI", interface
, "REMOVEMEMBER", "%s", "");
5986 ast_cli(a
->fd
, "Removed interface '%s' from queue '%s'\n", interface
, queuename
);
5989 ast_cli(a
->fd
, "Unable to remove interface '%s' from queue '%s': Not there\n", interface
, queuename
);
5991 case RES_NOSUCHQUEUE
:
5992 ast_cli(a
->fd
, "Unable to remove interface from queue '%s': No such queue\n", queuename
);
5994 case RES_OUTOFMEMORY
:
5995 ast_cli(a
->fd
, "Out of memory\n");
6002 static char *complete_queue_pause_member(const char *line
, const char *word
, int pos
, int state
)
6004 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
6006 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
6008 case 4: /* only one possible match, "queue" */
6009 return state
== 0 ? ast_strdup("queue") : NULL
;
6010 case 5: /* <queue> */
6011 return complete_queue(line
, word
, pos
, state
);
6012 case 6: /* "reason" */
6013 return state
== 0 ? ast_strdup("reason") : NULL
;
6014 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
6021 static char *handle_queue_pause_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6023 char *queuename
, *interface
, *reason
;
6028 e
->command
= "queue {pause|unpause} member";
6030 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
6031 " Pause or unpause a queue member. Not specifying a particular queue\n"
6032 " will pause or unpause a member across all queues to which the member\n"
6036 return complete_queue_pause_member(a
->line
, a
-> word
, a
->pos
, a
->n
);
6039 if (a
->argc
< 4 || a
->argc
== 5 || a
->argc
== 7 || a
->argc
> 8) {
6040 return CLI_SHOWUSAGE
;
6041 } else if (a
->argc
>= 5 && strcmp(a
->argv
[4], "queue")) {
6042 return CLI_SHOWUSAGE
;
6043 } else if (a
->argc
== 8 && strcmp(a
->argv
[6], "reason")) {
6044 return CLI_SHOWUSAGE
;
6048 interface
= a
->argv
[3];
6049 queuename
= a
->argc
>= 6 ? a
->argv
[5] : NULL
;
6050 reason
= a
->argc
== 8 ? a
->argv
[7] : NULL
;
6051 paused
= !strcasecmp(a
->argv
[1], "pause");
6053 if (set_member_paused(queuename
, interface
, reason
, paused
) == RESULT_SUCCESS
) {
6054 ast_cli(a
->fd
, "%spaused interface '%s'", paused
? "" : "un", interface
);
6055 if (!ast_strlen_zero(queuename
))
6056 ast_cli(a
->fd
, " in queue '%s'", queuename
);
6057 if (!ast_strlen_zero(reason
))
6058 ast_cli(a
->fd
, " for reason '%s'", reason
);
6059 ast_cli(a
->fd
, "\n");
6062 ast_cli(a
->fd
, "Unable to %spause interface '%s'", paused
? "" : "un", interface
);
6063 if (!ast_strlen_zero(queuename
))
6064 ast_cli(a
->fd
, " in queue '%s'", queuename
);
6065 if (!ast_strlen_zero(reason
))
6066 ast_cli(a
->fd
, " for reason '%s'", reason
);
6067 ast_cli(a
->fd
, "\n");
6072 static char *complete_queue_set_member_penalty(const char *line
, const char *word
, int pos
, int state
)
6074 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
6078 return ast_strdup("on");
6084 return ast_strdup("in");
6089 return complete_queue(line
, word
, pos
, state
);
6095 static char *handle_queue_set_member_penalty(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6097 char *queuename
= NULL
, *interface
;
6102 e
->command
= "queue set penalty";
6104 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
6105 "Set a member's penalty in the queue specified. If no queue is specified\n"
6106 "then that interface's penalty is set in all queues to which that interface is a member\n";
6109 return complete_queue_set_member_penalty(a
->line
, a
->word
, a
->pos
, a
->n
);
6112 if (a
->argc
!= 6 && a
->argc
!= 8) {
6113 return CLI_SHOWUSAGE
;
6114 } else if (strcmp(a
->argv
[4], "on") || (a
->argc
> 6 && strcmp(a
->argv
[6], "in"))) {
6115 return CLI_SHOWUSAGE
;
6119 queuename
= a
->argv
[7];
6120 interface
= a
->argv
[5];
6121 penalty
= atoi(a
->argv
[3]);
6123 switch (set_member_penalty(queuename
, interface
, penalty
)) {
6124 case RESULT_SUCCESS
:
6125 ast_cli(a
->fd
, "Set penalty on interface '%s' from queue '%s'\n", interface
, queuename
);
6127 case RESULT_FAILURE
:
6128 ast_cli(a
->fd
, "Failed to set penalty on interface '%s' from queue '%s'\n", interface
, queuename
);
6135 static char *complete_queue_rule_show(const char *line
, const char *word
, int pos
, int state
)
6138 struct rule_list
*rl_iter
;
6139 int wordlen
= strlen(word
);
6141 if (pos
!= 3) /* Wha? */ {
6142 ast_log(LOG_DEBUG
, "Hitting this???, pos is %d\n", pos
);
6146 AST_LIST_LOCK(&rule_lists
);
6147 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
6148 if (!strncasecmp(word
, rl_iter
->name
, wordlen
) && ++which
> state
) {
6149 ret
= ast_strdup(rl_iter
->name
);
6153 AST_LIST_UNLOCK(&rule_lists
);
6158 static char *handle_queue_rule_show(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6161 struct rule_list
*rl_iter
;
6162 struct penalty_rule
*pr_iter
;
6165 e
->command
= "queue rules show";
6167 "Usage: queue rules show [rulename]\n"
6168 "Show the list of rules associated with rulename. If no\n"
6169 "rulename is specified, list all rules defined in queuerules.conf\n";
6172 return complete_queue_rule_show(a
->line
, a
->word
, a
->pos
, a
->n
);
6175 if (a
->argc
!= 3 && a
->argc
!= 4)
6176 return CLI_SHOWUSAGE
;
6178 rule
= a
->argc
== 4 ? a
->argv
[3] : "";
6179 AST_LIST_LOCK(&rule_lists
);
6180 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
6181 if (ast_strlen_zero(rule
) || !strcasecmp(rl_iter
->name
, rule
)) {
6182 ast_cli(a
->fd
, "Rule: %s\n", rl_iter
->name
);
6183 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
6184 ast_cli(a
->fd
, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter
->time
, pr_iter
->max_relative
? "by" : "to", pr_iter
->max_value
, pr_iter
->min_relative
? "by" : "to", pr_iter
->min_value
);
6188 AST_LIST_UNLOCK(&rule_lists
);
6192 static char *handle_queue_rule_reload(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6196 e
->command
= "queue rules reload";
6198 "Usage: queue rules reload\n"
6199 "Reloads rules defined in queuerules.conf\n";
6204 reload_queue_rules(1);
6208 static const char qpm_cmd_usage
[] =
6209 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
6211 static const char qum_cmd_usage
[] =
6212 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
6214 static const char qsmp_cmd_usage
[] =
6215 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
6217 static struct ast_cli_entry cli_queue
[] = {
6218 AST_CLI_DEFINE(queue_show
, "Show status of a specified queue"),
6219 AST_CLI_DEFINE(handle_queue_add_member
, "Add a channel to a specified queue"),
6220 AST_CLI_DEFINE(handle_queue_remove_member
, "Removes a channel from a specified queue"),
6221 AST_CLI_DEFINE(handle_queue_pause_member
, "Pause or unpause a queue member"),
6222 AST_CLI_DEFINE(handle_queue_set_member_penalty
, "Set penalty for a channel of a specified queue"),
6223 AST_CLI_DEFINE(handle_queue_rule_show
, "Show the rules defined in queuerules.conf"),
6224 AST_CLI_DEFINE(handle_queue_rule_reload
, "Reload the rules defined in queuerules.conf"),
6227 static int unload_module(void)
6230 struct ast_context
*con
;
6231 struct ao2_iterator q_iter
;
6232 struct call_queue
*q
= NULL
;
6234 ast_cli_unregister_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
6235 res
= ast_manager_unregister("QueueStatus");
6236 res
|= ast_manager_unregister("Queues");
6237 res
|= ast_manager_unregister("QueueRule");
6238 res
|= ast_manager_unregister("QueueSummary");
6239 res
|= ast_manager_unregister("QueueAdd");
6240 res
|= ast_manager_unregister("QueueRemove");
6241 res
|= ast_manager_unregister("QueuePause");
6242 res
|= ast_manager_unregister("QueueLog");
6243 res
|= ast_manager_unregister("QueuePenalty");
6244 res
|= ast_unregister_application(app_aqm
);
6245 res
|= ast_unregister_application(app_rqm
);
6246 res
|= ast_unregister_application(app_pqm
);
6247 res
|= ast_unregister_application(app_upqm
);
6248 res
|= ast_unregister_application(app_ql
);
6249 res
|= ast_unregister_application(app
);
6250 res
|= ast_custom_function_unregister(&queuevar_function
);
6251 res
|= ast_custom_function_unregister(&queuemembercount_function
);
6252 res
|= ast_custom_function_unregister(&queuemembercount_dep
);
6253 res
|= ast_custom_function_unregister(&queuememberlist_function
);
6254 res
|= ast_custom_function_unregister(&queuewaitingcount_function
);
6255 res
|= ast_custom_function_unregister(&queuememberpenalty_function
);
6257 if (device_state_sub
)
6258 ast_event_unsubscribe(device_state_sub
);
6260 if ((con
= ast_context_find("app_queue_gosub_virtual_context"))) {
6261 ast_context_remove_extension2(con
, "s", 1, NULL
);
6262 ast_context_destroy(con
, "app_queue"); /* leave no trace */
6265 clear_and_free_interfaces();
6267 q_iter
= ao2_iterator_init(queues
, 0);
6268 while ((q
= ao2_iterator_next(&q_iter
))) {
6269 ao2_unlink(queues
, q
);
6272 ao2_ref(queues
, -1);
6273 devicestate_tps
= ast_taskprocessor_unreference(devicestate_tps
);
6277 static int load_module(void)
6280 struct ast_context
*con
;
6282 queues
= ao2_container_alloc(MAX_QUEUE_BUCKETS
, queue_hash_cb
, queue_cmp_cb
);
6284 if (!reload_queues(0))
6285 return AST_MODULE_LOAD_DECLINE
;
6287 con
= ast_context_find_or_create(NULL
, NULL
, "app_queue_gosub_virtual_context", "app_queue");
6289 ast_log(LOG_ERROR
, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
6291 ast_add_extension2(con
, 1, "s", 1, NULL
, NULL
, "KeepAlive", ast_strdup(""), ast_free_ptr
, "app_queue");
6293 if (queue_persistent_members
)
6294 reload_queue_members();
6296 ast_cli_register_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
6297 res
= ast_register_application(app
, queue_exec
, synopsis
, descrip
);
6298 res
|= ast_register_application(app_aqm
, aqm_exec
, app_aqm_synopsis
, app_aqm_descrip
);
6299 res
|= ast_register_application(app_rqm
, rqm_exec
, app_rqm_synopsis
, app_rqm_descrip
);
6300 res
|= ast_register_application(app_pqm
, pqm_exec
, app_pqm_synopsis
, app_pqm_descrip
);
6301 res
|= ast_register_application(app_upqm
, upqm_exec
, app_upqm_synopsis
, app_upqm_descrip
);
6302 res
|= ast_register_application(app_ql
, ql_exec
, app_ql_synopsis
, app_ql_descrip
);
6303 res
|= ast_manager_register("Queues", 0, manager_queues_show
, "Queues");
6304 res
|= ast_manager_register("QueueStatus", 0, manager_queues_status
, "Queue Status");
6305 res
|= ast_manager_register("QueueSummary", 0, manager_queues_summary
, "Queue Summary");
6306 res
|= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT
, manager_add_queue_member
, "Add interface to queue.");
6307 res
|= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT
, manager_remove_queue_member
, "Remove interface from queue.");
6308 res
|= ast_manager_register("QueuePause", EVENT_FLAG_AGENT
, manager_pause_queue_member
, "Makes a queue member temporarily unavailable");
6309 res
|= ast_manager_register("QueueLog", EVENT_FLAG_AGENT
, manager_queue_log_custom
, "Adds custom entry in queue_log");
6310 res
|= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT
, manager_queue_member_penalty
, "Set the penalty for a queue member");
6311 res
|= ast_manager_register("QueueRule", 0, manager_queue_rule_show
, "Queue Rules");
6312 res
|= ast_custom_function_register(&queuevar_function
);
6313 res
|= ast_custom_function_register(&queuemembercount_function
);
6314 res
|= ast_custom_function_register(&queuemembercount_dep
);
6315 res
|= ast_custom_function_register(&queuememberlist_function
);
6316 res
|= ast_custom_function_register(&queuewaitingcount_function
);
6317 res
|= ast_custom_function_register(&queuememberpenalty_function
);
6319 if (!(devicestate_tps
= ast_taskprocessor_get("app_queue", 0))) {
6320 ast_log(LOG_WARNING
, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
6323 if (!(device_state_sub
= ast_event_subscribe(AST_EVENT_DEVICE_STATE
, device_state_cb
, NULL
, AST_EVENT_IE_END
)))
6326 return res
? AST_MODULE_LOAD_DECLINE
: 0;
6329 static int reload(void)
6335 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "True Call Queueing",
6336 .load
= load_module
,
6337 .unload
= unload_module
,