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
112 QUEUE_STRATEGY_RINGALL
= 0,
113 QUEUE_STRATEGY_LEASTRECENT
,
114 QUEUE_STRATEGY_FEWESTCALLS
,
115 QUEUE_STRATEGY_RANDOM
,
116 QUEUE_STRATEGY_RRMEMORY
,
117 QUEUE_STRATEGY_LINEAR
,
118 QUEUE_STRATEGY_WRANDOM
121 static const struct strategy
{
125 { QUEUE_STRATEGY_RINGALL
, "ringall" },
126 { QUEUE_STRATEGY_LEASTRECENT
, "leastrecent" },
127 { QUEUE_STRATEGY_FEWESTCALLS
, "fewestcalls" },
128 { QUEUE_STRATEGY_RANDOM
, "random" },
129 { QUEUE_STRATEGY_RRMEMORY
, "rrmemory" },
130 { QUEUE_STRATEGY_LINEAR
, "linear" },
131 { QUEUE_STRATEGY_WRANDOM
, "wrandom"},
134 static struct ast_taskprocessor
*devicestate_tps
;
136 #define DEFAULT_RETRY 5
137 #define DEFAULT_TIMEOUT 15
138 #define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
139 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
140 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /*!< The minimum number of seconds between position announcements \
141 The default value of 15 provides backwards compatibility */
142 #define MAX_QUEUE_BUCKETS 53
144 #define RES_OKAY 0 /*!< Action completed */
145 #define RES_EXISTS (-1) /*!< Entry already exists */
146 #define RES_OUTOFMEMORY (-2) /*!< Out of memory */
147 #define RES_NOSUCHQUEUE (-3) /*!< No such queue */
148 #define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
150 static char *app
= "Queue";
152 static char *synopsis
= "Queue a call for a call queue";
154 static char *descrip
=
155 " Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
156 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
157 "This application will return to the dialplan if the queue does not exist, or\n"
158 "any of the join options cause the caller to not enter the queue.\n"
159 "The option string may contain zero or more of the following characters:\n"
160 " 'c' -- continue in the dialplan if the callee hangs up.\n"
161 " 'd' -- data-quality (modem) call (minimum delay).\n"
162 " 'h' -- allow callee to hang up by pressing *.\n"
163 " 'H' -- allow caller to hang up by pressing *.\n"
164 " 'n' -- no retries on the timeout; will exit this application and \n"
165 " go to the next step.\n"
166 " 'i' -- ignore call forward requests from queue members and do nothing\n"
167 " when they are requested.\n"
168 " 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
169 " 't' -- allow the called user to transfer the calling user.\n"
170 " 'T' -- allow the calling user to transfer the call.\n"
171 " 'w' -- allow the called user to write the conversation to disk via Monitor.\n"
172 " 'W' -- allow the calling user to write the conversation to disk via Monitor.\n"
173 " 'k' -- Allow the called party to enable parking of the call by sending\n"
174 " the DTMF sequence defined for call parking in features.conf.\n"
175 " 'K' -- Allow the calling party to enable parking of the call by sending\n"
176 " the DTMF sequence defined for call parking in features.conf.\n"
177 " 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
178 " 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
180 " In addition to transferring the call, a call may be parked and then picked\n"
181 "up by another user.\n"
182 " The optional URL will be sent to the called party if the channel supports\n"
184 " The optional AGI parameter will setup an AGI script to be executed on the \n"
185 "calling party's channel once they are connected to a queue member.\n"
186 " The optional macro parameter will run a macro on the \n"
187 "calling party's channel once they are connected to a queue member.\n"
188 " The optional gosub parameter will run a gosub on the \n"
189 "calling party's channel once they are connected to a queue member.\n"
190 " The optional rule parameter will cause the queue's defaultrule to be\n"
191 "overridden by the rule specified.\n"
192 " The timeout will cause the queue to fail out after a specified number of\n"
193 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
194 " This application sets the following channel variable upon completion:\n"
195 " QUEUESTATUS The status of the call as a text string, one of\n"
196 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
198 static char *app_aqm
= "AddQueueMember" ;
199 static char *app_aqm_synopsis
= "Dynamically adds queue members" ;
200 static char *app_aqm_descrip
=
201 " AddQueueMember(queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]):\n"
202 "Dynamically adds interface to an existing queue.\n"
203 "If the interface is already in the queue it will return an error.\n"
204 " This application sets the following channel variable upon completion:\n"
205 " AQMSTATUS The status of the attempt to add a queue member as a \n"
206 " text string, one of\n"
207 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
208 "Example: AddQueueMember(techsupport,SIP/3000)\n"
211 static char *app_rqm
= "RemoveQueueMember" ;
212 static char *app_rqm_synopsis
= "Dynamically removes queue members" ;
213 static char *app_rqm_descrip
=
214 " RemoveQueueMember(queuename[,interface[,options]]):\n"
215 "Dynamically removes interface to an existing queue\n"
216 "If the interface is NOT in the queue it will return an error.\n"
217 " This application sets the following channel variable upon completion:\n"
218 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
219 " text string, one of\n"
220 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
221 "Example: RemoveQueueMember(techsupport,SIP/3000)\n"
224 static char *app_pqm
= "PauseQueueMember" ;
225 static char *app_pqm_synopsis
= "Pauses a queue member" ;
226 static char *app_pqm_descrip
=
227 " PauseQueueMember([queuename],interface[,options[,reason]]):\n"
228 "Pauses (blocks calls for) a queue member.\n"
229 "The given interface will be paused in the given queue. This prevents\n"
230 "any calls from being sent from the queue to the interface until it is\n"
231 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
232 "queuename is given, the interface is paused in every queue it is a\n"
233 "member of. The application will fail if the interface is not found.\n"
234 "The reason string is entirely optional and is used to add extra information\n"
235 "to the appropriate queue_log entries and manager events.\n"
236 " This application sets the following channel variable upon completion:\n"
237 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
238 " text string, one of\n"
239 " PAUSED | NOTFOUND\n"
240 "Example: PauseQueueMember(,SIP/3000)\n";
242 static char *app_upqm
= "UnpauseQueueMember" ;
243 static char *app_upqm_synopsis
= "Unpauses a queue member" ;
244 static char *app_upqm_descrip
=
245 " UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
246 "Unpauses (resumes calls to) a queue member.\n"
247 "This is the counterpart to PauseQueueMember and operates exactly the\n"
248 "same way, except it unpauses instead of pausing the given interface.\n"
249 "The reason string is entirely optional and is used to add extra information\n"
250 "to the appropriate queue_log entries and manager events.\n"
251 " This application sets the following channel variable upon completion:\n"
252 " UPQMSTATUS The status of the attempt to unpause a queue \n"
253 " member as a text string, one of\n"
254 " UNPAUSED | NOTFOUND\n"
255 "Example: UnpauseQueueMember(,SIP/3000)\n";
257 static char *app_ql
= "QueueLog" ;
258 static char *app_ql_synopsis
= "Writes to the queue_log" ;
259 static char *app_ql_descrip
=
260 " QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
261 "Allows you to write your own events into the queue log\n"
262 "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
264 /*! \brief Persistent Members astdb family */
265 static const char *pm_family
= "Queue/PersistentMembers";
266 /* The maximum length of each persistent member queue database entry */
267 #define PM_MAX_LEN 8192
269 /*! \brief queues.conf [general] option */
270 static int queue_keep_stats
= 0;
272 /*! \brief queues.conf [general] option */
273 static int queue_persistent_members
= 0;
275 /*! \brief queues.conf per-queue weight option */
276 static int use_weight
= 0;
278 /*! \brief queues.conf [general] option */
279 static int autofill_default
= 0;
281 /*! \brief queues.conf [general] option */
282 static int montype_default
= 0;
284 /*! \brief queues.conf [general] option */
285 static int shared_lastcall
= 0;
287 /*! \brief Subscription to device state change events */
288 static struct ast_event_sub
*device_state_sub
;
290 /*! \brief queues.conf [general] option */
291 static int update_cdr
= 0;
297 QUEUE_LEAVEEMPTY
= 3,
298 QUEUE_JOINUNAVAIL
= 4,
299 QUEUE_LEAVEUNAVAIL
= 5,
305 enum queue_result id
;
307 } queue_results
[] = {
308 { QUEUE_UNKNOWN
, "UNKNOWN" },
309 { QUEUE_TIMEOUT
, "TIMEOUT" },
310 { QUEUE_JOINEMPTY
,"JOINEMPTY" },
311 { QUEUE_LEAVEEMPTY
, "LEAVEEMPTY" },
312 { QUEUE_JOINUNAVAIL
, "JOINUNAVAIL" },
313 { QUEUE_LEAVEUNAVAIL
, "LEAVEUNAVAIL" },
314 { QUEUE_FULL
, "FULL" },
315 { QUEUE_CONTINUE
, "CONTINUE" },
318 enum queue_timeout_priority
{
319 TIMEOUT_PRIORITY_APP
,
320 TIMEOUT_PRIORITY_CONF
,
323 /*! \brief We define a custom "local user" structure because we
324 * use it not only for keeping track of what is in use but
325 * also for keeping track of who we're dialing.
327 * There are two "links" defined in this structure, q_next and call_next.
328 * q_next links ALL defined callattempt structures into a linked list. call_next is
329 * a link which allows for a subset of the callattempts to be traversed. This subset
330 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
331 * also is helpful so that queue logs are always accurate in the case where a call to
332 * a member times out, especially if using the ringall strategy.
336 struct callattempt
*q_next
;
337 struct callattempt
*call_next
;
338 struct ast_channel
*chan
;
344 struct call_queue
*lastqueue
;
345 struct member
*member
;
350 struct call_queue
*parent
; /*!< What queue is our parent */
351 char moh
[80]; /*!< Name of musiconhold to be used */
352 char announce
[80]; /*!< Announcement to play for member when call is answered */
353 char context
[AST_MAX_CONTEXT
]; /*!< Context when user exits queue */
354 char digits
[AST_MAX_EXTENSION
]; /*!< Digits entered while in queue */
355 int valid_digits
; /*!< Digits entered correspond to valid extension. Exited */
356 int pos
; /*!< Where we are in the queue */
357 int prio
; /*!< Our priority */
358 int last_pos_said
; /*!< Last position we told the user */
359 time_t last_periodic_announce_time
; /*!< The last time we played a periodic announcement */
360 int last_periodic_announce_sound
; /*!< The last periodic announcement we made */
361 time_t last_pos
; /*!< Last time we told the user their position */
362 int opos
; /*!< Where we started in the queue */
363 int handled
; /*!< Whether our call was handled */
364 int pending
; /*!< Non-zero if we are attempting to call a member */
365 int max_penalty
; /*!< Limit the members that can take this call to this penalty or lower */
366 int min_penalty
; /*!< Limit the members that can take this call to this penalty or higher */
367 int linpos
; /*!< If using linear strategy, what position are we at? */
368 int linwrapped
; /*!< Is the linpos wrapped? */
369 time_t start
; /*!< When we started holding */
370 time_t expire
; /*!< When this entry should expire (time out of queue) */
371 struct ast_channel
*chan
; /*!< Our channel */
372 AST_LIST_HEAD_NOLOCK(,penalty_rule
) qe_rules
; /*!< Local copy of the queue's penalty rules */
373 struct penalty_rule
*pr
; /*!< Pointer to the next penalty rule to implement */
374 struct queue_ent
*next
; /*!< The next queue entry */
378 char interface
[80]; /*!< Technology/Location to dial to reach this member*/
379 char state_interface
[80]; /*!< Technology/Location from which to read devicestate changes */
380 char membername
[80]; /*!< Member name to use in queue logs */
381 int penalty
; /*!< Are we a last resort? */
382 int calls
; /*!< Number of calls serviced by this member */
383 int dynamic
; /*!< Are we dynamically added? */
384 int realtime
; /*!< Is this member realtime? */
385 int status
; /*!< Status of queue member */
386 int paused
; /*!< Are we paused (not accepting calls)? */
387 time_t lastcall
; /*!< When last successful call was hungup */
388 struct call_queue
*lastqueue
; /*!< Last queue we received a call */
389 unsigned int dead
:1; /*!< Used to detect members deleted in realtime */
390 unsigned int delme
:1; /*!< Flag to delete entry on reload */
391 char rt_uniqueid
[80]; /*!< Unique id of realtime member entry */
394 struct member_interface
{
396 AST_LIST_ENTRY(member_interface
) list
; /*!< Next call queue */
399 static AST_LIST_HEAD_STATIC(interfaces
, member_interface
);
401 /* values used in multi-bit flags in call_queue */
402 #define QUEUE_EMPTY_NORMAL 1
403 #define QUEUE_EMPTY_STRICT 2
404 #define QUEUE_EMPTY_LOOSE 3
405 #define ANNOUNCEHOLDTIME_ALWAYS 1
406 #define ANNOUNCEHOLDTIME_ONCE 2
407 #define QUEUE_EVENT_VARIABLES 3
409 struct penalty_rule
{
410 int time
; /*!< Number of seconds that need to pass before applying this rule */
411 int max_value
; /*!< The amount specified in the penalty rule for max penalty */
412 int min_value
; /*!< The amount specified in the penalty rule for min penalty */
413 int max_relative
; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
414 int min_relative
; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
415 AST_LIST_ENTRY(penalty_rule
) list
; /*!< Next penalty_rule */
418 #define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
419 #define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
420 #define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
421 #define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
424 AST_DECLARE_STRING_FIELDS(
426 AST_STRING_FIELD(name
);
427 /*! Music on Hold class */
428 AST_STRING_FIELD(moh
);
429 /*! Announcement to play when call is answered */
430 AST_STRING_FIELD(announce
);
432 AST_STRING_FIELD(context
);
433 /*! Macro to run upon member connection */
434 AST_STRING_FIELD(membermacro
);
435 /*! Gosub to run upon member connection */
436 AST_STRING_FIELD(membergosub
);
437 /*! Default rule to use if none specified in call to Queue() */
438 AST_STRING_FIELD(defaultrule
);
439 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
440 AST_STRING_FIELD(sound_next
);
441 /*! Sound file: "There are currently" (def. queue-thereare) */
442 AST_STRING_FIELD(sound_thereare
);
443 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
444 AST_STRING_FIELD(sound_calls
);
445 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
446 AST_STRING_FIELD(queue_quantity1
);
447 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
448 AST_STRING_FIELD(queue_quantity2
);
449 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
450 AST_STRING_FIELD(sound_holdtime
);
451 /*! Sound file: "minutes." (def. queue-minutes) */
452 AST_STRING_FIELD(sound_minutes
);
453 /*! Sound file: "minute." (def. queue-minute) */
454 AST_STRING_FIELD(sound_minute
);
455 /*! Sound file: "seconds." (def. queue-seconds) */
456 AST_STRING_FIELD(sound_seconds
);
457 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
458 AST_STRING_FIELD(sound_thanks
);
459 /*! Sound file: Custom announce for caller, no default */
460 AST_STRING_FIELD(sound_callerannounce
);
461 /*! Sound file: "Hold time" (def. queue-reporthold) */
462 AST_STRING_FIELD(sound_reporthold
);
464 /*! Sound files: Custom announce, no default */
465 struct ast_str
*sound_periodicannounce
[MAX_PERIODIC_ANNOUNCEMENTS
];
467 unsigned int joinempty
:2;
468 unsigned int eventwhencalled
:2;
469 unsigned int leavewhenempty
:2;
470 unsigned int ringinuse
:1;
471 unsigned int setinterfacevar
:1;
472 unsigned int setqueuevar
:1;
473 unsigned int setqueueentryvar
:1;
474 unsigned int reportholdtime
:1;
475 unsigned int wrapped
:1;
476 unsigned int timeoutrestart
:1;
477 unsigned int announceholdtime
:2;
478 unsigned int announceposition
:3;
480 unsigned int maskmemberstatus
:1;
481 unsigned int realtime
:1;
482 unsigned int found
:1;
483 int announcepositionlimit
; /*!< How many positions we announce? */
484 int announcefrequency
; /*!< How often to announce their position */
485 int minannouncefrequency
; /*!< The minimum number of seconds between position announcements (def. 15) */
486 int periodicannouncefrequency
; /*!< How often to play periodic announcement */
487 int numperiodicannounce
; /*!< The number of periodic announcements configured */
488 int randomperiodicannounce
; /*!< Are periodic announcments randomly chosen */
489 int roundingseconds
; /*!< How many seconds do we round to? */
490 int holdtime
; /*!< Current avg holdtime, based on an exponential average */
491 int callscompleted
; /*!< Number of queue calls completed */
492 int callsabandoned
; /*!< Number of queue calls abandoned */
493 int servicelevel
; /*!< seconds setting for servicelevel*/
494 int callscompletedinsl
; /*!< Number of calls answered with servicelevel*/
495 char monfmt
[8]; /*!< Format to use when recording calls */
496 int montype
; /*!< Monitor type Monitor vs. MixMonitor */
497 int count
; /*!< How many entries */
498 int maxlen
; /*!< Max number of entries */
499 int wrapuptime
; /*!< Wrapup Time */
501 int retry
; /*!< Retry calling everyone after this amount of time */
502 int timeout
; /*!< How long to wait for an answer */
503 int weight
; /*!< Respective weight */
504 int autopause
; /*!< Auto pause queue members if they fail to answer */
505 int timeoutpriority
; /*!< Do we allow a fraction of the timeout to occur for a ring? */
507 /* Queue strategy things */
508 int rrpos
; /*!< Round Robin - position */
509 int memberdelay
; /*!< Seconds to delay connecting member to caller */
510 int autofill
; /*!< Ignore the head call status and ring an available agent */
512 struct ao2_container
*members
; /*!< Head of the list of members */
514 * \brief Number of members _logged in_
515 * \note There will be members in the members container that are not logged
516 * in, so this can not simply be replaced with ao2_container_count().
519 struct queue_ent
*head
; /*!< Head of the list of callers */
520 AST_LIST_ENTRY(call_queue
) list
; /*!< Next call queue */
521 AST_LIST_HEAD_NOLOCK(, penalty_rule
) rules
; /*!< The list of penalty rules to invoke */
526 AST_LIST_HEAD_NOLOCK(,penalty_rule
) rules
;
527 AST_LIST_ENTRY(rule_list
) list
;
530 AST_LIST_HEAD_STATIC(rule_lists
, rule_list
);
532 static struct ao2_container
*queues
;
534 static void update_realtime_members(struct call_queue
*q
);
535 static int set_member_paused(const char *queuename
, const char *interface
, const char *reason
, int paused
);
537 static void queue_transfer_fixup(void *data
, struct ast_channel
*old_chan
, struct ast_channel
*new_chan
);
538 /*! \brief sets the QUEUESTATUS channel variable */
539 static void set_queue_result(struct ast_channel
*chan
, enum queue_result res
)
543 for (i
= 0; i
< ARRAY_LEN(queue_results
); i
++) {
544 if (queue_results
[i
].id
== res
) {
545 pbx_builtin_setvar_helper(chan
, "QUEUESTATUS", queue_results
[i
].text
);
551 static const char *int2strat(int strategy
)
555 for (x
= 0; x
< ARRAY_LEN(strategies
); x
++) {
556 if (strategy
== strategies
[x
].strategy
)
557 return strategies
[x
].name
;
563 static int strat2int(const char *strategy
)
567 for (x
= 0; x
< ARRAY_LEN(strategies
); x
++) {
568 if (!strcasecmp(strategy
, strategies
[x
].name
))
569 return strategies
[x
].strategy
;
575 static int queue_hash_cb(const void *obj
, const int flags
)
577 const struct call_queue
*q
= obj
;
578 return ast_str_hash(q
->name
);
581 static int queue_cmp_cb(void *obj
, void *arg
, int flags
)
583 struct call_queue
*q
= obj
, *q2
= arg
;
584 return !strcasecmp(q
->name
, q2
->name
) ? CMP_MATCH
: 0;
587 static inline struct call_queue
*queue_ref(struct call_queue
*q
)
593 static inline struct call_queue
*queue_unref(struct call_queue
*q
)
599 /*! \brief Set variables of queue */
600 static void set_queue_variables(struct queue_ent
*qe
)
602 char interfacevar
[256]="";
605 if (qe
->parent
->setqueuevar
) {
607 if (qe
->parent
->callscompleted
> 0)
608 sl
= 100 * ((float) qe
->parent
->callscompletedinsl
/ (float) qe
->parent
->callscompleted
);
610 snprintf(interfacevar
, sizeof(interfacevar
),
611 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
612 qe
->parent
->name
, qe
->parent
->maxlen
, int2strat(qe
->parent
->strategy
), qe
->parent
->count
, qe
->parent
->holdtime
, qe
->parent
->callscompleted
,
613 qe
->parent
->callsabandoned
, qe
->parent
->servicelevel
, sl
);
615 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
619 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
620 static inline void insert_entry(struct call_queue
*q
, struct queue_ent
*prev
, struct queue_ent
*new, int *pos
)
622 struct queue_ent
*cur
;
639 enum queue_member_status
{
641 QUEUE_NO_REACHABLE_MEMBERS
,
642 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
,
646 /*! \brief Check if members are available
648 * This function checks to see if members are available to be called. If any member
649 * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
650 * the appropriate reason why is returned
652 static enum queue_member_status
get_member_status(struct call_queue
*q
, int max_penalty
, int min_penalty
)
654 struct member
*member
;
655 struct ao2_iterator mem_iter
;
656 enum queue_member_status result
= QUEUE_NO_MEMBERS
;
659 mem_iter
= ao2_iterator_init(q
->members
, 0);
660 for (; (member
= ao2_iterator_next(&mem_iter
)); ao2_ref(member
, -1)) {
661 if ((max_penalty
&& (member
->penalty
> max_penalty
)) || (min_penalty
&& (member
->penalty
< min_penalty
)))
664 switch (member
->status
) {
665 case AST_DEVICE_INVALID
:
668 case AST_DEVICE_UNAVAILABLE
:
669 if (result
!= QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)
670 result
= QUEUE_NO_REACHABLE_MEMBERS
;
673 if (member
->paused
) {
674 result
= QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
;
689 AST_LIST_ENTRY(statechange
) entry
;
694 /*! \brief set a member's status based on device state of that member's state_interface.
696 * Lock interface list find sc, iterate through each queues queue_member list for member to
697 * update state inside queues
699 static int update_status(const char *interface
, const int status
)
702 struct ao2_iterator mem_iter
, queue_iter
;
703 struct call_queue
*q
;
705 queue_iter
= ao2_iterator_init(queues
, 0);
706 while ((q
= ao2_iterator_next(&queue_iter
))) {
708 mem_iter
= ao2_iterator_init(q
->members
, 0);
709 while ((cur
= ao2_iterator_next(&mem_iter
))) {
712 tmp_interface
= ast_strdupa(cur
->state_interface
);
713 if ((slash_pos
= strchr(interface
, '/')))
714 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
717 if (strcasecmp(interface
, tmp_interface
)) {
722 if (cur
->status
!= status
) {
723 cur
->status
= status
;
724 if (q
->maskmemberstatus
) {
729 manager_event(EVENT_FLAG_AGENT
, "QueueMemberStatus",
739 q
->name
, cur
->interface
, cur
->membername
, cur
->dynamic
? "dynamic" : cur
->realtime
? "realtime" : "static",
740 cur
->penalty
, cur
->calls
, (int)cur
->lastcall
, cur
->status
, cur
->paused
);
751 /*! \brief set a member's status based on device state of that member's interface*/
752 static int handle_statechange(void *datap
)
754 struct member_interface
*curint
;
757 struct statechange
*sc
= datap
;
759 technology
= ast_strdupa(sc
->dev
);
760 loc
= strchr(technology
, '/');
768 AST_LIST_LOCK(&interfaces
);
769 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
772 interface
= ast_strdupa(curint
->interface
);
773 if ((slash_pos
= strchr(interface
, '/')))
774 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
777 if (!strcasecmp(interface
, sc
->dev
))
780 AST_LIST_UNLOCK(&interfaces
);
783 if (option_debug
> 2)
784 ast_log(LOG_DEBUG
, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology
, loc
, sc
->state
, devstate2str(sc
->state
));
790 ast_log(LOG_DEBUG
, "Device '%s/%s' changed to state '%d' (%s)\n", technology
, loc
, sc
->state
, devstate2str(sc
->state
));
792 update_status(sc
->dev
, sc
->state
);
797 static void device_state_cb(const struct ast_event
*event
, void *unused
)
799 enum ast_device_state state
;
801 struct statechange
*sc
;
804 state
= ast_event_get_ie_uint(event
, AST_EVENT_IE_STATE
);
805 device
= ast_event_get_ie_str(event
, AST_EVENT_IE_DEVICE
);
807 if (ast_strlen_zero(device
)) {
808 ast_log(LOG_ERROR
, "Received invalid event that had no device IE\n");
811 datapsize
= sizeof(*sc
) + strlen(device
) + 1;
812 if (!(sc
= ast_calloc(1, datapsize
))) {
813 ast_log(LOG_ERROR
, "failed to calloc a state change struct\n");
817 strcpy(sc
->dev
, device
);
818 if (ast_taskprocessor_push(devicestate_tps
, handle_statechange
, sc
) < 0) {
823 /*! \brief allocate space for new queue member and set fields based on parameters passed */
824 static struct member
*create_queue_member(const char *interface
, const char *membername
, int penalty
, int paused
, const char *state_interface
)
828 if ((cur
= ao2_alloc(sizeof(*cur
), NULL
))) {
829 cur
->penalty
= penalty
;
830 cur
->paused
= paused
;
831 ast_copy_string(cur
->interface
, interface
, sizeof(cur
->interface
));
832 if (!ast_strlen_zero(state_interface
))
833 ast_copy_string(cur
->state_interface
, state_interface
, sizeof(cur
->state_interface
));
835 ast_copy_string(cur
->state_interface
, interface
, sizeof(cur
->state_interface
));
836 if (!ast_strlen_zero(membername
))
837 ast_copy_string(cur
->membername
, membername
, sizeof(cur
->membername
));
839 ast_copy_string(cur
->membername
, interface
, sizeof(cur
->membername
));
840 if (!strchr(cur
->interface
, '/'))
841 ast_log(LOG_WARNING
, "No location at interface '%s'\n", interface
);
842 cur
->status
= ast_device_state(cur
->state_interface
);
849 static int compress_char(const char c
)
859 static int member_hash_fn(const void *obj
, const int flags
)
861 const struct member
*mem
= obj
;
862 const char *chname
= strchr(mem
->interface
, '/');
865 chname
= mem
->interface
;
866 for (i
= 0; i
< 5 && chname
[i
]; i
++)
867 ret
+= compress_char(chname
[i
]) << (i
* 6);
871 static int member_cmp_fn(void *obj1
, void *obj2
, int flags
)
873 struct member
*mem1
= obj1
, *mem2
= obj2
;
874 return strcasecmp(mem1
->interface
, mem2
->interface
) ? 0 : CMP_MATCH
;
878 * \brief Initialize Queue default values.
879 * \note the queue's lock must be held before executing this function
881 static void init_queue(struct call_queue
*q
)
884 struct penalty_rule
*pr_iter
;
887 q
->retry
= DEFAULT_RETRY
;
890 q
->announcefrequency
= 0;
891 q
->minannouncefrequency
= DEFAULT_MIN_ANNOUNCE_FREQUENCY
;
892 q
->announceholdtime
= 1;
893 q
->announcepositionlimit
= 10; /* Default 10 positions */
894 q
->announceposition
= ANNOUNCEPOSITION_YES
; /* Default yes */
895 q
->roundingseconds
= 0; /* Default - don't announce seconds */
898 q
->setinterfacevar
= 0;
900 q
->setqueueentryvar
= 0;
901 q
->autofill
= autofill_default
;
902 q
->montype
= montype_default
;
904 q
->reportholdtime
= 0;
907 q
->leavewhenempty
= 0;
909 q
->maskmemberstatus
= 0;
910 q
->eventwhencalled
= 0;
912 q
->timeoutrestart
= 0;
913 q
->periodicannouncefrequency
= 0;
914 q
->randomperiodicannounce
= 0;
915 q
->numperiodicannounce
= 0;
916 q
->timeoutpriority
= TIMEOUT_PRIORITY_APP
;
918 if (q
->strategy
== QUEUE_STRATEGY_LINEAR
)
919 /* linear strategy depends on order, so we have to place all members in a single bucket */
920 q
->members
= ao2_container_alloc(1, member_hash_fn
, member_cmp_fn
);
922 q
->members
= ao2_container_alloc(37, member_hash_fn
, member_cmp_fn
);
927 ast_string_field_set(q
, sound_next
, "queue-youarenext");
928 ast_string_field_set(q
, sound_thereare
, "queue-thereare");
929 ast_string_field_set(q
, sound_calls
, "queue-callswaiting");
930 ast_string_field_set(q
, queue_quantity1
, "queue-quantity1");
931 ast_string_field_set(q
, queue_quantity2
, "queue-quantity2");
932 ast_string_field_set(q
, sound_holdtime
, "queue-holdtime");
933 ast_string_field_set(q
, sound_minutes
, "queue-minutes");
934 ast_string_field_set(q
, sound_minute
, "queue-minute");
935 ast_string_field_set(q
, sound_seconds
, "queue-seconds");
936 ast_string_field_set(q
, sound_thanks
, "queue-thankyou");
937 ast_string_field_set(q
, sound_reporthold
, "queue-reporthold");
939 if ((q
->sound_periodicannounce
[0] = ast_str_create(32)))
940 ast_str_set(&q
->sound_periodicannounce
[0], 0, "queue-periodic-announce");
942 for (i
= 1; i
< MAX_PERIODIC_ANNOUNCEMENTS
; i
++) {
943 if (q
->sound_periodicannounce
[i
])
944 ast_str_set(&q
->sound_periodicannounce
[i
], 0, "%s", "");
947 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&q
->rules
,list
)))
951 static void clear_queue(struct call_queue
*q
)
954 q
->callscompleted
= 0;
955 q
->callsabandoned
= 0;
956 q
->callscompletedinsl
= 0;
960 static int add_to_interfaces(const char *interface
)
962 struct member_interface
*curint
;
964 AST_LIST_LOCK(&interfaces
);
965 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
966 if (!strcasecmp(curint
->interface
, interface
))
971 AST_LIST_UNLOCK(&interfaces
);
975 ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface
);
977 if ((curint
= ast_calloc(1, sizeof(*curint
)))) {
978 ast_copy_string(curint
->interface
, interface
, sizeof(curint
->interface
));
979 AST_LIST_INSERT_HEAD(&interfaces
, curint
, list
);
981 AST_LIST_UNLOCK(&interfaces
);
986 static int interface_exists_global(const char *interface
)
988 struct call_queue
*q
;
989 struct member
*mem
, tmpmem
;
990 struct ao2_iterator queue_iter
, mem_iter
;
993 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
994 queue_iter
= ao2_iterator_init(queues
, 0);
995 while ((q
= ao2_iterator_next(&queue_iter
))) {
997 mem_iter
= ao2_iterator_init(q
->members
, 0);
998 while ((mem
= ao2_iterator_next(&mem_iter
))) {
999 if (!strcasecmp(mem
->state_interface
, interface
)) {
1012 static int remove_from_interfaces(const char *interface
)
1014 struct member_interface
*curint
;
1016 if (interface_exists_global(interface
))
1019 AST_LIST_LOCK(&interfaces
);
1020 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces
, curint
, list
) {
1021 if (!strcasecmp(curint
->interface
, interface
)) {
1022 ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface
);
1023 AST_LIST_REMOVE_CURRENT(list
);
1028 AST_LIST_TRAVERSE_SAFE_END
;
1029 AST_LIST_UNLOCK(&interfaces
);
1034 static void clear_and_free_interfaces(void)
1036 struct member_interface
*curint
;
1038 AST_LIST_LOCK(&interfaces
);
1039 while ((curint
= AST_LIST_REMOVE_HEAD(&interfaces
, list
)))
1041 AST_LIST_UNLOCK(&interfaces
);
1045 * \brief Change queue penalty by adding rule.
1047 * Check rule for errors with time or fomatting, see if rule is relative to rest
1048 * of queue, iterate list of rules to find correct insertion point, insert and return.
1049 * \retval -1 on failure
1050 * \retval 0 on success
1051 * \note Call this with the rule_lists locked
1053 static int insert_penaltychange (const char *list_name
, const char *content
, const int linenum
)
1055 char *timestr
, *maxstr
, *minstr
, *contentdup
;
1056 struct penalty_rule
*rule
= NULL
, *rule_iter
;
1057 struct rule_list
*rl_iter
;
1058 int time
, inserted
= 0;
1060 if (!(rule
= ast_calloc(1, sizeof(*rule
)))) {
1061 ast_log(LOG_ERROR
, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum
);
1065 contentdup
= ast_strdupa(content
);
1067 if (!(maxstr
= strchr(contentdup
, ','))) {
1068 ast_log(LOG_WARNING
, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum
);
1074 timestr
= contentdup
;
1076 if ((time
= atoi(timestr
)) < 0) {
1077 ast_log(LOG_WARNING
, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum
);
1084 if ((minstr
= strchr(maxstr
,',')))
1087 /* The last check will evaluate true if either no penalty change is indicated for a given rule
1088 * OR if a min penalty change is indicated but no max penalty change is */
1089 if (*maxstr
== '+' || *maxstr
== '-' || *maxstr
== '\0') {
1090 rule
->max_relative
= 1;
1093 rule
->max_value
= atoi(maxstr
);
1095 if (!ast_strlen_zero(minstr
)) {
1096 if (*minstr
== '+' || *minstr
== '-')
1097 rule
->min_relative
= 1;
1098 rule
->min_value
= atoi(minstr
);
1099 } else /*there was no minimum specified, so assume this means no change*/
1100 rule
->min_relative
= 1;
1102 /*We have the rule made, now we need to insert it where it belongs*/
1103 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
){
1104 if (strcasecmp(rl_iter
->name
, list_name
))
1107 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter
->rules
, rule_iter
, list
) {
1108 if (rule
->time
< rule_iter
->time
) {
1109 AST_LIST_INSERT_BEFORE_CURRENT(rule
, list
);
1114 AST_LIST_TRAVERSE_SAFE_END
;
1117 AST_LIST_INSERT_TAIL(&rl_iter
->rules
, rule
, list
);
1124 /*! \brief Configure a queue parameter.
1126 * The failunknown flag is set for config files (and static realtime) to show
1127 * errors for unknown parameters. It is cleared for dynamic realtime to allow
1128 * extra fields in the tables.
1129 * \note For error reporting, line number is passed for .conf static configuration,
1130 * for Realtime queues, linenum is -1.
1132 static void queue_set_param(struct call_queue
*q
, const char *param
, const char *val
, int linenum
, int failunknown
)
1134 if (!strcasecmp(param
, "musicclass") ||
1135 !strcasecmp(param
, "music") || !strcasecmp(param
, "musiconhold")) {
1136 ast_string_field_set(q
, moh
, val
);
1137 } else if (!strcasecmp(param
, "announce")) {
1138 ast_string_field_set(q
, announce
, val
);
1139 } else if (!strcasecmp(param
, "context")) {
1140 ast_string_field_set(q
, context
, val
);
1141 } else if (!strcasecmp(param
, "timeout")) {
1142 q
->timeout
= atoi(val
);
1144 q
->timeout
= DEFAULT_TIMEOUT
;
1145 } else if (!strcasecmp(param
, "ringinuse")) {
1146 q
->ringinuse
= ast_true(val
);
1147 } else if (!strcasecmp(param
, "setinterfacevar")) {
1148 q
->setinterfacevar
= ast_true(val
);
1149 } else if (!strcasecmp(param
, "setqueuevar")) {
1150 q
->setqueuevar
= ast_true(val
);
1151 } else if (!strcasecmp(param
, "setqueueentryvar")) {
1152 q
->setqueueentryvar
= ast_true(val
);
1153 } else if (!strcasecmp(param
, "monitor-format")) {
1154 ast_copy_string(q
->monfmt
, val
, sizeof(q
->monfmt
));
1155 } else if (!strcasecmp(param
, "membermacro")) {
1156 ast_string_field_set(q
, membermacro
, val
);
1157 } else if (!strcasecmp(param
, "membergosub")) {
1158 ast_string_field_set(q
, membergosub
, val
);
1159 } else if (!strcasecmp(param
, "queue-youarenext")) {
1160 ast_string_field_set(q
, sound_next
, val
);
1161 } else if (!strcasecmp(param
, "queue-thereare")) {
1162 ast_string_field_set(q
, sound_thereare
, val
);
1163 } else if (!strcasecmp(param
, "queue-callswaiting")) {
1164 ast_string_field_set(q
, sound_calls
, val
);
1165 } else if (!strcasecmp(param
, "queue-quantity1")) {
1166 ast_string_field_set(q
, queue_quantity1
, val
);
1167 } else if (!strcasecmp(param
, "queue-quantity2")) {
1168 ast_string_field_set(q
, queue_quantity2
, val
);
1169 } else if (!strcasecmp(param
, "queue-holdtime")) {
1170 ast_string_field_set(q
, sound_holdtime
, val
);
1171 } else if (!strcasecmp(param
, "queue-minutes")) {
1172 ast_string_field_set(q
, sound_minutes
, val
);
1173 } else if (!strcasecmp(param
, "queue-minute")) {
1174 ast_string_field_set(q
, sound_minute
, val
);
1175 } else if (!strcasecmp(param
, "queue-seconds")) {
1176 ast_string_field_set(q
, sound_seconds
, val
);
1177 } else if (!strcasecmp(param
, "queue-thankyou")) {
1178 ast_string_field_set(q
, sound_thanks
, val
);
1179 } else if (!strcasecmp(param
, "queue-callerannounce")) {
1180 ast_string_field_set(q
, sound_callerannounce
, val
);
1181 } else if (!strcasecmp(param
, "queue-reporthold")) {
1182 ast_string_field_set(q
, sound_reporthold
, val
);
1183 } else if (!strcasecmp(param
, "announce-frequency")) {
1184 q
->announcefrequency
= atoi(val
);
1185 } else if (!strcasecmp(param
, "min-announce-frequency")) {
1186 q
->minannouncefrequency
= atoi(val
);
1187 ast_debug(1, "%s=%s for queue '%s'\n", param
, val
, q
->name
);
1188 } else if (!strcasecmp(param
, "announce-round-seconds")) {
1189 q
->roundingseconds
= atoi(val
);
1190 /* Rounding to any other values just doesn't make sense... */
1191 if (!(q
->roundingseconds
== 0 || q
->roundingseconds
== 5 || q
->roundingseconds
== 10
1192 || q
->roundingseconds
== 15 || q
->roundingseconds
== 20 || q
->roundingseconds
== 30)) {
1194 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
1195 "using 0 instead for queue '%s' at line %d of queues.conf\n",
1196 val
, param
, q
->name
, linenum
);
1198 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
1199 "using 0 instead for queue '%s'\n", val
, param
, q
->name
);
1201 q
->roundingseconds
=0;
1203 } else if (!strcasecmp(param
, "announce-holdtime")) {
1204 if (!strcasecmp(val
, "once"))
1205 q
->announceholdtime
= ANNOUNCEHOLDTIME_ONCE
;
1206 else if (ast_true(val
))
1207 q
->announceholdtime
= ANNOUNCEHOLDTIME_ALWAYS
;
1209 q
->announceholdtime
= 0;
1210 } else if (!strcasecmp(param
, "announce-position")) {
1211 if (!strcasecmp(val
, "limit"))
1212 q
->announceposition
= ANNOUNCEPOSITION_LIMIT
;
1213 else if (!strcasecmp(val
, "more"))
1214 q
->announceposition
= ANNOUNCEPOSITION_MORE_THAN
;
1215 else if (ast_true(val
))
1216 q
->announceposition
= ANNOUNCEPOSITION_YES
;
1218 q
->announceposition
= ANNOUNCEPOSITION_NO
;
1219 } else if (!strcasecmp(param
, "announce-position-limit")) {
1220 q
->announcepositionlimit
= atoi(val
);
1221 } else if (!strcasecmp(param
, "periodic-announce")) {
1222 if (strchr(val
, ',')) {
1223 char *s
, *buf
= ast_strdupa(val
);
1226 while ((s
= strsep(&buf
, ",|"))) {
1227 if (!q
->sound_periodicannounce
[i
])
1228 q
->sound_periodicannounce
[i
] = ast_str_create(16);
1229 ast_str_set(&q
->sound_periodicannounce
[i
], 0, "%s", s
);
1231 if (i
== MAX_PERIODIC_ANNOUNCEMENTS
)
1234 q
->numperiodicannounce
= i
;
1236 ast_str_set(&q
->sound_periodicannounce
[0], 0, "%s", val
);
1237 q
->numperiodicannounce
= 1;
1239 } else if (!strcasecmp(param
, "periodic-announce-frequency")) {
1240 q
->periodicannouncefrequency
= atoi(val
);
1241 } else if (!strcasecmp(param
, "random-periodic-announce")) {
1242 q
->randomperiodicannounce
= ast_true(val
);
1243 } else if (!strcasecmp(param
, "retry")) {
1244 q
->retry
= atoi(val
);
1246 q
->retry
= DEFAULT_RETRY
;
1247 } else if (!strcasecmp(param
, "wrapuptime")) {
1248 q
->wrapuptime
= atoi(val
);
1249 } else if (!strcasecmp(param
, "autofill")) {
1250 q
->autofill
= ast_true(val
);
1251 } else if (!strcasecmp(param
, "monitor-type")) {
1252 if (!strcasecmp(val
, "mixmonitor"))
1254 } else if (!strcasecmp(param
, "autopause")) {
1255 q
->autopause
= ast_true(val
);
1256 } else if (!strcasecmp(param
, "maxlen")) {
1257 q
->maxlen
= atoi(val
);
1260 } else if (!strcasecmp(param
, "servicelevel")) {
1261 q
->servicelevel
= atoi(val
);
1262 } else if (!strcasecmp(param
, "strategy")) {
1263 /* We already have set this, no need to do it again */
1265 } else if (!strcasecmp(param
, "joinempty")) {
1266 if (!strcasecmp(val
, "loose"))
1267 q
->joinempty
= QUEUE_EMPTY_LOOSE
;
1268 else if (!strcasecmp(val
, "strict"))
1269 q
->joinempty
= QUEUE_EMPTY_STRICT
;
1270 else if (ast_true(val
))
1271 q
->joinempty
= QUEUE_EMPTY_NORMAL
;
1274 } else if (!strcasecmp(param
, "leavewhenempty")) {
1275 if (!strcasecmp(val
, "loose"))
1276 q
->leavewhenempty
= QUEUE_EMPTY_LOOSE
;
1277 else if (!strcasecmp(val
, "strict"))
1278 q
->leavewhenempty
= QUEUE_EMPTY_STRICT
;
1279 else if (ast_true(val
))
1280 q
->leavewhenempty
= QUEUE_EMPTY_NORMAL
;
1282 q
->leavewhenempty
= 0;
1283 } else if (!strcasecmp(param
, "eventmemberstatus")) {
1284 q
->maskmemberstatus
= !ast_true(val
);
1285 } else if (!strcasecmp(param
, "eventwhencalled")) {
1286 if (!strcasecmp(val
, "vars")) {
1287 q
->eventwhencalled
= QUEUE_EVENT_VARIABLES
;
1289 q
->eventwhencalled
= ast_true(val
) ? 1 : 0;
1291 } else if (!strcasecmp(param
, "reportholdtime")) {
1292 q
->reportholdtime
= ast_true(val
);
1293 } else if (!strcasecmp(param
, "memberdelay")) {
1294 q
->memberdelay
= atoi(val
);
1295 } else if (!strcasecmp(param
, "weight")) {
1296 q
->weight
= atoi(val
);
1299 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1300 we will not see any effect on use_weight until next reload. */
1301 } else if (!strcasecmp(param
, "timeoutrestart")) {
1302 q
->timeoutrestart
= ast_true(val
);
1303 } else if (!strcasecmp(param
, "defaultrule")) {
1304 ast_string_field_set(q
, defaultrule
, val
);
1305 } else if (!strcasecmp(param
, "timeoutpriority")) {
1306 if (!strcasecmp(val
, "conf")) {
1307 q
->timeoutpriority
= TIMEOUT_PRIORITY_CONF
;
1309 q
->timeoutpriority
= TIMEOUT_PRIORITY_APP
;
1311 } else if (failunknown
) {
1313 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1314 q
->name
, param
, linenum
);
1316 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s\n", q
->name
, param
);
1322 * \brief Find rt member record to update otherwise create one.
1324 * Search for member in queue, if found update penalty/paused state,
1325 * if no memeber exists create one flag it as a RT member and add to queue member list.
1327 static void rt_handle_member_record(struct call_queue
*q
, char *interface
, const char *rt_uniqueid
, const char *membername
, const char *penalty_str
, const char *paused_str
, const char* state_interface
)
1330 struct ao2_iterator mem_iter
;
1336 penalty
= atoi(penalty_str
);
1342 paused
= atoi(paused_str
);
1347 /* Find member by realtime uniqueid and update */
1348 mem_iter
= ao2_iterator_init(q
->members
, 0);
1349 while ((m
= ao2_iterator_next(&mem_iter
))) {
1350 if (!strcasecmp(m
->rt_uniqueid
, rt_uniqueid
)) {
1351 m
->dead
= 0; /* Do not delete this one. */
1352 ast_copy_string(m
->rt_uniqueid
, rt_uniqueid
, sizeof(m
->rt_uniqueid
));
1355 if (strcasecmp(state_interface
, m
->state_interface
)) {
1356 remove_from_interfaces(m
->state_interface
);
1357 ast_copy_string(m
->state_interface
, state_interface
, sizeof(m
->state_interface
));
1358 add_to_interfaces(m
->state_interface
);
1360 m
->penalty
= penalty
;
1368 /* Create a new member */
1370 if ((m
= create_queue_member(interface
, membername
, penalty
, paused
, state_interface
))) {
1373 ast_copy_string(m
->rt_uniqueid
, rt_uniqueid
, sizeof(m
->rt_uniqueid
));
1374 add_to_interfaces(m
->state_interface
);
1375 ast_queue_log(q
->name
, "REALTIME", m
->interface
, "ADDMEMBER", "%s", "");
1376 ao2_link(q
->members
, m
);
1384 /*! \brief Iterate through queue's member list and delete them */
1385 static void free_members(struct call_queue
*q
, int all
)
1387 /* Free non-dynamic members */
1389 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
1391 while ((cur
= ao2_iterator_next(&mem_iter
))) {
1392 if (all
|| !cur
->dynamic
) {
1393 ao2_unlink(q
->members
, cur
);
1394 remove_from_interfaces(cur
->state_interface
);
1401 /*! \brief Free queue's member list then its string fields */
1402 static void destroy_queue(void *obj
)
1404 struct call_queue
*q
= obj
;
1408 ast_string_field_free_memory(q
);
1409 for (i
= 0; i
< MAX_PERIODIC_ANNOUNCEMENTS
; i
++) {
1410 if (q
->sound_periodicannounce
[i
])
1411 free(q
->sound_periodicannounce
[i
]);
1413 ao2_ref(q
->members
, -1);
1416 static struct call_queue
*alloc_queue(const char *queuename
)
1418 struct call_queue
*q
;
1420 if ((q
= ao2_alloc(sizeof(*q
), destroy_queue
))) {
1421 if (ast_string_field_init(q
, 64)) {
1425 ast_string_field_set(q
, name
, queuename
);
1431 * \brief Reload a single queue via realtime.
1433 * Check for statically defined queue first, check if deleted RT queue,
1434 * check for new RT queue, if queue vars are not defined init them with defaults.
1435 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
1436 * \retval the queue,
1437 * \retval NULL if it doesn't exist.
1438 * \note Should be called with the "queues" container locked.
1440 static struct call_queue
*find_queue_by_name_rt(const char *queuename
, struct ast_variable
*queue_vars
, struct ast_config
*member_config
)
1442 struct ast_variable
*v
;
1443 struct call_queue
*q
, tmpq
= {
1447 struct ao2_iterator mem_iter
;
1448 char *interface
= NULL
;
1449 const char *tmp_name
;
1451 char tmpbuf
[64]; /* Must be longer than the longest queue param name. */
1453 /* Static queues override realtime. */
1454 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
1462 ast_log(LOG_WARNING
, "Static queue '%s' already exists. Not loading from realtime\n", q
->name
);
1468 } else if (!member_config
)
1469 /* Not found in the list, and it's not realtime ... */
1472 /* Check if queue is defined in realtime. */
1474 /* Delete queue from in-core list if it has been deleted in realtime. */
1476 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1477 found condition... So we might delete an in-core queue
1478 in case of DB failure. */
1479 ast_debug(1, "Queue %s not found in realtime.\n", queuename
);
1482 /* Delete if unused (else will be deleted when last caller leaves). */
1483 ao2_unlink(queues
, q
);
1490 /* Create a new queue if an in-core entry does not exist yet. */
1492 struct ast_variable
*tmpvar
= NULL
;
1493 if (!(q
= alloc_queue(queuename
)))
1498 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
1499 * will allocate the members properly
1501 for (tmpvar
= queue_vars
; tmpvar
; tmpvar
= tmpvar
->next
) {
1502 if (!strcasecmp(tmpvar
->name
, "strategy")) {
1503 q
->strategy
= strat2int(tmpvar
->value
);
1504 if (q
->strategy
< 0) {
1505 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1506 tmpvar
->value
, q
->name
);
1507 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
1512 /* We traversed all variables and didn't find a strategy */
1514 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
1515 ao2_link(queues
, q
);
1517 init_queue(q
); /* Ensure defaults for all parameters not set explicitly. */
1519 memset(tmpbuf
, 0, sizeof(tmpbuf
));
1520 for (v
= queue_vars
; v
; v
= v
->next
) {
1521 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1522 if ((tmp
= strchr(v
->name
, '_'))) {
1523 ast_copy_string(tmpbuf
, v
->name
, sizeof(tmpbuf
));
1526 while ((tmp
= strchr(tmp
, '_')))
1531 if (!ast_strlen_zero(v
->value
)) {
1532 /* Don't want to try to set the option if the value is empty */
1533 queue_set_param(q
, tmp_name
, v
->value
, -1, 0);
1537 /* Temporarily set realtime members dead so we can detect deleted ones.
1538 * Also set the membercount correctly for realtime*/
1539 mem_iter
= ao2_iterator_init(q
->members
, 0);
1540 while ((m
= ao2_iterator_next(&mem_iter
))) {
1547 while ((interface
= ast_category_browse(member_config
, interface
))) {
1548 rt_handle_member_record(q
, interface
,
1549 ast_variable_retrieve(member_config
, interface
, "uniqueid"),
1550 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"),interface
),
1551 ast_variable_retrieve(member_config
, interface
, "penalty"),
1552 ast_variable_retrieve(member_config
, interface
, "paused"),
1553 S_OR(ast_variable_retrieve(member_config
, interface
, "state_interface"),interface
));
1556 /* Delete all realtime members that have been deleted in DB. */
1557 mem_iter
= ao2_iterator_init(q
->members
, 0);
1558 while ((m
= ao2_iterator_next(&mem_iter
))) {
1560 ast_queue_log(q
->name
, "REALTIME", m
->interface
, "REMOVEMEMBER", "%s", "");
1561 ao2_unlink(q
->members
, m
);
1562 remove_from_interfaces(m
->state_interface
);
1573 static struct call_queue
*load_realtime_queue(const char *queuename
)
1575 struct ast_variable
*queue_vars
;
1576 struct ast_config
*member_config
= NULL
;
1577 struct call_queue
*q
= NULL
, tmpq
= {
1581 /* Find the queue in the in-core list first. */
1582 q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
);
1584 if (!q
|| q
->realtime
) {
1585 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
1586 queue operations while waiting for the DB.
1588 This will be two separate database transactions, so we might
1589 see queue parameters as they were before another process
1590 changed the queue and member list as it was after the change.
1591 Thus we might see an empty member list when a queue is
1592 deleted. In practise, this is unlikely to cause a problem. */
1594 queue_vars
= ast_load_realtime("queues", "name", queuename
, SENTINEL
);
1596 member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename
, SENTINEL
);
1597 if (!member_config
) {
1598 ast_log(LOG_ERROR
, "no queue_members defined in your config (extconfig.conf).\n");
1599 ast_variables_destroy(queue_vars
);
1605 q
= find_queue_by_name_rt(queuename
, queue_vars
, member_config
);
1607 ast_config_destroy(member_config
);
1609 ast_variables_destroy(queue_vars
);
1613 update_realtime_members(q
);
1618 static int update_realtime_member_field(struct member
*mem
, const char *queue_name
, const char *field
, const char *value
)
1622 if (ast_strlen_zero(mem
->rt_uniqueid
))
1625 if ((ast_update_realtime("queue_members", "uniqueid", mem
->rt_uniqueid
, field
, value
, SENTINEL
)) > 0)
1632 static void update_realtime_members(struct call_queue
*q
)
1634 struct ast_config
*member_config
= NULL
;
1636 char *interface
= NULL
;
1637 struct ao2_iterator mem_iter
;
1639 if (!(member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q
->name
, SENTINEL
))) {
1640 /*This queue doesn't have realtime members*/
1641 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q
->name
);
1647 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1648 mem_iter
= ao2_iterator_init(q
->members
, 0);
1649 while ((m
= ao2_iterator_next(&mem_iter
))) {
1655 while ((interface
= ast_category_browse(member_config
, interface
))) {
1656 rt_handle_member_record(q
, interface
,
1657 ast_variable_retrieve(member_config
, interface
, "uniqueid"),
1658 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"), interface
),
1659 ast_variable_retrieve(member_config
, interface
, "penalty"),
1660 ast_variable_retrieve(member_config
, interface
, "paused"),
1661 S_OR(ast_variable_retrieve(member_config
, interface
, "state_interface"), interface
));
1664 /* Delete all realtime members that have been deleted in DB. */
1665 mem_iter
= ao2_iterator_init(q
->members
, 0);
1666 while ((m
= ao2_iterator_next(&mem_iter
))) {
1668 ast_queue_log(q
->name
, "REALTIME", m
->interface
, "REMOVEMEMBER", "%s", "");
1669 ao2_unlink(q
->members
, m
);
1670 remove_from_interfaces(m
->state_interface
);
1676 ast_config_destroy(member_config
);
1679 static int join_queue(char *queuename
, struct queue_ent
*qe
, enum queue_result
*reason
)
1681 struct call_queue
*q
;
1682 struct queue_ent
*cur
, *prev
= NULL
;
1686 enum queue_member_status stat
;
1688 if (!(q
= load_realtime_queue(queuename
)))
1694 /* This is our one */
1695 stat
= get_member_status(q
, qe
->max_penalty
, qe
->min_penalty
);
1696 if (!q
->joinempty
&& (stat
== QUEUE_NO_MEMBERS
))
1697 *reason
= QUEUE_JOINEMPTY
;
1698 else if ((q
->joinempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_MEMBERS
))
1699 *reason
= QUEUE_JOINUNAVAIL
;
1700 else if ((q
->joinempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_MEMBERS
))
1701 *reason
= QUEUE_JOINUNAVAIL
;
1702 else if (q
->maxlen
&& (q
->count
>= q
->maxlen
))
1703 *reason
= QUEUE_FULL
;
1705 /* There's space for us, put us at the right position inside
1707 * Take into account the priority of the calling user */
1712 /* We have higher priority than the current user, enter
1713 * before him, after all the other users with priority
1714 * higher or equal to our priority. */
1715 if ((!inserted
) && (qe
->prio
> cur
->prio
)) {
1716 insert_entry(q
, prev
, qe
, &pos
);
1723 /* No luck, join at the end of the queue */
1725 insert_entry(q
, prev
, qe
, &pos
);
1726 ast_copy_string(qe
->moh
, q
->moh
, sizeof(qe
->moh
));
1727 ast_copy_string(qe
->announce
, q
->announce
, sizeof(qe
->announce
));
1728 ast_copy_string(qe
->context
, q
->context
, sizeof(qe
->context
));
1731 manager_event(EVENT_FLAG_CALL
, "Join",
1732 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1734 S_OR(qe
->chan
->cid
.cid_num
, "unknown"), /* XXX somewhere else it is <unknown> */
1735 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
1736 q
->name
, qe
->pos
, q
->count
, qe
->chan
->uniqueid
);
1737 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q
->name
, qe
->chan
->name
, qe
->pos
);
1745 static int play_file(struct ast_channel
*chan
, const char *filename
)
1749 ast_stopstream(chan
);
1751 res
= ast_streamfile(chan
, filename
, chan
->language
);
1753 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
1755 ast_stopstream(chan
);
1761 * \brief Check for valid exit from queue via goto
1762 * \retval 0 if failure
1763 * \retval 1 if successful
1765 static int valid_exit(struct queue_ent
*qe
, char digit
)
1767 int digitlen
= strlen(qe
->digits
);
1769 /* Prevent possible buffer overflow */
1770 if (digitlen
< sizeof(qe
->digits
) - 2) {
1771 qe
->digits
[digitlen
] = digit
;
1772 qe
->digits
[digitlen
+ 1] = '\0';
1774 qe
->digits
[0] = '\0';
1778 /* If there's no context to goto, short-circuit */
1779 if (ast_strlen_zero(qe
->context
))
1782 /* If the extension is bad, then reset the digits to blank */
1783 if (!ast_canmatch_extension(qe
->chan
, qe
->context
, qe
->digits
, 1, qe
->chan
->cid
.cid_num
)) {
1784 qe
->digits
[0] = '\0';
1788 /* We have an exact match */
1789 if (!ast_goto_if_exists(qe
->chan
, qe
->context
, qe
->digits
, 1)) {
1790 qe
->valid_digits
= 1;
1791 /* Return 1 on a successful goto */
1798 static int say_position(struct queue_ent
*qe
, int ringing
)
1800 int res
= 0, avgholdmins
, avgholdsecs
, announceposition
= 0;
1803 /* Let minannouncefrequency seconds pass between the start of each position announcement */
1805 if ((now
- qe
->last_pos
) < qe
->parent
->minannouncefrequency
)
1808 /* If either our position has changed, or we are over the freq timer, say position */
1809 if ((qe
->last_pos_said
== qe
->pos
) && ((now
- qe
->last_pos
) < qe
->parent
->announcefrequency
))
1813 ast_indicate(qe
->chan
,-1);
1815 ast_moh_stop(qe
->chan
);
1818 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_YES
||
1819 qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
||
1820 (qe
->parent
->announceposition
== ANNOUNCEPOSITION_LIMIT
&&
1821 qe
->pos
<= qe
->parent
->announcepositionlimit
))
1822 announceposition
= 1;
1825 if (announceposition
== 1) {
1826 /* Say we're next, if we are */
1828 res
= play_file(qe
->chan
, qe
->parent
->sound_next
);
1834 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
&& qe
->pos
> qe
->parent
->announcepositionlimit
){
1836 res
= play_file(qe
->chan
, qe
->parent
->queue_quantity1
);
1839 res
= ast_say_number(qe
->chan
, qe
->parent
->announcepositionlimit
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
); /* Needs gender */
1844 res
= play_file(qe
->chan
, qe
->parent
->sound_thereare
);
1847 res
= ast_say_number(qe
->chan
, qe
->pos
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
); /* Needs gender */
1851 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
&& qe
->pos
> qe
->parent
->announcepositionlimit
){
1853 res
= play_file(qe
->chan
, qe
->parent
->queue_quantity2
);
1857 res
= play_file(qe
->chan
, qe
->parent
->sound_calls
);
1863 /* Round hold time to nearest minute */
1864 avgholdmins
= abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
)) / 60);
1866 /* If they have specified a rounding then round the seconds as well */
1867 if (qe
->parent
->roundingseconds
) {
1868 avgholdsecs
= (abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
))) - 60 * avgholdmins
) / qe
->parent
->roundingseconds
;
1869 avgholdsecs
*= qe
->parent
->roundingseconds
;
1874 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe
->parent
->name
, avgholdmins
, avgholdsecs
);
1876 /* If the hold time is >1 min, if it's enabled, and if it's not
1877 supposed to be only once and we have already said it, say it */
1878 if ((avgholdmins
+avgholdsecs
) > 0 && qe
->parent
->announceholdtime
&&
1879 ((qe
->parent
->announceholdtime
== ANNOUNCEHOLDTIME_ONCE
&& !qe
->last_pos
) ||
1880 !(qe
->parent
->announceholdtime
== ANNOUNCEHOLDTIME_ONCE
))) {
1881 res
= play_file(qe
->chan
, qe
->parent
->sound_holdtime
);
1885 if (avgholdmins
> 1) {
1886 res
= ast_say_number(qe
->chan
, avgholdmins
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1890 if (avgholdmins
== 1) {
1891 res
= play_file(qe
->chan
, qe
->parent
->sound_minute
);
1895 res
= play_file(qe
->chan
, qe
->parent
->sound_minutes
);
1900 if (avgholdsecs
> 1) {
1901 res
= ast_say_number(qe
->chan
, avgholdmins
> 1 ? avgholdsecs
: avgholdmins
* 60 + avgholdsecs
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1905 res
= play_file(qe
->chan
, qe
->parent
->sound_seconds
);
1913 if (announceposition
== 1){
1914 if (qe
->parent
->announceposition
) {
1915 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
1916 qe
->chan
->name
, qe
->parent
->name
, qe
->pos
);
1918 res
= play_file(qe
->chan
, qe
->parent
->sound_thanks
);
1921 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
1924 /* Set our last_pos indicators */
1926 qe
->last_pos_said
= qe
->pos
;
1928 /* Don't restart music on hold if we're about to exit the caller from the queue */
1931 ast_indicate(qe
->chan
, AST_CONTROL_RINGING
);
1933 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
1939 static void recalc_holdtime(struct queue_ent
*qe
, int newholdtime
)
1943 /* Calculate holdtime using an exponential average */
1944 /* Thanks to SRT for this contribution */
1945 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1947 ao2_lock(qe
->parent
);
1948 oldvalue
= qe
->parent
->holdtime
;
1949 qe
->parent
->holdtime
= (((oldvalue
<< 2) - oldvalue
) + newholdtime
) >> 2;
1950 ao2_unlock(qe
->parent
);
1953 /*! \brief Caller leaving queue.
1955 * Search the queue to find the leaving client, if found remove from queue
1956 * create manager event, move others up the queue.
1958 static void leave_queue(struct queue_ent
*qe
)
1960 struct call_queue
*q
;
1961 struct queue_ent
*cur
, *prev
= NULL
;
1962 struct penalty_rule
*pr_iter
;
1965 if (!(q
= qe
->parent
))
1971 for (cur
= q
->head
; cur
; cur
= cur
->next
) {
1975 /* Take us out of the queue */
1976 manager_event(EVENT_FLAG_CALL
, "Leave",
1977 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1978 qe
->chan
->name
, q
->name
, q
->count
, qe
->chan
->uniqueid
);
1979 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q
->name
, qe
->chan
->name
);
1980 /* Take us out of the queue */
1982 prev
->next
= cur
->next
;
1984 q
->head
= cur
->next
;
1985 /* Free penalty rules */
1986 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&qe
->qe_rules
, list
)))
1989 /* Renumber the people after us in the queue based on a new count */
1996 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
1998 if (!ast_load_realtime("queues", "name", q
->name
, SENTINEL
))
2003 /* It's dead and nobody is in it, so kill it */
2004 ao2_unlink(queues
, q
);
2005 /* unref the container's reference to the queue */
2008 /* unref the explicit ref earlier in the function */
2012 /*! \brief Hang up a list of outgoing calls */
2013 static void hangupcalls(struct callattempt
*outgoing
, struct ast_channel
*exception
)
2015 struct callattempt
*oo
;
2018 /* Hangup any existing lines we have open */
2019 if (outgoing
->chan
&& (outgoing
->chan
!= exception
))
2020 ast_hangup(outgoing
->chan
);
2022 outgoing
= outgoing
->q_next
;
2024 ao2_ref(oo
->member
, -1);
2030 * \brief traverse all defined queues which have calls waiting and contain this member
2031 * \retval 0 if no other queue has precedence (higher weight)
2032 * \retval 1 if found
2034 static int compare_weight(struct call_queue
*rq
, struct member
*member
)
2036 struct call_queue
*q
;
2039 struct ao2_iterator queue_iter
;
2041 /* q's lock and rq's lock already set by try_calling()
2042 * to solve deadlock */
2043 queue_iter
= ao2_iterator_init(queues
, 0);
2044 while ((q
= ao2_iterator_next(&queue_iter
))) {
2045 if (q
== rq
) { /* don't check myself, could deadlock */
2050 if (q
->count
&& q
->members
) {
2051 if ((mem
= ao2_find(q
->members
, member
, OBJ_POINTER
))) {
2052 ast_debug(1, "Found matching member %s in queue '%s'\n", mem
->interface
, q
->name
);
2053 if (q
->weight
> rq
->weight
) {
2054 ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q
->name
, q
->weight
, q
->count
, rq
->name
, rq
->weight
, rq
->count
);
2070 /*! \brief common hangup actions */
2071 static void do_hang(struct callattempt
*o
)
2074 ast_hangup(o
->chan
);
2078 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2079 static char *vars2manager(struct ast_channel
*chan
, char *vars
, size_t len
)
2081 struct ast_str
*buf
= ast_str_alloca(len
+ 1);
2084 if (pbx_builtin_serialize_variables(chan
, &buf
)) {
2087 /* convert "\n" to "\nVariable: " */
2088 strcpy(vars
, "Variable: ");
2091 for (i
= 0, j
= 10; (i
< len
- 1) && (j
< len
- 1); i
++, j
++) {
2094 if (tmp
[i
+ 1] == '\0')
2096 if (tmp
[i
] == '\n') {
2100 ast_copy_string(&(vars
[j
]), "Variable: ", len
- j
);
2110 /* there are no channel variables; leave it blank */
2117 * \brief Part 2 of ring_one
2119 * Does error checking before attempting to request a channel and call a member.
2120 * This function is only called from ring_one().
2121 * Failure can occur if:
2124 * - Wrapup time not expired
2125 * - Priority by another queue
2127 * \retval 1 on success to reach a free agent
2128 * \retval 0 on failure to get agent.
2130 static int ring_entry(struct queue_ent
*qe
, struct callattempt
*tmp
, int *busies
)
2136 const char *macrocontext
, *macroexten
;
2138 /* on entry here, we know that tmp->chan == NULL */
2139 if ((tmp
->lastqueue
&& tmp
->lastqueue
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< tmp
->lastqueue
->wrapuptime
)) ||
2140 (!tmp
->lastqueue
&& qe
->parent
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< qe
->parent
->wrapuptime
))) {
2141 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
2142 (tmp
->lastqueue
? tmp
->lastqueue
->name
: qe
->parent
->name
), tmp
->interface
);
2144 ast_cdr_busy(qe
->chan
->cdr
);
2145 tmp
->stillgoing
= 0;
2150 if (!qe
->parent
->ringinuse
&& (tmp
->member
->status
!= AST_DEVICE_NOT_INUSE
) && (tmp
->member
->status
!= AST_DEVICE_UNKNOWN
)) {
2151 ast_debug(1, "%s in use, can't receive call\n", tmp
->interface
);
2153 ast_cdr_busy(qe
->chan
->cdr
);
2154 tmp
->stillgoing
= 0;
2158 if (tmp
->member
->paused
) {
2159 ast_debug(1, "%s paused, can't receive call\n", tmp
->interface
);
2161 ast_cdr_busy(qe
->chan
->cdr
);
2162 tmp
->stillgoing
= 0;
2165 if (use_weight
&& compare_weight(qe
->parent
,tmp
->member
)) {
2166 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe
->parent
->name
, tmp
->interface
);
2168 ast_cdr_busy(qe
->chan
->cdr
);
2169 tmp
->stillgoing
= 0;
2174 ast_copy_string(tech
, tmp
->interface
, sizeof(tech
));
2175 if ((location
= strchr(tech
, '/')))
2180 /* Request the peer */
2181 tmp
->chan
= ast_request(tech
, qe
->chan
->nativeformats
, location
, &status
);
2182 if (!tmp
->chan
) { /* If we can't, just go on to the next call */
2184 ast_cdr_busy(qe
->chan
->cdr
);
2185 tmp
->stillgoing
= 0;
2187 update_status(tmp
->member
->state_interface
, ast_device_state(tmp
->member
->state_interface
));
2189 ao2_lock(qe
->parent
);
2190 qe
->parent
->rrpos
++;
2192 ao2_unlock(qe
->parent
);
2199 tmp
->chan
->appl
= "AppQueue";
2200 tmp
->chan
->data
= "(Outgoing Line)";
2201 memset(&tmp
->chan
->whentohangup
, 0, sizeof(tmp
->chan
->whentohangup
));
2202 if (tmp
->chan
->cid
.cid_num
)
2203 ast_free(tmp
->chan
->cid
.cid_num
);
2204 tmp
->chan
->cid
.cid_num
= ast_strdup(qe
->chan
->cid
.cid_num
);
2205 if (tmp
->chan
->cid
.cid_name
)
2206 ast_free(tmp
->chan
->cid
.cid_name
);
2207 tmp
->chan
->cid
.cid_name
= ast_strdup(qe
->chan
->cid
.cid_name
);
2208 if (tmp
->chan
->cid
.cid_ani
)
2209 ast_free(tmp
->chan
->cid
.cid_ani
);
2210 tmp
->chan
->cid
.cid_ani
= ast_strdup(qe
->chan
->cid
.cid_ani
);
2212 /* Inherit specially named variables from parent channel */
2213 ast_channel_inherit_variables(qe
->chan
, tmp
->chan
);
2215 /* Presense of ADSI CPE on outgoing channel follows ours */
2216 tmp
->chan
->adsicpe
= qe
->chan
->adsicpe
;
2218 /* Inherit context and extension */
2219 ast_channel_lock(qe
->chan
);
2220 macrocontext
= pbx_builtin_getvar_helper(qe
->chan
, "MACRO_CONTEXT");
2221 ast_string_field_set(tmp
->chan
, dialcontext
, ast_strlen_zero(macrocontext
) ? qe
->chan
->context
: macrocontext
);
2222 macroexten
= pbx_builtin_getvar_helper(qe
->chan
, "MACRO_EXTEN");
2223 if (!ast_strlen_zero(macroexten
))
2224 ast_copy_string(tmp
->chan
->exten
, macroexten
, sizeof(tmp
->chan
->exten
));
2226 ast_copy_string(tmp
->chan
->exten
, qe
->chan
->exten
, sizeof(tmp
->chan
->exten
));
2227 ast_channel_unlock(qe
->chan
);
2229 /* Place the call, but don't wait on the answer */
2230 if ((res
= ast_call(tmp
->chan
, location
, 0))) {
2231 /* Again, keep going even if there's an error */
2232 ast_debug(1, "ast call on peer returned %d\n", res
);
2233 ast_verb(3, "Couldn't call %s\n", tmp
->interface
);
2237 } else if (qe
->parent
->eventwhencalled
) {
2240 manager_event(EVENT_FLAG_AGENT
, "AgentCalled",
2242 "AgentCalled: %s\r\n"
2244 "ChannelCalling: %s\r\n"
2245 "DestinationChannel: %s\r\n"
2246 "CallerIDNum: %s\r\n"
2247 "CallerIDName: %s\r\n"
2253 qe
->parent
->name
, tmp
->interface
, tmp
->member
->membername
, qe
->chan
->name
, tmp
->chan
->name
,
2254 tmp
->chan
->cid
.cid_num
? tmp
->chan
->cid
.cid_num
: "unknown",
2255 tmp
->chan
->cid
.cid_name
? tmp
->chan
->cid
.cid_name
: "unknown",
2256 qe
->chan
->context
, qe
->chan
->exten
, qe
->chan
->priority
, qe
->chan
->uniqueid
,
2257 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2258 ast_verb(3, "Called %s\n", tmp
->interface
);
2264 /*! \brief find the entry with the best metric, or NULL */
2265 static struct callattempt
*find_best(struct callattempt
*outgoing
)
2267 struct callattempt
*best
= NULL
, *cur
;
2269 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
2270 if (cur
->stillgoing
&& /* Not already done */
2271 !cur
->chan
&& /* Isn't already going */
2272 (!best
|| cur
->metric
< best
->metric
)) { /* We haven't found one yet, or it's better */
2281 * \brief Place a call to a queue member.
2283 * Once metrics have been calculated for each member, this function is used
2284 * to place a call to the appropriate member (or members). The low-level
2285 * channel-handling and error detection is handled in ring_entry
2287 * \retval 1 if a member was called successfully
2288 * \retval 0 otherwise
2290 static int ring_one(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *busies
)
2295 struct callattempt
*best
= find_best(outgoing
);
2297 ast_debug(1, "Nobody left to try ringing in queue\n");
2300 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2301 struct callattempt
*cur
;
2302 /* Ring everyone who shares this best metric (for ringall) */
2303 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
2304 if (cur
->stillgoing
&& !cur
->chan
&& cur
->metric
<= best
->metric
) {
2305 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur
->interface
, cur
->metric
);
2306 ret
|= ring_entry(qe
, cur
, busies
);
2310 /* Ring just the best channel */
2311 ast_debug(1, "Trying '%s' with metric %d\n", best
->interface
, best
->metric
);
2312 ret
= ring_entry(qe
, best
, busies
);
2319 /*! \brief Search for best metric and add to Round Robbin queue */
2320 static int store_next_rr(struct queue_ent
*qe
, struct callattempt
*outgoing
)
2322 struct callattempt
*best
= find_best(outgoing
);
2325 /* Ring just the best channel */
2326 ast_debug(1, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
2327 qe
->parent
->rrpos
= best
->metric
% 1000;
2329 /* Just increment rrpos */
2330 if (qe
->parent
->wrapped
) {
2331 /* No more channels, start over */
2332 qe
->parent
->rrpos
= 0;
2334 /* Prioritize next entry */
2335 qe
->parent
->rrpos
++;
2338 qe
->parent
->wrapped
= 0;
2343 /*! \brief Search for best metric and add to Linear queue */
2344 static int store_next_lin(struct queue_ent
*qe
, struct callattempt
*outgoing
)
2346 struct callattempt
*best
= find_best(outgoing
);
2349 /* Ring just the best channel */
2350 ast_debug(1, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
2351 qe
->linpos
= best
->metric
% 1000;
2353 /* Just increment rrpos */
2354 if (qe
->linwrapped
) {
2355 /* No more channels, start over */
2358 /* Prioritize next entry */
2367 /*! \brief Playback announcement to queued members if peroid has elapsed */
2368 static int say_periodic_announcement(struct queue_ent
*qe
, int ringing
)
2373 /* Get the current time */
2376 /* Check to see if it is time to announce */
2377 if ((now
- qe
->last_periodic_announce_time
) < qe
->parent
->periodicannouncefrequency
)
2380 /* Stop the music on hold so we can play our own file */
2382 ast_indicate(qe
->chan
,-1);
2384 ast_moh_stop(qe
->chan
);
2386 ast_verb(3, "Playing periodic announcement\n");
2388 if (qe
->parent
->randomperiodicannounce
) {
2389 qe
->last_periodic_announce_sound
= ((unsigned long) ast_random()) % qe
->parent
->numperiodicannounce
;
2390 } else if (qe
->last_periodic_announce_sound
>= qe
->parent
->numperiodicannounce
||
2391 ast_strlen_zero(qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]->str
)) {
2392 qe
->last_periodic_announce_sound
= 0;
2395 /* play the announcement */
2396 res
= play_file(qe
->chan
, qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]->str
);
2398 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
2401 /* Resume Music on Hold if the caller is going to stay in the queue */
2404 ast_indicate(qe
->chan
, AST_CONTROL_RINGING
);
2406 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
2409 /* update last_periodic_announce_time */
2410 qe
->last_periodic_announce_time
= now
;
2412 /* Update the current periodic announcement to the next announcement */
2413 if (!qe
->parent
->randomperiodicannounce
) {
2414 qe
->last_periodic_announce_sound
++;
2420 /*! \brief Record that a caller gave up on waiting in queue */
2421 static void record_abandoned(struct queue_ent
*qe
)
2423 ao2_lock(qe
->parent
);
2424 set_queue_variables(qe
);
2425 manager_event(EVENT_FLAG_AGENT
, "QueueCallerAbandon",
2429 "OriginalPosition: %d\r\n"
2431 qe
->parent
->name
, qe
->chan
->uniqueid
, qe
->pos
, qe
->opos
, (int)(time(NULL
) - qe
->start
));
2433 qe
->parent
->callsabandoned
++;
2434 ao2_unlock(qe
->parent
);
2437 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2438 static void rna(int rnatime
, struct queue_ent
*qe
, char *interface
, char *membername
)
2440 ast_verb(3, "Nobody picked up in %d ms\n", rnatime
);
2441 if (qe
->parent
->eventwhencalled
)
2442 manager_event(EVENT_FLAG_AGENT
, "AgentRingNoAnswer",
2447 "MemberName: %s\r\n"
2455 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, membername
, "RINGNOANSWER", "%d", rnatime
);
2456 if (qe
->parent
->autopause
) {
2457 if (!set_member_paused(qe
->parent
->name
, interface
, "Auto-Pause", 1)) {
2458 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface
, qe
->parent
->name
);
2460 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface
, qe
->parent
->name
);
2466 #define AST_MAX_WATCHERS 256
2467 /*! \brief Wait for a member to answer the call
2469 * \param[in] qe the queue_ent corresponding to the caller in the queue
2470 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
2471 * \param[in] to the amount of time (in milliseconds) to wait for a response
2472 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
2473 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
2474 * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
2475 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
2477 static struct callattempt
*wait_for_answer(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *to
, char *digit
, int prebusies
, int caller_disconnect
, int forwardsallowed
)
2479 const char *queue
= qe
->parent
->name
;
2480 struct callattempt
*o
, *start
= NULL
, *prev
= NULL
;
2482 int numbusies
= prebusies
;
2486 struct ast_frame
*f
;
2487 struct callattempt
*peer
= NULL
;
2488 struct ast_channel
*winner
;
2489 struct ast_channel
*in
= qe
->chan
;
2491 char membername
[80] = "";
2495 struct callattempt
*epollo
;
2498 starttime
= (long) time(NULL
);
2500 for (epollo
= outgoing
; epollo
; epollo
= epollo
->q_next
) {
2502 ast_poll_channel_add(in
, epollo
->chan
);
2506 while (*to
&& !peer
) {
2507 int numlines
, retry
, pos
= 1;
2508 struct ast_channel
*watchers
[AST_MAX_WATCHERS
];
2512 for (retry
= 0; retry
< 2; retry
++) {
2514 for (o
= outgoing
; o
; o
= o
->q_next
) { /* Keep track of important channels */
2515 if (o
->stillgoing
) { /* Keep track of important channels */
2518 watchers
[pos
++] = o
->chan
;
2522 prev
->call_next
= o
;
2528 if (pos
> 1 /* found */ || !stillgoing
/* nobody listening */ ||
2529 (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) /* ring would not be delivered */)
2531 /* On "ringall" strategy we only move to the next penalty level
2532 when *all* ringing phones are done in the current penalty level */
2533 ring_one(qe
, outgoing
, &numbusies
);
2536 if (pos
== 1 /* not found */) {
2537 if (numlines
== (numbusies
+ numnochan
)) {
2538 ast_debug(1, "Everyone is busy at this time\n");
2540 ast_log(LOG_NOTICE
, "No one is answering queue '%s' (%d/%d/%d)\n", queue
, numlines
, numbusies
, numnochan
);
2545 winner
= ast_waitfor_n(watchers
, pos
, to
);
2546 for (o
= start
; o
; o
= o
->call_next
) {
2547 if (o
->stillgoing
&& (o
->chan
) && (o
->chan
->_state
== AST_STATE_UP
)) {
2549 ast_verb(3, "%s answered %s\n", o
->chan
->name
, in
->name
);
2552 } else if (o
->chan
&& (o
->chan
== winner
)) {
2554 ast_copy_string(on
, o
->member
->interface
, sizeof(on
));
2555 ast_copy_string(membername
, o
->member
->membername
, sizeof(membername
));
2557 if (!ast_strlen_zero(o
->chan
->call_forward
) && !forwardsallowed
) {
2558 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in
->name
, o
->chan
->call_forward
);
2563 } else if (!ast_strlen_zero(o
->chan
->call_forward
)) {
2568 ast_copy_string(tmpchan
, o
->chan
->call_forward
, sizeof(tmpchan
));
2569 if ((stuff
= strchr(tmpchan
, '/'))) {
2573 snprintf(tmpchan
, sizeof(tmpchan
), "%s@%s", o
->chan
->call_forward
, o
->chan
->context
);
2577 /* Before processing channel, go ahead and check for forwarding */
2578 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in
->name
, tech
, stuff
, o
->chan
->name
);
2579 /* Setup parameters */
2580 o
->chan
= ast_request(tech
, in
->nativeformats
, stuff
, &status
);
2582 ast_log(LOG_NOTICE
, "Unable to create local channel for call forward to '%s/%s'\n", tech
, stuff
);
2586 ast_channel_inherit_variables(in
, o
->chan
);
2587 ast_channel_datastore_inherit(in
, o
->chan
);
2588 if (o
->chan
->cid
.cid_num
)
2589 ast_free(o
->chan
->cid
.cid_num
);
2590 o
->chan
->cid
.cid_num
= ast_strdup(in
->cid
.cid_num
);
2592 if (o
->chan
->cid
.cid_name
)
2593 ast_free(o
->chan
->cid
.cid_name
);
2594 o
->chan
->cid
.cid_name
= ast_strdup(in
->cid
.cid_name
);
2596 ast_string_field_set(o
->chan
, accountcode
, in
->accountcode
);
2597 o
->chan
->cdrflags
= in
->cdrflags
;
2599 if (in
->cid
.cid_ani
) {
2600 if (o
->chan
->cid
.cid_ani
)
2601 ast_free(o
->chan
->cid
.cid_ani
);
2602 o
->chan
->cid
.cid_ani
= ast_strdup(in
->cid
.cid_ani
);
2604 if (o
->chan
->cid
.cid_rdnis
)
2605 ast_free(o
->chan
->cid
.cid_rdnis
);
2606 o
->chan
->cid
.cid_rdnis
= ast_strdup(S_OR(in
->macroexten
, in
->exten
));
2607 if (ast_call(o
->chan
, tmpchan
, 0)) {
2608 ast_log(LOG_NOTICE
, "Failed to dial on local channel for call forward to '%s'\n", tmpchan
);
2613 /* Hangup the original channel now, in case we needed it */
2617 f
= ast_read(winner
);
2619 if (f
->frametype
== AST_FRAME_CONTROL
) {
2620 switch (f
->subclass
) {
2621 case AST_CONTROL_ANSWER
:
2622 /* This is our guy if someone answered. */
2624 ast_verb(3, "%s answered %s\n", o
->chan
->name
, in
->name
);
2628 case AST_CONTROL_BUSY
:
2629 ast_verb(3, "%s is busy\n", o
->chan
->name
);
2631 ast_cdr_busy(in
->cdr
);
2633 endtime
= (long) time(NULL
);
2634 endtime
-= starttime
;
2635 rna(endtime
*1000, qe
, on
, membername
);
2636 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2637 if (qe
->parent
->timeoutrestart
)
2639 ring_one(qe
, outgoing
, &numbusies
);
2643 case AST_CONTROL_CONGESTION
:
2644 ast_verb(3, "%s is circuit-busy\n", o
->chan
->name
);
2646 ast_cdr_busy(in
->cdr
);
2647 endtime
= (long) time(NULL
);
2648 endtime
-= starttime
;
2649 rna(endtime
*1000, qe
, on
, membername
);
2651 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2652 if (qe
->parent
->timeoutrestart
)
2654 ring_one(qe
, outgoing
, &numbusies
);
2658 case AST_CONTROL_RINGING
:
2659 ast_verb(3, "%s is ringing\n", o
->chan
->name
);
2661 case AST_CONTROL_OFFHOOK
:
2662 /* Ignore going off hook */
2665 ast_debug(1, "Dunno what to do with control type %d\n", f
->subclass
);
2670 endtime
= (long) time(NULL
) - starttime
;
2671 rna(endtime
* 1000, qe
, on
, membername
);
2673 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2674 if (qe
->parent
->timeoutrestart
)
2676 ring_one(qe
, outgoing
, &numbusies
);
2683 if (!f
|| ((f
->frametype
== AST_FRAME_CONTROL
) && (f
->subclass
== AST_CONTROL_HANGUP
))) {
2687 if (f
->data
.uint32
) {
2688 in
->hangupcause
= f
->data
.uint32
;
2694 if ((f
->frametype
== AST_FRAME_DTMF
) && caller_disconnect
&& (f
->subclass
== '*')) {
2695 ast_verb(3, "User hit %c to disconnect call.\n", f
->subclass
);
2700 if ((f
->frametype
== AST_FRAME_DTMF
) && valid_exit(qe
, f
->subclass
)) {
2701 ast_verb(3, "User pressed digit: %c\n", f
->subclass
);
2703 *digit
= f
->subclass
;
2710 for (o
= start
; o
; o
= o
->call_next
)
2711 rna(orig
, qe
, o
->interface
, o
->member
->membername
);
2716 for (epollo
= outgoing
; epollo
; epollo
= epollo
->q_next
) {
2718 ast_poll_channel_del(in
, epollo
->chan
);
2726 * \brief Check if we should start attempting to call queue members.
2728 * The behavior of this function is dependent first on whether autofill is enabled
2729 * and second on whether the ring strategy is ringall. If autofill is not enabled,
2730 * then return true if we're the head of the queue. If autofill is enabled, then
2731 * we count the available members and see if the number of available members is enough
2732 * that given our position in the queue, we would theoretically be able to connect to
2733 * one of those available members
2735 static int is_our_turn(struct queue_ent
*qe
)
2737 struct queue_ent
*ch
;
2743 if (!qe
->parent
->autofill
) {
2744 /* Atomically read the parent head -- does not need a lock */
2745 ch
= qe
->parent
->head
;
2746 /* If we are now at the top of the head, break out */
2748 ast_debug(1, "It's our turn (%s).\n", qe
->chan
->name
);
2751 ast_debug(1, "It's not our turn (%s).\n", qe
->chan
->name
);
2756 /* This needs a lock. How many members are available to be served? */
2757 ao2_lock(qe
->parent
);
2759 ch
= qe
->parent
->head
;
2761 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2762 ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
2765 struct ao2_iterator mem_iter
= ao2_iterator_init(qe
->parent
->members
, 0);
2766 while ((cur
= ao2_iterator_next(&mem_iter
))) {
2767 switch (cur
->status
) {
2768 case AST_DEVICE_INUSE
:
2769 if (!qe
->parent
->ringinuse
)
2771 /* else fall through */
2772 case AST_DEVICE_NOT_INUSE
:
2773 case AST_DEVICE_UNKNOWN
:
2782 ast_debug(1, "There are %d available members.\n", avl
);
2784 while ((idx
< avl
) && (ch
) && (ch
!= qe
)) {
2790 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2791 if (ch
&& idx
< avl
) {
2792 ast_debug(1, "It's our turn (%s).\n", qe
->chan
->name
);
2795 ast_debug(1, "It's not our turn (%s).\n", qe
->chan
->name
);
2799 ao2_unlock(qe
->parent
);
2806 * \brief update rules for queues
2808 * Calculate min/max penalties making sure if relative they stay within bounds.
2809 * Update queues penalty and set dialplan vars, goto next list entry.
2811 static void update_qe_rule(struct queue_ent
*qe
)
2813 int max_penalty
= qe
->pr
->max_relative
? qe
->max_penalty
+ qe
->pr
->max_value
: qe
->pr
->max_value
;
2814 int min_penalty
= qe
->pr
->min_relative
? qe
->min_penalty
+ qe
->pr
->min_value
: qe
->pr
->min_value
;
2815 char max_penalty_str
[20], min_penalty_str
[20];
2816 /* a relative change to the penalty could put it below 0 */
2817 if (max_penalty
< 0)
2819 if (min_penalty
< 0)
2821 if (min_penalty
> max_penalty
)
2822 min_penalty
= max_penalty
;
2823 snprintf(max_penalty_str
, sizeof(max_penalty_str
), "%d", max_penalty
);
2824 snprintf(min_penalty_str
, sizeof(min_penalty_str
), "%d", min_penalty
);
2825 pbx_builtin_setvar_helper(qe
->chan
, "QUEUE_MAX_PENALTY", max_penalty_str
);
2826 pbx_builtin_setvar_helper(qe
->chan
, "QUEUE_MIN_PENALTY", min_penalty_str
);
2827 qe
->max_penalty
= max_penalty
;
2828 qe
->min_penalty
= min_penalty
;
2829 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe
->max_penalty
, qe
->min_penalty
, qe
->chan
->name
, qe
->pr
->time
);
2830 qe
->pr
= AST_LIST_NEXT(qe
->pr
, list
);
2833 /*! \brief The waiting areas for callers who are not actively calling members
2835 * This function is one large loop. This function will return if a caller
2836 * either exits the queue or it becomes that caller's turn to attempt calling
2837 * queue members. Inside the loop, we service the caller with periodic announcements,
2838 * holdtime announcements, etc. as configured in queues.conf
2840 * \retval 0 if the caller's turn has arrived
2841 * \retval -1 if the caller should exit the queue.
2843 static int wait_our_turn(struct queue_ent
*qe
, int ringing
, enum queue_result
*reason
)
2847 /* This is the holding pen for callers 2 through maxlen */
2849 enum queue_member_status stat
;
2851 if (is_our_turn(qe
))
2854 /* If we have timed out, break out */
2855 if (qe
->expire
&& (time(NULL
) > qe
->expire
)) {
2856 *reason
= QUEUE_TIMEOUT
;
2860 stat
= get_member_status(qe
->parent
, qe
->max_penalty
, qe
->min_penalty
);
2862 /* leave the queue if no agents, if enabled */
2863 if (qe
->parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
2864 *reason
= QUEUE_LEAVEEMPTY
;
2865 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2870 /* leave the queue if no reachable agents, if enabled */
2871 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)) {
2872 *reason
= QUEUE_LEAVEUNAVAIL
;
2873 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2877 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
2878 *reason
= QUEUE_LEAVEUNAVAIL
;
2879 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2884 /* Make a position announcement, if enabled */
2885 if (qe
->parent
->announcefrequency
&&
2886 (res
= say_position(qe
,ringing
)))
2889 /* Make a periodic announcement, if enabled */
2890 if (qe
->parent
->periodicannouncefrequency
&&
2891 (res
= say_periodic_announcement(qe
,ringing
)))
2894 /* see if we need to move to the next penalty level for this queue */
2895 while (qe
->pr
&& ((time(NULL
) - qe
->start
) > qe
->pr
->time
)) {
2899 /* Wait a second before checking again */
2900 if ((res
= ast_waitfordigit(qe
->chan
, RECHECK
* 1000))) {
2901 if (res
> 0 && !valid_exit(qe
, res
))
2912 * \brief update the queue status
2915 static int update_queue(struct call_queue
*q
, struct member
*member
, int callcompletedinsl
)
2918 struct call_queue
*qtmp
;
2919 struct ao2_iterator queue_iter
;
2921 if (shared_lastcall
) {
2922 queue_iter
= ao2_iterator_init(queues
, 0);
2923 while ((qtmp
= ao2_iterator_next(&queue_iter
))) {
2925 if ((mem
= ao2_find(qtmp
->members
, member
, OBJ_POINTER
))) {
2926 time(&mem
->lastcall
);
2936 time(&member
->lastcall
);
2938 member
->lastqueue
= q
;
2942 q
->callscompleted
++;
2943 if (callcompletedinsl
)
2944 q
->callscompletedinsl
++;
2949 /*! \brief Calculate the metric of each member in the outgoing callattempts
2951 * A numeric metric is given to each member depending on the ring strategy used
2952 * by the queue. Members with lower metrics will be called before members with
2954 * \retval -1 if penalties are exceeded
2955 * \retval 0 otherwise
2957 static int calc_metric(struct call_queue
*q
, struct member
*mem
, int pos
, struct queue_ent
*qe
, struct callattempt
*tmp
)
2959 if ((qe
->max_penalty
&& (mem
->penalty
> qe
->max_penalty
)) || (qe
->min_penalty
&& (mem
->penalty
< qe
->min_penalty
)))
2962 switch (q
->strategy
) {
2963 case QUEUE_STRATEGY_RINGALL
:
2964 /* Everyone equal, except for penalty */
2965 tmp
->metric
= mem
->penalty
* 1000000;
2967 case QUEUE_STRATEGY_LINEAR
:
2968 if (pos
< qe
->linpos
) {
2969 tmp
->metric
= 1000 + pos
;
2971 if (pos
> qe
->linpos
)
2972 /* Indicate there is another priority */
2976 tmp
->metric
+= mem
->penalty
* 1000000;
2978 case QUEUE_STRATEGY_RRMEMORY
:
2979 if (pos
< q
->rrpos
) {
2980 tmp
->metric
= 1000 + pos
;
2983 /* Indicate there is another priority */
2987 tmp
->metric
+= mem
->penalty
* 1000000;
2989 case QUEUE_STRATEGY_RANDOM
:
2990 tmp
->metric
= ast_random() % 1000;
2991 tmp
->metric
+= mem
->penalty
* 1000000;
2993 case QUEUE_STRATEGY_WRANDOM
:
2994 tmp
->metric
= ast_random() % ((1 + mem
->penalty
) * 1000);
2996 case QUEUE_STRATEGY_FEWESTCALLS
:
2997 tmp
->metric
= mem
->calls
;
2998 tmp
->metric
+= mem
->penalty
* 1000000;
3000 case QUEUE_STRATEGY_LEASTRECENT
:
3004 tmp
->metric
= 1000000 - (time(NULL
) - mem
->lastcall
);
3005 tmp
->metric
+= mem
->penalty
* 1000000;
3008 ast_log(LOG_WARNING
, "Can't calculate metric for unknown strategy %d\n", q
->strategy
);
3014 enum agent_complete_reason
{
3020 /*! \brief Send out AMI message with member call completion status information */
3021 static void send_agent_complete(const struct queue_ent
*qe
, const char *queuename
,
3022 const struct ast_channel
*peer
, const struct member
*member
, time_t callstart
,
3023 char *vars
, size_t vars_len
, enum agent_complete_reason rsn
)
3025 const char *reason
= NULL
; /* silence dumb compilers */
3027 if (!qe
->parent
->eventwhencalled
)
3038 reason
= "transfer";
3042 manager_event(EVENT_FLAG_AGENT
, "AgentComplete",
3047 "MemberName: %s\r\n"
3052 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3053 (long)(callstart
- qe
->start
), (long)(time(NULL
) - callstart
), reason
,
3054 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, vars_len
) : "");
3057 struct queue_transfer_ds
{
3058 struct queue_ent
*qe
;
3059 struct member
*member
;
3063 static void queue_transfer_destroy(void *data
)
3065 struct queue_transfer_ds
*qtds
= data
;
3069 /*! \brief a datastore used to help correctly log attended transfers of queue callers
3071 static const struct ast_datastore_info queue_transfer_info
= {
3072 .type
= "queue_transfer",
3073 .chan_fixup
= queue_transfer_fixup
,
3074 .destroy
= queue_transfer_destroy
,
3077 /*! \brief Log an attended transfer when a queue caller channel is masqueraded
3079 * When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when
3080 * the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan
3081 * to new_chan. This is why new_chan is referenced for exten, context, and datastore information.
3083 * At the end of this, we want to remove the datastore so that this fixup function is not called on any
3084 * future masquerades of the caller during the current call.
3086 static void queue_transfer_fixup(void *data
, struct ast_channel
*old_chan
, struct ast_channel
*new_chan
)
3088 struct queue_transfer_ds
*qtds
= data
;
3089 struct queue_ent
*qe
= qtds
->qe
;
3090 struct member
*member
= qtds
->member
;
3091 int callstart
= qtds
->starttime
;
3092 struct ast_datastore
*datastore
;
3094 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, member
->membername
, "TRANSFER", "%s|%s|%ld|%ld|%d",
3095 new_chan
->exten
, new_chan
->context
, (long) (callstart
- qe
->start
),
3096 (long) (time(NULL
) - callstart
), qe
->opos
);
3098 if (!(datastore
= ast_channel_datastore_find(new_chan
, &queue_transfer_info
, NULL
))) {
3099 ast_log(LOG_WARNING
, "Can't find the queue_transfer datastore.\n");
3103 ast_channel_datastore_remove(new_chan
, datastore
);
3104 ast_channel_datastore_free(datastore
);
3107 /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
3109 * When a caller is atxferred, then the queue_transfer_info datastore
3110 * is removed from the channel. If it's still there after the bridge is
3111 * broken, then the caller was not atxferred.
3113 static int attended_transfer_occurred(struct ast_channel
*chan
)
3115 return ast_channel_datastore_find(chan
, &queue_transfer_info
, NULL
) ? 0 : 1;
3118 /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
3120 static void setup_transfer_datastore(struct queue_ent
*qe
, struct member
*member
, int starttime
)
3122 struct ast_datastore
*ds
;
3123 struct queue_transfer_ds
*qtds
= ast_calloc(1, sizeof(*qtds
));
3126 ast_log(LOG_WARNING
, "Memory allocation error!\n");
3130 ast_channel_lock(qe
->chan
);
3131 if (!(ds
= ast_channel_datastore_alloc(&queue_transfer_info
, NULL
))) {
3132 ast_channel_unlock(qe
->chan
);
3133 ast_log(LOG_WARNING
, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
3138 /* This member is refcounted in try_calling, so no need to add it here, too */
3139 qtds
->member
= member
;
3140 qtds
->starttime
= starttime
;
3142 ast_channel_datastore_add(qe
->chan
, ds
);
3143 ast_channel_unlock(qe
->chan
);
3146 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
3148 * Here is the process of this function
3149 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
3150 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
3151 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
3152 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
3153 * during each iteration, we call calc_metric to determine which members should be rung when.
3154 * 3. Call ring_one to place a call to the appropriate member(s)
3155 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
3156 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
3157 * 6. Start the monitor or mixmonitor if the option is set
3158 * 7. Remove the caller from the queue to allow other callers to advance
3159 * 8. Bridge the call.
3160 * 9. Do any post processing after the call has disconnected.
3162 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
3163 * \param[in] options the options passed as the third parameter to the Queue() application
3164 * \param[in] announceoverride filename to play to user when waiting
3165 * \param[in] url the url passed as the fourth parameter to the Queue() application
3166 * \param[in,out] tries the number of times we have tried calling queue members
3167 * \param[out] noption set if the call to Queue() has the 'n' option set.
3168 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
3169 * \param[in] macro the macro passed as the sixth parameter to the Queue() application
3170 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
3171 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
3173 static int try_calling(struct queue_ent
*qe
, const char *options
, char *announceoverride
, const char *url
, int *tries
, int *noption
, const char *agi
, const char *macro
, const char *gosub
, int ringing
)
3176 struct callattempt
*outgoing
= NULL
; /* the list of calls we are building */
3178 char oldexten
[AST_MAX_EXTENSION
]="";
3179 char oldcontext
[AST_MAX_CONTEXT
]="";
3180 char queuename
[256]="";
3181 char interfacevar
[256]="";
3182 struct ast_channel
*peer
;
3183 struct ast_channel
*which
;
3184 struct callattempt
*lpeer
;
3185 struct member
*member
;
3186 struct ast_app
*app
;
3187 int res
= 0, bridge
= 0;
3190 char *announce
= NULL
;
3193 time_t now
= time(NULL
);
3194 struct ast_bridge_config bridge_config
;
3195 char nondataquality
= 1;
3196 char *agiexec
= NULL
;
3197 char *macroexec
= NULL
;
3198 char *gosubexec
= NULL
;
3200 const char *monitorfilename
;
3201 const char *monitor_exec
;
3202 const char *monitor_options
;
3203 char tmpid
[256], tmpid2
[256];
3204 char meid
[1024], meid2
[1024];
3205 char mixmonargs
[1512];
3206 struct ast_app
*mixmonapp
= NULL
;
3209 int forwardsallowed
= 1;
3210 int callcompletedinsl
;
3211 struct ao2_iterator memi
;
3212 struct ast_datastore
*datastore
;
3214 ast_channel_lock(qe
->chan
);
3215 datastore
= ast_channel_datastore_find(qe
->chan
, &dialed_interface_info
, NULL
);
3216 ast_channel_unlock(qe
->chan
);
3218 memset(&bridge_config
, 0, sizeof(bridge_config
));
3223 for (; options
&& *options
; options
++)
3226 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_REDIRECT
);
3229 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_REDIRECT
);
3232 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMON
);
3235 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMON
);
3241 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_DISCONNECT
);
3244 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
);
3247 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_PARKCALL
);
3250 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_PARKCALL
);
3253 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
|| qe
->parent
->strategy
== QUEUE_STRATEGY_LINEAR
)
3256 *tries
= qe
->parent
->membercount
;
3260 forwardsallowed
= 0;
3263 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMIXMON
);
3266 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMIXMON
);
3271 /* Hold the lock while we setup the outgoing calls */
3274 ao2_lock(qe
->parent
);
3275 ast_debug(1, "%s is trying to call a queue member.\n",
3277 ast_copy_string(queuename
, qe
->parent
->name
, sizeof(queuename
));
3278 if (!ast_strlen_zero(qe
->announce
))
3279 announce
= qe
->announce
;
3280 if (!ast_strlen_zero(announceoverride
))
3281 announce
= announceoverride
;
3283 memi
= ao2_iterator_init(qe
->parent
->members
, 0);
3284 while ((cur
= ao2_iterator_next(&memi
))) {
3285 struct callattempt
*tmp
= ast_calloc(1, sizeof(*tmp
));
3286 struct ast_dialed_interface
*di
;
3287 AST_LIST_HEAD(, ast_dialed_interface
) *dialed_interfaces
;
3290 ao2_unlock(qe
->parent
);
3296 if (!(datastore
= ast_channel_datastore_alloc(&dialed_interface_info
, NULL
))) {
3298 ao2_unlock(qe
->parent
);
3304 datastore
->inheritance
= DATASTORE_INHERIT_FOREVER
;
3305 if (!(dialed_interfaces
= ast_calloc(1, sizeof(*dialed_interfaces
)))) {
3307 ao2_unlock(&qe
->parent
);
3313 datastore
->data
= dialed_interfaces
;
3314 AST_LIST_HEAD_INIT(dialed_interfaces
);
3316 ast_channel_lock(qe
->chan
);
3317 ast_channel_datastore_add(qe
->chan
, datastore
);
3318 ast_channel_unlock(qe
->chan
);
3320 dialed_interfaces
= datastore
->data
;
3322 AST_LIST_LOCK(dialed_interfaces
);
3323 AST_LIST_TRAVERSE(dialed_interfaces
, di
, list
) {
3324 if (!strcasecmp(cur
->interface
, di
->interface
)) {
3325 ast_log(LOG_DEBUG
, "Skipping dialing interface '%s' since it has already been dialed\n",
3330 AST_LIST_UNLOCK(dialed_interfaces
);
3337 /* It is always ok to dial a Local interface. We only keep track of
3338 * which "real" interfaces have been dialed. The Local channel will
3339 * inherit this list so that if it ends up dialing a real interface,
3340 * it won't call one that has already been called. */
3341 if (strncasecmp(cur
->interface
, "Local/", 6)) {
3342 if (!(di
= ast_calloc(1, sizeof(*di
) + strlen(cur
->interface
)))) {
3344 ao2_unlock(qe
->parent
);
3350 strcpy(di
->interface
, cur
->interface
);
3352 AST_LIST_LOCK(dialed_interfaces
);
3353 AST_LIST_INSERT_TAIL(dialed_interfaces
, di
, list
);
3354 AST_LIST_UNLOCK(dialed_interfaces
);
3357 tmp
->stillgoing
= -1;
3359 tmp
->oldstatus
= cur
->status
;
3360 tmp
->lastcall
= cur
->lastcall
;
3361 tmp
->lastqueue
= cur
->lastqueue
;
3362 ast_copy_string(tmp
->interface
, cur
->interface
, sizeof(tmp
->interface
));
3363 /* Special case: If we ring everyone, go ahead and ring them, otherwise
3364 just calculate their metric for the appropriate strategy */
3365 if (!calc_metric(qe
->parent
, cur
, x
++, qe
, tmp
)) {
3366 /* Put them in the list of outgoing thingies... We're ready now.
3367 XXX If we're forcibly removed, these outgoing calls won't get
3369 tmp
->q_next
= outgoing
;
3371 /* If this line is up, don't try anybody else */
3372 if (outgoing
->chan
&& (outgoing
->chan
->_state
== AST_STATE_UP
))
3380 if (qe
->expire
&& (!qe
->parent
->timeout
|| (qe
->parent
->timeoutpriority
== TIMEOUT_PRIORITY_APP
&& (qe
->expire
- now
) <= qe
->parent
->timeout
)))
3381 to
= (qe
->expire
- now
) * 1000;
3383 to
= (qe
->parent
->timeout
) ? qe
->parent
->timeout
* 1000 : -1;
3386 ao2_unlock(qe
->parent
);
3387 ring_one(qe
, outgoing
, &numbusies
);
3390 lpeer
= wait_for_answer(qe
, outgoing
, &to
, &digit
, numbusies
, ast_test_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
), forwardsallowed
);
3391 /* The ast_channel_datastore_remove() function could fail here if the
3392 * datastore was moved to another channel during a masquerade. If this is
3393 * the case, don't free the datastore here because later, when the channel
3394 * to which the datastore was moved hangs up, it will attempt to free this
3395 * datastore again, causing a crash
3397 if (datastore
&& !ast_channel_datastore_remove(qe
->chan
, datastore
)) {
3398 ast_channel_datastore_free(datastore
);
3400 ao2_lock(qe
->parent
);
3401 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
) {
3402 store_next_rr(qe
, outgoing
);
3404 if (qe
->parent
->strategy
== QUEUE_STRATEGY_LINEAR
) {
3405 store_next_lin(qe
, outgoing
);
3407 ao2_unlock(qe
->parent
);
3408 peer
= lpeer
? lpeer
->chan
: NULL
;
3412 /* Must gotten hung up */
3415 /* User exited by pressing a digit */
3419 ast_debug(1, "%s: Nobody answered.\n", qe
->chan
->name
);
3420 } else { /* peer is valid */
3421 /* Ah ha! Someone answered within the desired timeframe. Of course after this
3422 we will always return with -1 so that it is hung up properly after the
3424 if (!strcmp(qe
->chan
->tech
->type
, "DAHDI"))
3425 ast_channel_setoption(qe
->chan
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
3426 if (!strcmp(peer
->tech
->type
, "DAHDI"))
3427 ast_channel_setoption(peer
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
3428 /* Update parameters for the queue */
3430 recalc_holdtime(qe
, (now
- qe
->start
));
3431 ao2_lock(qe
->parent
);
3432 callcompletedinsl
= ((now
- qe
->start
) <= qe
->parent
->servicelevel
);
3433 ao2_unlock(qe
->parent
);
3434 member
= lpeer
->member
;
3435 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
3437 hangupcalls(outgoing
, peer
);
3439 if (announce
|| qe
->parent
->reportholdtime
|| qe
->parent
->memberdelay
) {
3442 res2
= ast_autoservice_start(qe
->chan
);
3444 if (qe
->parent
->memberdelay
) {
3445 ast_log(LOG_NOTICE
, "Delaying member connect for %d seconds\n", qe
->parent
->memberdelay
);
3446 res2
|= ast_safe_sleep(peer
, qe
->parent
->memberdelay
* 1000);
3448 if (!res2
&& announce
) {
3449 play_file(peer
, announce
);
3451 if (!res2
&& qe
->parent
->reportholdtime
) {
3452 if (!play_file(peer
, qe
->parent
->sound_reporthold
)) {
3453 int holdtime
, holdtimesecs
;
3456 holdtime
= abs((now
- qe
->start
) / 60);
3457 holdtimesecs
= abs((now
- qe
->start
));
3458 if (holdtime
== 1) {
3459 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3460 play_file(peer
, qe
->parent
->sound_minute
);
3462 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3463 play_file(peer
, qe
->parent
->sound_minutes
);
3465 if (holdtimesecs
> 1) {
3466 ast_say_number(peer
, holdtimesecs
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3467 play_file(peer
, qe
->parent
->sound_seconds
);
3472 res2
|= ast_autoservice_stop(qe
->chan
);
3473 if (ast_check_hangup(peer
)) {
3474 /* Agent must have hung up */
3475 ast_log(LOG_WARNING
, "Agent on %s hungup on the customer.\n", peer
->name
);
3476 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "AGENTDUMP", "%s", "");
3477 if (qe
->parent
->eventwhencalled
)
3478 manager_event(EVENT_FLAG_AGENT
, "AgentDump",
3483 "MemberName: %s\r\n"
3485 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3486 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
3488 ao2_ref(member
, -1);
3491 /* Caller must have hung up just before being connected*/
3492 ast_log(LOG_NOTICE
, "Caller was about to talk to agent on %s but the caller hungup.\n", peer
->name
);
3493 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "ABANDON", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
3494 record_abandoned(qe
);
3496 ao2_ref(member
, -1);
3500 /* Stop music on hold */
3502 ast_indicate(qe
->chan
,-1);
3504 ast_moh_stop(qe
->chan
);
3505 /* If appropriate, log that we have a destination channel */
3507 ast_cdr_setdestchan(qe
->chan
->cdr
, peer
->name
);
3508 /* Make sure channels are compatible */
3509 res
= ast_channel_make_compatible(qe
->chan
, peer
);
3511 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "SYSCOMPAT", "%s", "");
3512 ast_log(LOG_WARNING
, "Had to drop call because I couldn't make %s compatible with %s\n", qe
->chan
->name
, peer
->name
);
3513 record_abandoned(qe
);
3515 ao2_ref(member
, -1);
3519 /* Play announcement to the caller telling it's his turn if defined */
3520 if (!ast_strlen_zero(qe
->parent
->sound_callerannounce
)) {
3521 if (play_file(qe
->chan
, qe
->parent
->sound_callerannounce
))
3522 ast_log(LOG_WARNING
, "Announcement file '%s' is unavailable, continuing anyway...\n", qe
->parent
->sound_callerannounce
);
3525 ao2_lock(qe
->parent
);
3526 /* if setinterfacevar is defined, make member variables available to the channel */
3527 /* use pbx_builtin_setvar to set a load of variables with one call */
3528 if (qe
->parent
->setinterfacevar
) {
3529 snprintf(interfacevar
, sizeof(interfacevar
), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
3530 member
->interface
, member
->membername
, member
->calls
, (long)member
->lastcall
, member
->penalty
, member
->dynamic
, member
->realtime
);
3531 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
3534 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
3535 /* use pbx_builtin_setvar to set a load of variables with one call */
3536 if (qe
->parent
->setqueueentryvar
) {
3537 snprintf(interfacevar
, sizeof(interfacevar
), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
3538 (long) time(NULL
) - qe
->start
, qe
->opos
);
3539 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
3542 /* try to set queue variables if configured to do so*/
3543 set_queue_variables(qe
);
3544 ao2_unlock(qe
->parent
);
3546 ast_channel_lock(qe
->chan
);
3547 if ((monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME"))) {
3548 monitorfilename
= ast_strdupa(monitorfilename
);
3550 ast_channel_unlock(qe
->chan
);
3551 /* Begin Monitoring */
3552 if (qe
->parent
->monfmt
&& *qe
->parent
->monfmt
) {
3553 if (!qe
->parent
->montype
) {
3554 const char *monexec
, *monargs
;
3555 ast_debug(1, "Starting Monitor as requested.\n");
3556 ast_channel_lock(qe
->chan
);
3557 if ((monexec
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC")) || (monargs
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC_ARGS"))) {
3559 monexec
= monexec
? ast_strdupa(monexec
) : NULL
;
3563 ast_channel_unlock(qe
->chan
);
3564 if (monitorfilename
)
3565 ast_monitor_start(which
, qe
->parent
->monfmt
, monitorfilename
, 1, X_REC_IN
| X_REC_OUT
);
3566 else if (qe
->chan
->cdr
)
3567 ast_monitor_start(which
, qe
->parent
->monfmt
, qe
->chan
->cdr
->uniqueid
, 1, X_REC_IN
| X_REC_OUT
);
3569 /* Last ditch effort -- no CDR, make up something */
3570 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
3571 ast_monitor_start(which
, qe
->parent
->monfmt
, tmpid
, 1, X_REC_IN
| X_REC_OUT
);
3573 if (!ast_strlen_zero(monexec
)) {
3574 ast_monitor_setjoinfiles(which
, 1);
3577 mixmonapp
= pbx_findapp("MixMonitor");
3580 ast_debug(1, "Starting MixMonitor as requested.\n");
3581 if (!monitorfilename
) {
3583 ast_copy_string(tmpid
, qe
->chan
->cdr
->uniqueid
, sizeof(tmpid
));
3585 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
3587 const char *m
= monitorfilename
;
3588 for (p
= tmpid2
; p
< tmpid2
+ sizeof(tmpid2
) - 1; p
++, m
++) {
3591 if (*(m
+ 1) == '{')
3603 if (p
== tmpid2
+ sizeof(tmpid2
))
3604 tmpid2
[sizeof(tmpid2
) - 1] = '\0';
3606 pbx_substitute_variables_helper(qe
->chan
, tmpid2
, tmpid
, sizeof(tmpid
) - 1);
3609 ast_channel_lock(qe
->chan
);
3610 if ((monitor_exec
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC"))) {
3611 monitor_exec
= ast_strdupa(monitor_exec
);
3613 if ((monitor_options
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_OPTIONS"))) {
3614 monitor_options
= ast_strdupa(monitor_options
);
3616 monitor_options
= "";
3618 ast_channel_unlock(qe
->chan
);
3621 const char *m
= monitor_exec
;
3622 for (p
= meid2
; p
< meid2
+ sizeof(meid2
) - 1; p
++, m
++) {
3625 if (*(m
+ 1) == '{')
3637 if (p
== meid2
+ sizeof(meid2
))
3638 meid2
[sizeof(meid2
) - 1] = '\0';
3640 pbx_substitute_variables_helper(qe
->chan
, meid2
, meid
, sizeof(meid
) - 1);
3643 snprintf(tmpid2
, sizeof(tmpid2
), "%s.%s", tmpid
, qe
->parent
->monfmt
);
3645 if (!ast_strlen_zero(monitor_exec
))
3646 snprintf(mixmonargs
, sizeof(mixmonargs
), "%s,b%s,%s", tmpid2
, monitor_options
, monitor_exec
);
3648 snprintf(mixmonargs
, sizeof(mixmonargs
), "%s,b%s", tmpid2
, monitor_options
);
3650 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs
);
3651 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3653 ast_set_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
3654 ret
= pbx_exec(qe
->chan
, mixmonapp
, mixmonargs
);
3656 ast_clear_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
3659 ast_log(LOG_WARNING
, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3663 /* Drop out of the queue at this point, to prepare for next caller */
3665 if (!ast_strlen_zero(url
) && ast_channel_supports_html(peer
)) {
3666 ast_debug(1, "app_queue: sendurl=%s.\n", url
);
3667 ast_channel_sendurl(peer
, url
);
3670 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
3671 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
3672 if (!ast_strlen_zero(macro
)) {
3673 macroexec
= ast_strdupa(macro
);
3675 if (qe
->parent
->membermacro
)
3676 macroexec
= ast_strdupa(qe
->parent
->membermacro
);
3679 if (!ast_strlen_zero(macroexec
)) {
3680 ast_debug(1, "app_queue: macro=%s.\n", macroexec
);
3682 res
= ast_autoservice_start(qe
->chan
);
3684 ast_log(LOG_ERROR
, "Unable to start autoservice on calling channel\n");
3688 app
= pbx_findapp("Macro");
3691 res
= pbx_exec(qe
->chan
, app
, macroexec
);
3692 ast_debug(1, "Macro exited with status %d\n", res
);
3695 ast_log(LOG_ERROR
, "Could not find application Macro\n");
3699 if (ast_autoservice_stop(qe
->chan
) < 0) {
3700 ast_log(LOG_ERROR
, "Could not stop autoservice on calling channel\n");
3705 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
3706 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
3707 if (!ast_strlen_zero(gosub
)) {
3708 gosubexec
= ast_strdupa(gosub
);
3710 if (qe
->parent
->membergosub
)
3711 gosubexec
= ast_strdupa(qe
->parent
->membergosub
);
3714 if (!ast_strlen_zero(gosubexec
)) {
3716 ast_log(LOG_DEBUG
, "app_queue: gosub=%s.\n", gosubexec
);
3718 res
= ast_autoservice_start(qe
->chan
);
3720 ast_log(LOG_ERROR
, "Unable to start autoservice on calling channel\n");
3724 app
= pbx_findapp("Gosub");
3727 char *gosub_args
, *gosub_argstart
;
3729 /* Set where we came from */
3730 ast_copy_string(qe
->chan
->context
, "app_dial_gosub_virtual_context", sizeof(qe
->chan
->context
));
3731 ast_copy_string(qe
->chan
->exten
, "s", sizeof(qe
->chan
->exten
));
3732 qe
->chan
->priority
= 0;
3734 gosub_argstart
= strchr(gosubexec
, ',');
3735 if (gosub_argstart
) {
3736 *gosub_argstart
= 0;
3737 asprintf(&gosub_args
, "%s,s,1(%s)", gosubexec
, gosub_argstart
+ 1);
3738 *gosub_argstart
= '|';
3740 asprintf(&gosub_args
, "%s,s,1", gosubexec
);
3743 res
= pbx_exec(qe
->chan
, app
, gosub_args
);
3744 ast_pbx_run(qe
->chan
);
3747 ast_log(LOG_DEBUG
, "Gosub exited with status %d\n", res
);
3749 ast_log(LOG_ERROR
, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
3753 ast_log(LOG_ERROR
, "Could not find application Gosub\n");
3757 if (ast_autoservice_stop(qe
->chan
) < 0) {
3758 ast_log(LOG_ERROR
, "Could not stop autoservice on calling channel\n");
3763 if (!ast_strlen_zero(agi
)) {
3764 ast_debug(1, "app_queue: agi=%s.\n", agi
);
3765 app
= pbx_findapp("agi");
3767 agiexec
= ast_strdupa(agi
);
3768 ret
= pbx_exec(qe
->chan
, app
, agiexec
);
3770 ast_log(LOG_WARNING
, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3773 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "CONNECT", "%ld|%s|%ld", (long) time(NULL
) - qe
->start
, peer
->uniqueid
,
3774 (long)(orig
- to
> 0 ? (orig
- to
) / 1000 : 0));
3775 if (update_cdr
&& qe
->chan
->cdr
)
3776 ast_copy_string(qe
->chan
->cdr
->dstchannel
, member
->membername
, sizeof(qe
->chan
->cdr
->dstchannel
));
3777 if (qe
->parent
->eventwhencalled
)
3778 manager_event(EVENT_FLAG_AGENT
, "AgentConnect",
3783 "MemberName: %s\r\n"
3785 "BridgedChannel: %s\r\n"
3788 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3789 (long) time(NULL
) - qe
->start
, peer
->uniqueid
, (long)(orig
- to
> 0 ? (orig
- to
) / 1000 : 0),
3790 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
3791 ast_copy_string(oldcontext
, qe
->chan
->context
, sizeof(oldcontext
));
3792 ast_copy_string(oldexten
, qe
->chan
->exten
, sizeof(oldexten
));
3794 setup_transfer_datastore(qe
, member
, callstart
);
3795 bridge
= ast_bridge_call(qe
->chan
,peer
, &bridge_config
);
3797 /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
3798 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
3800 if (!attended_transfer_occurred(qe
->chan
)) {
3801 struct ast_datastore
*transfer_ds
;
3802 if (strcasecmp(oldcontext
, qe
->chan
->context
) || strcasecmp(oldexten
, qe
->chan
->exten
)) {
3803 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "TRANSFER", "%s|%s|%ld|%ld|%d",
3804 qe
->chan
->exten
, qe
->chan
->context
, (long) (callstart
- qe
->start
),
3805 (long) (time(NULL
) - callstart
), qe
->opos
);
3806 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), TRANSFER
);
3807 } else if (ast_check_hangup(qe
->chan
)) {
3808 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETECALLER", "%ld|%ld|%d",
3809 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
3810 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), CALLER
);
3812 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETEAGENT", "%ld|%ld|%d",
3813 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
3814 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), AGENT
);
3816 ast_channel_lock(qe
->chan
);
3817 transfer_ds
= ast_channel_datastore_find(qe
->chan
, &queue_transfer_info
, NULL
);
3819 ast_channel_datastore_remove(qe
->chan
, transfer_ds
);
3820 ast_channel_datastore_free(transfer_ds
);
3822 ast_channel_unlock(qe
->chan
);
3825 if (bridge
!= AST_PBX_NO_HANGUP_PEER
)
3827 update_queue(qe
->parent
, member
, callcompletedinsl
);
3828 res
= bridge
? bridge
: 1;
3829 ao2_ref(member
, -1);
3832 hangupcalls(outgoing
, NULL
);
3837 static int wait_a_bit(struct queue_ent
*qe
)
3839 /* Don't need to hold the lock while we setup the outgoing calls */
3840 int retrywait
= qe
->parent
->retry
* 1000;
3842 int res
= ast_waitfordigit(qe
->chan
, retrywait
);
3843 if (res
> 0 && !valid_exit(qe
, res
))
3849 static struct member
*interface_exists(struct call_queue
*q
, const char *interface
)
3852 struct ao2_iterator mem_iter
;
3857 mem_iter
= ao2_iterator_init(q
->members
, 0);
3858 while ((mem
= ao2_iterator_next(&mem_iter
))) {
3859 if (!strcasecmp(interface
, mem
->interface
))
3868 /*! \brief Dump all members in a specific queue to the database
3870 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
3872 static void dump_queue_members(struct call_queue
*pm_queue
)
3874 struct member
*cur_member
;
3875 char value
[PM_MAX_LEN
];
3878 struct ao2_iterator mem_iter
;
3880 memset(value
, 0, sizeof(value
));
3885 mem_iter
= ao2_iterator_init(pm_queue
->members
, 0);
3886 while ((cur_member
= ao2_iterator_next(&mem_iter
))) {
3887 if (!cur_member
->dynamic
) {
3888 ao2_ref(cur_member
, -1);
3892 res
= snprintf(value
+ value_len
, sizeof(value
) - value_len
, "%s%s;%d;%d;%s;%s",
3893 value_len
? "|" : "", cur_member
->interface
, cur_member
->penalty
, cur_member
->paused
, cur_member
->membername
, cur_member
->state_interface
);
3895 ao2_ref(cur_member
, -1);
3897 if (res
!= strlen(value
+ value_len
)) {
3898 ast_log(LOG_WARNING
, "Could not create persistent member string, out of space\n");
3904 if (value_len
&& !cur_member
) {
3905 if (ast_db_put(pm_family
, pm_queue
->name
, value
))
3906 ast_log(LOG_WARNING
, "failed to create persistent dynamic entry!\n");
3908 /* Delete the entry if the queue is empty or there is an error */
3909 ast_db_del(pm_family
, pm_queue
->name
);
3912 /*! \brief Remove member from queue
3913 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3914 * \retval RES_NOSUCHQUEUE queue does not exist
3915 * \retval RES_OKAY removed member from queue
3916 * \retval RES_EXISTS queue exists but no members
3918 static int remove_from_queue(const char *queuename
, const char *interface
)
3920 struct call_queue
*q
, tmpq
= {
3923 struct member
*mem
, tmpmem
;
3924 int res
= RES_NOSUCHQUEUE
;
3926 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
3927 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
3929 if ((mem
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
))) {
3930 /* XXX future changes should beware of this assumption!! */
3931 if (!mem
->dynamic
) {
3934 return RES_NOT_DYNAMIC
;
3937 manager_event(EVENT_FLAG_AGENT
, "QueueMemberRemoved",
3940 "MemberName: %s\r\n",
3941 q
->name
, mem
->interface
, mem
->membername
);
3942 ao2_unlink(q
->members
, mem
);
3943 remove_from_interfaces(mem
->state_interface
);
3946 if (queue_persistent_members
)
3947 dump_queue_members(q
);
3960 /*! \brief Add member to queue
3961 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3962 * \retval RES_NOSUCHQUEUE queue does not exist
3963 * \retval RES_OKAY added member from queue
3964 * \retval RES_EXISTS queue exists but no members
3965 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
3967 static int add_to_queue(const char *queuename
, const char *interface
, const char *membername
, int penalty
, int paused
, int dump
, const char *state_interface
)
3969 struct call_queue
*q
;
3970 struct member
*new_member
, *old_member
;
3971 int res
= RES_NOSUCHQUEUE
;
3973 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
3974 * short-circuits if the queue is already in memory. */
3975 if (!(q
= load_realtime_queue(queuename
)))
3981 if ((old_member
= interface_exists(q
, interface
)) == NULL
) {
3982 if ((new_member
= create_queue_member(interface
, membername
, penalty
, paused
, state_interface
))) {
3983 add_to_interfaces(new_member
->state_interface
);
3984 new_member
->dynamic
= 1;
3985 ao2_link(q
->members
, new_member
);
3987 manager_event(EVENT_FLAG_AGENT
, "QueueMemberAdded",
3990 "MemberName: %s\r\n"
3991 "Membership: %s\r\n"
3993 "CallsTaken: %d\r\n"
3997 q
->name
, new_member
->interface
, new_member
->membername
,
3999 new_member
->penalty
, new_member
->calls
, (int) new_member
->lastcall
,
4000 new_member
->status
, new_member
->paused
);
4002 ao2_ref(new_member
, -1);
4006 dump_queue_members(q
);
4010 res
= RES_OUTOFMEMORY
;
4013 ao2_ref(old_member
, -1);
4022 static int set_member_paused(const char *queuename
, const char *interface
, const char *reason
, int paused
)
4025 struct call_queue
*q
;
4027 struct ao2_iterator queue_iter
;
4030 /* Special event for when all queues are paused - individual events still generated */
4031 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
4032 if (ast_strlen_zero(queuename
))
4033 ast_queue_log("NONE", "NONE", interface
, (paused
? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
4035 queue_iter
= ao2_iterator_init(queues
, 0);
4036 while ((q
= ao2_iterator_next(&queue_iter
))) {
4038 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
4039 if ((mem
= interface_exists(q
, interface
))) {
4040 if (mem
->paused
== paused
) {
4041 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused
? "" : "un"), (paused
? "" : "un"), q
->name
, interface
);
4045 if (mem
->realtime
) {
4046 failed
= update_realtime_member_field(mem
, q
->name
, "paused", paused
? "1" : "0");
4050 ast_log(LOG_WARNING
, "Failed %spausing realtime queue member %s:%s\n", (paused
? "" : "un"), q
->name
, interface
);
4055 mem
->paused
= paused
;
4057 if (queue_persistent_members
)
4058 dump_queue_members(q
);
4060 ast_queue_log(q
->name
, "NONE", mem
->membername
, (paused
? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason
, ""));
4062 if (!ast_strlen_zero(reason
)) {
4063 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
4066 "MemberName: %s\r\n"
4069 q
->name
, mem
->interface
, mem
->membername
, paused
, reason
);
4071 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
4074 "MemberName: %s\r\n"
4076 q
->name
, mem
->interface
, mem
->membername
, paused
);
4082 if (!ast_strlen_zero(queuename
) && !strcasecmp(queuename
, q
->name
)) {
4092 return found
? RESULT_SUCCESS
: RESULT_FAILURE
;
4095 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
4096 static int set_member_penalty(char *queuename
, char *interface
, int penalty
)
4098 int foundinterface
= 0, foundqueue
= 0;
4099 struct call_queue
*q
;
4101 struct ao2_iterator queue_iter
;
4104 ast_log(LOG_ERROR
, "Invalid penalty (%d)\n", penalty
);
4105 return RESULT_FAILURE
;
4108 queue_iter
= ao2_iterator_init(queues
, 0);
4109 while ((q
= ao2_iterator_next(&queue_iter
))) {
4111 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
4113 if ((mem
= interface_exists(q
, interface
))) {
4115 mem
->penalty
= penalty
;
4117 ast_queue_log(q
->name
, "NONE", interface
, "PENALTY", "%d", penalty
);
4118 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPenalty",
4122 q
->name
, mem
->interface
, penalty
);
4130 if (foundinterface
) {
4131 return RESULT_SUCCESS
;
4132 } else if (!foundqueue
) {
4133 ast_log (LOG_ERROR
, "Invalid queuename\n");
4135 ast_log (LOG_ERROR
, "Invalid interface\n");
4138 return RESULT_FAILURE
;
4141 /* \brief Gets members penalty.
4142 * \return Return the members penalty or RESULT_FAILURE on error.
4144 static int get_member_penalty(char *queuename
, char *interface
)
4146 int foundqueue
= 0, penalty
;
4147 struct call_queue
*q
, tmpq
= {
4152 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4155 if ((mem
= interface_exists(q
, interface
))) {
4156 penalty
= mem
->penalty
;
4165 /* some useful debuging */
4167 ast_log (LOG_ERROR
, "Invalid queuename\n");
4169 ast_log (LOG_ERROR
, "Invalid interface\n");
4171 return RESULT_FAILURE
;
4174 /*! \brief Reload dynamic queue members persisted into the astdb */
4175 static void reload_queue_members(void)
4178 const char *queue_name
;
4181 char *membername
= NULL
;
4182 char *state_interface
;
4187 struct ast_db_entry
*db_tree
;
4188 struct ast_db_entry
*entry
;
4189 struct call_queue
*cur_queue
;
4190 char queue_data
[PM_MAX_LEN
];
4194 /* Each key in 'pm_family' is the name of a queue */
4195 db_tree
= ast_db_gettree(pm_family
, NULL
);
4196 for (entry
= db_tree
; entry
; entry
= entry
->next
) {
4198 queue_name
= entry
->key
+ strlen(pm_family
) + 2;
4201 struct call_queue tmpq
= {
4204 cur_queue
= ao2_find(queues
, &tmpq
, OBJ_POINTER
);
4208 cur_queue
= load_realtime_queue(queue_name
);
4211 /* If the queue no longer exists, remove it from the
4213 ast_log(LOG_WARNING
, "Error loading persistent queue: '%s': it does not exist\n", queue_name
);
4214 ast_db_del(pm_family
, queue_name
);
4218 if (ast_db_get(pm_family
, queue_name
, queue_data
, PM_MAX_LEN
)) {
4219 queue_unref(cur_queue
);
4223 cur_ptr
= queue_data
;
4224 while ((member
= strsep(&cur_ptr
, ",|"))) {
4225 if (ast_strlen_zero(member
))
4228 interface
= strsep(&member
, ";");
4229 penalty_tok
= strsep(&member
, ";");
4230 paused_tok
= strsep(&member
, ";");
4231 membername
= strsep(&member
, ";");
4232 state_interface
= strsep(&member
, ";");
4235 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (penalty)\n", queue_name
);
4238 penalty
= strtol(penalty_tok
, NULL
, 10);
4239 if (errno
== ERANGE
) {
4240 ast_log(LOG_WARNING
, "Error converting penalty: %s: Out of range.\n", penalty_tok
);
4245 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (paused)\n", queue_name
);
4248 paused
= strtol(paused_tok
, NULL
, 10);
4249 if ((errno
== ERANGE
) || paused
< 0 || paused
> 1) {
4250 ast_log(LOG_WARNING
, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok
);
4254 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name
, interface
, membername
, penalty
, paused
);
4256 if (add_to_queue(queue_name
, interface
, membername
, penalty
, paused
, 0, state_interface
) == RES_OUTOFMEMORY
) {
4257 ast_log(LOG_ERROR
, "Out of Memory when reloading persistent queue member\n");
4261 queue_unref(cur_queue
);
4266 ast_log(LOG_NOTICE
, "Queue members successfully reloaded from database.\n");
4267 ast_db_freetree(db_tree
);
4271 /*! \brief PauseQueueMember application */
4272 static int pqm_exec(struct ast_channel
*chan
, void *data
)
4275 AST_DECLARE_APP_ARGS(args
,
4276 AST_APP_ARG(queuename
);
4277 AST_APP_ARG(interface
);
4278 AST_APP_ARG(options
);
4279 AST_APP_ARG(reason
);
4282 if (ast_strlen_zero(data
)) {
4283 ast_log(LOG_WARNING
, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
4287 parse
= ast_strdupa(data
);
4289 AST_STANDARD_APP_ARGS(args
, parse
);
4291 if (ast_strlen_zero(args
.interface
)) {
4292 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4296 if (set_member_paused(args
.queuename
, args
.interface
, args
.reason
, 1)) {
4297 ast_log(LOG_WARNING
, "Attempt to pause interface %s, not found\n", args
.interface
);
4298 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "NOTFOUND");
4302 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "PAUSED");
4307 /*! \brief UnPauseQueueMember application */
4308 static int upqm_exec(struct ast_channel
*chan
, void *data
)
4311 AST_DECLARE_APP_ARGS(args
,
4312 AST_APP_ARG(queuename
);
4313 AST_APP_ARG(interface
);
4314 AST_APP_ARG(options
);
4315 AST_APP_ARG(reason
);
4318 if (ast_strlen_zero(data
)) {
4319 ast_log(LOG_WARNING
, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
4323 parse
= ast_strdupa(data
);
4325 AST_STANDARD_APP_ARGS(args
, parse
);
4327 if (ast_strlen_zero(args
.interface
)) {
4328 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4332 if (set_member_paused(args
.queuename
, args
.interface
, args
.reason
, 0)) {
4333 ast_log(LOG_WARNING
, "Attempt to unpause interface %s, not found\n", args
.interface
);
4334 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "NOTFOUND");
4338 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "UNPAUSED");
4343 /*! \brief RemoveQueueMember application */
4344 static int rqm_exec(struct ast_channel
*chan
, void *data
)
4347 char *parse
, *temppos
= NULL
;
4348 AST_DECLARE_APP_ARGS(args
,
4349 AST_APP_ARG(queuename
);
4350 AST_APP_ARG(interface
);
4351 AST_APP_ARG(options
);
4355 if (ast_strlen_zero(data
)) {
4356 ast_log(LOG_WARNING
, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
4360 parse
= ast_strdupa(data
);
4362 AST_STANDARD_APP_ARGS(args
, parse
);
4364 if (ast_strlen_zero(args
.interface
)) {
4365 args
.interface
= ast_strdupa(chan
->name
);
4366 temppos
= strrchr(args
.interface
, '-');
4371 switch (remove_from_queue(args
.queuename
, args
.interface
)) {
4373 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "REMOVEMEMBER", "%s", "");
4374 ast_log(LOG_NOTICE
, "Removed interface '%s' from queue '%s'\n", args
.interface
, args
.queuename
);
4375 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "REMOVED");
4379 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args
.interface
, args
.queuename
);
4380 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTINQUEUE");
4383 case RES_NOSUCHQUEUE
:
4384 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': No such queue\n", args
.queuename
);
4385 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOSUCHQUEUE");
4388 case RES_NOT_DYNAMIC
:
4389 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args
.queuename
, args
.interface
);
4390 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTDYNAMIC");
4398 /*! \brief AddQueueMember application */
4399 static int aqm_exec(struct ast_channel
*chan
, void *data
)
4402 char *parse
, *temppos
= NULL
;
4403 AST_DECLARE_APP_ARGS(args
,
4404 AST_APP_ARG(queuename
);
4405 AST_APP_ARG(interface
);
4406 AST_APP_ARG(penalty
);
4407 AST_APP_ARG(options
);
4408 AST_APP_ARG(membername
);
4409 AST_APP_ARG(state_interface
);
4413 if (ast_strlen_zero(data
)) {
4414 ast_log(LOG_WARNING
, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
4418 parse
= ast_strdupa(data
);
4420 AST_STANDARD_APP_ARGS(args
, parse
);
4422 if (ast_strlen_zero(args
.interface
)) {
4423 args
.interface
= ast_strdupa(chan
->name
);
4424 temppos
= strrchr(args
.interface
, '-');
4429 if (!ast_strlen_zero(args
.penalty
)) {
4430 if ((sscanf(args
.penalty
, "%d", &penalty
) != 1) || penalty
< 0) {
4431 ast_log(LOG_WARNING
, "Penalty '%s' is invalid, must be an integer >= 0\n", args
.penalty
);
4436 switch (add_to_queue(args
.queuename
, args
.interface
, args
.membername
, penalty
, 0, queue_persistent_members
, args
.state_interface
)) {
4438 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "ADDMEMBER", "%s", "");
4439 ast_log(LOG_NOTICE
, "Added interface '%s' to queue '%s'\n", args
.interface
, args
.queuename
);
4440 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "ADDED");
4444 ast_log(LOG_WARNING
, "Unable to add interface '%s' to queue '%s': Already there\n", args
.interface
, args
.queuename
);
4445 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "MEMBERALREADY");
4448 case RES_NOSUCHQUEUE
:
4449 ast_log(LOG_WARNING
, "Unable to add interface to queue '%s': No such queue\n", args
.queuename
);
4450 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "NOSUCHQUEUE");
4453 case RES_OUTOFMEMORY
:
4454 ast_log(LOG_ERROR
, "Out of memory adding member %s to queue %s\n", args
.interface
, args
.queuename
);
4461 /*! \brief QueueLog application */
4462 static int ql_exec(struct ast_channel
*chan
, void *data
)
4466 AST_DECLARE_APP_ARGS(args
,
4467 AST_APP_ARG(queuename
);
4468 AST_APP_ARG(uniqueid
);
4469 AST_APP_ARG(membername
);
4471 AST_APP_ARG(params
);
4474 if (ast_strlen_zero(data
)) {
4475 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
4479 parse
= ast_strdupa(data
);
4481 AST_STANDARD_APP_ARGS(args
, parse
);
4483 if (ast_strlen_zero(args
.queuename
) || ast_strlen_zero(args
.uniqueid
)
4484 || ast_strlen_zero(args
.membername
) || ast_strlen_zero(args
.event
)) {
4485 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
4489 ast_queue_log(args
.queuename
, args
.uniqueid
, args
.membername
, args
.event
,
4490 "%s", args
.params
? args
.params
: "");
4495 /*! \brief Copy rule from global list into specified queue */
4496 static void copy_rules(struct queue_ent
*qe
, const char *rulename
)
4498 struct penalty_rule
*pr_iter
;
4499 struct rule_list
*rl_iter
;
4500 const char *tmp
= ast_strlen_zero(rulename
) ? qe
->parent
->defaultrule
: rulename
;
4501 AST_LIST_LOCK(&rule_lists
);
4502 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
4503 if (!strcasecmp(rl_iter
->name
, tmp
))
4507 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
4508 struct penalty_rule
*new_pr
= ast_calloc(1, sizeof(*new_pr
));
4510 ast_log(LOG_ERROR
, "Memory allocation error when copying penalty rules! Aborting!\n");
4511 AST_LIST_UNLOCK(&rule_lists
);
4514 new_pr
->time
= pr_iter
->time
;
4515 new_pr
->max_value
= pr_iter
->max_value
;
4516 new_pr
->min_value
= pr_iter
->min_value
;
4517 new_pr
->max_relative
= pr_iter
->max_relative
;
4518 new_pr
->min_relative
= pr_iter
->min_relative
;
4519 AST_LIST_INSERT_TAIL(&qe
->qe_rules
, new_pr
, list
);
4522 AST_LIST_UNLOCK(&rule_lists
);
4525 /*!\brief The starting point for all queue calls
4527 * The process involved here is to
4528 * 1. Parse the options specified in the call to Queue()
4530 * 3. Wait in a loop until it is our turn to try calling a queue member
4531 * 4. Attempt to call a queue member
4532 * 5. If 4. did not result in a bridged call, then check for between
4533 * call options such as periodic announcements etc.
4534 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
4537 static int queue_exec(struct ast_channel
*chan
, void *data
)
4541 const char *user_priority
;
4542 const char *max_penalty_str
;
4543 const char *min_penalty_str
;
4546 int max_penalty
, min_penalty
;
4547 enum queue_result reason
= QUEUE_UNKNOWN
;
4548 /* whether to exit Queue application after the timeout hits */
4552 int makeannouncement
= 0;
4553 AST_DECLARE_APP_ARGS(args
,
4554 AST_APP_ARG(queuename
);
4555 AST_APP_ARG(options
);
4557 AST_APP_ARG(announceoverride
);
4558 AST_APP_ARG(queuetimeoutstr
);
4564 /* Our queue entry */
4565 struct queue_ent qe
;
4567 if (ast_strlen_zero(data
)) {
4568 ast_log(LOG_WARNING
, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
4572 parse
= ast_strdupa(data
);
4573 AST_STANDARD_APP_ARGS(args
, parse
);
4575 /* Setup our queue entry */
4576 memset(&qe
, 0, sizeof(qe
));
4577 qe
.start
= time(NULL
);
4579 /* set the expire time based on the supplied timeout; */
4580 if (!ast_strlen_zero(args
.queuetimeoutstr
))
4581 qe
.expire
= qe
.start
+ atoi(args
.queuetimeoutstr
);
4585 /* Get the priority from the variable ${QUEUE_PRIO} */
4586 ast_channel_lock(chan
);
4587 user_priority
= pbx_builtin_getvar_helper(chan
, "QUEUE_PRIO");
4588 if (user_priority
) {
4589 if (sscanf(user_priority
, "%d", &prio
) == 1) {
4590 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan
->name
, prio
);
4592 ast_log(LOG_WARNING
, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
4593 user_priority
, chan
->name
);
4597 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
4601 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
4603 if ((max_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MAX_PENALTY"))) {
4604 if (sscanf(max_penalty_str
, "%d", &max_penalty
) == 1) {
4605 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan
->name
, max_penalty
);
4607 ast_log(LOG_WARNING
, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
4608 max_penalty_str
, chan
->name
);
4615 if ((min_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MIN_PENALTY"))) {
4616 if (sscanf(min_penalty_str
, "%d", &min_penalty
) == 1) {
4617 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan
->name
, min_penalty
);
4619 ast_log(LOG_WARNING
, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
4620 min_penalty_str
, chan
->name
);
4626 ast_channel_unlock(chan
);
4628 if (args
.options
&& (strchr(args
.options
, 'r')))
4631 if (args
.options
&& (strchr(args
.options
, 'c')))
4634 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
4635 args
.queuename
, args
.options
, args
.url
, args
.announceoverride
, (long)qe
.expire
, prio
);
4639 qe
.max_penalty
= max_penalty
;
4640 qe
.min_penalty
= min_penalty
;
4641 qe
.last_pos_said
= 0;
4643 qe
.last_periodic_announce_time
= time(NULL
);
4644 qe
.last_periodic_announce_sound
= 0;
4645 qe
.valid_digits
= 0;
4646 if (join_queue(args
.queuename
, &qe
, &reason
)) {
4647 ast_log(LOG_WARNING
, "Unable to join queue '%s'\n", args
.queuename
);
4648 set_queue_result(chan
, reason
);
4651 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args
.url
, ""),
4652 S_OR(chan
->cid
.cid_num
, ""));
4653 copy_rules(&qe
, args
.rule
);
4654 qe
.pr
= AST_LIST_FIRST(&qe
.qe_rules
);
4657 ast_indicate(chan
, AST_CONTROL_RINGING
);
4659 ast_moh_start(chan
, qe
.moh
, NULL
);
4662 /* This is the wait loop for callers 2 through maxlen */
4663 res
= wait_our_turn(&qe
, ringing
, &reason
);
4668 makeannouncement
= 0;
4671 /* This is the wait loop for the head caller*/
4672 /* To exit, they may get their call answered; */
4673 /* they may dial a digit from the queue context; */
4674 /* or, they may timeout. */
4676 enum queue_member_status stat
;
4678 /* Leave if we have exceeded our queuetimeout */
4679 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
4680 record_abandoned(&qe
);
4681 reason
= QUEUE_TIMEOUT
;
4683 ast_queue_log(args
.queuename
, chan
->uniqueid
,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
4684 qe
.pos
, qe
.opos
, (long) time(NULL
) - qe
.start
);
4688 if (makeannouncement
) {
4689 /* Make a position announcement, if enabled */
4690 if (qe
.parent
->announcefrequency
)
4691 if ((res
= say_position(&qe
,ringing
)))
4694 makeannouncement
= 1;
4696 /* Make a periodic announcement, if enabled */
4697 if (qe
.parent
->periodicannouncefrequency
)
4698 if ((res
= say_periodic_announcement(&qe
,ringing
)))
4701 /* see if we need to move to the next penalty level for this queue */
4702 while (qe
.pr
&& ((time(NULL
) - qe
.start
) > qe
.pr
->time
)) {
4703 update_qe_rule(&qe
);
4706 /* Try calling all queue members for 'timeout' seconds */
4707 res
= try_calling(&qe
, args
.options
, args
.announceoverride
, args
.url
, &tries
, &noption
, args
.agi
, args
.macro
, args
.gosub
, ringing
);
4712 stat
= get_member_status(qe
.parent
, qe
.max_penalty
, qe
.min_penalty
);
4714 /* exit after 'timeout' cycle if 'n' option enabled */
4715 if (noption
&& tries
>= qe
.parent
->membercount
) {
4716 ast_verb(3, "Exiting on time-out cycle\n");
4717 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
4718 record_abandoned(&qe
);
4719 reason
= QUEUE_TIMEOUT
;
4724 /* leave the queue if no agents, if enabled */
4725 if (qe
.parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
4726 record_abandoned(&qe
);
4727 reason
= QUEUE_LEAVEEMPTY
;
4728 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
4733 /* leave the queue if no reachable agents, if enabled */
4734 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)) {
4735 record_abandoned(&qe
);
4736 reason
= QUEUE_LEAVEUNAVAIL
;
4737 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
4741 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
4742 record_abandoned(&qe
);
4743 reason
= QUEUE_LEAVEUNAVAIL
;
4748 /* Leave if we have exceeded our queuetimeout */
4749 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
4750 record_abandoned(&qe
);
4751 reason
= QUEUE_TIMEOUT
;
4753 ast_queue_log(qe
.parent
->name
, qe
.chan
->uniqueid
,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long) time(NULL
) - qe
.start
);
4757 /* If using dynamic realtime members, we should regenerate the member list for this queue */
4758 update_realtime_members(qe
.parent
);
4760 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
4761 res
= wait_a_bit(&qe
);
4765 /* Since this is a priority queue and
4766 * it is not sure that we are still at the head
4767 * of the queue, go and check for our turn again.
4769 if (!is_our_turn(&qe
)) {
4770 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe
.chan
->name
);
4779 record_abandoned(&qe
);
4780 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ABANDON",
4781 "%d|%d|%ld", qe
.pos
, qe
.opos
,
4782 (long) time(NULL
) - qe
.start
);
4784 } else if (qcontinue
) {
4785 reason
= QUEUE_CONTINUE
;
4788 } else if (qe
.valid_digits
) {
4789 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY",
4790 "%s|%d", qe
.digits
, qe
.pos
);
4794 /* Don't allow return code > 0 */
4795 if (res
>= 0 && res
!= AST_PBX_KEEPALIVE
) {
4798 ast_indicate(chan
, -1);
4802 ast_stopstream(chan
);
4805 set_queue_variables(&qe
);
4808 if (reason
!= QUEUE_UNKNOWN
)
4809 set_queue_result(chan
, reason
);
4815 * \brief create interface var with all queue details.
4816 * \retval 0 on success
4817 * \retval -1 on error
4819 static int queue_function_var(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4822 struct call_queue
*q
, tmpq
= {
4826 char interfacevar
[256] = "";
4829 if (ast_strlen_zero(data
)) {
4830 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4834 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4836 if (q
->setqueuevar
) {
4840 if (q
->callscompleted
> 0) {
4841 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
4844 snprintf(interfacevar
, sizeof(interfacevar
),
4845 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
4846 q
->maxlen
, int2strat(q
->strategy
), q
->count
, q
->holdtime
, q
->callscompleted
, q
->callsabandoned
, q
->servicelevel
, sl
);
4848 pbx_builtin_setvar_multiple(chan
, interfacevar
);
4854 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4857 snprintf(buf
, len
, "%d", res
);
4863 * \brief Get number either busy / free or total members of a specific queue
4864 * \retval number of members (busy / free / total)
4865 * \retval -1 on error
4867 static int queue_function_qac(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4871 struct ao2_iterator mem_iter
;
4872 struct call_queue
*q
;
4875 if (ast_strlen_zero(data
)) {
4876 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4880 if ((option
= strchr(data
, ',')))
4884 if ((q
= load_realtime_queue(data
))) {
4886 if (!strcasecmp(option
, "logged")) {
4887 mem_iter
= ao2_iterator_init(q
->members
, 0);
4888 while ((m
= ao2_iterator_next(&mem_iter
))) {
4889 /* Count the agents who are logged in and presently answering calls */
4890 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
4895 } else if (!strcasecmp(option
, "free")) {
4896 mem_iter
= ao2_iterator_init(q
->members
, 0);
4897 while ((m
= ao2_iterator_next(&mem_iter
))) {
4898 /* Count the agents who are logged in and presently answering calls */
4899 if ((m
->status
== AST_DEVICE_NOT_INUSE
) && (!m
->paused
)) {
4904 } else /* must be "count" */
4905 count
= q
->membercount
;
4909 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4911 snprintf(buf
, len
, "%d", count
);
4917 * \brief Get the total number of members in a specific queue (Deprecated)
4918 * \retval number of members
4919 * \retval -1 on error
4921 static int queue_function_qac_dep(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4925 struct call_queue
*q
;
4926 struct ao2_iterator mem_iter
;
4927 static int depflag
= 1;
4931 ast_log(LOG_NOTICE
, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
4934 if (ast_strlen_zero(data
)) {
4935 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4939 if ((q
= load_realtime_queue(data
))) {
4941 mem_iter
= ao2_iterator_init(q
->members
, 0);
4942 while ((m
= ao2_iterator_next(&mem_iter
))) {
4943 /* Count the agents who are logged in and presently answering calls */
4944 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
4952 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4954 snprintf(buf
, len
, "%d", count
);
4959 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
4960 static int queue_function_queuewaitingcount(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4963 struct call_queue
*q
, tmpq
= {
4966 struct ast_variable
*var
= NULL
;
4970 if (ast_strlen_zero(data
)) {
4971 ast_log(LOG_ERROR
, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
4975 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4980 } else if ((var
= ast_load_realtime("queues", "name", data
, SENTINEL
))) {
4981 /* if the queue is realtime but was not found in memory, this
4982 * means that the queue had been deleted from memory since it was
4983 * "dead." This means it has a 0 waiting count
4986 ast_variables_destroy(var
);
4988 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4990 snprintf(buf
, len
, "%d", count
);
4995 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
4996 static int queue_function_queuememberlist(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4998 struct call_queue
*q
, tmpq
= {
5003 /* Ensure an otherwise empty list doesn't return garbage */
5006 if (ast_strlen_zero(data
)) {
5007 ast_log(LOG_ERROR
, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
5011 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
5012 int buflen
= 0, count
= 0;
5013 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
5016 while ((m
= ao2_iterator_next(&mem_iter
))) {
5017 /* strcat() is always faster than printf() */
5019 strncat(buf
+ buflen
, ",", len
- buflen
- 1);
5022 strncat(buf
+ buflen
, m
->membername
, len
- buflen
- 1);
5023 buflen
+= strlen(m
->membername
);
5024 /* Safeguard against overflow (negative length) */
5025 if (buflen
>= len
- 2) {
5027 ast_log(LOG_WARNING
, "Truncating list\n");
5035 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
5037 /* We should already be terminated, but let's make sure. */
5038 buf
[len
- 1] = '\0';
5043 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
5044 static int queue_function_memberpenalty_read(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
5047 AST_DECLARE_APP_ARGS(args
,
5048 AST_APP_ARG(queuename
);
5049 AST_APP_ARG(interface
);
5051 /* Make sure the returned value on error is NULL. */
5054 if (ast_strlen_zero(data
)) {
5055 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5059 AST_STANDARD_APP_ARGS(args
, data
);
5061 if (args
.argc
< 2) {
5062 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5066 penalty
= get_member_penalty (args
.queuename
, args
.interface
);
5068 if (penalty
>= 0) /* remember that buf is already '\0' */
5069 snprintf (buf
, len
, "%d", penalty
);
5074 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
5075 static int queue_function_memberpenalty_write(struct ast_channel
*chan
, const char *cmd
, char *data
, const char *value
)
5078 AST_DECLARE_APP_ARGS(args
,
5079 AST_APP_ARG(queuename
);
5080 AST_APP_ARG(interface
);
5083 if (ast_strlen_zero(data
)) {
5084 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5088 AST_STANDARD_APP_ARGS(args
, data
);
5090 if (args
.argc
< 2) {
5091 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
5095 penalty
= atoi(value
);
5097 if (ast_strlen_zero(args
.interface
)) {
5098 ast_log (LOG_ERROR
, "<interface> parameter can't be null\n");
5102 /* if queuename = NULL then penalty will be set for interface in all the queues. */
5103 if (set_member_penalty(args
.queuename
, args
.interface
, penalty
)) {
5104 ast_log(LOG_ERROR
, "Invalid interface, queue or penalty\n");
5111 static struct ast_custom_function queuevar_function
= {
5112 .name
= "QUEUE_VARIABLES",
5113 .synopsis
= "Return Queue information in variables",
5114 .syntax
= "QUEUE_VARIABLES(<queuename>)",
5116 "Makes the following queue variables available.\n"
5117 "QUEUEMAX maxmimum number of calls allowed\n"
5118 "QUEUESTRATEGY the strategy of the queue\n"
5119 "QUEUECALLS number of calls currently in the queue\n"
5120 "QUEUEHOLDTIME current average hold time\n"
5121 "QUEUECOMPLETED number of completed calls for the queue\n"
5122 "QUEUEABANDONED number of abandoned calls\n"
5123 "QUEUESRVLEVEL queue service level\n"
5124 "QUEUESRVLEVELPERF current service level performance\n"
5125 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
5126 .read
= queue_function_var
,
5129 static struct ast_custom_function queuemembercount_function
= {
5130 .name
= "QUEUE_MEMBER",
5131 .synopsis
= "Count number of members answering a queue",
5132 .syntax
= "QUEUE_MEMBER(<queuename>, <option>)",
5134 "Returns the number of members currently associated with the specified queue.\n"
5135 "One of three options may be passed to determine the count returned:\n"
5136 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
5137 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
5138 "\"count\" - Returns the total number of members for the specified queue\n",
5139 .read
= queue_function_qac
,
5142 static struct ast_custom_function queuemembercount_dep
= {
5143 .name
= "QUEUE_MEMBER_COUNT",
5144 .synopsis
= "Count number of members answering a queue",
5145 .syntax
= "QUEUE_MEMBER_COUNT(<queuename>)",
5147 "Returns the number of members currently associated with the specified queue.\n\n"
5148 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
5149 .read
= queue_function_qac_dep
,
5152 static struct ast_custom_function queuewaitingcount_function
= {
5153 .name
= "QUEUE_WAITING_COUNT",
5154 .synopsis
= "Count number of calls currently waiting in a queue",
5155 .syntax
= "QUEUE_WAITING_COUNT(<queuename>)",
5157 "Returns the number of callers currently waiting in the specified queue.\n",
5158 .read
= queue_function_queuewaitingcount
,
5161 static struct ast_custom_function queuememberlist_function
= {
5162 .name
= "QUEUE_MEMBER_LIST",
5163 .synopsis
= "Returns a list of interfaces on a queue",
5164 .syntax
= "QUEUE_MEMBER_LIST(<queuename>)",
5166 "Returns a comma-separated list of members associated with the specified queue.\n",
5167 .read
= queue_function_queuememberlist
,
5170 static struct ast_custom_function queuememberpenalty_function
= {
5171 .name
= "QUEUE_MEMBER_PENALTY",
5172 .synopsis
= "Gets or sets queue members penalty.",
5173 .syntax
= "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
5175 "Gets or sets queue members penalty\n",
5176 .read
= queue_function_memberpenalty_read
,
5177 .write
= queue_function_memberpenalty_write
,
5180 static int reload_queue_rules(int reload
)
5182 struct ast_config
*cfg
;
5183 struct rule_list
*rl_iter
, *new_rl
;
5184 struct penalty_rule
*pr_iter
;
5185 char *rulecat
= NULL
;
5186 struct ast_variable
*rulevar
= NULL
;
5187 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
5189 if (!(cfg
= ast_config_load("queuerules.conf", config_flags
))) {
5190 ast_log(LOG_NOTICE
, "No queuerules.conf file found, queues will not follow penalty rules\n");
5191 } else if (cfg
== CONFIG_STATUS_FILEUNCHANGED
) {
5192 ast_log(LOG_NOTICE
, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
5193 return AST_MODULE_LOAD_SUCCESS
;
5195 AST_LIST_LOCK(&rule_lists
);
5196 while ((rl_iter
= AST_LIST_REMOVE_HEAD(&rule_lists
, list
))) {
5197 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&rl_iter
->rules
, list
)))
5201 while ((rulecat
= ast_category_browse(cfg
, rulecat
))) {
5202 if (!(new_rl
= ast_calloc(1, sizeof(*new_rl
)))) {
5203 ast_log(LOG_ERROR
, "Memory allocation error while loading queuerules.conf! Aborting!\n");
5204 AST_LIST_UNLOCK(&rule_lists
);
5205 return AST_MODULE_LOAD_FAILURE
;
5207 ast_copy_string(new_rl
->name
, rulecat
, sizeof(new_rl
->name
));
5208 AST_LIST_INSERT_TAIL(&rule_lists
, new_rl
, list
);
5209 for (rulevar
= ast_variable_browse(cfg
, rulecat
); rulevar
; rulevar
= rulevar
->next
)
5210 if(!strcasecmp(rulevar
->name
, "penaltychange"))
5211 insert_penaltychange(new_rl
->name
, rulevar
->value
, rulevar
->lineno
);
5213 ast_log(LOG_WARNING
, "Don't know how to handle rule type '%s' on line %d\n", rulevar
->name
, rulevar
->lineno
);
5216 AST_LIST_UNLOCK(&rule_lists
);
5219 ast_config_destroy(cfg
);
5221 return AST_MODULE_LOAD_SUCCESS
;
5225 static int reload_queues(int reload
)
5227 struct call_queue
*q
;
5228 struct ast_config
*cfg
;
5230 struct ast_variable
*var
;
5231 struct member
*cur
, *newm
;
5232 struct ao2_iterator mem_iter
;
5234 const char *general_val
= NULL
;
5236 char *interface
, *state_interface
;
5237 char *membername
= NULL
;
5239 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
5240 struct ao2_iterator queue_iter
;
5241 AST_DECLARE_APP_ARGS(args
,
5242 AST_APP_ARG(interface
);
5243 AST_APP_ARG(penalty
);
5244 AST_APP_ARG(membername
);
5245 AST_APP_ARG(state_interface
);
5248 /*First things first. Let's load queuerules.conf*/
5249 if (reload_queue_rules(reload
) == AST_MODULE_LOAD_FAILURE
)
5250 return AST_MODULE_LOAD_FAILURE
;
5252 if (!(cfg
= ast_config_load("queues.conf", config_flags
))) {
5253 ast_log(LOG_NOTICE
, "No call queueing config file (queues.conf), so no call queues\n");
5255 } else if (cfg
== CONFIG_STATUS_FILEUNCHANGED
)
5259 /* Mark all queues as dead for the moment */
5260 queue_iter
= ao2_iterator_init(queues
, F_AO2I_DONTLOCK
);
5261 while ((q
= ao2_iterator_next(&queue_iter
))) {
5269 /* Chug through config file */
5271 while ((cat
= ast_category_browse(cfg
, cat
)) ) {
5272 if (!strcasecmp(cat
, "general")) {
5273 /* Initialize global settings */
5274 queue_keep_stats
= 0;
5275 if ((general_val
= ast_variable_retrieve(cfg
, "general", "keepstats")))
5276 queue_keep_stats
= ast_true(general_val
);
5277 queue_persistent_members
= 0;
5278 if ((general_val
= ast_variable_retrieve(cfg
, "general", "persistentmembers")))
5279 queue_persistent_members
= ast_true(general_val
);
5280 autofill_default
= 0;
5281 if ((general_val
= ast_variable_retrieve(cfg
, "general", "autofill")))
5282 autofill_default
= ast_true(general_val
);
5283 montype_default
= 0;
5284 if ((general_val
= ast_variable_retrieve(cfg
, "general", "monitor-type"))) {
5285 if (!strcasecmp(general_val
, "mixmonitor"))
5286 montype_default
= 1;
5289 if ((general_val
= ast_variable_retrieve(cfg
, "general", "updatecdr")))
5290 update_cdr
= ast_true(general_val
);
5291 shared_lastcall
= 0;
5292 if ((general_val
= ast_variable_retrieve(cfg
, "general", "shared_lastcall")))
5293 shared_lastcall
= ast_true(general_val
);
5294 } else { /* Define queue */
5295 /* Look for an existing one */
5296 struct call_queue tmpq
= {
5299 if (!(q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
5301 if (!(q
= alloc_queue(cat
))) {
5302 /* TODO: Handle memory allocation failure */
5308 const char *tmpvar
= NULL
;
5311 /* Check if a queue with this name already exists */
5313 ast_log(LOG_WARNING
, "Queue '%s' already defined! Skipping!\n", cat
);
5320 /* Due to the fact that the "linear" strategy will have a different allocation
5321 * scheme for queue members, we must devise the queue's strategy before other initializations
5323 if ((tmpvar
= ast_variable_retrieve(cfg
, cat
, "strategy"))) {
5324 q
->strategy
= strat2int(tmpvar
);
5325 if (q
->strategy
< 0) {
5326 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
5328 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
5331 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
5332 /* Re-initialize the queue, and clear statistics */
5334 if (!queue_keep_stats
)
5336 mem_iter
= ao2_iterator_init(q
->members
, 0);
5337 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5338 if (!cur
->dynamic
) {
5343 for (var
= ast_variable_browse(cfg
, cat
); var
; var
= var
->next
) {
5344 if (!strcasecmp(var
->name
, "member")) {
5345 struct member tmpmem
;
5348 /* Add a new member */
5349 ast_copy_string(parse
, var
->value
, sizeof(parse
));
5351 AST_STANDARD_APP_ARGS(args
, parse
);
5353 interface
= args
.interface
;
5354 if (!ast_strlen_zero(args
.penalty
)) {
5356 while (*tmp
&& *tmp
< 33) tmp
++;
5357 penalty
= atoi(tmp
);
5364 if (!ast_strlen_zero(args
.membername
)) {
5365 membername
= args
.membername
;
5366 while (*membername
&& *membername
< 33) membername
++;
5369 if (!ast_strlen_zero(args
.state_interface
)) {
5370 state_interface
= args
.state_interface
;
5371 while (*state_interface
&& *state_interface
< 33) state_interface
++;
5373 state_interface
= interface
;
5375 /* Find the old position in the list */
5376 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
5377 cur
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
| OBJ_UNLINK
);
5378 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
5379 if (cur
&& strcasecmp(cur
->state_interface
, state_interface
)) {
5380 remove_from_interfaces(cur
->state_interface
);
5382 newm
= create_queue_member(interface
, membername
, penalty
, cur
? cur
->paused
: 0, state_interface
);
5383 if (!cur
|| (cur
&& strcasecmp(cur
->state_interface
, state_interface
)))
5384 add_to_interfaces(state_interface
);
5385 ao2_link(q
->members
, newm
);
5395 queue_set_param(q
, var
->name
, var
->value
, var
->lineno
, 1);
5399 /* Free remaining members marked as delme */
5400 mem_iter
= ao2_iterator_init(q
->members
, 0);
5401 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5407 ao2_unlink(q
->members
, cur
);
5408 remove_from_interfaces(cur
->interface
);
5413 ao2_link(queues
, q
);
5420 ast_config_destroy(cfg
);
5421 queue_iter
= ao2_iterator_init(queues
, 0);
5422 while ((q
= ao2_iterator_next(&queue_iter
))) {
5424 ao2_unlink(queues
, q
);
5427 mem_iter
= ao2_iterator_init(q
->members
, 0);
5428 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5431 cur
->status
= ast_device_state(cur
->interface
);
5442 /*! \brief direct ouput to manager or cli with proper terminator */
5443 static void do_print(struct mansession
*s
, int fd
, const char *str
)
5446 astman_append(s
, "%s\r\n", str
);
5448 ast_cli(fd
, "%s\n", str
);
5452 * \brief Show queue(s) status and statistics
5454 * List the queues strategy, calls processed, members logged in,
5455 * other queue statistics such as avg hold time.
5457 static char *__queues_show(struct mansession
*s
, int fd
, int argc
, char **argv
)
5459 struct call_queue
*q
;
5460 struct ast_str
*out
= ast_str_alloca(240);
5462 time_t now
= time(NULL
);
5463 struct ao2_iterator queue_iter
;
5464 struct ao2_iterator mem_iter
;
5466 if (argc
!= 2 && argc
!= 3)
5467 return CLI_SHOWUSAGE
;
5469 if (argc
== 3) { /* specific queue */
5470 load_realtime_queue(argv
[2]);
5472 else if (ast_check_realtime("queues")) {
5473 struct ast_config
*cfg
= ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL
);
5476 for (queuename
= ast_category_browse(cfg
, NULL
); !ast_strlen_zero(queuename
); queuename
= ast_category_browse(cfg
, queuename
)) {
5477 load_realtime_queue(queuename
);
5479 ast_config_destroy(cfg
);
5483 queue_iter
= ao2_iterator_init(queues
, 0);
5484 while ((q
= ao2_iterator_next(&queue_iter
))) {
5488 if (argc
== 3 && strcasecmp(q
->name
, argv
[2])) {
5494 ast_str_set(&out
, 0, "%-12.12s has %d calls (max ", q
->name
, q
->count
);
5496 ast_str_append(&out
, 0, "%d", q
->maxlen
);
5498 ast_str_append(&out
, 0, "unlimited");
5500 if (q
->callscompleted
> 0)
5501 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
5502 ast_str_append(&out
, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
5503 int2strat(q
->strategy
), q
->holdtime
, q
->weight
,
5504 q
->callscompleted
, q
->callsabandoned
,sl
,q
->servicelevel
);
5505 do_print(s
, fd
, out
->str
);
5506 if (!ao2_container_count(q
->members
))
5507 do_print(s
, fd
, " No Members");
5511 do_print(s
, fd
, " Members: ");
5512 mem_iter
= ao2_iterator_init(q
->members
, 0);
5513 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5514 ast_str_set(&out
, 0, " %s", mem
->membername
);
5515 if (strcasecmp(mem
->membername
, mem
->interface
)) {
5516 ast_str_append(&out
, 0, " (%s)", mem
->interface
);
5519 ast_str_append(&out
, 0, " with penalty %d", mem
->penalty
);
5520 ast_str_append(&out
, 0, "%s%s%s (%s)",
5521 mem
->dynamic
? " (dynamic)" : "",
5522 mem
->realtime
? " (realtime)" : "",
5523 mem
->paused
? " (paused)" : "",
5524 devstate2str(mem
->status
));
5526 ast_str_append(&out
, 0, " has taken %d calls (last was %ld secs ago)",
5527 mem
->calls
, (long) (time(NULL
) - mem
->lastcall
));
5529 ast_str_append(&out
, 0, " has taken no calls yet");
5530 do_print(s
, fd
, out
->str
);
5535 do_print(s
, fd
, " No Callers");
5537 struct queue_ent
*qe
;
5540 do_print(s
, fd
, " Callers: ");
5541 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5542 ast_str_set(&out
, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
5543 pos
++, qe
->chan
->name
, (long) (now
- qe
->start
) / 60,
5544 (long) (now
- qe
->start
) % 60, qe
->prio
);
5545 do_print(s
, fd
, out
->str
);
5548 do_print(s
, fd
, ""); /* blank line between entries */
5550 if (q
->realtime
|| argc
== 3) {
5551 /* If a queue is realtime, then that means we used load_realtime_queue() above
5552 * to get its information. This means we have an extra reference we need to
5553 * remove at this point. If a specific queue was requested, then it also needs
5554 * to be unreffed here even if it is not a realtime queue.
5558 queue_unref(q
); /* Unref the iterator's reference */
5562 ast_str_set(&out
, 0, "No such queue: %s.", argv
[2]);
5564 ast_str_set(&out
, 0, "No queues.");
5565 do_print(s
, fd
, out
->str
);
5570 static char *complete_queue(const char *line
, const char *word
, int pos
, int state
)
5572 struct call_queue
*q
;
5575 int wordlen
= strlen(word
);
5576 struct ao2_iterator queue_iter
;
5578 queue_iter
= ao2_iterator_init(queues
, 0);
5579 while ((q
= ao2_iterator_next(&queue_iter
))) {
5580 if (!strncasecmp(word
, q
->name
, wordlen
) && ++which
> state
) {
5581 ret
= ast_strdup(q
->name
);
5591 static char *complete_queue_show(const char *line
, const char *word
, int pos
, int state
)
5594 return complete_queue(line
, word
, pos
, state
);
5598 static char *queue_show(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
5602 e
->command
= "queue show";
5604 "Usage: queue show\n"
5605 " Provides summary information on a specified queue.\n";
5608 return complete_queue_show(a
->line
, a
->word
, a
->pos
, a
->n
);
5611 return __queues_show(NULL
, a
->fd
, a
->argc
, a
->argv
);
5614 /*!\brief callback to display queues status in manager
5615 \addtogroup Group_AMI
5617 static int manager_queues_show(struct mansession
*s
, const struct message
*m
)
5619 char *a
[] = { "queue", "show" };
5621 __queues_show(s
, -1, 2, a
);
5622 astman_append(s
, "\r\n\r\n"); /* Properly terminate Manager output */
5624 return RESULT_SUCCESS
;
5627 static int manager_queue_rule_show(struct mansession
*s
, const struct message
*m
)
5629 const char *rule
= astman_get_header(m
, "Rule");
5630 struct rule_list
*rl_iter
;
5631 struct penalty_rule
*pr_iter
;
5633 AST_LIST_LOCK(&rule_lists
);
5634 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
5635 if (ast_strlen_zero(rule
) || !strcasecmp(rule
, rl_iter
->name
)) {
5636 astman_append(s
, "RuleList: %s\r\n", rl_iter
->name
);
5637 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
5638 astman_append(s
, "Rule: %d,%s%d,%s%d\r\n", pr_iter
->time
, pr_iter
->max_relative
&& pr_iter
->max_value
>= 0 ? "+" : "", pr_iter
->max_value
, pr_iter
->min_relative
&& pr_iter
->min_value
>= 0 ? "+" : "", pr_iter
->min_value
);
5640 if (!ast_strlen_zero(rule
))
5644 AST_LIST_UNLOCK(&rule_lists
);
5646 astman_append(s
, "\r\n\r\n");
5648 return RESULT_SUCCESS
;
5651 /*! \brief Summary of queue info via the AMI */
5652 static int manager_queues_summary(struct mansession
*s
, const struct message
*m
)
5658 int qlongestholdtime
= 0;
5659 const char *id
= astman_get_header(m
, "ActionID");
5660 const char *queuefilter
= astman_get_header(m
, "Queue");
5661 char idText
[256] = "";
5662 struct call_queue
*q
;
5663 struct queue_ent
*qe
;
5665 struct ao2_iterator queue_iter
;
5666 struct ao2_iterator mem_iter
;
5668 astman_send_ack(s
, m
, "Queue summary will follow");
5670 if (!ast_strlen_zero(id
))
5671 snprintf(idText
, 256, "ActionID: %s\r\n", id
);
5672 queue_iter
= ao2_iterator_init(queues
, 0);
5673 while ((q
= ao2_iterator_next(&queue_iter
))) {
5676 /* List queue properties */
5677 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
5678 /* Reset the necessary local variables if no queuefilter is set*/
5682 qlongestholdtime
= 0;
5684 /* List Queue Members */
5685 mem_iter
= ao2_iterator_init(q
->members
, 0);
5686 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5687 if ((mem
->status
!= AST_DEVICE_UNAVAILABLE
) && (mem
->status
!= AST_DEVICE_INVALID
)) {
5689 if (((mem
->status
== AST_DEVICE_NOT_INUSE
) || (mem
->status
== AST_DEVICE_UNKNOWN
)) && !(mem
->paused
)) {
5695 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5696 if ((now
- qe
->start
) > qlongestholdtime
) {
5697 qlongestholdtime
= now
- qe
->start
;
5701 astman_append(s
, "Event: QueueSummary\r\n"
5707 "LongestHoldTime: %d\r\n"
5710 q
->name
, qmemcount
, qmemavail
, qchancount
, q
->holdtime
, qlongestholdtime
, idText
);
5716 "Event: QueueSummaryComplete\r\n"
5720 return RESULT_SUCCESS
;
5723 /*! \brief Queue status info via AMI */
5724 static int manager_queues_status(struct mansession
*s
, const struct message
*m
)
5728 const char *id
= astman_get_header(m
,"ActionID");
5729 const char *queuefilter
= astman_get_header(m
,"Queue");
5730 const char *memberfilter
= astman_get_header(m
,"Member");
5731 char idText
[256] = "";
5732 struct call_queue
*q
;
5733 struct queue_ent
*qe
;
5736 struct ao2_iterator queue_iter
;
5737 struct ao2_iterator mem_iter
;
5739 astman_send_ack(s
, m
, "Queue status will follow");
5741 if (!ast_strlen_zero(id
))
5742 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
5744 queue_iter
= ao2_iterator_init(queues
, 0);
5745 while ((q
= ao2_iterator_next(&queue_iter
))) {
5748 /* List queue properties */
5749 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
5750 sl
= ((q
->callscompleted
> 0) ? 100 * ((float)q
->callscompletedinsl
/ (float)q
->callscompleted
) : 0);
5751 astman_append(s
, "Event: QueueParams\r\n"
5759 "ServiceLevel: %d\r\n"
5760 "ServicelevelPerf: %2.1f\r\n"
5764 q
->name
, q
->maxlen
, int2strat(q
->strategy
), q
->count
, q
->holdtime
, q
->callscompleted
,
5765 q
->callsabandoned
, q
->servicelevel
, sl
, q
->weight
, idText
);
5766 /* List Queue Members */
5767 mem_iter
= ao2_iterator_init(q
->members
, 0);
5768 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5769 if (ast_strlen_zero(memberfilter
) || !strcmp(mem
->interface
, memberfilter
)) {
5770 astman_append(s
, "Event: QueueMember\r\n"
5774 "Membership: %s\r\n"
5776 "CallsTaken: %d\r\n"
5782 q
->name
, mem
->membername
, mem
->interface
, mem
->dynamic
? "dynamic" : "static",
5783 mem
->penalty
, mem
->calls
, (int)mem
->lastcall
, mem
->status
, mem
->paused
, idText
);
5787 /* List Queue Entries */
5789 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5790 astman_append(s
, "Event: QueueEntry\r\n"
5794 "CallerIDNum: %s\r\n"
5795 "CallerIDName: %s\r\n"
5799 q
->name
, pos
++, qe
->chan
->name
,
5800 S_OR(qe
->chan
->cid
.cid_num
, "unknown"),
5801 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
5802 (long) (now
- qe
->start
), idText
);
5810 "Event: QueueStatusComplete\r\n"
5814 return RESULT_SUCCESS
;
5817 static int manager_add_queue_member(struct mansession
*s
, const struct message
*m
)
5819 const char *queuename
, *interface
, *penalty_s
, *paused_s
, *membername
, *state_interface
;
5820 int paused
, penalty
= 0;
5822 queuename
= astman_get_header(m
, "Queue");
5823 interface
= astman_get_header(m
, "Interface");
5824 penalty_s
= astman_get_header(m
, "Penalty");
5825 paused_s
= astman_get_header(m
, "Paused");
5826 membername
= astman_get_header(m
, "MemberName");
5827 state_interface
= astman_get_header(m
, "StateInterface");
5829 if (ast_strlen_zero(queuename
)) {
5830 astman_send_error(s
, m
, "'Queue' not specified.");
5834 if (ast_strlen_zero(interface
)) {
5835 astman_send_error(s
, m
, "'Interface' not specified.");
5839 if (ast_strlen_zero(penalty_s
))
5841 else if (sscanf(penalty_s
, "%d", &penalty
) != 1 || penalty
< 0)
5844 if (ast_strlen_zero(paused_s
))
5847 paused
= abs(ast_true(paused_s
));
5849 switch (add_to_queue(queuename
, interface
, membername
, penalty
, paused
, queue_persistent_members
, state_interface
)) {
5851 ast_queue_log(queuename
, "MANAGER", interface
, "ADDMEMBER", "%s", "");
5852 astman_send_ack(s
, m
, "Added interface to queue");
5855 astman_send_error(s
, m
, "Unable to add interface: Already there");
5857 case RES_NOSUCHQUEUE
:
5858 astman_send_error(s
, m
, "Unable to add interface to queue: No such queue");
5860 case RES_OUTOFMEMORY
:
5861 astman_send_error(s
, m
, "Out of memory");
5868 static int manager_remove_queue_member(struct mansession
*s
, const struct message
*m
)
5870 const char *queuename
, *interface
;
5872 queuename
= astman_get_header(m
, "Queue");
5873 interface
= astman_get_header(m
, "Interface");
5875 if (ast_strlen_zero(queuename
) || ast_strlen_zero(interface
)) {
5876 astman_send_error(s
, m
, "Need 'Queue' and 'Interface' parameters.");
5880 switch (remove_from_queue(queuename
, interface
)) {
5882 ast_queue_log(queuename
, "MANAGER", interface
, "REMOVEMEMBER", "%s", "");
5883 astman_send_ack(s
, m
, "Removed interface from queue");
5886 astman_send_error(s
, m
, "Unable to remove interface: Not there");
5888 case RES_NOSUCHQUEUE
:
5889 astman_send_error(s
, m
, "Unable to remove interface from queue: No such queue");
5891 case RES_OUTOFMEMORY
:
5892 astman_send_error(s
, m
, "Out of memory");
5894 case RES_NOT_DYNAMIC
:
5895 astman_send_error(s
, m
, "Member not dynamic");
5902 static int manager_pause_queue_member(struct mansession
*s
, const struct message
*m
)
5904 const char *queuename
, *interface
, *paused_s
, *reason
;
5907 interface
= astman_get_header(m
, "Interface");
5908 paused_s
= astman_get_header(m
, "Paused");
5909 queuename
= astman_get_header(m
, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
5910 reason
= astman_get_header(m
, "Reason"); /* Optional - Only used for logging purposes */
5912 if (ast_strlen_zero(interface
) || ast_strlen_zero(paused_s
)) {
5913 astman_send_error(s
, m
, "Need 'Interface' and 'Paused' parameters.");
5917 paused
= abs(ast_true(paused_s
));
5919 if (set_member_paused(queuename
, interface
, reason
, paused
))
5920 astman_send_error(s
, m
, "Interface not found");
5922 astman_send_ack(s
, m
, paused
? "Interface paused successfully" : "Interface unpaused successfully");
5926 static int manager_queue_log_custom(struct mansession
*s
, const struct message
*m
)
5928 const char *queuename
, *event
, *message
, *interface
, *uniqueid
;
5930 queuename
= astman_get_header(m
, "Queue");
5931 uniqueid
= astman_get_header(m
, "UniqueId");
5932 interface
= astman_get_header(m
, "Interface");
5933 event
= astman_get_header(m
, "Event");
5934 message
= astman_get_header(m
, "Message");
5936 if (ast_strlen_zero(queuename
) || ast_strlen_zero(event
)) {
5937 astman_send_error(s
, m
, "Need 'Queue' and 'Event' parameters.");
5941 ast_queue_log(queuename
, S_OR(uniqueid
, "NONE"), interface
, event
, "%s", message
);
5942 astman_send_ack(s
, m
, "Event added successfully");
5947 static char *complete_queue_add_member(const char *line
, const char *word
, int pos
, int state
)
5949 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
5951 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
5953 case 4: /* only one possible match, "to" */
5954 return state
== 0 ? ast_strdup("to") : NULL
;
5955 case 5: /* <queue> */
5956 return complete_queue(line
, word
, pos
, state
);
5957 case 6: /* only one possible match, "penalty" */
5958 return state
== 0 ? ast_strdup("penalty") : NULL
;
5960 if (state
< 100) { /* 0-99 */
5962 if ((num
= ast_malloc(3))) {
5963 sprintf(num
, "%d", state
);
5969 case 8: /* only one possible match, "as" */
5970 return state
== 0 ? ast_strdup("as") : NULL
;
5971 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
5978 static int manager_queue_member_penalty(struct mansession
*s
, const struct message
*m
)
5980 const char *queuename
, *interface
, *penalty_s
;
5983 interface
= astman_get_header(m
, "Interface");
5984 penalty_s
= astman_get_header(m
, "Penalty");
5985 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
5986 queuename
= astman_get_header(m
, "Queue");
5988 if (ast_strlen_zero(interface
) || ast_strlen_zero(penalty_s
)) {
5989 astman_send_error(s
, m
, "Need 'Interface' and 'Penalty' parameters.");
5993 penalty
= atoi(penalty_s
);
5995 if (set_member_penalty((char *)queuename
, (char *)interface
, penalty
))
5996 astman_send_error(s
, m
, "Invalid interface, queuename or penalty");
5998 astman_send_ack(s
, m
, "Interface penalty set successfully");
6003 static char *handle_queue_add_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6005 char *queuename
, *interface
, *membername
= NULL
, *state_interface
= NULL
;
6010 e
->command
= "queue add member";
6012 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
6015 return complete_queue_add_member(a
->line
, a
->word
, a
->pos
, a
->n
);
6018 if ((a
->argc
!= 6) && (a
->argc
!= 8) && (a
->argc
!= 10) && (a
->argc
!= 12)) {
6019 return CLI_SHOWUSAGE
;
6020 } else if (strcmp(a
->argv
[4], "to")) {
6021 return CLI_SHOWUSAGE
;
6022 } else if ((a
->argc
>= 8) && strcmp(a
->argv
[6], "penalty")) {
6023 return CLI_SHOWUSAGE
;
6024 } else if ((a
->argc
>= 10) && strcmp(a
->argv
[8], "as")) {
6025 return CLI_SHOWUSAGE
;
6026 } else if ((a
->argc
== 12) && strcmp(a
->argv
[10], "state_interface")) {
6027 return CLI_SHOWUSAGE
;
6030 queuename
= a
->argv
[5];
6031 interface
= a
->argv
[3];
6033 if (sscanf(a
->argv
[7], "%d", &penalty
) == 1) {
6035 ast_cli(a
->fd
, "Penalty must be >= 0\n");
6039 ast_cli(a
->fd
, "Penalty must be an integer >= 0\n");
6046 if (a
->argc
>= 10) {
6047 membername
= a
->argv
[9];
6050 if (a
->argc
>= 12) {
6051 state_interface
= a
->argv
[11];
6054 switch (add_to_queue(queuename
, interface
, membername
, penalty
, 0, queue_persistent_members
, state_interface
)) {
6056 ast_queue_log(queuename
, "CLI", interface
, "ADDMEMBER", "%s", "");
6057 ast_cli(a
->fd
, "Added interface '%s' to queue '%s'\n", interface
, queuename
);
6060 ast_cli(a
->fd
, "Unable to add interface '%s' to queue '%s': Already there\n", interface
, queuename
);
6062 case RES_NOSUCHQUEUE
:
6063 ast_cli(a
->fd
, "Unable to add interface to queue '%s': No such queue\n", queuename
);
6065 case RES_OUTOFMEMORY
:
6066 ast_cli(a
->fd
, "Out of memory\n");
6068 case RES_NOT_DYNAMIC
:
6069 ast_cli(a
->fd
, "Member not dynamic\n");
6076 static char *complete_queue_remove_member(const char *line
, const char *word
, int pos
, int state
)
6079 struct call_queue
*q
;
6081 struct ao2_iterator queue_iter
;
6082 struct ao2_iterator mem_iter
;
6083 int wordlen
= strlen(word
);
6085 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
6086 if (pos
> 5 || pos
< 3)
6088 if (pos
== 4) /* only one possible match, 'from' */
6089 return (state
== 0 ? ast_strdup("from") : NULL
);
6091 if (pos
== 5) /* No need to duplicate code */
6092 return complete_queue(line
, word
, pos
, state
);
6094 /* here is the case for 3, <member> */
6095 queue_iter
= ao2_iterator_init(queues
, 0);
6096 while ((q
= ao2_iterator_next(&queue_iter
))) {
6098 mem_iter
= ao2_iterator_init(q
->members
, 0);
6099 while ((m
= ao2_iterator_next(&mem_iter
))) {
6100 if (!strncasecmp(word
, m
->membername
, wordlen
) && ++which
> state
) {
6103 tmp
= ast_strdup(m
->interface
);
6117 static char *handle_queue_remove_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6119 char *queuename
, *interface
;
6123 e
->command
= "queue remove member";
6124 e
->usage
= "Usage: queue remove member <channel> from <queue>\n";
6127 return complete_queue_remove_member(a
->line
, a
->word
, a
->pos
, a
->n
);
6131 return CLI_SHOWUSAGE
;
6132 } else if (strcmp(a
->argv
[4], "from")) {
6133 return CLI_SHOWUSAGE
;
6136 queuename
= a
->argv
[5];
6137 interface
= a
->argv
[3];
6139 switch (remove_from_queue(queuename
, interface
)) {
6141 ast_queue_log(queuename
, "CLI", interface
, "REMOVEMEMBER", "%s", "");
6142 ast_cli(a
->fd
, "Removed interface '%s' from queue '%s'\n", interface
, queuename
);
6145 ast_cli(a
->fd
, "Unable to remove interface '%s' from queue '%s': Not there\n", interface
, queuename
);
6147 case RES_NOSUCHQUEUE
:
6148 ast_cli(a
->fd
, "Unable to remove interface from queue '%s': No such queue\n", queuename
);
6150 case RES_OUTOFMEMORY
:
6151 ast_cli(a
->fd
, "Out of memory\n");
6158 static char *complete_queue_pause_member(const char *line
, const char *word
, int pos
, int state
)
6160 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
6162 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
6164 case 4: /* only one possible match, "queue" */
6165 return state
== 0 ? ast_strdup("queue") : NULL
;
6166 case 5: /* <queue> */
6167 return complete_queue(line
, word
, pos
, state
);
6168 case 6: /* "reason" */
6169 return state
== 0 ? ast_strdup("reason") : NULL
;
6170 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
6177 static char *handle_queue_pause_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6179 char *queuename
, *interface
, *reason
;
6184 e
->command
= "queue {pause|unpause} member";
6186 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
6187 " Pause or unpause a queue member. Not specifying a particular queue\n"
6188 " will pause or unpause a member across all queues to which the member\n"
6192 return complete_queue_pause_member(a
->line
, a
-> word
, a
->pos
, a
->n
);
6195 if (a
->argc
< 4 || a
->argc
== 5 || a
->argc
== 7 || a
->argc
> 8) {
6196 return CLI_SHOWUSAGE
;
6197 } else if (a
->argc
>= 5 && strcmp(a
->argv
[4], "queue")) {
6198 return CLI_SHOWUSAGE
;
6199 } else if (a
->argc
== 8 && strcmp(a
->argv
[6], "reason")) {
6200 return CLI_SHOWUSAGE
;
6204 interface
= a
->argv
[3];
6205 queuename
= a
->argc
>= 6 ? a
->argv
[5] : NULL
;
6206 reason
= a
->argc
== 8 ? a
->argv
[7] : NULL
;
6207 paused
= !strcasecmp(a
->argv
[1], "pause");
6209 if (set_member_paused(queuename
, interface
, reason
, paused
) == RESULT_SUCCESS
) {
6210 ast_cli(a
->fd
, "%spaused interface '%s'", paused
? "" : "un", interface
);
6211 if (!ast_strlen_zero(queuename
))
6212 ast_cli(a
->fd
, " in queue '%s'", queuename
);
6213 if (!ast_strlen_zero(reason
))
6214 ast_cli(a
->fd
, " for reason '%s'", reason
);
6215 ast_cli(a
->fd
, "\n");
6218 ast_cli(a
->fd
, "Unable to %spause interface '%s'", paused
? "" : "un", interface
);
6219 if (!ast_strlen_zero(queuename
))
6220 ast_cli(a
->fd
, " in queue '%s'", queuename
);
6221 if (!ast_strlen_zero(reason
))
6222 ast_cli(a
->fd
, " for reason '%s'", reason
);
6223 ast_cli(a
->fd
, "\n");
6228 static char *complete_queue_set_member_penalty(const char *line
, const char *word
, int pos
, int state
)
6230 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
6234 return ast_strdup("on");
6240 return ast_strdup("in");
6245 return complete_queue(line
, word
, pos
, state
);
6251 static char *handle_queue_set_member_penalty(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6253 char *queuename
= NULL
, *interface
;
6258 e
->command
= "queue set penalty";
6260 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
6261 "Set a member's penalty in the queue specified. If no queue is specified\n"
6262 "then that interface's penalty is set in all queues to which that interface is a member\n";
6265 return complete_queue_set_member_penalty(a
->line
, a
->word
, a
->pos
, a
->n
);
6268 if (a
->argc
!= 6 && a
->argc
!= 8) {
6269 return CLI_SHOWUSAGE
;
6270 } else if (strcmp(a
->argv
[4], "on") || (a
->argc
> 6 && strcmp(a
->argv
[6], "in"))) {
6271 return CLI_SHOWUSAGE
;
6275 queuename
= a
->argv
[7];
6276 interface
= a
->argv
[5];
6277 penalty
= atoi(a
->argv
[3]);
6279 switch (set_member_penalty(queuename
, interface
, penalty
)) {
6280 case RESULT_SUCCESS
:
6281 ast_cli(a
->fd
, "Set penalty on interface '%s' from queue '%s'\n", interface
, queuename
);
6283 case RESULT_FAILURE
:
6284 ast_cli(a
->fd
, "Failed to set penalty on interface '%s' from queue '%s'\n", interface
, queuename
);
6291 static char *complete_queue_rule_show(const char *line
, const char *word
, int pos
, int state
)
6294 struct rule_list
*rl_iter
;
6295 int wordlen
= strlen(word
);
6297 if (pos
!= 3) /* Wha? */ {
6298 ast_log(LOG_DEBUG
, "Hitting this???, pos is %d\n", pos
);
6302 AST_LIST_LOCK(&rule_lists
);
6303 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
6304 if (!strncasecmp(word
, rl_iter
->name
, wordlen
) && ++which
> state
) {
6305 ret
= ast_strdup(rl_iter
->name
);
6309 AST_LIST_UNLOCK(&rule_lists
);
6314 static char *handle_queue_rule_show(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6317 struct rule_list
*rl_iter
;
6318 struct penalty_rule
*pr_iter
;
6321 e
->command
= "queue rules show";
6323 "Usage: queue rules show [rulename]\n"
6324 "Show the list of rules associated with rulename. If no\n"
6325 "rulename is specified, list all rules defined in queuerules.conf\n";
6328 return complete_queue_rule_show(a
->line
, a
->word
, a
->pos
, a
->n
);
6331 if (a
->argc
!= 3 && a
->argc
!= 4)
6332 return CLI_SHOWUSAGE
;
6334 rule
= a
->argc
== 4 ? a
->argv
[3] : "";
6335 AST_LIST_LOCK(&rule_lists
);
6336 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
6337 if (ast_strlen_zero(rule
) || !strcasecmp(rl_iter
->name
, rule
)) {
6338 ast_cli(a
->fd
, "Rule: %s\n", rl_iter
->name
);
6339 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
6340 ast_cli(a
->fd
, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter
->time
, pr_iter
->max_relative
? "by" : "to", pr_iter
->max_value
, pr_iter
->min_relative
? "by" : "to", pr_iter
->min_value
);
6344 AST_LIST_UNLOCK(&rule_lists
);
6348 static char *handle_queue_rule_reload(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6352 e
->command
= "queue rules reload";
6354 "Usage: queue rules reload\n"
6355 "Reloads rules defined in queuerules.conf\n";
6360 reload_queue_rules(1);
6364 static const char qpm_cmd_usage
[] =
6365 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
6367 static const char qum_cmd_usage
[] =
6368 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
6370 static const char qsmp_cmd_usage
[] =
6371 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
6373 static struct ast_cli_entry cli_queue
[] = {
6374 AST_CLI_DEFINE(queue_show
, "Show status of a specified queue"),
6375 AST_CLI_DEFINE(handle_queue_add_member
, "Add a channel to a specified queue"),
6376 AST_CLI_DEFINE(handle_queue_remove_member
, "Removes a channel from a specified queue"),
6377 AST_CLI_DEFINE(handle_queue_pause_member
, "Pause or unpause a queue member"),
6378 AST_CLI_DEFINE(handle_queue_set_member_penalty
, "Set penalty for a channel of a specified queue"),
6379 AST_CLI_DEFINE(handle_queue_rule_show
, "Show the rules defined in queuerules.conf"),
6380 AST_CLI_DEFINE(handle_queue_rule_reload
, "Reload the rules defined in queuerules.conf"),
6383 static int unload_module(void)
6386 struct ast_context
*con
;
6387 struct ao2_iterator q_iter
;
6388 struct call_queue
*q
= NULL
;
6390 ast_cli_unregister_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
6391 res
= ast_manager_unregister("QueueStatus");
6392 res
|= ast_manager_unregister("Queues");
6393 res
|= ast_manager_unregister("QueueRule");
6394 res
|= ast_manager_unregister("QueueSummary");
6395 res
|= ast_manager_unregister("QueueAdd");
6396 res
|= ast_manager_unregister("QueueRemove");
6397 res
|= ast_manager_unregister("QueuePause");
6398 res
|= ast_manager_unregister("QueueLog");
6399 res
|= ast_manager_unregister("QueuePenalty");
6400 res
|= ast_unregister_application(app_aqm
);
6401 res
|= ast_unregister_application(app_rqm
);
6402 res
|= ast_unregister_application(app_pqm
);
6403 res
|= ast_unregister_application(app_upqm
);
6404 res
|= ast_unregister_application(app_ql
);
6405 res
|= ast_unregister_application(app
);
6406 res
|= ast_custom_function_unregister(&queuevar_function
);
6407 res
|= ast_custom_function_unregister(&queuemembercount_function
);
6408 res
|= ast_custom_function_unregister(&queuemembercount_dep
);
6409 res
|= ast_custom_function_unregister(&queuememberlist_function
);
6410 res
|= ast_custom_function_unregister(&queuewaitingcount_function
);
6411 res
|= ast_custom_function_unregister(&queuememberpenalty_function
);
6413 if (device_state_sub
)
6414 ast_event_unsubscribe(device_state_sub
);
6416 if ((con
= ast_context_find("app_queue_gosub_virtual_context"))) {
6417 ast_context_remove_extension2(con
, "s", 1, NULL
, 0);
6418 ast_context_destroy(con
, "app_queue"); /* leave no trace */
6421 clear_and_free_interfaces();
6423 q_iter
= ao2_iterator_init(queues
, 0);
6424 while ((q
= ao2_iterator_next(&q_iter
))) {
6425 ao2_unlink(queues
, q
);
6428 ao2_ref(queues
, -1);
6429 devicestate_tps
= ast_taskprocessor_unreference(devicestate_tps
);
6430 ast_unload_realtime("queue_members");
6434 static int load_module(void)
6437 struct ast_context
*con
;
6439 queues
= ao2_container_alloc(MAX_QUEUE_BUCKETS
, queue_hash_cb
, queue_cmp_cb
);
6441 if (!reload_queues(0))
6442 return AST_MODULE_LOAD_DECLINE
;
6444 con
= ast_context_find_or_create(NULL
, NULL
, "app_queue_gosub_virtual_context", "app_queue");
6446 ast_log(LOG_ERROR
, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
6448 ast_add_extension2(con
, 1, "s", 1, NULL
, NULL
, "KeepAlive", ast_strdup(""), ast_free_ptr
, "app_queue");
6450 if (queue_persistent_members
)
6451 reload_queue_members();
6453 ast_cli_register_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
6454 res
= ast_register_application(app
, queue_exec
, synopsis
, descrip
);
6455 res
|= ast_register_application(app_aqm
, aqm_exec
, app_aqm_synopsis
, app_aqm_descrip
);
6456 res
|= ast_register_application(app_rqm
, rqm_exec
, app_rqm_synopsis
, app_rqm_descrip
);
6457 res
|= ast_register_application(app_pqm
, pqm_exec
, app_pqm_synopsis
, app_pqm_descrip
);
6458 res
|= ast_register_application(app_upqm
, upqm_exec
, app_upqm_synopsis
, app_upqm_descrip
);
6459 res
|= ast_register_application(app_ql
, ql_exec
, app_ql_synopsis
, app_ql_descrip
);
6460 res
|= ast_manager_register("Queues", 0, manager_queues_show
, "Queues");
6461 res
|= ast_manager_register("QueueStatus", 0, manager_queues_status
, "Queue Status");
6462 res
|= ast_manager_register("QueueSummary", 0, manager_queues_summary
, "Queue Summary");
6463 res
|= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT
, manager_add_queue_member
, "Add interface to queue.");
6464 res
|= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT
, manager_remove_queue_member
, "Remove interface from queue.");
6465 res
|= ast_manager_register("QueuePause", EVENT_FLAG_AGENT
, manager_pause_queue_member
, "Makes a queue member temporarily unavailable");
6466 res
|= ast_manager_register("QueueLog", EVENT_FLAG_AGENT
, manager_queue_log_custom
, "Adds custom entry in queue_log");
6467 res
|= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT
, manager_queue_member_penalty
, "Set the penalty for a queue member");
6468 res
|= ast_manager_register("QueueRule", 0, manager_queue_rule_show
, "Queue Rules");
6469 res
|= ast_custom_function_register(&queuevar_function
);
6470 res
|= ast_custom_function_register(&queuemembercount_function
);
6471 res
|= ast_custom_function_register(&queuemembercount_dep
);
6472 res
|= ast_custom_function_register(&queuememberlist_function
);
6473 res
|= ast_custom_function_register(&queuewaitingcount_function
);
6474 res
|= ast_custom_function_register(&queuememberpenalty_function
);
6476 if (!(devicestate_tps
= ast_taskprocessor_get("app_queue", 0))) {
6477 ast_log(LOG_WARNING
, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
6480 if (!(device_state_sub
= ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE
, device_state_cb
, NULL
, AST_EVENT_IE_END
))) {
6484 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1
, 1, "uniqueid", RQ_UINTEGER2
, 5, SENTINEL
);
6486 return res
? AST_MODULE_LOAD_DECLINE
: 0;
6489 static int reload(void)
6491 ast_unload_realtime("queue_members");
6496 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "True Call Queueing",
6497 .load
= load_module
,
6498 .unload
= unload_module
,