2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief True call queues with optional send URL on answer
23 * \author Mark Spencer <markster@digium.com>
25 * \arg Config in \ref Config_qu queues.conf
27 * \par Development notes
28 * \note 2004-11-25: Persistent Dynamic Members added by:
29 * NetNation Communications (www.netnation.com)
30 * Kevin Lindsay <kevinl@netnation.com>
32 * Each dynamic agent in each queue is now stored in the astdb.
33 * When asterisk is restarted, each agent will be automatically
34 * readded into their recorded queues. This feature can be
35 * configured with the 'persistent_members=<1|0>' setting in the
36 * '[general]' category in queues.conf. The default is on.
38 * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
40 * \note These features added by David C. Troy <dave@toad.net>:
41 * - Per-queue holdtime calculation
42 * - Estimated holdtime announcement
43 * - Position announcement
44 * - Abandoned/completed call counters
45 * - Failout timer passed as optional app parameter
46 * - Optional monitoring of calls, started when call is answered
48 * Patch Version 1.07 2003-12-24 01
50 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
51 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
53 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
54 * by Matthew Enger <m.enger@xi.com.au>
56 * \ingroup applications
60 <depend>res_monitor</depend>
65 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
68 #include <sys/signal.h>
69 #include <netinet/in.h>
71 #include "asterisk/lock.h"
72 #include "asterisk/file.h"
73 #include "asterisk/channel.h"
74 #include "asterisk/pbx.h"
75 #include "asterisk/app.h"
76 #include "asterisk/linkedlists.h"
77 #include "asterisk/module.h"
78 #include "asterisk/translate.h"
79 #include "asterisk/say.h"
80 #include "asterisk/features.h"
81 #include "asterisk/musiconhold.h"
82 #include "asterisk/cli.h"
83 #include "asterisk/manager.h"
84 #include "asterisk/config.h"
85 #include "asterisk/monitor.h"
86 #include "asterisk/utils.h"
87 #include "asterisk/causes.h"
88 #include "asterisk/astdb.h"
89 #include "asterisk/devicestate.h"
90 #include "asterisk/stringfields.h"
91 #include "asterisk/event.h"
92 #include "asterisk/astobj2.h"
93 #include "asterisk/strings.h"
94 #include "asterisk/global_datastores.h"
95 #include "asterisk/taskprocessor.h"
98 * \par Please read before modifying this file.
99 * There are three locks which are regularly used
100 * throughout this file, the queue list lock, the lock
101 * for each individual queue, and the interface list lock.
102 * Please be extra careful to always lock in the following order
104 * 2) individual queue lock
105 * 3) interface list lock
106 * This order has sort of "evolved" over the lifetime of this
107 * application, but it is now in place this way, so please adhere
113 QUEUE_STRATEGY_RINGALL
= 0,
114 QUEUE_STRATEGY_LEASTRECENT
,
115 QUEUE_STRATEGY_FEWESTCALLS
,
116 QUEUE_STRATEGY_RANDOM
,
117 QUEUE_STRATEGY_RRMEMORY
,
118 QUEUE_STRATEGY_LINEAR
,
119 QUEUE_STRATEGY_WRANDOM
122 static const struct strategy
{
126 { QUEUE_STRATEGY_RINGALL
, "ringall" },
127 { QUEUE_STRATEGY_LEASTRECENT
, "leastrecent" },
128 { QUEUE_STRATEGY_FEWESTCALLS
, "fewestcalls" },
129 { QUEUE_STRATEGY_RANDOM
, "random" },
130 { QUEUE_STRATEGY_RRMEMORY
, "rrmemory" },
131 { QUEUE_STRATEGY_LINEAR
, "linear" },
132 { QUEUE_STRATEGY_WRANDOM
, "wrandom"},
135 static struct ast_taskprocessor
*devicestate_tps
;
137 #define DEFAULT_RETRY 5
138 #define DEFAULT_TIMEOUT 15
139 #define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
140 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
141 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /*!< The minimum number of seconds between position announcements \
142 The default value of 15 provides backwards compatibility */
143 #define MAX_QUEUE_BUCKETS 53
145 #define RES_OKAY 0 /*!< Action completed */
146 #define RES_EXISTS (-1) /*!< Entry already exists */
147 #define RES_OUTOFMEMORY (-2) /*!< Out of memory */
148 #define RES_NOSUCHQUEUE (-3) /*!< No such queue */
149 #define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
151 static char *app
= "Queue";
153 static char *synopsis
= "Queue a call for a call queue";
155 static char *descrip
=
156 " Queue(queuename[,options[,URL][,announceoverride][,timeout][,AGI][,macro][,gosub][,rule]):\n"
157 "Queues an incoming call in a particular call queue as defined in queues.conf.\n"
158 "This application will return to the dialplan if the queue does not exist, or\n"
159 "any of the join options cause the caller to not enter the queue.\n"
160 "The option string may contain zero or more of the following characters:\n"
161 " 'c' -- continue in the dialplan if the callee hangs up.\n"
162 " 'd' -- data-quality (modem) call (minimum delay).\n"
163 " 'h' -- allow callee to hang up by pressing *.\n"
164 " 'H' -- allow caller to hang up by pressing *.\n"
165 " 'n' -- no retries on the timeout; will exit this application and \n"
166 " go to the next step.\n"
167 " 'i' -- ignore call forward requests from queue members and do nothing\n"
168 " when they are requested.\n"
169 " 'r' -- ring instead of playing MOH. Periodic Announcements are still made, if applicable.\n"
170 " 't' -- allow the called user to transfer the calling user.\n"
171 " 'T' -- allow the calling user to transfer the call.\n"
172 " 'w' -- allow the called user to write the conversation to disk via Monitor.\n"
173 " 'W' -- allow the calling user to write the conversation to disk via Monitor.\n"
174 " 'k' -- Allow the called party to enable parking of the call by sending\n"
175 " the DTMF sequence defined for call parking in features.conf.\n"
176 " 'K' -- Allow the calling party to enable parking of the call by sending\n"
177 " the DTMF sequence defined for call parking in features.conf.\n"
178 " 'x' -- allow the called user to write the conversation to disk via MixMonitor\n"
179 " 'X' -- allow the calling user to write the conversation to disk via MixMonitor\n"
181 " In addition to transferring the call, a call may be parked and then picked\n"
182 "up by another user.\n"
183 " The optional URL will be sent to the called party if the channel supports\n"
185 " The optional AGI parameter will setup an AGI script to be executed on the \n"
186 "calling party's channel once they are connected to a queue member.\n"
187 " The optional macro parameter will run a macro on the \n"
188 "calling party's channel once they are connected to a queue member.\n"
189 " The optional gosub parameter will run a gosub on the \n"
190 "calling party's channel once they are connected to a queue member.\n"
191 " The optional rule parameter will cause the queue's defaultrule to be\n"
192 "overridden by the rule specified.\n"
193 " The timeout will cause the queue to fail out after a specified number of\n"
194 "seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n"
195 " This application sets the following channel variable upon completion:\n"
196 " QUEUESTATUS The status of the call as a text string, one of\n"
197 " TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL | CONTINUE\n";
199 static char *app_aqm
= "AddQueueMember" ;
200 static char *app_aqm_synopsis
= "Dynamically adds queue members" ;
201 static char *app_aqm_descrip
=
202 " AddQueueMember(queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]):\n"
203 "Dynamically adds interface to an existing queue.\n"
204 "If the interface is already in the queue it will return an error.\n"
205 " This application sets the following channel variable upon completion:\n"
206 " AQMSTATUS The status of the attempt to add a queue member as a \n"
207 " text string, one of\n"
208 " ADDED | MEMBERALREADY | NOSUCHQUEUE \n"
209 "Example: AddQueueMember(techsupport,SIP/3000)\n"
212 static char *app_rqm
= "RemoveQueueMember" ;
213 static char *app_rqm_synopsis
= "Dynamically removes queue members" ;
214 static char *app_rqm_descrip
=
215 " RemoveQueueMember(queuename[,interface[,options]]):\n"
216 "Dynamically removes interface to an existing queue\n"
217 "If the interface is NOT in the queue it will return an error.\n"
218 " This application sets the following channel variable upon completion:\n"
219 " RQMSTATUS The status of the attempt to remove a queue member as a\n"
220 " text string, one of\n"
221 " REMOVED | NOTINQUEUE | NOSUCHQUEUE \n"
222 "Example: RemoveQueueMember(techsupport,SIP/3000)\n"
225 static char *app_pqm
= "PauseQueueMember" ;
226 static char *app_pqm_synopsis
= "Pauses a queue member" ;
227 static char *app_pqm_descrip
=
228 " PauseQueueMember([queuename],interface[,options[,reason]]):\n"
229 "Pauses (blocks calls for) a queue member.\n"
230 "The given interface will be paused in the given queue. This prevents\n"
231 "any calls from being sent from the queue to the interface until it is\n"
232 "unpaused with UnpauseQueueMember or the manager interface. If no\n"
233 "queuename is given, the interface is paused in every queue it is a\n"
234 "member of. The application will fail if the interface is not found.\n"
235 "The reason string is entirely optional and is used to add extra information\n"
236 "to the appropriate queue_log entries and manager events.\n"
237 " This application sets the following channel variable upon completion:\n"
238 " PQMSTATUS The status of the attempt to pause a queue member as a\n"
239 " text string, one of\n"
240 " PAUSED | NOTFOUND\n"
241 "Example: PauseQueueMember(,SIP/3000)\n";
243 static char *app_upqm
= "UnpauseQueueMember" ;
244 static char *app_upqm_synopsis
= "Unpauses a queue member" ;
245 static char *app_upqm_descrip
=
246 " UnpauseQueueMember([queuename],interface[,options[,reason]]):\n"
247 "Unpauses (resumes calls to) a queue member.\n"
248 "This is the counterpart to PauseQueueMember and operates exactly the\n"
249 "same way, except it unpauses instead of pausing the given interface.\n"
250 "The reason string is entirely optional and is used to add extra information\n"
251 "to the appropriate queue_log entries and manager events.\n"
252 " This application sets the following channel variable upon completion:\n"
253 " UPQMSTATUS The status of the attempt to unpause a queue \n"
254 " member as a text string, one of\n"
255 " UNPAUSED | NOTFOUND\n"
256 "Example: UnpauseQueueMember(,SIP/3000)\n";
258 static char *app_ql
= "QueueLog" ;
259 static char *app_ql_synopsis
= "Writes to the queue_log" ;
260 static char *app_ql_descrip
=
261 " QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
262 "Allows you to write your own events into the queue log\n"
263 "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n";
265 /*! \brief Persistent Members astdb family */
266 static const char *pm_family
= "Queue/PersistentMembers";
267 /* The maximum length of each persistent member queue database entry */
268 #define PM_MAX_LEN 8192
270 /*! \brief queues.conf [general] option */
271 static int queue_keep_stats
= 0;
273 /*! \brief queues.conf [general] option */
274 static int queue_persistent_members
= 0;
276 /*! \brief queues.conf per-queue weight option */
277 static int use_weight
= 0;
279 /*! \brief queues.conf [general] option */
280 static int autofill_default
= 0;
282 /*! \brief queues.conf [general] option */
283 static int montype_default
= 0;
285 /*! \brief queues.conf [general] option */
286 static int shared_lastcall
= 0;
288 /*! \brief Subscription to device state change events */
289 static struct ast_event_sub
*device_state_sub
;
291 /*! \brief queues.conf [general] option */
292 static int update_cdr
= 0;
298 QUEUE_LEAVEEMPTY
= 3,
299 QUEUE_JOINUNAVAIL
= 4,
300 QUEUE_LEAVEUNAVAIL
= 5,
306 enum queue_result id
;
308 } queue_results
[] = {
309 { QUEUE_UNKNOWN
, "UNKNOWN" },
310 { QUEUE_TIMEOUT
, "TIMEOUT" },
311 { QUEUE_JOINEMPTY
,"JOINEMPTY" },
312 { QUEUE_LEAVEEMPTY
, "LEAVEEMPTY" },
313 { QUEUE_JOINUNAVAIL
, "JOINUNAVAIL" },
314 { QUEUE_LEAVEUNAVAIL
, "LEAVEUNAVAIL" },
315 { QUEUE_FULL
, "FULL" },
316 { QUEUE_CONTINUE
, "CONTINUE" },
319 /*! \brief We define a custom "local user" structure because we
320 * use it not only for keeping track of what is in use but
321 * also for keeping track of who we're dialing.
323 * There are two "links" defined in this structure, q_next and call_next.
324 * q_next links ALL defined callattempt structures into a linked list. call_next is
325 * a link which allows for a subset of the callattempts to be traversed. This subset
326 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
327 * also is helpful so that queue logs are always accurate in the case where a call to
328 * a member times out, especially if using the ringall strategy.
332 struct callattempt
*q_next
;
333 struct callattempt
*call_next
;
334 struct ast_channel
*chan
;
340 struct call_queue
*lastqueue
;
341 struct member
*member
;
346 struct call_queue
*parent
; /*!< What queue is our parent */
347 char moh
[80]; /*!< Name of musiconhold to be used */
348 char announce
[80]; /*!< Announcement to play for member when call is answered */
349 char context
[AST_MAX_CONTEXT
]; /*!< Context when user exits queue */
350 char digits
[AST_MAX_EXTENSION
]; /*!< Digits entered while in queue */
351 int valid_digits
; /*!< Digits entered correspond to valid extension. Exited */
352 int pos
; /*!< Where we are in the queue */
353 int prio
; /*!< Our priority */
354 int last_pos_said
; /*!< Last position we told the user */
355 time_t last_periodic_announce_time
; /*!< The last time we played a periodic announcement */
356 int last_periodic_announce_sound
; /*!< The last periodic announcement we made */
357 time_t last_pos
; /*!< Last time we told the user their position */
358 int opos
; /*!< Where we started in the queue */
359 int handled
; /*!< Whether our call was handled */
360 int pending
; /*!< Non-zero if we are attempting to call a member */
361 int max_penalty
; /*!< Limit the members that can take this call to this penalty or lower */
362 int min_penalty
; /*!< Limit the members that can take this call to this penalty or higher */
363 int linpos
; /*!< If using linear strategy, what position are we at? */
364 int linwrapped
; /*!< Is the linpos wrapped? */
365 time_t start
; /*!< When we started holding */
366 time_t expire
; /*!< When this entry should expire (time out of queue) */
367 struct ast_channel
*chan
; /*!< Our channel */
368 AST_LIST_HEAD_NOLOCK(,penalty_rule
) qe_rules
; /*!< Local copy of the queue's penalty rules */
369 struct penalty_rule
*pr
; /*!< Pointer to the next penalty rule to implement */
370 struct queue_ent
*next
; /*!< The next queue entry */
374 char interface
[80]; /*!< Technology/Location to dial to reach this member*/
375 char state_interface
[80]; /*!< Technology/Location from which to read devicestate changes */
376 char membername
[80]; /*!< Member name to use in queue logs */
377 int penalty
; /*!< Are we a last resort? */
378 int calls
; /*!< Number of calls serviced by this member */
379 int dynamic
; /*!< Are we dynamically added? */
380 int realtime
; /*!< Is this member realtime? */
381 int status
; /*!< Status of queue member */
382 int paused
; /*!< Are we paused (not accepting calls)? */
383 time_t lastcall
; /*!< When last successful call was hungup */
384 struct call_queue
*lastqueue
; /*!< Last queue we received a call */
385 unsigned int dead
:1; /*!< Used to detect members deleted in realtime */
386 unsigned int delme
:1; /*!< Flag to delete entry on reload */
387 char rt_uniqueid
[80]; /*!< Unique id of realtime member entry */
390 struct member_interface
{
392 AST_LIST_ENTRY(member_interface
) list
; /*!< Next call queue */
395 static AST_LIST_HEAD_STATIC(interfaces
, member_interface
);
397 /* values used in multi-bit flags in call_queue */
398 #define QUEUE_EMPTY_NORMAL 1
399 #define QUEUE_EMPTY_STRICT 2
400 #define QUEUE_EMPTY_LOOSE 3
401 #define ANNOUNCEHOLDTIME_ALWAYS 1
402 #define ANNOUNCEHOLDTIME_ONCE 2
403 #define QUEUE_EVENT_VARIABLES 3
405 struct penalty_rule
{
406 int time
; /*!< Number of seconds that need to pass before applying this rule */
407 int max_value
; /*!< The amount specified in the penalty rule for max penalty */
408 int min_value
; /*!< The amount specified in the penalty rule for min penalty */
409 int max_relative
; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
410 int min_relative
; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
411 AST_LIST_ENTRY(penalty_rule
) list
; /*!< Next penalty_rule */
414 #define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
415 #define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
416 #define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
417 #define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
420 AST_DECLARE_STRING_FIELDS(
422 AST_STRING_FIELD(name
);
423 /*! Music on Hold class */
424 AST_STRING_FIELD(moh
);
425 /*! Announcement to play when call is answered */
426 AST_STRING_FIELD(announce
);
428 AST_STRING_FIELD(context
);
429 /*! Macro to run upon member connection */
430 AST_STRING_FIELD(membermacro
);
431 /*! Gosub to run upon member connection */
432 AST_STRING_FIELD(membergosub
);
433 /*! Default rule to use if none specified in call to Queue() */
434 AST_STRING_FIELD(defaultrule
);
435 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
436 AST_STRING_FIELD(sound_next
);
437 /*! Sound file: "There are currently" (def. queue-thereare) */
438 AST_STRING_FIELD(sound_thereare
);
439 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
440 AST_STRING_FIELD(sound_calls
);
441 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
442 AST_STRING_FIELD(queue_quantity1
);
443 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
444 AST_STRING_FIELD(queue_quantity2
);
445 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
446 AST_STRING_FIELD(sound_holdtime
);
447 /*! Sound file: "minutes." (def. queue-minutes) */
448 AST_STRING_FIELD(sound_minutes
);
449 /*! Sound file: "minute." (def. queue-minute) */
450 AST_STRING_FIELD(sound_minute
);
451 /*! Sound file: "seconds." (def. queue-seconds) */
452 AST_STRING_FIELD(sound_seconds
);
453 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
454 AST_STRING_FIELD(sound_thanks
);
455 /*! Sound file: Custom announce for caller, no default */
456 AST_STRING_FIELD(sound_callerannounce
);
457 /*! Sound file: "Hold time" (def. queue-reporthold) */
458 AST_STRING_FIELD(sound_reporthold
);
460 /*! Sound files: Custom announce, no default */
461 struct ast_str
*sound_periodicannounce
[MAX_PERIODIC_ANNOUNCEMENTS
];
463 unsigned int joinempty
:2;
464 unsigned int eventwhencalled
:2;
465 unsigned int leavewhenempty
:2;
466 unsigned int ringinuse
:1;
467 unsigned int setinterfacevar
:1;
468 unsigned int setqueuevar
:1;
469 unsigned int setqueueentryvar
:1;
470 unsigned int reportholdtime
:1;
471 unsigned int wrapped
:1;
472 unsigned int timeoutrestart
:1;
473 unsigned int announceholdtime
:2;
474 unsigned int announceposition
:3;
476 unsigned int maskmemberstatus
:1;
477 unsigned int realtime
:1;
478 unsigned int found
:1;
479 int announcepositionlimit
; /*!< How many positions we announce? */
480 int announcefrequency
; /*!< How often to announce their position */
481 int minannouncefrequency
; /*!< The minimum number of seconds between position announcements (def. 15) */
482 int periodicannouncefrequency
; /*!< How often to play periodic announcement */
483 int numperiodicannounce
; /*!< The number of periodic announcements configured */
484 int randomperiodicannounce
; /*!< Are periodic announcments randomly chosen */
485 int roundingseconds
; /*!< How many seconds do we round to? */
486 int holdtime
; /*!< Current avg holdtime, based on recursive boxcar filter */
487 int callscompleted
; /*!< Number of queue calls completed */
488 int callsabandoned
; /*!< Number of queue calls abandoned */
489 int servicelevel
; /*!< seconds setting for servicelevel*/
490 int callscompletedinsl
; /*!< Number of calls answered with servicelevel*/
491 char monfmt
[8]; /*!< Format to use when recording calls */
492 int montype
; /*!< Monitor type Monitor vs. MixMonitor */
493 int count
; /*!< How many entries */
494 int maxlen
; /*!< Max number of entries */
495 int wrapuptime
; /*!< Wrapup Time */
497 int retry
; /*!< Retry calling everyone after this amount of time */
498 int timeout
; /*!< How long to wait for an answer */
499 int weight
; /*!< Respective weight */
500 int autopause
; /*!< Auto pause queue members if they fail to answer */
502 /* Queue strategy things */
503 int rrpos
; /*!< Round Robin - position */
504 int memberdelay
; /*!< Seconds to delay connecting member to caller */
505 int autofill
; /*!< Ignore the head call status and ring an available agent */
507 struct ao2_container
*members
; /*!< Head of the list of members */
509 * \brief Number of members _logged in_
510 * \note There will be members in the members container that are not logged
511 * in, so this can not simply be replaced with ao2_container_count().
514 struct queue_ent
*head
; /*!< Head of the list of callers */
515 AST_LIST_ENTRY(call_queue
) list
; /*!< Next call queue */
516 AST_LIST_HEAD_NOLOCK(, penalty_rule
) rules
; /*!< The list of penalty rules to invoke */
521 AST_LIST_HEAD_NOLOCK(,penalty_rule
) rules
;
522 AST_LIST_ENTRY(rule_list
) list
;
525 AST_LIST_HEAD_STATIC(rule_lists
, rule_list
);
527 static struct ao2_container
*queues
;
529 static void update_realtime_members(struct call_queue
*q
);
530 static int set_member_paused(const char *queuename
, const char *interface
, const char *reason
, int paused
);
532 /*! \brief sets the QUEUESTATUS channel variable */
533 static void set_queue_result(struct ast_channel
*chan
, enum queue_result res
)
537 for (i
= 0; i
< ARRAY_LEN(queue_results
); i
++) {
538 if (queue_results
[i
].id
== res
) {
539 pbx_builtin_setvar_helper(chan
, "QUEUESTATUS", queue_results
[i
].text
);
545 static const char *int2strat(int strategy
)
549 for (x
= 0; x
< ARRAY_LEN(strategies
); x
++) {
550 if (strategy
== strategies
[x
].strategy
)
551 return strategies
[x
].name
;
557 static int strat2int(const char *strategy
)
561 for (x
= 0; x
< ARRAY_LEN(strategies
); x
++) {
562 if (!strcasecmp(strategy
, strategies
[x
].name
))
563 return strategies
[x
].strategy
;
569 static int queue_hash_cb(const void *obj
, const int flags
)
571 const struct call_queue
*q
= obj
;
572 return ast_str_hash(q
->name
);
575 static int queue_cmp_cb(void *obj
, void *arg
, int flags
)
577 struct call_queue
*q
= obj
, *q2
= arg
;
578 return !strcasecmp(q
->name
, q2
->name
) ? CMP_MATCH
: 0;
581 static inline struct call_queue
*queue_ref(struct call_queue
*q
)
587 static inline struct call_queue
*queue_unref(struct call_queue
*q
)
593 /*! \brief Set variables of queue */
594 static void set_queue_variables(struct queue_ent
*qe
)
596 char interfacevar
[256]="";
599 if (qe
->parent
->setqueuevar
) {
601 if (qe
->parent
->callscompleted
> 0)
602 sl
= 100 * ((float) qe
->parent
->callscompletedinsl
/ (float) qe
->parent
->callscompleted
);
604 snprintf(interfacevar
, sizeof(interfacevar
),
605 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
606 qe
->parent
->name
, qe
->parent
->maxlen
, int2strat(qe
->parent
->strategy
), qe
->parent
->count
, qe
->parent
->holdtime
, qe
->parent
->callscompleted
,
607 qe
->parent
->callsabandoned
, qe
->parent
->servicelevel
, sl
);
609 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
613 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
614 static inline void insert_entry(struct call_queue
*q
, struct queue_ent
*prev
, struct queue_ent
*new, int *pos
)
616 struct queue_ent
*cur
;
633 enum queue_member_status
{
635 QUEUE_NO_REACHABLE_MEMBERS
,
636 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
,
640 /*! \brief Check if members are available
642 * This function checks to see if members are available to be called. If any member
643 * is available, the function immediately returns QUEUE_NORMAL. If no members are available,
644 * the appropriate reason why is returned
646 static enum queue_member_status
get_member_status(struct call_queue
*q
, int max_penalty
, int min_penalty
)
648 struct member
*member
;
649 struct ao2_iterator mem_iter
;
650 enum queue_member_status result
= QUEUE_NO_MEMBERS
;
653 mem_iter
= ao2_iterator_init(q
->members
, 0);
654 for (; (member
= ao2_iterator_next(&mem_iter
)); ao2_ref(member
, -1)) {
655 if ((max_penalty
&& (member
->penalty
> max_penalty
)) || (min_penalty
&& (member
->penalty
< min_penalty
)))
658 switch (member
->status
) {
659 case AST_DEVICE_INVALID
:
662 case AST_DEVICE_UNAVAILABLE
:
663 if (result
!= QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)
664 result
= QUEUE_NO_REACHABLE_MEMBERS
;
667 if (member
->paused
) {
668 result
= QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
;
683 AST_LIST_ENTRY(statechange
) entry
;
688 /*! \brief set a member's status based on device state of that member's state_interface.
690 * Lock interface list find sc, iterate through each queues queue_member list for member to
691 * update state inside queues
693 static int update_status(const char *interface
, const int status
)
696 struct ao2_iterator mem_iter
, queue_iter
;
697 struct call_queue
*q
;
699 queue_iter
= ao2_iterator_init(queues
, 0);
700 while ((q
= ao2_iterator_next(&queue_iter
))) {
702 mem_iter
= ao2_iterator_init(q
->members
, 0);
703 while ((cur
= ao2_iterator_next(&mem_iter
))) {
706 tmp_interface
= ast_strdupa(cur
->state_interface
);
707 if ((slash_pos
= strchr(interface
, '/')))
708 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
711 if (strcasecmp(interface
, tmp_interface
)) {
716 if (cur
->status
!= status
) {
717 cur
->status
= status
;
718 if (q
->maskmemberstatus
) {
723 manager_event(EVENT_FLAG_AGENT
, "QueueMemberStatus",
733 q
->name
, cur
->interface
, cur
->membername
, cur
->dynamic
? "dynamic" : cur
->realtime
? "realtime" : "static",
734 cur
->penalty
, cur
->calls
, (int)cur
->lastcall
, cur
->status
, cur
->paused
);
745 /*! \brief set a member's status based on device state of that member's interface*/
746 static int handle_statechange(void *datap
)
748 struct member_interface
*curint
;
751 struct statechange
*sc
= datap
;
753 technology
= ast_strdupa(sc
->dev
);
754 loc
= strchr(technology
, '/');
762 AST_LIST_LOCK(&interfaces
);
763 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
766 interface
= ast_strdupa(curint
->interface
);
767 if ((slash_pos
= strchr(interface
, '/')))
768 if ((slash_pos
= strchr(slash_pos
+ 1, '/')))
771 if (!strcasecmp(interface
, sc
->dev
))
774 AST_LIST_UNLOCK(&interfaces
);
777 if (option_debug
> 2)
778 ast_log(LOG_DEBUG
, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology
, loc
, sc
->state
, devstate2str(sc
->state
));
783 ast_log(LOG_DEBUG
, "Device '%s/%s' changed to state '%d' (%s)\n", technology
, loc
, sc
->state
, devstate2str(sc
->state
));
785 update_status(sc
->dev
, sc
->state
);
790 static void device_state_cb(const struct ast_event
*event
, void *unused
)
792 enum ast_device_state state
;
794 struct statechange
*sc
;
797 state
= ast_event_get_ie_uint(event
, AST_EVENT_IE_STATE
);
798 device
= ast_event_get_ie_str(event
, AST_EVENT_IE_DEVICE
);
800 if (ast_strlen_zero(device
)) {
801 ast_log(LOG_ERROR
, "Received invalid event that had no device IE\n");
804 datapsize
= sizeof(*sc
) + strlen(device
) + 1;
805 if (!(sc
= ast_calloc(1, datapsize
))) {
806 ast_log(LOG_ERROR
, "failed to calloc a state change struct\n");
810 strcpy(sc
->dev
, device
);
811 if (ast_taskprocessor_push(devicestate_tps
, handle_statechange
, sc
) < 0) {
816 /*! \brief allocate space for new queue member and set fields based on parameters passed */
817 static struct member
*create_queue_member(const char *interface
, const char *membername
, int penalty
, int paused
, const char *state_interface
)
821 if ((cur
= ao2_alloc(sizeof(*cur
), NULL
))) {
822 cur
->penalty
= penalty
;
823 cur
->paused
= paused
;
824 ast_copy_string(cur
->interface
, interface
, sizeof(cur
->interface
));
825 if (!ast_strlen_zero(state_interface
))
826 ast_copy_string(cur
->state_interface
, state_interface
, sizeof(cur
->state_interface
));
828 ast_copy_string(cur
->state_interface
, interface
, sizeof(cur
->state_interface
));
829 if (!ast_strlen_zero(membername
))
830 ast_copy_string(cur
->membername
, membername
, sizeof(cur
->membername
));
832 ast_copy_string(cur
->membername
, interface
, sizeof(cur
->membername
));
833 if (!strchr(cur
->interface
, '/'))
834 ast_log(LOG_WARNING
, "No location at interface '%s'\n", interface
);
835 cur
->status
= ast_device_state(cur
->state_interface
);
842 static int compress_char(const char c
)
852 static int member_hash_fn(const void *obj
, const int flags
)
854 const struct member
*mem
= obj
;
855 const char *chname
= strchr(mem
->interface
, '/');
858 chname
= mem
->interface
;
859 for (i
= 0; i
< 5 && chname
[i
]; i
++)
860 ret
+= compress_char(chname
[i
]) << (i
* 6);
864 static int member_cmp_fn(void *obj1
, void *obj2
, int flags
)
866 struct member
*mem1
= obj1
, *mem2
= obj2
;
867 return strcasecmp(mem1
->interface
, mem2
->interface
) ? 0 : CMP_MATCH
;
871 * \brief Initialize Queue default values.
872 * \note the queue's lock must be held before executing this function
874 static void init_queue(struct call_queue
*q
)
877 struct penalty_rule
*pr_iter
;
880 q
->retry
= DEFAULT_RETRY
;
883 q
->announcefrequency
= 0;
884 q
->minannouncefrequency
= DEFAULT_MIN_ANNOUNCE_FREQUENCY
;
885 q
->announceholdtime
= 1;
886 q
->announcepositionlimit
= 10; /* Default 10 positions */
887 q
->announceposition
= ANNOUNCEPOSITION_YES
; /* Default yes */
888 q
->roundingseconds
= 0; /* Default - don't announce seconds */
891 q
->setinterfacevar
= 0;
893 q
->setqueueentryvar
= 0;
894 q
->autofill
= autofill_default
;
895 q
->montype
= montype_default
;
897 q
->reportholdtime
= 0;
900 q
->leavewhenempty
= 0;
902 q
->maskmemberstatus
= 0;
903 q
->eventwhencalled
= 0;
905 q
->timeoutrestart
= 0;
906 q
->periodicannouncefrequency
= 0;
907 q
->randomperiodicannounce
= 0;
908 q
->numperiodicannounce
= 0;
910 if (q
->strategy
== QUEUE_STRATEGY_LINEAR
)
911 /* linear strategy depends on order, so we have to place all members in a single bucket */
912 q
->members
= ao2_container_alloc(1, member_hash_fn
, member_cmp_fn
);
914 q
->members
= ao2_container_alloc(37, member_hash_fn
, member_cmp_fn
);
919 ast_string_field_set(q
, sound_next
, "queue-youarenext");
920 ast_string_field_set(q
, sound_thereare
, "queue-thereare");
921 ast_string_field_set(q
, sound_calls
, "queue-callswaiting");
922 ast_string_field_set(q
, queue_quantity1
, "queue-quantity1");
923 ast_string_field_set(q
, queue_quantity2
, "queue-quantity2");
924 ast_string_field_set(q
, sound_holdtime
, "queue-holdtime");
925 ast_string_field_set(q
, sound_minutes
, "queue-minutes");
926 ast_string_field_set(q
, sound_minute
, "queue-minute");
927 ast_string_field_set(q
, sound_seconds
, "queue-seconds");
928 ast_string_field_set(q
, sound_thanks
, "queue-thankyou");
929 ast_string_field_set(q
, sound_reporthold
, "queue-reporthold");
931 if ((q
->sound_periodicannounce
[0] = ast_str_create(32)))
932 ast_str_set(&q
->sound_periodicannounce
[0], 0, "queue-periodic-announce");
934 for (i
= 1; i
< MAX_PERIODIC_ANNOUNCEMENTS
; i
++) {
935 if (q
->sound_periodicannounce
[i
])
936 ast_str_set(&q
->sound_periodicannounce
[i
], 0, "%s", "");
939 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&q
->rules
,list
)))
943 static void clear_queue(struct call_queue
*q
)
946 q
->callscompleted
= 0;
947 q
->callsabandoned
= 0;
948 q
->callscompletedinsl
= 0;
952 static int add_to_interfaces(const char *interface
)
954 struct member_interface
*curint
;
956 AST_LIST_LOCK(&interfaces
);
957 AST_LIST_TRAVERSE(&interfaces
, curint
, list
) {
958 if (!strcasecmp(curint
->interface
, interface
))
963 AST_LIST_UNLOCK(&interfaces
);
967 ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface
);
969 if ((curint
= ast_calloc(1, sizeof(*curint
)))) {
970 ast_copy_string(curint
->interface
, interface
, sizeof(curint
->interface
));
971 AST_LIST_INSERT_HEAD(&interfaces
, curint
, list
);
973 AST_LIST_UNLOCK(&interfaces
);
978 static int interface_exists_global(const char *interface
)
980 struct call_queue
*q
;
981 struct member
*mem
, tmpmem
;
982 struct ao2_iterator queue_iter
, mem_iter
;
985 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
986 queue_iter
= ao2_iterator_init(queues
, 0);
987 while ((q
= ao2_iterator_next(&queue_iter
))) {
989 mem_iter
= ao2_iterator_init(q
->members
, 0);
990 while ((mem
= ao2_iterator_next(&mem_iter
))) {
991 if (!strcasecmp(mem
->state_interface
, interface
)) {
1004 static int remove_from_interfaces(const char *interface
)
1006 struct member_interface
*curint
;
1008 if (interface_exists_global(interface
))
1011 AST_LIST_LOCK(&interfaces
);
1012 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces
, curint
, list
) {
1013 if (!strcasecmp(curint
->interface
, interface
)) {
1014 ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface
);
1015 AST_LIST_REMOVE_CURRENT(list
);
1020 AST_LIST_TRAVERSE_SAFE_END
;
1021 AST_LIST_UNLOCK(&interfaces
);
1026 static void clear_and_free_interfaces(void)
1028 struct member_interface
*curint
;
1030 AST_LIST_LOCK(&interfaces
);
1031 while ((curint
= AST_LIST_REMOVE_HEAD(&interfaces
, list
)))
1033 AST_LIST_UNLOCK(&interfaces
);
1037 * \brief Change queue penalty by adding rule.
1039 * Check rule for errors with time or fomatting, see if rule is relative to rest
1040 * of queue, iterate list of rules to find correct insertion point, insert and return.
1041 * \retval -1 on failure
1042 * \retval 0 on success
1043 * \note Call this with the rule_lists locked
1045 static int insert_penaltychange (const char *list_name
, const char *content
, const int linenum
)
1047 char *timestr
, *maxstr
, *minstr
, *contentdup
;
1048 struct penalty_rule
*rule
= NULL
, *rule_iter
;
1049 struct rule_list
*rl_iter
;
1050 int time
, inserted
= 0;
1052 if (!(rule
= ast_calloc(1, sizeof(*rule
)))) {
1053 ast_log(LOG_ERROR
, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum
);
1057 contentdup
= ast_strdupa(content
);
1059 if (!(maxstr
= strchr(contentdup
, ','))) {
1060 ast_log(LOG_WARNING
, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum
);
1066 timestr
= contentdup
;
1068 if ((time
= atoi(timestr
)) < 0) {
1069 ast_log(LOG_WARNING
, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum
);
1076 if ((minstr
= strchr(maxstr
,',')))
1079 /* The last check will evaluate true if either no penalty change is indicated for a given rule
1080 * OR if a min penalty change is indicated but no max penalty change is */
1081 if (*maxstr
== '+' || *maxstr
== '-' || *maxstr
== '\0') {
1082 rule
->max_relative
= 1;
1085 rule
->max_value
= atoi(maxstr
);
1087 if (!ast_strlen_zero(minstr
)) {
1088 if (*minstr
== '+' || *minstr
== '-')
1089 rule
->min_relative
= 1;
1090 rule
->min_value
= atoi(minstr
);
1091 } else /*there was no minimum specified, so assume this means no change*/
1092 rule
->min_relative
= 1;
1094 /*We have the rule made, now we need to insert it where it belongs*/
1095 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
){
1096 if (strcasecmp(rl_iter
->name
, list_name
))
1099 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter
->rules
, rule_iter
, list
) {
1100 if (rule
->time
< rule_iter
->time
) {
1101 AST_LIST_INSERT_BEFORE_CURRENT(rule
, list
);
1106 AST_LIST_TRAVERSE_SAFE_END
;
1109 AST_LIST_INSERT_TAIL(&rl_iter
->rules
, rule
, list
);
1116 /*! \brief Configure a queue parameter.
1118 * The failunknown flag is set for config files (and static realtime) to show
1119 * errors for unknown parameters. It is cleared for dynamic realtime to allow
1120 * extra fields in the tables.
1121 * \note For error reporting, line number is passed for .conf static configuration,
1122 * for Realtime queues, linenum is -1.
1124 static void queue_set_param(struct call_queue
*q
, const char *param
, const char *val
, int linenum
, int failunknown
)
1126 if (!strcasecmp(param
, "musicclass") ||
1127 !strcasecmp(param
, "music") || !strcasecmp(param
, "musiconhold")) {
1128 ast_string_field_set(q
, moh
, val
);
1129 } else if (!strcasecmp(param
, "announce")) {
1130 ast_string_field_set(q
, announce
, val
);
1131 } else if (!strcasecmp(param
, "context")) {
1132 ast_string_field_set(q
, context
, val
);
1133 } else if (!strcasecmp(param
, "timeout")) {
1134 q
->timeout
= atoi(val
);
1136 q
->timeout
= DEFAULT_TIMEOUT
;
1137 } else if (!strcasecmp(param
, "ringinuse")) {
1138 q
->ringinuse
= ast_true(val
);
1139 } else if (!strcasecmp(param
, "setinterfacevar")) {
1140 q
->setinterfacevar
= ast_true(val
);
1141 } else if (!strcasecmp(param
, "setqueuevar")) {
1142 q
->setqueuevar
= ast_true(val
);
1143 } else if (!strcasecmp(param
, "setqueueentryvar")) {
1144 q
->setqueueentryvar
= ast_true(val
);
1145 } else if (!strcasecmp(param
, "monitor-format")) {
1146 ast_copy_string(q
->monfmt
, val
, sizeof(q
->monfmt
));
1147 } else if (!strcasecmp(param
, "membermacro")) {
1148 ast_string_field_set(q
, membermacro
, val
);
1149 } else if (!strcasecmp(param
, "membergosub")) {
1150 ast_string_field_set(q
, membergosub
, val
);
1151 } else if (!strcasecmp(param
, "queue-youarenext")) {
1152 ast_string_field_set(q
, sound_next
, val
);
1153 } else if (!strcasecmp(param
, "queue-thereare")) {
1154 ast_string_field_set(q
, sound_thereare
, val
);
1155 } else if (!strcasecmp(param
, "queue-callswaiting")) {
1156 ast_string_field_set(q
, sound_calls
, val
);
1157 } else if (!strcasecmp(param
, "queue-quantity1")) {
1158 ast_string_field_set(q
, queue_quantity1
, val
);
1159 } else if (!strcasecmp(param
, "queue-quantity2")) {
1160 ast_string_field_set(q
, queue_quantity2
, val
);
1161 } else if (!strcasecmp(param
, "queue-holdtime")) {
1162 ast_string_field_set(q
, sound_holdtime
, val
);
1163 } else if (!strcasecmp(param
, "queue-minutes")) {
1164 ast_string_field_set(q
, sound_minutes
, val
);
1165 } else if (!strcasecmp(param
, "queue-minute")) {
1166 ast_string_field_set(q
, sound_minute
, val
);
1167 } else if (!strcasecmp(param
, "queue-seconds")) {
1168 ast_string_field_set(q
, sound_seconds
, val
);
1169 } else if (!strcasecmp(param
, "queue-thankyou")) {
1170 ast_string_field_set(q
, sound_thanks
, val
);
1171 } else if (!strcasecmp(param
, "queue-callerannounce")) {
1172 ast_string_field_set(q
, sound_callerannounce
, val
);
1173 } else if (!strcasecmp(param
, "queue-reporthold")) {
1174 ast_string_field_set(q
, sound_reporthold
, val
);
1175 } else if (!strcasecmp(param
, "announce-frequency")) {
1176 q
->announcefrequency
= atoi(val
);
1177 } else if (!strcasecmp(param
, "min-announce-frequency")) {
1178 q
->minannouncefrequency
= atoi(val
);
1179 ast_debug(1, "%s=%s for queue '%s'\n", param
, val
, q
->name
);
1180 } else if (!strcasecmp(param
, "announce-round-seconds")) {
1181 q
->roundingseconds
= atoi(val
);
1182 /* Rounding to any other values just doesn't make sense... */
1183 if (!(q
->roundingseconds
== 0 || q
->roundingseconds
== 5 || q
->roundingseconds
== 10
1184 || q
->roundingseconds
== 15 || q
->roundingseconds
== 20 || q
->roundingseconds
== 30)) {
1186 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
1187 "using 0 instead for queue '%s' at line %d of queues.conf\n",
1188 val
, param
, q
->name
, linenum
);
1190 ast_log(LOG_WARNING
, "'%s' isn't a valid value for %s "
1191 "using 0 instead for queue '%s'\n", val
, param
, q
->name
);
1193 q
->roundingseconds
=0;
1195 } else if (!strcasecmp(param
, "announce-holdtime")) {
1196 if (!strcasecmp(val
, "once"))
1197 q
->announceholdtime
= ANNOUNCEHOLDTIME_ONCE
;
1198 else if (ast_true(val
))
1199 q
->announceholdtime
= ANNOUNCEHOLDTIME_ALWAYS
;
1201 q
->announceholdtime
= 0;
1202 } else if (!strcasecmp(param
, "announce-position")) {
1203 if (!strcasecmp(val
, "limit"))
1204 q
->announceposition
= ANNOUNCEPOSITION_LIMIT
;
1205 else if (!strcasecmp(val
, "more"))
1206 q
->announceposition
= ANNOUNCEPOSITION_MORE_THAN
;
1207 else if (ast_true(val
))
1208 q
->announceposition
= ANNOUNCEPOSITION_YES
;
1210 q
->announceposition
= ANNOUNCEPOSITION_NO
;
1211 } else if (!strcasecmp(param
, "announce-position-limit")) {
1212 q
->announcepositionlimit
= atoi(val
);
1213 } else if (!strcasecmp(param
, "periodic-announce")) {
1214 if (strchr(val
, ',')) {
1215 char *s
, *buf
= ast_strdupa(val
);
1218 while ((s
= strsep(&buf
, ",|"))) {
1219 if (!q
->sound_periodicannounce
[i
])
1220 q
->sound_periodicannounce
[i
] = ast_str_create(16);
1221 ast_str_set(&q
->sound_periodicannounce
[i
], 0, "%s", s
);
1223 if (i
== MAX_PERIODIC_ANNOUNCEMENTS
)
1226 q
->numperiodicannounce
= i
;
1228 ast_str_set(&q
->sound_periodicannounce
[0], 0, "%s", val
);
1229 q
->numperiodicannounce
= 1;
1231 } else if (!strcasecmp(param
, "periodic-announce-frequency")) {
1232 q
->periodicannouncefrequency
= atoi(val
);
1233 } else if (!strcasecmp(param
, "random-periodic-announce")) {
1234 q
->randomperiodicannounce
= ast_true(val
);
1235 } else if (!strcasecmp(param
, "retry")) {
1236 q
->retry
= atoi(val
);
1238 q
->retry
= DEFAULT_RETRY
;
1239 } else if (!strcasecmp(param
, "wrapuptime")) {
1240 q
->wrapuptime
= atoi(val
);
1241 } else if (!strcasecmp(param
, "autofill")) {
1242 q
->autofill
= ast_true(val
);
1243 } else if (!strcasecmp(param
, "monitor-type")) {
1244 if (!strcasecmp(val
, "mixmonitor"))
1246 } else if (!strcasecmp(param
, "autopause")) {
1247 q
->autopause
= ast_true(val
);
1248 } else if (!strcasecmp(param
, "maxlen")) {
1249 q
->maxlen
= atoi(val
);
1252 } else if (!strcasecmp(param
, "servicelevel")) {
1253 q
->servicelevel
= atoi(val
);
1254 } else if (!strcasecmp(param
, "strategy")) {
1255 /* We already have set this, no need to do it again */
1257 } else if (!strcasecmp(param
, "joinempty")) {
1258 if (!strcasecmp(val
, "loose"))
1259 q
->joinempty
= QUEUE_EMPTY_LOOSE
;
1260 else if (!strcasecmp(val
, "strict"))
1261 q
->joinempty
= QUEUE_EMPTY_STRICT
;
1262 else if (ast_true(val
))
1263 q
->joinempty
= QUEUE_EMPTY_NORMAL
;
1266 } else if (!strcasecmp(param
, "leavewhenempty")) {
1267 if (!strcasecmp(val
, "loose"))
1268 q
->leavewhenempty
= QUEUE_EMPTY_LOOSE
;
1269 else if (!strcasecmp(val
, "strict"))
1270 q
->leavewhenempty
= QUEUE_EMPTY_STRICT
;
1271 else if (ast_true(val
))
1272 q
->leavewhenempty
= QUEUE_EMPTY_NORMAL
;
1274 q
->leavewhenempty
= 0;
1275 } else if (!strcasecmp(param
, "eventmemberstatus")) {
1276 q
->maskmemberstatus
= !ast_true(val
);
1277 } else if (!strcasecmp(param
, "eventwhencalled")) {
1278 if (!strcasecmp(val
, "vars")) {
1279 q
->eventwhencalled
= QUEUE_EVENT_VARIABLES
;
1281 q
->eventwhencalled
= ast_true(val
) ? 1 : 0;
1283 } else if (!strcasecmp(param
, "reportholdtime")) {
1284 q
->reportholdtime
= ast_true(val
);
1285 } else if (!strcasecmp(param
, "memberdelay")) {
1286 q
->memberdelay
= atoi(val
);
1287 } else if (!strcasecmp(param
, "weight")) {
1288 q
->weight
= atoi(val
);
1291 /* With Realtime queues, if the last queue using weights is deleted in realtime,
1292 we will not see any effect on use_weight until next reload. */
1293 } else if (!strcasecmp(param
, "timeoutrestart")) {
1294 q
->timeoutrestart
= ast_true(val
);
1295 } else if (!strcasecmp(param
, "defaultrule")) {
1296 ast_string_field_set(q
, defaultrule
, val
);
1297 } else if (failunknown
) {
1299 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1300 q
->name
, param
, linenum
);
1302 ast_log(LOG_WARNING
, "Unknown keyword in queue '%s': %s\n", q
->name
, param
);
1308 * \brief Find rt member record to update otherwise create one.
1310 * Search for member in queue, if found update penalty/paused state,
1311 * if no memeber exists create one flag it as a RT member and add to queue member list.
1313 static void rt_handle_member_record(struct call_queue
*q
, char *interface
, const char *rt_uniqueid
, const char *membername
, const char *penalty_str
, const char *paused_str
, const char* state_interface
)
1316 struct ao2_iterator mem_iter
;
1322 penalty
= atoi(penalty_str
);
1328 paused
= atoi(paused_str
);
1333 /* Find member by realtime uniqueid and update */
1334 mem_iter
= ao2_iterator_init(q
->members
, 0);
1335 while ((m
= ao2_iterator_next(&mem_iter
))) {
1336 if (!strcasecmp(m
->rt_uniqueid
, rt_uniqueid
)) {
1337 m
->dead
= 0; /* Do not delete this one. */
1338 ast_copy_string(m
->rt_uniqueid
, rt_uniqueid
, sizeof(m
->rt_uniqueid
));
1341 if (strcasecmp(state_interface
, m
->state_interface
)) {
1342 remove_from_interfaces(m
->state_interface
);
1343 ast_copy_string(m
->state_interface
, state_interface
, sizeof(m
->state_interface
));
1344 add_to_interfaces(m
->state_interface
);
1346 m
->penalty
= penalty
;
1354 /* Create a new member */
1356 if ((m
= create_queue_member(interface
, membername
, penalty
, paused
, state_interface
))) {
1359 ast_copy_string(m
->rt_uniqueid
, rt_uniqueid
, sizeof(m
->rt_uniqueid
));
1360 add_to_interfaces(m
->state_interface
);
1361 ast_queue_log(q
->name
, "REALTIME", m
->interface
, "ADDMEMBER", "%s", "");
1362 ao2_link(q
->members
, m
);
1370 /*! \brief Iterate through queue's member list and delete them */
1371 static void free_members(struct call_queue
*q
, int all
)
1373 /* Free non-dynamic members */
1375 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
1377 while ((cur
= ao2_iterator_next(&mem_iter
))) {
1378 if (all
|| !cur
->dynamic
) {
1379 ao2_unlink(q
->members
, cur
);
1380 remove_from_interfaces(cur
->state_interface
);
1387 /*! \brief Free queue's member list then its string fields */
1388 static void destroy_queue(void *obj
)
1390 struct call_queue
*q
= obj
;
1394 ast_string_field_free_memory(q
);
1395 for (i
= 0; i
< MAX_PERIODIC_ANNOUNCEMENTS
; i
++) {
1396 if (q
->sound_periodicannounce
[i
])
1397 free(q
->sound_periodicannounce
[i
]);
1399 ao2_ref(q
->members
, -1);
1402 static struct call_queue
*alloc_queue(const char *queuename
)
1404 struct call_queue
*q
;
1406 if ((q
= ao2_alloc(sizeof(*q
), destroy_queue
))) {
1407 if (ast_string_field_init(q
, 64)) {
1411 ast_string_field_set(q
, name
, queuename
);
1417 * \brief Reload a single queue via realtime.
1419 * Check for statically defined queue first, check if deleted RT queue,
1420 * check for new RT queue, if queue vars are not defined init them with defaults.
1421 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
1422 * \retval the queue,
1423 * \retval NULL if it doesn't exist.
1424 * \note Should be called with the "queues" container locked.
1426 static struct call_queue
*find_queue_by_name_rt(const char *queuename
, struct ast_variable
*queue_vars
, struct ast_config
*member_config
)
1428 struct ast_variable
*v
;
1429 struct call_queue
*q
, tmpq
= {
1433 struct ao2_iterator mem_iter
;
1434 char *interface
= NULL
;
1435 const char *tmp_name
;
1437 char tmpbuf
[64]; /* Must be longer than the longest queue param name. */
1439 /* Static queues override realtime. */
1440 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
1448 ast_log(LOG_WARNING
, "Static queue '%s' already exists. Not loading from realtime\n", q
->name
);
1454 } else if (!member_config
)
1455 /* Not found in the list, and it's not realtime ... */
1458 /* Check if queue is defined in realtime. */
1460 /* Delete queue from in-core list if it has been deleted in realtime. */
1462 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1463 found condition... So we might delete an in-core queue
1464 in case of DB failure. */
1465 ast_debug(1, "Queue %s not found in realtime.\n", queuename
);
1468 /* Delete if unused (else will be deleted when last caller leaves). */
1469 ao2_unlink(queues
, q
);
1476 /* Create a new queue if an in-core entry does not exist yet. */
1478 struct ast_variable
*tmpvar
= NULL
;
1479 if (!(q
= alloc_queue(queuename
)))
1484 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
1485 * will allocate the members properly
1487 for (tmpvar
= queue_vars
; tmpvar
; tmpvar
= tmpvar
->next
) {
1488 if (!strcasecmp(tmpvar
->name
, "strategy")) {
1489 q
->strategy
= strat2int(tmpvar
->value
);
1490 if (q
->strategy
< 0) {
1491 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1492 tmpvar
->value
, q
->name
);
1493 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
1498 /* We traversed all variables and didn't find a strategy */
1500 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
1501 init_queue(q
); /* Ensure defaults for all parameters not set explicitly. */
1502 ao2_link(queues
, q
);
1505 memset(tmpbuf
, 0, sizeof(tmpbuf
));
1506 for (v
= queue_vars
; v
; v
= v
->next
) {
1507 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
1508 if ((tmp
= strchr(v
->name
, '_'))) {
1509 ast_copy_string(tmpbuf
, v
->name
, sizeof(tmpbuf
));
1512 while ((tmp
= strchr(tmp
, '_')))
1517 if (!ast_strlen_zero(v
->value
)) {
1518 /* Don't want to try to set the option if the value is empty */
1519 queue_set_param(q
, tmp_name
, v
->value
, -1, 0);
1523 /* Temporarily set realtime members dead so we can detect deleted ones.
1524 * Also set the membercount correctly for realtime*/
1525 mem_iter
= ao2_iterator_init(q
->members
, 0);
1526 while ((m
= ao2_iterator_next(&mem_iter
))) {
1533 while ((interface
= ast_category_browse(member_config
, interface
))) {
1534 rt_handle_member_record(q
, interface
,
1535 ast_variable_retrieve(member_config
, interface
, "uniqueid"),
1536 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"),interface
),
1537 ast_variable_retrieve(member_config
, interface
, "penalty"),
1538 ast_variable_retrieve(member_config
, interface
, "paused"),
1539 S_OR(ast_variable_retrieve(member_config
, interface
, "state_interface"),interface
));
1542 /* Delete all realtime members that have been deleted in DB. */
1543 mem_iter
= ao2_iterator_init(q
->members
, 0);
1544 while ((m
= ao2_iterator_next(&mem_iter
))) {
1546 ast_queue_log(q
->name
, "REALTIME", m
->interface
, "REMOVEMEMBER", "%s", "");
1547 ao2_unlink(q
->members
, m
);
1548 remove_from_interfaces(m
->state_interface
);
1559 static struct call_queue
*load_realtime_queue(const char *queuename
)
1561 struct ast_variable
*queue_vars
;
1562 struct ast_config
*member_config
= NULL
;
1563 struct call_queue
*q
= NULL
, tmpq
= {
1567 /* Find the queue in the in-core list first. */
1568 q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
);
1570 if (!q
|| q
->realtime
) {
1571 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
1572 queue operations while waiting for the DB.
1574 This will be two separate database transactions, so we might
1575 see queue parameters as they were before another process
1576 changed the queue and member list as it was after the change.
1577 Thus we might see an empty member list when a queue is
1578 deleted. In practise, this is unlikely to cause a problem. */
1580 queue_vars
= ast_load_realtime("queues", "name", queuename
, NULL
);
1582 member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename
, NULL
);
1583 if (!member_config
) {
1584 ast_log(LOG_ERROR
, "no queue_members defined in your config (extconfig.conf).\n");
1585 ast_variables_destroy(queue_vars
);
1591 q
= find_queue_by_name_rt(queuename
, queue_vars
, member_config
);
1593 ast_config_destroy(member_config
);
1595 ast_variables_destroy(queue_vars
);
1599 update_realtime_members(q
);
1604 static int update_realtime_member_field(struct member
*mem
, const char *queue_name
, const char *field
, const char *value
)
1608 if (ast_strlen_zero(mem
->rt_uniqueid
))
1611 if ((ast_update_realtime("queue_members", "uniqueid", mem
->rt_uniqueid
, field
, value
, NULL
)) > 0)
1618 static void update_realtime_members(struct call_queue
*q
)
1620 struct ast_config
*member_config
= NULL
;
1622 char *interface
= NULL
;
1623 struct ao2_iterator mem_iter
;
1625 if (!(member_config
= ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q
->name
, NULL
))) {
1626 /*This queue doesn't have realtime members*/
1627 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q
->name
);
1633 /* Temporarily set realtime members dead so we can detect deleted ones.*/
1634 mem_iter
= ao2_iterator_init(q
->members
, 0);
1635 while ((m
= ao2_iterator_next(&mem_iter
))) {
1641 while ((interface
= ast_category_browse(member_config
, interface
))) {
1642 rt_handle_member_record(q
, interface
,
1643 ast_variable_retrieve(member_config
, interface
, "uniqueid"),
1644 S_OR(ast_variable_retrieve(member_config
, interface
, "membername"), interface
),
1645 ast_variable_retrieve(member_config
, interface
, "penalty"),
1646 ast_variable_retrieve(member_config
, interface
, "paused"),
1647 S_OR(ast_variable_retrieve(member_config
, interface
, "state_interface"), interface
));
1650 /* Delete all realtime members that have been deleted in DB. */
1651 mem_iter
= ao2_iterator_init(q
->members
, 0);
1652 while ((m
= ao2_iterator_next(&mem_iter
))) {
1654 ast_queue_log(q
->name
, "REALTIME", m
->interface
, "REMOVEMEMBER", "%s", "");
1655 ao2_unlink(q
->members
, m
);
1656 remove_from_interfaces(m
->state_interface
);
1662 ast_config_destroy(member_config
);
1665 static int join_queue(char *queuename
, struct queue_ent
*qe
, enum queue_result
*reason
)
1667 struct call_queue
*q
;
1668 struct queue_ent
*cur
, *prev
= NULL
;
1672 enum queue_member_status stat
;
1674 if (!(q
= load_realtime_queue(queuename
)))
1680 /* This is our one */
1681 stat
= get_member_status(q
, qe
->max_penalty
, qe
->min_penalty
);
1682 if (!q
->joinempty
&& (stat
== QUEUE_NO_MEMBERS
))
1683 *reason
= QUEUE_JOINEMPTY
;
1684 else if ((q
->joinempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_MEMBERS
))
1685 *reason
= QUEUE_JOINUNAVAIL
;
1686 else if ((q
->joinempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_MEMBERS
))
1687 *reason
= QUEUE_JOINUNAVAIL
;
1688 else if (q
->maxlen
&& (q
->count
>= q
->maxlen
))
1689 *reason
= QUEUE_FULL
;
1691 /* There's space for us, put us at the right position inside
1693 * Take into account the priority of the calling user */
1698 /* We have higher priority than the current user, enter
1699 * before him, after all the other users with priority
1700 * higher or equal to our priority. */
1701 if ((!inserted
) && (qe
->prio
> cur
->prio
)) {
1702 insert_entry(q
, prev
, qe
, &pos
);
1709 /* No luck, join at the end of the queue */
1711 insert_entry(q
, prev
, qe
, &pos
);
1712 ast_copy_string(qe
->moh
, q
->moh
, sizeof(qe
->moh
));
1713 ast_copy_string(qe
->announce
, q
->announce
, sizeof(qe
->announce
));
1714 ast_copy_string(qe
->context
, q
->context
, sizeof(qe
->context
));
1717 manager_event(EVENT_FLAG_CALL
, "Join",
1718 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
1720 S_OR(qe
->chan
->cid
.cid_num
, "unknown"), /* XXX somewhere else it is <unknown> */
1721 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
1722 q
->name
, qe
->pos
, q
->count
, qe
->chan
->uniqueid
);
1723 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q
->name
, qe
->chan
->name
, qe
->pos
);
1731 static int play_file(struct ast_channel
*chan
, const char *filename
)
1735 ast_stopstream(chan
);
1737 res
= ast_streamfile(chan
, filename
, chan
->language
);
1739 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
1741 ast_stopstream(chan
);
1747 * \brief Check for valid exit from queue via goto
1748 * \retval 0 if failure
1749 * \retval 1 if successful
1751 static int valid_exit(struct queue_ent
*qe
, char digit
)
1753 int digitlen
= strlen(qe
->digits
);
1755 /* Prevent possible buffer overflow */
1756 if (digitlen
< sizeof(qe
->digits
) - 2) {
1757 qe
->digits
[digitlen
] = digit
;
1758 qe
->digits
[digitlen
+ 1] = '\0';
1760 qe
->digits
[0] = '\0';
1764 /* If there's no context to goto, short-circuit */
1765 if (ast_strlen_zero(qe
->context
))
1768 /* If the extension is bad, then reset the digits to blank */
1769 if (!ast_canmatch_extension(qe
->chan
, qe
->context
, qe
->digits
, 1, qe
->chan
->cid
.cid_num
)) {
1770 qe
->digits
[0] = '\0';
1774 /* We have an exact match */
1775 if (!ast_goto_if_exists(qe
->chan
, qe
->context
, qe
->digits
, 1)) {
1776 qe
->valid_digits
= 1;
1777 /* Return 1 on a successful goto */
1784 static int say_position(struct queue_ent
*qe
, int ringing
)
1786 int res
= 0, avgholdmins
, avgholdsecs
, announceposition
= 0;
1789 /* Let minannouncefrequency seconds pass between the start of each position announcement */
1791 if ((now
- qe
->last_pos
) < qe
->parent
->minannouncefrequency
)
1794 /* If either our position has changed, or we are over the freq timer, say position */
1795 if ((qe
->last_pos_said
== qe
->pos
) && ((now
- qe
->last_pos
) < qe
->parent
->announcefrequency
))
1799 ast_indicate(qe
->chan
,-1);
1801 ast_moh_stop(qe
->chan
);
1804 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_YES
||
1805 qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
||
1806 (qe
->parent
->announceposition
== ANNOUNCEPOSITION_LIMIT
&&
1807 qe
->pos
<= qe
->parent
->announcepositionlimit
))
1808 announceposition
= 1;
1811 if (announceposition
== 1) {
1812 /* Say we're next, if we are */
1814 res
= play_file(qe
->chan
, qe
->parent
->sound_next
);
1820 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
&& qe
->pos
> qe
->parent
->announcepositionlimit
){
1822 res
= play_file(qe
->chan
, qe
->parent
->queue_quantity1
);
1825 res
= ast_say_number(qe
->chan
, qe
->parent
->announcepositionlimit
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
); /* Needs gender */
1830 res
= play_file(qe
->chan
, qe
->parent
->sound_thereare
);
1833 res
= ast_say_number(qe
->chan
, qe
->pos
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
); /* Needs gender */
1837 if (qe
->parent
->announceposition
== ANNOUNCEPOSITION_MORE_THAN
&& qe
->pos
> qe
->parent
->announcepositionlimit
){
1839 res
= play_file(qe
->chan
, qe
->parent
->queue_quantity2
);
1843 res
= play_file(qe
->chan
, qe
->parent
->sound_calls
);
1849 /* Round hold time to nearest minute */
1850 avgholdmins
= abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
)) / 60);
1852 /* If they have specified a rounding then round the seconds as well */
1853 if (qe
->parent
->roundingseconds
) {
1854 avgholdsecs
= (abs(((qe
->parent
->holdtime
+ 30) - (now
- qe
->start
))) - 60 * avgholdmins
) / qe
->parent
->roundingseconds
;
1855 avgholdsecs
*= qe
->parent
->roundingseconds
;
1860 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe
->parent
->name
, avgholdmins
, avgholdsecs
);
1862 /* If the hold time is >1 min, if it's enabled, and if it's not
1863 supposed to be only once and we have already said it, say it */
1864 if ((avgholdmins
+avgholdsecs
) > 0 && (qe
->parent
->announceholdtime
) &&
1865 (!(qe
->parent
->announceholdtime
== ANNOUNCEHOLDTIME_ONCE
) && qe
->last_pos
)) {
1866 res
= play_file(qe
->chan
, qe
->parent
->sound_holdtime
);
1870 if (avgholdmins
> 1) {
1871 res
= ast_say_number(qe
->chan
, avgholdmins
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1875 if (avgholdmins
== 1) {
1876 res
= play_file(qe
->chan
, qe
->parent
->sound_minute
);
1880 res
= play_file(qe
->chan
, qe
->parent
->sound_minutes
);
1885 if (avgholdsecs
> 1) {
1886 res
= ast_say_number(qe
->chan
, avgholdmins
> 1 ? avgholdsecs
: avgholdmins
* 60 + avgholdsecs
, AST_DIGIT_ANY
, qe
->chan
->language
, NULL
);
1890 res
= play_file(qe
->chan
, qe
->parent
->sound_seconds
);
1898 if (announceposition
== 1){
1899 if (qe
->parent
->announceposition
) {
1900 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
1901 qe
->chan
->name
, qe
->parent
->name
, qe
->pos
);
1903 res
= play_file(qe
->chan
, qe
->parent
->sound_thanks
);
1906 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
1909 /* Set our last_pos indicators */
1911 qe
->last_pos_said
= qe
->pos
;
1913 /* Don't restart music on hold if we're about to exit the caller from the queue */
1916 ast_indicate(qe
->chan
, AST_CONTROL_RINGING
);
1918 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
1924 static void recalc_holdtime(struct queue_ent
*qe
, int newholdtime
)
1928 /* Calculate holdtime using a recursive boxcar filter */
1929 /* Thanks to SRT for this contribution */
1930 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
1932 ao2_lock(qe
->parent
);
1933 oldvalue
= qe
->parent
->holdtime
;
1934 qe
->parent
->holdtime
= (((oldvalue
<< 2) - oldvalue
) + newholdtime
) >> 2;
1935 ao2_unlock(qe
->parent
);
1938 /*! \brief Caller leaving queue.
1940 * Search the queue to find the leaving client, if found remove from queue
1941 * create manager event, move others up the queue.
1943 static void leave_queue(struct queue_ent
*qe
)
1945 struct call_queue
*q
;
1946 struct queue_ent
*cur
, *prev
= NULL
;
1947 struct penalty_rule
*pr_iter
;
1950 if (!(q
= qe
->parent
))
1956 for (cur
= q
->head
; cur
; cur
= cur
->next
) {
1960 /* Take us out of the queue */
1961 manager_event(EVENT_FLAG_CALL
, "Leave",
1962 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
1963 qe
->chan
->name
, q
->name
, q
->count
, qe
->chan
->uniqueid
);
1964 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q
->name
, qe
->chan
->name
);
1965 /* Take us out of the queue */
1967 prev
->next
= cur
->next
;
1969 q
->head
= cur
->next
;
1970 /* Free penalty rules */
1971 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&qe
->qe_rules
, list
)))
1974 /* Renumber the people after us in the queue based on a new count */
1981 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
1983 if (!ast_load_realtime("queues", "name", q
->name
, NULL
))
1988 /* It's dead and nobody is in it, so kill it */
1989 ao2_unlink(queues
, q
);
1990 /* unref the container's reference to the queue */
1993 /* unref the explicit ref earlier in the function */
1997 /*! \brief Hang up a list of outgoing calls */
1998 static void hangupcalls(struct callattempt
*outgoing
, struct ast_channel
*exception
)
2000 struct callattempt
*oo
;
2003 /* Hangup any existing lines we have open */
2004 if (outgoing
->chan
&& (outgoing
->chan
!= exception
))
2005 ast_hangup(outgoing
->chan
);
2007 outgoing
= outgoing
->q_next
;
2009 ao2_ref(oo
->member
, -1);
2015 * \brief traverse all defined queues which have calls waiting and contain this member
2016 * \retval 0 if no other queue has precedence (higher weight)
2017 * \retval 1 if found
2019 static int compare_weight(struct call_queue
*rq
, struct member
*member
)
2021 struct call_queue
*q
;
2024 struct ao2_iterator queue_iter
;
2026 /* q's lock and rq's lock already set by try_calling()
2027 * to solve deadlock */
2028 queue_iter
= ao2_iterator_init(queues
, 0);
2029 while ((q
= ao2_iterator_next(&queue_iter
))) {
2030 if (q
== rq
) { /* don't check myself, could deadlock */
2035 if (q
->count
&& q
->members
) {
2036 if ((mem
= ao2_find(q
->members
, member
, OBJ_POINTER
))) {
2037 ast_debug(1, "Found matching member %s in queue '%s'\n", mem
->interface
, q
->name
);
2038 if (q
->weight
> rq
->weight
) {
2039 ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q
->name
, q
->weight
, q
->count
, rq
->name
, rq
->weight
, rq
->count
);
2055 /*! \brief common hangup actions */
2056 static void do_hang(struct callattempt
*o
)
2059 ast_hangup(o
->chan
);
2063 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2064 static char *vars2manager(struct ast_channel
*chan
, char *vars
, size_t len
)
2066 struct ast_str
*buf
= ast_str_alloca(len
+ 1);
2069 if (pbx_builtin_serialize_variables(chan
, &buf
)) {
2072 /* convert "\n" to "\nVariable: " */
2073 strcpy(vars
, "Variable: ");
2076 for (i
= 0, j
= 10; (i
< len
- 1) && (j
< len
- 1); i
++, j
++) {
2079 if (tmp
[i
+ 1] == '\0')
2081 if (tmp
[i
] == '\n') {
2085 ast_copy_string(&(vars
[j
]), "Variable: ", len
- j
);
2095 /* there are no channel variables; leave it blank */
2102 * \brief Part 2 of ring_one
2104 * Does error checking before attempting to request a channel and call a member.
2105 * This function is only called from ring_one().
2106 * Failure can occur if:
2109 * - Wrapup time not expired
2110 * - Priority by another queue
2112 * \retval 1 on success to reach a free agent
2113 * \retval 0 on failure to get agent.
2115 static int ring_entry(struct queue_ent
*qe
, struct callattempt
*tmp
, int *busies
)
2121 const char *macrocontext
, *macroexten
;
2123 /* on entry here, we know that tmp->chan == NULL */
2124 if ((tmp
->lastqueue
&& tmp
->lastqueue
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< tmp
->lastqueue
->wrapuptime
)) ||
2125 (!tmp
->lastqueue
&& qe
->parent
->wrapuptime
&& (time(NULL
) - tmp
->lastcall
< qe
->parent
->wrapuptime
))) {
2126 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
2127 (tmp
->lastqueue
? tmp
->lastqueue
->name
: qe
->parent
->name
), tmp
->interface
);
2129 ast_cdr_busy(qe
->chan
->cdr
);
2130 tmp
->stillgoing
= 0;
2135 if (!qe
->parent
->ringinuse
&& (tmp
->member
->status
!= AST_DEVICE_NOT_INUSE
) && (tmp
->member
->status
!= AST_DEVICE_UNKNOWN
)) {
2136 ast_debug(1, "%s in use, can't receive call\n", tmp
->interface
);
2138 ast_cdr_busy(qe
->chan
->cdr
);
2139 tmp
->stillgoing
= 0;
2143 if (tmp
->member
->paused
) {
2144 ast_debug(1, "%s paused, can't receive call\n", tmp
->interface
);
2146 ast_cdr_busy(qe
->chan
->cdr
);
2147 tmp
->stillgoing
= 0;
2150 if (use_weight
&& compare_weight(qe
->parent
,tmp
->member
)) {
2151 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe
->parent
->name
, tmp
->interface
);
2153 ast_cdr_busy(qe
->chan
->cdr
);
2154 tmp
->stillgoing
= 0;
2159 ast_copy_string(tech
, tmp
->interface
, sizeof(tech
));
2160 if ((location
= strchr(tech
, '/')))
2165 /* Request the peer */
2166 tmp
->chan
= ast_request(tech
, qe
->chan
->nativeformats
, location
, &status
);
2167 if (!tmp
->chan
) { /* If we can't, just go on to the next call */
2169 ast_cdr_busy(qe
->chan
->cdr
);
2170 tmp
->stillgoing
= 0;
2172 update_status(tmp
->member
->state_interface
, ast_device_state(tmp
->member
->state_interface
));
2174 ao2_lock(qe
->parent
);
2175 qe
->parent
->rrpos
++;
2177 ao2_unlock(qe
->parent
);
2184 tmp
->chan
->appl
= "AppQueue";
2185 tmp
->chan
->data
= "(Outgoing Line)";
2186 memset(&tmp
->chan
->whentohangup
, 0, sizeof(tmp
->chan
->whentohangup
));
2187 if (tmp
->chan
->cid
.cid_num
)
2188 ast_free(tmp
->chan
->cid
.cid_num
);
2189 tmp
->chan
->cid
.cid_num
= ast_strdup(qe
->chan
->cid
.cid_num
);
2190 if (tmp
->chan
->cid
.cid_name
)
2191 ast_free(tmp
->chan
->cid
.cid_name
);
2192 tmp
->chan
->cid
.cid_name
= ast_strdup(qe
->chan
->cid
.cid_name
);
2193 if (tmp
->chan
->cid
.cid_ani
)
2194 ast_free(tmp
->chan
->cid
.cid_ani
);
2195 tmp
->chan
->cid
.cid_ani
= ast_strdup(qe
->chan
->cid
.cid_ani
);
2197 /* Inherit specially named variables from parent channel */
2198 ast_channel_inherit_variables(qe
->chan
, tmp
->chan
);
2200 /* Presense of ADSI CPE on outgoing channel follows ours */
2201 tmp
->chan
->adsicpe
= qe
->chan
->adsicpe
;
2203 /* Inherit context and extension */
2204 ast_channel_lock(qe
->chan
);
2205 macrocontext
= pbx_builtin_getvar_helper(qe
->chan
, "MACRO_CONTEXT");
2206 if (!ast_strlen_zero(macrocontext
))
2207 ast_copy_string(tmp
->chan
->dialcontext
, macrocontext
, sizeof(tmp
->chan
->dialcontext
));
2209 ast_copy_string(tmp
->chan
->dialcontext
, qe
->chan
->context
, sizeof(tmp
->chan
->dialcontext
));
2210 macroexten
= pbx_builtin_getvar_helper(qe
->chan
, "MACRO_EXTEN");
2211 if (!ast_strlen_zero(macroexten
))
2212 ast_copy_string(tmp
->chan
->exten
, macroexten
, sizeof(tmp
->chan
->exten
));
2214 ast_copy_string(tmp
->chan
->exten
, qe
->chan
->exten
, sizeof(tmp
->chan
->exten
));
2215 ast_channel_unlock(qe
->chan
);
2217 /* Place the call, but don't wait on the answer */
2218 if ((res
= ast_call(tmp
->chan
, location
, 0))) {
2219 /* Again, keep going even if there's an error */
2220 ast_debug(1, "ast call on peer returned %d\n", res
);
2221 ast_verb(3, "Couldn't call %s\n", tmp
->interface
);
2225 } else if (qe
->parent
->eventwhencalled
) {
2228 manager_event(EVENT_FLAG_AGENT
, "AgentCalled",
2230 "AgentCalled: %s\r\n"
2232 "ChannelCalling: %s\r\n"
2233 "DestinationChannel: %s\r\n"
2234 "CallerIDNum: %s\r\n"
2235 "CallerIDName: %s\r\n"
2241 qe
->parent
->name
, tmp
->interface
, tmp
->member
->membername
, qe
->chan
->name
, tmp
->chan
->name
,
2242 tmp
->chan
->cid
.cid_num
? tmp
->chan
->cid
.cid_num
: "unknown",
2243 tmp
->chan
->cid
.cid_name
? tmp
->chan
->cid
.cid_name
: "unknown",
2244 qe
->chan
->context
, qe
->chan
->exten
, qe
->chan
->priority
, qe
->chan
->uniqueid
,
2245 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
2246 ast_verb(3, "Called %s\n", tmp
->interface
);
2252 /*! \brief find the entry with the best metric, or NULL */
2253 static struct callattempt
*find_best(struct callattempt
*outgoing
)
2255 struct callattempt
*best
= NULL
, *cur
;
2257 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
2258 if (cur
->stillgoing
&& /* Not already done */
2259 !cur
->chan
&& /* Isn't already going */
2260 (!best
|| cur
->metric
< best
->metric
)) { /* We haven't found one yet, or it's better */
2269 * \brief Place a call to a queue member.
2271 * Once metrics have been calculated for each member, this function is used
2272 * to place a call to the appropriate member (or members). The low-level
2273 * channel-handling and error detection is handled in ring_entry
2275 * \retval 1 if a member was called successfully
2276 * \retval 0 otherwise
2278 static int ring_one(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *busies
)
2283 struct callattempt
*best
= find_best(outgoing
);
2285 ast_debug(1, "Nobody left to try ringing in queue\n");
2288 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2289 struct callattempt
*cur
;
2290 /* Ring everyone who shares this best metric (for ringall) */
2291 for (cur
= outgoing
; cur
; cur
= cur
->q_next
) {
2292 if (cur
->stillgoing
&& !cur
->chan
&& cur
->metric
<= best
->metric
) {
2293 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur
->interface
, cur
->metric
);
2294 ret
|= ring_entry(qe
, cur
, busies
);
2298 /* Ring just the best channel */
2299 ast_debug(1, "Trying '%s' with metric %d\n", best
->interface
, best
->metric
);
2300 ret
= ring_entry(qe
, best
, busies
);
2307 /*! \brief Search for best metric and add to Round Robbin queue */
2308 static int store_next_rr(struct queue_ent
*qe
, struct callattempt
*outgoing
)
2310 struct callattempt
*best
= find_best(outgoing
);
2313 /* Ring just the best channel */
2314 ast_debug(1, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
2315 qe
->parent
->rrpos
= best
->metric
% 1000;
2317 /* Just increment rrpos */
2318 if (qe
->parent
->wrapped
) {
2319 /* No more channels, start over */
2320 qe
->parent
->rrpos
= 0;
2322 /* Prioritize next entry */
2323 qe
->parent
->rrpos
++;
2326 qe
->parent
->wrapped
= 0;
2331 /*! \brief Search for best metric and add to Linear queue */
2332 static int store_next_lin(struct queue_ent
*qe
, struct callattempt
*outgoing
)
2334 struct callattempt
*best
= find_best(outgoing
);
2337 /* Ring just the best channel */
2338 ast_debug(1, "Next is '%s' with metric %d\n", best
->interface
, best
->metric
);
2339 qe
->linpos
= best
->metric
% 1000;
2341 /* Just increment rrpos */
2342 if (qe
->linwrapped
) {
2343 /* No more channels, start over */
2346 /* Prioritize next entry */
2355 /*! \brief Playback announcement to queued members if peroid has elapsed */
2356 static int say_periodic_announcement(struct queue_ent
*qe
, int ringing
)
2361 /* Get the current time */
2364 /* Check to see if it is time to announce */
2365 if ((now
- qe
->last_periodic_announce_time
) < qe
->parent
->periodicannouncefrequency
)
2368 /* Stop the music on hold so we can play our own file */
2370 ast_indicate(qe
->chan
,-1);
2372 ast_moh_stop(qe
->chan
);
2374 ast_verb(3, "Playing periodic announcement\n");
2376 if (qe
->parent
->randomperiodicannounce
) {
2377 qe
->last_periodic_announce_sound
= ((unsigned long) ast_random()) % qe
->parent
->numperiodicannounce
;
2378 } else if (qe
->last_periodic_announce_sound
>= qe
->parent
->numperiodicannounce
||
2379 ast_strlen_zero(qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]->str
)) {
2380 qe
->last_periodic_announce_sound
= 0;
2383 /* play the announcement */
2384 res
= play_file(qe
->chan
, qe
->parent
->sound_periodicannounce
[qe
->last_periodic_announce_sound
]->str
);
2386 if ((res
> 0 && !valid_exit(qe
, res
)) || res
< 0)
2389 /* Resume Music on Hold if the caller is going to stay in the queue */
2392 ast_indicate(qe
->chan
, AST_CONTROL_RINGING
);
2394 ast_moh_start(qe
->chan
, qe
->moh
, NULL
);
2397 /* update last_periodic_announce_time */
2398 qe
->last_periodic_announce_time
= now
;
2400 /* Update the current periodic announcement to the next announcement */
2401 if (!qe
->parent
->randomperiodicannounce
) {
2402 qe
->last_periodic_announce_sound
++;
2408 /*! \brief Record that a caller gave up on waiting in queue */
2409 static void record_abandoned(struct queue_ent
*qe
)
2411 ao2_lock(qe
->parent
);
2412 set_queue_variables(qe
);
2413 manager_event(EVENT_FLAG_AGENT
, "QueueCallerAbandon",
2417 "OriginalPosition: %d\r\n"
2419 qe
->parent
->name
, qe
->chan
->uniqueid
, qe
->pos
, qe
->opos
, (int)(time(NULL
) - qe
->start
));
2421 qe
->parent
->callsabandoned
++;
2422 ao2_unlock(qe
->parent
);
2425 /*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2426 static void rna(int rnatime
, struct queue_ent
*qe
, char *interface
, char *membername
)
2428 ast_verb(3, "Nobody picked up in %d ms\n", rnatime
);
2429 if (qe
->parent
->eventwhencalled
)
2430 manager_event(EVENT_FLAG_AGENT
, "AgentRingNoAnswer",
2435 "MemberName: %s\r\n"
2443 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, membername
, "RINGNOANSWER", "%d", rnatime
);
2444 if (qe
->parent
->autopause
) {
2445 if (!set_member_paused(qe
->parent
->name
, interface
, "Auto-Pause", 1)) {
2446 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface
, qe
->parent
->name
);
2448 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface
, qe
->parent
->name
);
2454 #define AST_MAX_WATCHERS 256
2455 /*! \brief Wait for a member to answer the call
2457 * \param[in] qe the queue_ent corresponding to the caller in the queue
2458 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
2459 * \param[in] to the amount of time (in milliseconds) to wait for a response
2460 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
2461 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
2462 * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
2463 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
2465 static struct callattempt
*wait_for_answer(struct queue_ent
*qe
, struct callattempt
*outgoing
, int *to
, char *digit
, int prebusies
, int caller_disconnect
, int forwardsallowed
)
2467 const char *queue
= qe
->parent
->name
;
2468 struct callattempt
*o
, *start
= NULL
, *prev
= NULL
;
2470 int numbusies
= prebusies
;
2474 struct ast_frame
*f
;
2475 struct callattempt
*peer
= NULL
;
2476 struct ast_channel
*winner
;
2477 struct ast_channel
*in
= qe
->chan
;
2479 char membername
[80] = "";
2483 struct callattempt
*epollo
;
2486 starttime
= (long) time(NULL
);
2488 for (epollo
= outgoing
; epollo
; epollo
= epollo
->q_next
) {
2490 ast_poll_channel_add(in
, epollo
->chan
);
2494 while (*to
&& !peer
) {
2495 int numlines
, retry
, pos
= 1;
2496 struct ast_channel
*watchers
[AST_MAX_WATCHERS
];
2500 for (retry
= 0; retry
< 2; retry
++) {
2502 for (o
= outgoing
; o
; o
= o
->q_next
) { /* Keep track of important channels */
2503 if (o
->stillgoing
) { /* Keep track of important channels */
2506 watchers
[pos
++] = o
->chan
;
2510 prev
->call_next
= o
;
2516 if (pos
> 1 /* found */ || !stillgoing
/* nobody listening */ ||
2517 (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) /* ring would not be delivered */)
2519 /* On "ringall" strategy we only move to the next penalty level
2520 when *all* ringing phones are done in the current penalty level */
2521 ring_one(qe
, outgoing
, &numbusies
);
2524 if (pos
== 1 /* not found */) {
2525 if (numlines
== (numbusies
+ numnochan
)) {
2526 ast_debug(1, "Everyone is busy at this time\n");
2528 ast_log(LOG_NOTICE
, "No one is answering queue '%s' (%d/%d/%d)\n", queue
, numlines
, numbusies
, numnochan
);
2533 winner
= ast_waitfor_n(watchers
, pos
, to
);
2534 for (o
= start
; o
; o
= o
->call_next
) {
2535 if (o
->stillgoing
&& (o
->chan
) && (o
->chan
->_state
== AST_STATE_UP
)) {
2537 ast_verb(3, "%s answered %s\n", o
->chan
->name
, in
->name
);
2540 } else if (o
->chan
&& (o
->chan
== winner
)) {
2542 ast_copy_string(on
, o
->member
->interface
, sizeof(on
));
2543 ast_copy_string(membername
, o
->member
->membername
, sizeof(membername
));
2545 if (!ast_strlen_zero(o
->chan
->call_forward
) && !forwardsallowed
) {
2546 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in
->name
, o
->chan
->call_forward
);
2551 } else if (!ast_strlen_zero(o
->chan
->call_forward
)) {
2556 ast_copy_string(tmpchan
, o
->chan
->call_forward
, sizeof(tmpchan
));
2557 if ((stuff
= strchr(tmpchan
, '/'))) {
2561 snprintf(tmpchan
, sizeof(tmpchan
), "%s@%s", o
->chan
->call_forward
, o
->chan
->context
);
2565 /* Before processing channel, go ahead and check for forwarding */
2566 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in
->name
, tech
, stuff
, o
->chan
->name
);
2567 /* Setup parameters */
2568 o
->chan
= ast_request(tech
, in
->nativeformats
, stuff
, &status
);
2570 ast_log(LOG_NOTICE
, "Unable to create local channel for call forward to '%s/%s'\n", tech
, stuff
);
2574 ast_channel_inherit_variables(in
, o
->chan
);
2575 ast_channel_datastore_inherit(in
, o
->chan
);
2576 if (o
->chan
->cid
.cid_num
)
2577 ast_free(o
->chan
->cid
.cid_num
);
2578 o
->chan
->cid
.cid_num
= ast_strdup(in
->cid
.cid_num
);
2580 if (o
->chan
->cid
.cid_name
)
2581 ast_free(o
->chan
->cid
.cid_name
);
2582 o
->chan
->cid
.cid_name
= ast_strdup(in
->cid
.cid_name
);
2584 ast_string_field_set(o
->chan
, accountcode
, in
->accountcode
);
2585 o
->chan
->cdrflags
= in
->cdrflags
;
2587 if (in
->cid
.cid_ani
) {
2588 if (o
->chan
->cid
.cid_ani
)
2589 ast_free(o
->chan
->cid
.cid_ani
);
2590 o
->chan
->cid
.cid_ani
= ast_strdup(in
->cid
.cid_ani
);
2592 if (o
->chan
->cid
.cid_rdnis
)
2593 ast_free(o
->chan
->cid
.cid_rdnis
);
2594 o
->chan
->cid
.cid_rdnis
= ast_strdup(S_OR(in
->macroexten
, in
->exten
));
2595 if (ast_call(o
->chan
, tmpchan
, 0)) {
2596 ast_log(LOG_NOTICE
, "Failed to dial on local channel for call forward to '%s'\n", tmpchan
);
2601 /* Hangup the original channel now, in case we needed it */
2605 f
= ast_read(winner
);
2607 if (f
->frametype
== AST_FRAME_CONTROL
) {
2608 switch (f
->subclass
) {
2609 case AST_CONTROL_ANSWER
:
2610 /* This is our guy if someone answered. */
2612 ast_verb(3, "%s answered %s\n", o
->chan
->name
, in
->name
);
2616 case AST_CONTROL_BUSY
:
2617 ast_verb(3, "%s is busy\n", o
->chan
->name
);
2619 ast_cdr_busy(in
->cdr
);
2621 endtime
= (long) time(NULL
);
2622 endtime
-= starttime
;
2623 rna(endtime
*1000, qe
, on
, membername
);
2624 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2625 if (qe
->parent
->timeoutrestart
)
2627 ring_one(qe
, outgoing
, &numbusies
);
2631 case AST_CONTROL_CONGESTION
:
2632 ast_verb(3, "%s is circuit-busy\n", o
->chan
->name
);
2634 ast_cdr_busy(in
->cdr
);
2635 endtime
= (long) time(NULL
);
2636 endtime
-= starttime
;
2637 rna(endtime
*1000, qe
, on
, membername
);
2639 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2640 if (qe
->parent
->timeoutrestart
)
2642 ring_one(qe
, outgoing
, &numbusies
);
2646 case AST_CONTROL_RINGING
:
2647 ast_verb(3, "%s is ringing\n", o
->chan
->name
);
2649 case AST_CONTROL_OFFHOOK
:
2650 /* Ignore going off hook */
2653 ast_debug(1, "Dunno what to do with control type %d\n", f
->subclass
);
2658 endtime
= (long) time(NULL
) - starttime
;
2659 rna(endtime
* 1000, qe
, on
, membername
);
2661 if (qe
->parent
->strategy
!= QUEUE_STRATEGY_RINGALL
) {
2662 if (qe
->parent
->timeoutrestart
)
2664 ring_one(qe
, outgoing
, &numbusies
);
2671 if (!f
|| ((f
->frametype
== AST_FRAME_CONTROL
) && (f
->subclass
== AST_CONTROL_HANGUP
))) {
2675 if (f
->data
.uint32
) {
2676 in
->hangupcause
= f
->data
.uint32
;
2682 if ((f
->frametype
== AST_FRAME_DTMF
) && caller_disconnect
&& (f
->subclass
== '*')) {
2683 ast_verb(3, "User hit %c to disconnect call.\n", f
->subclass
);
2688 if ((f
->frametype
== AST_FRAME_DTMF
) && valid_exit(qe
, f
->subclass
)) {
2689 ast_verb(3, "User pressed digit: %c\n", f
->subclass
);
2691 *digit
= f
->subclass
;
2698 for (o
= start
; o
; o
= o
->call_next
)
2699 rna(orig
, qe
, o
->interface
, o
->member
->membername
);
2704 for (epollo
= outgoing
; epollo
; epollo
= epollo
->q_next
) {
2706 ast_poll_channel_del(in
, epollo
->chan
);
2714 * \brief Check if we should start attempting to call queue members.
2716 * The behavior of this function is dependent first on whether autofill is enabled
2717 * and second on whether the ring strategy is ringall. If autofill is not enabled,
2718 * then return true if we're the head of the queue. If autofill is enabled, then
2719 * we count the available members and see if the number of available members is enough
2720 * that given our position in the queue, we would theoretically be able to connect to
2721 * one of those available members
2723 static int is_our_turn(struct queue_ent
*qe
)
2725 struct queue_ent
*ch
;
2731 if (!qe
->parent
->autofill
) {
2732 /* Atomically read the parent head -- does not need a lock */
2733 ch
= qe
->parent
->head
;
2734 /* If we are now at the top of the head, break out */
2736 ast_debug(1, "It's our turn (%s).\n", qe
->chan
->name
);
2739 ast_debug(1, "It's not our turn (%s).\n", qe
->chan
->name
);
2744 /* This needs a lock. How many members are available to be served? */
2745 ao2_lock(qe
->parent
);
2747 ch
= qe
->parent
->head
;
2749 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RINGALL
) {
2750 ast_debug(1, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
2753 struct ao2_iterator mem_iter
= ao2_iterator_init(qe
->parent
->members
, 0);
2754 while ((cur
= ao2_iterator_next(&mem_iter
))) {
2755 switch (cur
->status
) {
2756 case AST_DEVICE_INUSE
:
2757 if (!qe
->parent
->ringinuse
)
2759 /* else fall through */
2760 case AST_DEVICE_NOT_INUSE
:
2761 case AST_DEVICE_UNKNOWN
:
2770 ast_debug(1, "There are %d available members.\n", avl
);
2772 while ((idx
< avl
) && (ch
) && (ch
!= qe
)) {
2778 /* If the queue entry is within avl [the number of available members] calls from the top ... */
2779 if (ch
&& idx
< avl
) {
2780 ast_debug(1, "It's our turn (%s).\n", qe
->chan
->name
);
2783 ast_debug(1, "It's not our turn (%s).\n", qe
->chan
->name
);
2787 ao2_unlock(qe
->parent
);
2794 * \brief update rules for queues
2796 * Calculate min/max penalties making sure if relative they stay within bounds.
2797 * Update queues penalty and set dialplan vars, goto next list entry.
2799 static void update_qe_rule(struct queue_ent
*qe
)
2801 int max_penalty
= qe
->pr
->max_relative
? qe
->max_penalty
+ qe
->pr
->max_value
: qe
->pr
->max_value
;
2802 int min_penalty
= qe
->pr
->min_relative
? qe
->min_penalty
+ qe
->pr
->min_value
: qe
->pr
->min_value
;
2803 char max_penalty_str
[20], min_penalty_str
[20];
2804 /* a relative change to the penalty could put it below 0 */
2805 if (max_penalty
< 0)
2807 if (min_penalty
< 0)
2809 if (min_penalty
> max_penalty
)
2810 min_penalty
= max_penalty
;
2811 snprintf(max_penalty_str
, sizeof(max_penalty_str
), "%d", max_penalty
);
2812 snprintf(min_penalty_str
, sizeof(min_penalty_str
), "%d", min_penalty
);
2813 pbx_builtin_setvar_helper(qe
->chan
, "QUEUE_MAX_PENALTY", max_penalty_str
);
2814 pbx_builtin_setvar_helper(qe
->chan
, "QUEUE_MIN_PENALTY", min_penalty_str
);
2815 qe
->max_penalty
= max_penalty
;
2816 qe
->min_penalty
= min_penalty
;
2817 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe
->max_penalty
, qe
->min_penalty
, qe
->chan
->name
, qe
->pr
->time
);
2818 qe
->pr
= AST_LIST_NEXT(qe
->pr
, list
);
2821 /*! \brief The waiting areas for callers who are not actively calling members
2823 * This function is one large loop. This function will return if a caller
2824 * either exits the queue or it becomes that caller's turn to attempt calling
2825 * queue members. Inside the loop, we service the caller with periodic announcements,
2826 * holdtime announcements, etc. as configured in queues.conf
2828 * \retval 0 if the caller's turn has arrived
2829 * \retval -1 if the caller should exit the queue.
2831 static int wait_our_turn(struct queue_ent
*qe
, int ringing
, enum queue_result
*reason
)
2835 /* This is the holding pen for callers 2 through maxlen */
2837 enum queue_member_status stat
;
2839 if (is_our_turn(qe
))
2842 /* If we have timed out, break out */
2843 if (qe
->expire
&& (time(NULL
) > qe
->expire
)) {
2844 *reason
= QUEUE_TIMEOUT
;
2848 stat
= get_member_status(qe
->parent
, qe
->max_penalty
, qe
->min_penalty
);
2850 /* leave the queue if no agents, if enabled */
2851 if (qe
->parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
2852 *reason
= QUEUE_LEAVEEMPTY
;
2853 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2858 /* leave the queue if no reachable agents, if enabled */
2859 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)) {
2860 *reason
= QUEUE_LEAVEUNAVAIL
;
2861 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2865 if ((qe
->parent
->leavewhenempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
2866 *reason
= QUEUE_LEAVEUNAVAIL
;
2867 ast_queue_log(qe
->parent
->name
, qe
->chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
2872 /* Make a position announcement, if enabled */
2873 if (qe
->parent
->announcefrequency
&&
2874 (res
= say_position(qe
,ringing
)))
2877 /* Make a periodic announcement, if enabled */
2878 if (qe
->parent
->periodicannouncefrequency
&&
2879 (res
= say_periodic_announcement(qe
,ringing
)))
2882 /* see if we need to move to the next penalty level for this queue */
2883 while (qe
->pr
&& ((time(NULL
) - qe
->start
) > qe
->pr
->time
)) {
2887 /* Wait a second before checking again */
2888 if ((res
= ast_waitfordigit(qe
->chan
, RECHECK
* 1000))) {
2889 if (res
> 0 && !valid_exit(qe
, res
))
2900 * \brief update the queue status
2903 static int update_queue(struct call_queue
*q
, struct member
*member
, int callcompletedinsl
)
2906 struct call_queue
*qtmp
;
2907 struct ao2_iterator queue_iter
;
2909 if (shared_lastcall
) {
2910 queue_iter
= ao2_iterator_init(queues
, 0);
2911 while ((qtmp
= ao2_iterator_next(&queue_iter
))) {
2913 if ((mem
= ao2_find(qtmp
->members
, member
, OBJ_POINTER
))) {
2914 time(&mem
->lastcall
);
2924 time(&member
->lastcall
);
2926 member
->lastqueue
= q
;
2930 q
->callscompleted
++;
2931 if (callcompletedinsl
)
2932 q
->callscompletedinsl
++;
2937 /*! \brief Calculate the metric of each member in the outgoing callattempts
2939 * A numeric metric is given to each member depending on the ring strategy used
2940 * by the queue. Members with lower metrics will be called before members with
2942 * \retval -1 if penalties are exceeded
2943 * \retval 0 otherwise
2945 static int calc_metric(struct call_queue
*q
, struct member
*mem
, int pos
, struct queue_ent
*qe
, struct callattempt
*tmp
)
2947 if ((qe
->max_penalty
&& (mem
->penalty
> qe
->max_penalty
)) || (qe
->min_penalty
&& (mem
->penalty
< qe
->min_penalty
)))
2950 switch (q
->strategy
) {
2951 case QUEUE_STRATEGY_RINGALL
:
2952 /* Everyone equal, except for penalty */
2953 tmp
->metric
= mem
->penalty
* 1000000;
2955 case QUEUE_STRATEGY_LINEAR
:
2956 if (pos
< qe
->linpos
) {
2957 tmp
->metric
= 1000 + pos
;
2959 if (pos
> qe
->linpos
)
2960 /* Indicate there is another priority */
2964 tmp
->metric
+= mem
->penalty
* 1000000;
2966 case QUEUE_STRATEGY_RRMEMORY
:
2967 if (pos
< q
->rrpos
) {
2968 tmp
->metric
= 1000 + pos
;
2971 /* Indicate there is another priority */
2975 tmp
->metric
+= mem
->penalty
* 1000000;
2977 case QUEUE_STRATEGY_RANDOM
:
2978 tmp
->metric
= ast_random() % 1000;
2979 tmp
->metric
+= mem
->penalty
* 1000000;
2981 case QUEUE_STRATEGY_WRANDOM
:
2982 tmp
->metric
= ast_random() % ((1 + mem
->penalty
) * 1000);
2984 case QUEUE_STRATEGY_FEWESTCALLS
:
2985 tmp
->metric
= mem
->calls
;
2986 tmp
->metric
+= mem
->penalty
* 1000000;
2988 case QUEUE_STRATEGY_LEASTRECENT
:
2992 tmp
->metric
= 1000000 - (time(NULL
) - mem
->lastcall
);
2993 tmp
->metric
+= mem
->penalty
* 1000000;
2996 ast_log(LOG_WARNING
, "Can't calculate metric for unknown strategy %d\n", q
->strategy
);
3002 enum agent_complete_reason
{
3008 /*! \brief Send out AMI message with member call completion status information */
3009 static void send_agent_complete(const struct queue_ent
*qe
, const char *queuename
,
3010 const struct ast_channel
*peer
, const struct member
*member
, time_t callstart
,
3011 char *vars
, size_t vars_len
, enum agent_complete_reason rsn
)
3013 const char *reason
= NULL
; /* silence dumb compilers */
3015 if (!qe
->parent
->eventwhencalled
)
3026 reason
= "transfer";
3030 manager_event(EVENT_FLAG_AGENT
, "AgentComplete",
3035 "MemberName: %s\r\n"
3040 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3041 (long)(callstart
- qe
->start
), (long)(time(NULL
) - callstart
), reason
,
3042 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, vars_len
) : "");
3045 /*! \brief A large function which calls members, updates statistics, and bridges the caller and a member
3047 * Here is the process of this function
3048 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
3049 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this
3050 * iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this
3051 * member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also
3052 * during each iteration, we call calc_metric to determine which members should be rung when.
3053 * 3. Call ring_one to place a call to the appropriate member(s)
3054 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
3055 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
3056 * 6. Start the monitor or mixmonitor if the option is set
3057 * 7. Remove the caller from the queue to allow other callers to advance
3058 * 8. Bridge the call.
3059 * 9. Do any post processing after the call has disconnected.
3061 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
3062 * \param[in] options the options passed as the third parameter to the Queue() application
3063 * \param[in] announceoverride filename to play to user when waiting
3064 * \param[in] url the url passed as the fourth parameter to the Queue() application
3065 * \param[in,out] tries the number of times we have tried calling queue members
3066 * \param[out] noption set if the call to Queue() has the 'n' option set.
3067 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
3068 * \param[in] macro the macro passed as the sixth parameter to the Queue() application
3069 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
3070 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
3072 static int try_calling(struct queue_ent
*qe
, const char *options
, char *announceoverride
, const char *url
, int *tries
, int *noption
, const char *agi
, const char *macro
, const char *gosub
, int ringing
)
3075 struct callattempt
*outgoing
= NULL
; /* the list of calls we are building */
3077 char oldexten
[AST_MAX_EXTENSION
]="";
3078 char oldcontext
[AST_MAX_CONTEXT
]="";
3079 char queuename
[256]="";
3080 char interfacevar
[256]="";
3081 struct ast_channel
*peer
;
3082 struct ast_channel
*which
;
3083 struct callattempt
*lpeer
;
3084 struct member
*member
;
3085 struct ast_app
*app
;
3086 int res
= 0, bridge
= 0;
3089 char *announce
= NULL
;
3092 time_t now
= time(NULL
);
3093 struct ast_bridge_config bridge_config
;
3094 char nondataquality
= 1;
3095 char *agiexec
= NULL
;
3096 char *macroexec
= NULL
;
3097 char *gosubexec
= NULL
;
3099 const char *monitorfilename
;
3100 const char *monitor_exec
;
3101 const char *monitor_options
;
3102 char tmpid
[256], tmpid2
[256];
3103 char meid
[1024], meid2
[1024];
3104 char mixmonargs
[1512];
3105 struct ast_app
*mixmonapp
= NULL
;
3108 int forwardsallowed
= 1;
3109 int callcompletedinsl
;
3110 struct ao2_iterator memi
;
3111 struct ast_datastore
*datastore
;
3113 ast_channel_lock(qe
->chan
);
3114 datastore
= ast_channel_datastore_find(qe
->chan
, &dialed_interface_info
, NULL
);
3115 ast_channel_unlock(qe
->chan
);
3117 memset(&bridge_config
, 0, sizeof(bridge_config
));
3122 for (; options
&& *options
; options
++)
3125 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_REDIRECT
);
3128 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_REDIRECT
);
3131 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMON
);
3134 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMON
);
3140 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_DISCONNECT
);
3143 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
);
3146 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_PARKCALL
);
3149 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_PARKCALL
);
3152 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
|| qe
->parent
->strategy
== QUEUE_STRATEGY_LINEAR
)
3155 *tries
= qe
->parent
->membercount
;
3159 forwardsallowed
= 0;
3162 ast_set_flag(&(bridge_config
.features_callee
), AST_FEATURE_AUTOMIXMON
);
3165 ast_set_flag(&(bridge_config
.features_caller
), AST_FEATURE_AUTOMIXMON
);
3170 /* Hold the lock while we setup the outgoing calls */
3173 ao2_lock(qe
->parent
);
3174 ast_debug(1, "%s is trying to call a queue member.\n",
3176 ast_copy_string(queuename
, qe
->parent
->name
, sizeof(queuename
));
3177 if (!ast_strlen_zero(qe
->announce
))
3178 announce
= qe
->announce
;
3179 if (!ast_strlen_zero(announceoverride
))
3180 announce
= announceoverride
;
3182 memi
= ao2_iterator_init(qe
->parent
->members
, 0);
3183 while ((cur
= ao2_iterator_next(&memi
))) {
3184 struct callattempt
*tmp
= ast_calloc(1, sizeof(*tmp
));
3185 struct ast_dialed_interface
*di
;
3186 AST_LIST_HEAD(, ast_dialed_interface
) *dialed_interfaces
;
3189 ao2_unlock(qe
->parent
);
3195 if (!(datastore
= ast_channel_datastore_alloc(&dialed_interface_info
, NULL
))) {
3197 ao2_unlock(qe
->parent
);
3203 datastore
->inheritance
= DATASTORE_INHERIT_FOREVER
;
3204 if (!(dialed_interfaces
= ast_calloc(1, sizeof(*dialed_interfaces
)))) {
3206 ao2_unlock(&qe
->parent
);
3212 datastore
->data
= dialed_interfaces
;
3213 AST_LIST_HEAD_INIT(dialed_interfaces
);
3215 ast_channel_lock(qe
->chan
);
3216 ast_channel_datastore_add(qe
->chan
, datastore
);
3217 ast_channel_unlock(qe
->chan
);
3219 dialed_interfaces
= datastore
->data
;
3221 AST_LIST_LOCK(dialed_interfaces
);
3222 AST_LIST_TRAVERSE(dialed_interfaces
, di
, list
) {
3223 if (!strcasecmp(cur
->interface
, di
->interface
)) {
3224 ast_log(LOG_DEBUG
, "Skipping dialing interface '%s' since it has already been dialed\n",
3229 AST_LIST_UNLOCK(dialed_interfaces
);
3236 /* It is always ok to dial a Local interface. We only keep track of
3237 * which "real" interfaces have been dialed. The Local channel will
3238 * inherit this list so that if it ends up dialing a real interface,
3239 * it won't call one that has already been called. */
3240 if (strncasecmp(cur
->interface
, "Local/", 6)) {
3241 if (!(di
= ast_calloc(1, sizeof(*di
) + strlen(cur
->interface
)))) {
3243 ao2_unlock(qe
->parent
);
3249 strcpy(di
->interface
, cur
->interface
);
3251 AST_LIST_LOCK(dialed_interfaces
);
3252 AST_LIST_INSERT_TAIL(dialed_interfaces
, di
, list
);
3253 AST_LIST_UNLOCK(dialed_interfaces
);
3256 tmp
->stillgoing
= -1;
3258 tmp
->oldstatus
= cur
->status
;
3259 tmp
->lastcall
= cur
->lastcall
;
3260 tmp
->lastqueue
= cur
->lastqueue
;
3261 ast_copy_string(tmp
->interface
, cur
->interface
, sizeof(tmp
->interface
));
3262 /* Special case: If we ring everyone, go ahead and ring them, otherwise
3263 just calculate their metric for the appropriate strategy */
3264 if (!calc_metric(qe
->parent
, cur
, x
++, qe
, tmp
)) {
3265 /* Put them in the list of outgoing thingies... We're ready now.
3266 XXX If we're forcibly removed, these outgoing calls won't get
3268 tmp
->q_next
= outgoing
;
3270 /* If this line is up, don't try anybody else */
3271 if (outgoing
->chan
&& (outgoing
->chan
->_state
== AST_STATE_UP
))
3278 if (qe
->expire
&& (!qe
->parent
->timeout
|| (qe
->expire
- now
) <= qe
->parent
->timeout
))
3279 to
= (qe
->expire
- now
) * 1000;
3281 to
= (qe
->parent
->timeout
) ? qe
->parent
->timeout
* 1000 : -1;
3284 ao2_unlock(qe
->parent
);
3285 ring_one(qe
, outgoing
, &numbusies
);
3288 lpeer
= wait_for_answer(qe
, outgoing
, &to
, &digit
, numbusies
, ast_test_flag(&(bridge_config
.features_caller
), AST_FEATURE_DISCONNECT
), forwardsallowed
);
3289 /* The ast_channel_datastore_remove() function could fail here if the
3290 * datastore was moved to another channel during a masquerade. If this is
3291 * the case, don't free the datastore here because later, when the channel
3292 * to which the datastore was moved hangs up, it will attempt to free this
3293 * datastore again, causing a crash
3295 if (datastore
&& !ast_channel_datastore_remove(qe
->chan
, datastore
)) {
3296 ast_channel_datastore_free(datastore
);
3298 ao2_lock(qe
->parent
);
3299 if (qe
->parent
->strategy
== QUEUE_STRATEGY_RRMEMORY
) {
3300 store_next_rr(qe
, outgoing
);
3302 if (qe
->parent
->strategy
== QUEUE_STRATEGY_LINEAR
) {
3303 store_next_lin(qe
, outgoing
);
3305 ao2_unlock(qe
->parent
);
3306 peer
= lpeer
? lpeer
->chan
: NULL
;
3310 /* Must gotten hung up */
3313 /* User exited by pressing a digit */
3317 ast_debug(1, "%s: Nobody answered.\n", qe
->chan
->name
);
3318 } else { /* peer is valid */
3319 /* Ah ha! Someone answered within the desired timeframe. Of course after this
3320 we will always return with -1 so that it is hung up properly after the
3322 if (!strcmp(qe
->chan
->tech
->type
, "Zap"))
3323 ast_channel_setoption(qe
->chan
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
3324 if (!strcmp(peer
->tech
->type
, "Zap"))
3325 ast_channel_setoption(peer
, AST_OPTION_TONE_VERIFY
, &nondataquality
, sizeof(nondataquality
), 0);
3326 /* Update parameters for the queue */
3328 recalc_holdtime(qe
, (now
- qe
->start
));
3329 ao2_lock(qe
->parent
);
3330 callcompletedinsl
= ((now
- qe
->start
) <= qe
->parent
->servicelevel
);
3331 ao2_unlock(qe
->parent
);
3332 member
= lpeer
->member
;
3333 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
3335 hangupcalls(outgoing
, peer
);
3337 if (announce
|| qe
->parent
->reportholdtime
|| qe
->parent
->memberdelay
) {
3340 res2
= ast_autoservice_start(qe
->chan
);
3342 if (qe
->parent
->memberdelay
) {
3343 ast_log(LOG_NOTICE
, "Delaying member connect for %d seconds\n", qe
->parent
->memberdelay
);
3344 res2
|= ast_safe_sleep(peer
, qe
->parent
->memberdelay
* 1000);
3346 if (!res2
&& announce
) {
3347 play_file(peer
, announce
);
3349 if (!res2
&& qe
->parent
->reportholdtime
) {
3350 if (!play_file(peer
, qe
->parent
->sound_reporthold
)) {
3351 int holdtime
, holdtimesecs
;
3354 holdtime
= abs((now
- qe
->start
) / 60);
3355 holdtimesecs
= abs((now
- qe
->start
));
3356 if (holdtime
== 1) {
3357 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3358 play_file(peer
, qe
->parent
->sound_minute
);
3360 ast_say_number(peer
, holdtime
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3361 play_file(peer
, qe
->parent
->sound_minutes
);
3363 if (holdtimesecs
> 1) {
3364 ast_say_number(peer
, holdtimesecs
, AST_DIGIT_ANY
, peer
->language
, NULL
);
3365 play_file(peer
, qe
->parent
->sound_seconds
);
3370 res2
|= ast_autoservice_stop(qe
->chan
);
3371 if (ast_check_hangup(peer
)) {
3372 /* Agent must have hung up */
3373 ast_log(LOG_WARNING
, "Agent on %s hungup on the customer.\n", peer
->name
);
3374 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "AGENTDUMP", "%s", "");
3375 if (qe
->parent
->eventwhencalled
)
3376 manager_event(EVENT_FLAG_AGENT
, "AgentDump",
3381 "MemberName: %s\r\n"
3383 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3384 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
3386 ao2_ref(member
, -1);
3389 /* Caller must have hung up just before being connected*/
3390 ast_log(LOG_NOTICE
, "Caller was about to talk to agent on %s but the caller hungup.\n", peer
->name
);
3391 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "ABANDON", "%d|%d|%ld", qe
->pos
, qe
->opos
, (long) time(NULL
) - qe
->start
);
3392 record_abandoned(qe
);
3394 ao2_ref(member
, -1);
3398 /* Stop music on hold */
3400 ast_indicate(qe
->chan
,-1);
3402 ast_moh_stop(qe
->chan
);
3403 /* If appropriate, log that we have a destination channel */
3405 ast_cdr_setdestchan(qe
->chan
->cdr
, peer
->name
);
3406 /* Make sure channels are compatible */
3407 res
= ast_channel_make_compatible(qe
->chan
, peer
);
3409 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "SYSCOMPAT", "%s", "");
3410 ast_log(LOG_WARNING
, "Had to drop call because I couldn't make %s compatible with %s\n", qe
->chan
->name
, peer
->name
);
3411 record_abandoned(qe
);
3413 ao2_ref(member
, -1);
3417 /* Play announcement to the caller telling it's his turn if defined */
3418 if (!ast_strlen_zero(qe
->parent
->sound_callerannounce
)) {
3419 if (play_file(qe
->chan
, qe
->parent
->sound_callerannounce
))
3420 ast_log(LOG_WARNING
, "Announcement file '%s' is unavailable, continuing anyway...\n", qe
->parent
->sound_callerannounce
);
3423 ao2_lock(qe
->parent
);
3424 /* if setinterfacevar is defined, make member variables available to the channel */
3425 /* use pbx_builtin_setvar to set a load of variables with one call */
3426 if (qe
->parent
->setinterfacevar
) {
3427 snprintf(interfacevar
, sizeof(interfacevar
), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
3428 member
->interface
, member
->membername
, member
->calls
, (long)member
->lastcall
, member
->penalty
, member
->dynamic
, member
->realtime
);
3429 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
3432 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
3433 /* use pbx_builtin_setvar to set a load of variables with one call */
3434 if (qe
->parent
->setqueueentryvar
) {
3435 snprintf(interfacevar
, sizeof(interfacevar
), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
3436 (long) time(NULL
) - qe
->start
, qe
->opos
);
3437 pbx_builtin_setvar_multiple(qe
->chan
, interfacevar
);
3440 /* try to set queue variables if configured to do so*/
3441 set_queue_variables(qe
);
3442 ao2_unlock(qe
->parent
);
3444 ast_channel_lock(qe
->chan
);
3445 if ((monitorfilename
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_FILENAME"))) {
3446 monitorfilename
= ast_strdupa(monitorfilename
);
3448 ast_channel_unlock(qe
->chan
);
3449 /* Begin Monitoring */
3450 if (qe
->parent
->monfmt
&& *qe
->parent
->monfmt
) {
3451 if (!qe
->parent
->montype
) {
3452 ast_debug(1, "Starting Monitor as requested.\n");
3453 if (pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC_ARGS"))
3457 if (monitorfilename
)
3458 ast_monitor_start(which
, qe
->parent
->monfmt
, monitorfilename
, 1, X_REC_IN
| X_REC_OUT
);
3459 else if (qe
->chan
->cdr
)
3460 ast_monitor_start(which
, qe
->parent
->monfmt
, qe
->chan
->cdr
->uniqueid
, 1, X_REC_IN
| X_REC_OUT
);
3462 /* Last ditch effort -- no CDR, make up something */
3463 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
3464 ast_monitor_start(which
, qe
->parent
->monfmt
, tmpid
, 1, X_REC_IN
| X_REC_OUT
);
3467 mixmonapp
= pbx_findapp("MixMonitor");
3470 ast_debug(1, "Starting MixMonitor as requested.\n");
3471 if (!monitorfilename
) {
3473 ast_copy_string(tmpid
, qe
->chan
->cdr
->uniqueid
, sizeof(tmpid
));
3475 snprintf(tmpid
, sizeof(tmpid
), "chan-%lx", ast_random());
3477 const char *m
= monitorfilename
;
3478 for (p
= tmpid2
; p
< tmpid2
+ sizeof(tmpid2
) - 1; p
++, m
++) {
3481 if (*(m
+ 1) == '{')
3493 if (p
== tmpid2
+ sizeof(tmpid2
))
3494 tmpid2
[sizeof(tmpid2
) - 1] = '\0';
3496 pbx_substitute_variables_helper(qe
->chan
, tmpid2
, tmpid
, sizeof(tmpid
) - 1);
3499 ast_channel_lock(qe
->chan
);
3500 if ((monitor_exec
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_EXEC"))) {
3501 monitor_exec
= ast_strdupa(monitor_exec
);
3503 if ((monitor_options
= pbx_builtin_getvar_helper(qe
->chan
, "MONITOR_OPTIONS"))) {
3504 monitor_options
= ast_strdupa(monitor_options
);
3506 monitor_options
= "";
3508 ast_channel_unlock(qe
->chan
);
3511 const char *m
= monitor_exec
;
3512 for (p
= meid2
; p
< meid2
+ sizeof(meid2
) - 1; p
++, m
++) {
3515 if (*(m
+ 1) == '{')
3527 if (p
== meid2
+ sizeof(meid2
))
3528 meid2
[sizeof(meid2
) - 1] = '\0';
3530 pbx_substitute_variables_helper(qe
->chan
, meid2
, meid
, sizeof(meid
) - 1);
3533 snprintf(tmpid2
, sizeof(tmpid2
), "%s.%s", tmpid
, qe
->parent
->monfmt
);
3535 if (!ast_strlen_zero(monitor_exec
))
3536 snprintf(mixmonargs
, sizeof(mixmonargs
), "%s,b%s,%s", tmpid2
, monitor_options
, monitor_exec
);
3538 snprintf(mixmonargs
, sizeof(mixmonargs
), "%s,b%s", tmpid2
, monitor_options
);
3540 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs
);
3541 /* We purposely lock the CDR so that pbx_exec does not update the application data */
3543 ast_set_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
3544 ret
= pbx_exec(qe
->chan
, mixmonapp
, mixmonargs
);
3546 ast_clear_flag(qe
->chan
->cdr
, AST_CDR_FLAG_LOCKED
);
3549 ast_log(LOG_WARNING
, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
3553 /* Drop out of the queue at this point, to prepare for next caller */
3555 if (!ast_strlen_zero(url
) && ast_channel_supports_html(peer
)) {
3556 ast_debug(1, "app_queue: sendurl=%s.\n", url
);
3557 ast_channel_sendurl(peer
, url
);
3560 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
3561 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
3562 if (!ast_strlen_zero(macro
)) {
3563 macroexec
= ast_strdupa(macro
);
3565 if (qe
->parent
->membermacro
)
3566 macroexec
= ast_strdupa(qe
->parent
->membermacro
);
3569 if (!ast_strlen_zero(macroexec
)) {
3570 ast_debug(1, "app_queue: macro=%s.\n", macroexec
);
3572 res
= ast_autoservice_start(qe
->chan
);
3574 ast_log(LOG_ERROR
, "Unable to start autoservice on calling channel\n");
3578 app
= pbx_findapp("Macro");
3581 res
= pbx_exec(qe
->chan
, app
, macroexec
);
3582 ast_debug(1, "Macro exited with status %d\n", res
);
3585 ast_log(LOG_ERROR
, "Could not find application Macro\n");
3589 if (ast_autoservice_stop(qe
->chan
) < 0) {
3590 ast_log(LOG_ERROR
, "Could not stop autoservice on calling channel\n");
3595 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
3596 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
3597 if (!ast_strlen_zero(gosub
)) {
3598 gosubexec
= ast_strdupa(gosub
);
3600 if (qe
->parent
->membergosub
)
3601 gosubexec
= ast_strdupa(qe
->parent
->membergosub
);
3604 if (!ast_strlen_zero(gosubexec
)) {
3606 ast_log(LOG_DEBUG
, "app_queue: gosub=%s.\n", gosubexec
);
3608 res
= ast_autoservice_start(qe
->chan
);
3610 ast_log(LOG_ERROR
, "Unable to start autoservice on calling channel\n");
3614 app
= pbx_findapp("Gosub");
3617 char *gosub_args
, *gosub_argstart
;
3619 /* Set where we came from */
3620 ast_copy_string(qe
->chan
->context
, "app_dial_gosub_virtual_context", sizeof(qe
->chan
->context
));
3621 ast_copy_string(qe
->chan
->exten
, "s", sizeof(qe
->chan
->exten
));
3622 qe
->chan
->priority
= 0;
3624 gosub_argstart
= strchr(gosubexec
, ',');
3625 if (gosub_argstart
) {
3626 *gosub_argstart
= 0;
3627 asprintf(&gosub_args
, "%s,s,1(%s)", gosubexec
, gosub_argstart
+ 1);
3628 *gosub_argstart
= '|';
3630 asprintf(&gosub_args
, "%s,s,1", gosubexec
);
3633 res
= pbx_exec(qe
->chan
, app
, gosub_args
);
3634 ast_pbx_run(qe
->chan
);
3637 ast_log(LOG_DEBUG
, "Gosub exited with status %d\n", res
);
3639 ast_log(LOG_ERROR
, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
3643 ast_log(LOG_ERROR
, "Could not find application Gosub\n");
3647 if (ast_autoservice_stop(qe
->chan
) < 0) {
3648 ast_log(LOG_ERROR
, "Could not stop autoservice on calling channel\n");
3653 if (!ast_strlen_zero(agi
)) {
3654 ast_debug(1, "app_queue: agi=%s.\n", agi
);
3655 app
= pbx_findapp("agi");
3657 agiexec
= ast_strdupa(agi
);
3658 ret
= pbx_exec(qe
->chan
, app
, agiexec
);
3660 ast_log(LOG_WARNING
, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
3663 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "CONNECT", "%ld|%s|%ld", (long) time(NULL
) - qe
->start
, peer
->uniqueid
,
3664 (long)(orig
- to
> 0 ? (orig
- to
) / 1000 : 0));
3665 if (update_cdr
&& qe
->chan
->cdr
)
3666 ast_copy_string(qe
->chan
->cdr
->dstchannel
, member
->membername
, sizeof(qe
->chan
->cdr
->dstchannel
));
3667 if (qe
->parent
->eventwhencalled
)
3668 manager_event(EVENT_FLAG_AGENT
, "AgentConnect",
3673 "MemberName: %s\r\n"
3675 "BridgedChannel: %s\r\n"
3678 queuename
, qe
->chan
->uniqueid
, peer
->name
, member
->interface
, member
->membername
,
3679 (long) time(NULL
) - qe
->start
, peer
->uniqueid
, (long)(orig
- to
> 0 ? (orig
- to
) / 1000 : 0),
3680 qe
->parent
->eventwhencalled
== QUEUE_EVENT_VARIABLES
? vars2manager(qe
->chan
, vars
, sizeof(vars
)) : "");
3681 ast_copy_string(oldcontext
, qe
->chan
->context
, sizeof(oldcontext
));
3682 ast_copy_string(oldexten
, qe
->chan
->exten
, sizeof(oldexten
));
3685 bridge
= ast_bridge_call(qe
->chan
,peer
, &bridge_config
);
3687 if (strcasecmp(oldcontext
, qe
->chan
->context
) || strcasecmp(oldexten
, qe
->chan
->exten
)) {
3688 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "TRANSFER", "%s|%s|%ld|%ld",
3689 qe
->chan
->exten
, qe
->chan
->context
, (long) (callstart
- qe
->start
),
3690 (long) (time(NULL
) - callstart
));
3691 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), TRANSFER
);
3692 } else if (ast_check_hangup(qe
->chan
)) {
3693 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETECALLER", "%ld|%ld|%d",
3694 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
3695 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), CALLER
);
3697 ast_queue_log(queuename
, qe
->chan
->uniqueid
, member
->membername
, "COMPLETEAGENT", "%ld|%ld|%d",
3698 (long) (callstart
- qe
->start
), (long) (time(NULL
) - callstart
), qe
->opos
);
3699 send_agent_complete(qe
, queuename
, peer
, member
, callstart
, vars
, sizeof(vars
), AGENT
);
3702 if (bridge
!= AST_PBX_NO_HANGUP_PEER
)
3704 update_queue(qe
->parent
, member
, callcompletedinsl
);
3705 res
= bridge
? bridge
: 1;
3706 ao2_ref(member
, -1);
3709 hangupcalls(outgoing
, NULL
);
3714 static int wait_a_bit(struct queue_ent
*qe
)
3716 /* Don't need to hold the lock while we setup the outgoing calls */
3717 int retrywait
= qe
->parent
->retry
* 1000;
3719 int res
= ast_waitfordigit(qe
->chan
, retrywait
);
3720 if (res
> 0 && !valid_exit(qe
, res
))
3726 static struct member
*interface_exists(struct call_queue
*q
, const char *interface
)
3729 struct ao2_iterator mem_iter
;
3734 mem_iter
= ao2_iterator_init(q
->members
, 0);
3735 while ((mem
= ao2_iterator_next(&mem_iter
))) {
3736 if (!strcasecmp(interface
, mem
->interface
))
3745 /*! \brief Dump all members in a specific queue to the database
3747 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>[|...]
3749 static void dump_queue_members(struct call_queue
*pm_queue
)
3751 struct member
*cur_member
;
3752 char value
[PM_MAX_LEN
];
3755 struct ao2_iterator mem_iter
;
3757 memset(value
, 0, sizeof(value
));
3762 mem_iter
= ao2_iterator_init(pm_queue
->members
, 0);
3763 while ((cur_member
= ao2_iterator_next(&mem_iter
))) {
3764 if (!cur_member
->dynamic
) {
3765 ao2_ref(cur_member
, -1);
3769 res
= snprintf(value
+ value_len
, sizeof(value
) - value_len
, "%s%s;%d;%d;%s",
3770 value_len
? "|" : "", cur_member
->interface
, cur_member
->penalty
, cur_member
->paused
, cur_member
->membername
);
3772 ao2_ref(cur_member
, -1);
3774 if (res
!= strlen(value
+ value_len
)) {
3775 ast_log(LOG_WARNING
, "Could not create persistent member string, out of space\n");
3781 if (value_len
&& !cur_member
) {
3782 if (ast_db_put(pm_family
, pm_queue
->name
, value
))
3783 ast_log(LOG_WARNING
, "failed to create persistent dynamic entry!\n");
3785 /* Delete the entry if the queue is empty or there is an error */
3786 ast_db_del(pm_family
, pm_queue
->name
);
3789 /*! \brief Remove member from queue
3790 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3791 * \retval RES_NOSUCHQUEUE queue does not exist
3792 * \retval RES_OKAY removed member from queue
3793 * \retval RES_EXISTS queue exists but no members
3795 static int remove_from_queue(const char *queuename
, const char *interface
)
3797 struct call_queue
*q
, tmpq
= {
3800 struct member
*mem
, tmpmem
;
3801 int res
= RES_NOSUCHQUEUE
;
3803 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
3804 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
3806 if ((mem
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
))) {
3807 /* XXX future changes should beware of this assumption!! */
3808 if (!mem
->dynamic
) {
3811 return RES_NOT_DYNAMIC
;
3814 manager_event(EVENT_FLAG_AGENT
, "QueueMemberRemoved",
3817 "MemberName: %s\r\n",
3818 q
->name
, mem
->interface
, mem
->membername
);
3819 ao2_unlink(q
->members
, mem
);
3820 remove_from_interfaces(mem
->state_interface
);
3823 if (queue_persistent_members
)
3824 dump_queue_members(q
);
3837 /*! \brief Add member to queue
3838 * \retval RES_NOT_DYNAMIC when they aren't a RT member
3839 * \retval RES_NOSUCHQUEUE queue does not exist
3840 * \retval RES_OKAY added member from queue
3841 * \retval RES_EXISTS queue exists but no members
3842 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
3844 static int add_to_queue(const char *queuename
, const char *interface
, const char *membername
, int penalty
, int paused
, int dump
, const char *state_interface
)
3846 struct call_queue
*q
;
3847 struct member
*new_member
, *old_member
;
3848 int res
= RES_NOSUCHQUEUE
;
3850 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
3851 * short-circuits if the queue is already in memory. */
3852 if (!(q
= load_realtime_queue(queuename
)))
3858 if ((old_member
= interface_exists(q
, interface
)) == NULL
) {
3859 if ((new_member
= create_queue_member(interface
, membername
, penalty
, paused
, state_interface
))) {
3860 add_to_interfaces(new_member
->state_interface
);
3861 new_member
->dynamic
= 1;
3862 ao2_link(q
->members
, new_member
);
3864 manager_event(EVENT_FLAG_AGENT
, "QueueMemberAdded",
3867 "MemberName: %s\r\n"
3868 "Membership: %s\r\n"
3870 "CallsTaken: %d\r\n"
3874 q
->name
, new_member
->interface
, new_member
->membername
,
3876 new_member
->penalty
, new_member
->calls
, (int) new_member
->lastcall
,
3877 new_member
->status
, new_member
->paused
);
3879 ao2_ref(new_member
, -1);
3883 dump_queue_members(q
);
3887 res
= RES_OUTOFMEMORY
;
3890 ao2_ref(old_member
, -1);
3899 static int set_member_paused(const char *queuename
, const char *interface
, const char *reason
, int paused
)
3902 struct call_queue
*q
;
3904 struct ao2_iterator queue_iter
;
3907 /* Special event for when all queues are paused - individual events still generated */
3908 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
3909 if (ast_strlen_zero(queuename
))
3910 ast_queue_log("NONE", "NONE", interface
, (paused
? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
3912 queue_iter
= ao2_iterator_init(queues
, 0);
3913 while ((q
= ao2_iterator_next(&queue_iter
))) {
3915 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
3916 if ((mem
= interface_exists(q
, interface
))) {
3917 if (mem
->paused
== paused
) {
3918 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused
? "" : "un"), (paused
? "" : "un"), q
->name
, interface
);
3922 if (mem
->realtime
) {
3923 failed
= update_realtime_member_field(mem
, q
->name
, "paused", paused
? "1" : "0");
3927 ast_log(LOG_WARNING
, "Failed %spausing realtime queue member %s:%s\n", (paused
? "" : "un"), q
->name
, interface
);
3932 mem
->paused
= paused
;
3934 if (queue_persistent_members
)
3935 dump_queue_members(q
);
3937 ast_queue_log(q
->name
, "NONE", mem
->membername
, (paused
? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason
, ""));
3939 if (!ast_strlen_zero(reason
)) {
3940 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
3943 "MemberName: %s\r\n"
3946 q
->name
, mem
->interface
, mem
->membername
, paused
, reason
);
3948 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPaused",
3951 "MemberName: %s\r\n"
3953 q
->name
, mem
->interface
, mem
->membername
, paused
);
3959 if (!ast_strlen_zero(queuename
) && !strcasecmp(queuename
, q
->name
)) {
3969 return found
? RESULT_SUCCESS
: RESULT_FAILURE
;
3972 /* \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues. */
3973 static int set_member_penalty(char *queuename
, char *interface
, int penalty
)
3975 int foundinterface
= 0, foundqueue
= 0;
3976 struct call_queue
*q
;
3978 struct ao2_iterator queue_iter
;
3981 ast_log(LOG_ERROR
, "Invalid penalty (%d)\n", penalty
);
3982 return RESULT_FAILURE
;
3985 queue_iter
= ao2_iterator_init(queues
, 0);
3986 while ((q
= ao2_iterator_next(&queue_iter
))) {
3988 if (ast_strlen_zero(queuename
) || !strcasecmp(q
->name
, queuename
)) {
3990 if ((mem
= interface_exists(q
, interface
))) {
3992 mem
->penalty
= penalty
;
3994 ast_queue_log(q
->name
, "NONE", interface
, "PENALTY", "%d", penalty
);
3995 manager_event(EVENT_FLAG_AGENT
, "QueueMemberPenalty",
3999 q
->name
, mem
->interface
, penalty
);
4007 if (foundinterface
) {
4008 return RESULT_SUCCESS
;
4009 } else if (!foundqueue
) {
4010 ast_log (LOG_ERROR
, "Invalid queuename\n");
4012 ast_log (LOG_ERROR
, "Invalid interface\n");
4015 return RESULT_FAILURE
;
4018 /* \brief Gets members penalty.
4019 * \return Return the members penalty or RESULT_FAILURE on error.
4021 static int get_member_penalty(char *queuename
, char *interface
)
4023 int foundqueue
= 0, penalty
;
4024 struct call_queue
*q
, tmpq
= {
4029 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4032 if ((mem
= interface_exists(q
, interface
))) {
4033 penalty
= mem
->penalty
;
4042 /* some useful debuging */
4044 ast_log (LOG_ERROR
, "Invalid queuename\n");
4046 ast_log (LOG_ERROR
, "Invalid interface\n");
4048 return RESULT_FAILURE
;
4051 /*! \brief Reload dynamic queue members persisted into the astdb */
4052 static void reload_queue_members(void)
4055 const char *queue_name
;
4058 char *membername
= NULL
;
4059 char *state_interface
;
4064 struct ast_db_entry
*db_tree
;
4065 struct ast_db_entry
*entry
;
4066 struct call_queue
*cur_queue
;
4067 char queue_data
[PM_MAX_LEN
];
4071 /* Each key in 'pm_family' is the name of a queue */
4072 db_tree
= ast_db_gettree(pm_family
, NULL
);
4073 for (entry
= db_tree
; entry
; entry
= entry
->next
) {
4075 queue_name
= entry
->key
+ strlen(pm_family
) + 2;
4078 struct call_queue tmpq
= {
4081 cur_queue
= ao2_find(queues
, &tmpq
, OBJ_POINTER
);
4085 cur_queue
= load_realtime_queue(queue_name
);
4088 /* If the queue no longer exists, remove it from the
4090 ast_log(LOG_WARNING
, "Error loading persistent queue: '%s': it does not exist\n", queue_name
);
4091 ast_db_del(pm_family
, queue_name
);
4095 if (ast_db_get(pm_family
, queue_name
, queue_data
, PM_MAX_LEN
)) {
4096 queue_unref(cur_queue
);
4100 cur_ptr
= queue_data
;
4101 while ((member
= strsep(&cur_ptr
, ",|"))) {
4102 if (ast_strlen_zero(member
))
4105 interface
= strsep(&member
, ";");
4106 penalty_tok
= strsep(&member
, ";");
4107 paused_tok
= strsep(&member
, ";");
4108 membername
= strsep(&member
, ";");
4109 state_interface
= strsep(&member
, ";");
4112 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (penalty)\n", queue_name
);
4115 penalty
= strtol(penalty_tok
, NULL
, 10);
4116 if (errno
== ERANGE
) {
4117 ast_log(LOG_WARNING
, "Error converting penalty: %s: Out of range.\n", penalty_tok
);
4122 ast_log(LOG_WARNING
, "Error parsing persistent member string for '%s' (paused)\n", queue_name
);
4125 paused
= strtol(paused_tok
, NULL
, 10);
4126 if ((errno
== ERANGE
) || paused
< 0 || paused
> 1) {
4127 ast_log(LOG_WARNING
, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok
);
4131 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name
, interface
, membername
, penalty
, paused
);
4133 if (add_to_queue(queue_name
, interface
, membername
, penalty
, paused
, 0, state_interface
) == RES_OUTOFMEMORY
) {
4134 ast_log(LOG_ERROR
, "Out of Memory when reloading persistent queue member\n");
4138 queue_unref(cur_queue
);
4143 ast_log(LOG_NOTICE
, "Queue members successfully reloaded from database.\n");
4144 ast_db_freetree(db_tree
);
4148 /*! \brief PauseQueueMember application */
4149 static int pqm_exec(struct ast_channel
*chan
, void *data
)
4152 AST_DECLARE_APP_ARGS(args
,
4153 AST_APP_ARG(queuename
);
4154 AST_APP_ARG(interface
);
4155 AST_APP_ARG(options
);
4156 AST_APP_ARG(reason
);
4159 if (ast_strlen_zero(data
)) {
4160 ast_log(LOG_WARNING
, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
4164 parse
= ast_strdupa(data
);
4166 AST_STANDARD_APP_ARGS(args
, parse
);
4168 if (ast_strlen_zero(args
.interface
)) {
4169 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4173 if (set_member_paused(args
.queuename
, args
.interface
, args
.reason
, 1)) {
4174 ast_log(LOG_WARNING
, "Attempt to pause interface %s, not found\n", args
.interface
);
4175 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "NOTFOUND");
4179 pbx_builtin_setvar_helper(chan
, "PQMSTATUS", "PAUSED");
4184 /*! \brief UnPauseQueueMember application */
4185 static int upqm_exec(struct ast_channel
*chan
, void *data
)
4188 AST_DECLARE_APP_ARGS(args
,
4189 AST_APP_ARG(queuename
);
4190 AST_APP_ARG(interface
);
4191 AST_APP_ARG(options
);
4192 AST_APP_ARG(reason
);
4195 if (ast_strlen_zero(data
)) {
4196 ast_log(LOG_WARNING
, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
4200 parse
= ast_strdupa(data
);
4202 AST_STANDARD_APP_ARGS(args
, parse
);
4204 if (ast_strlen_zero(args
.interface
)) {
4205 ast_log(LOG_WARNING
, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
4209 if (set_member_paused(args
.queuename
, args
.interface
, args
.reason
, 0)) {
4210 ast_log(LOG_WARNING
, "Attempt to unpause interface %s, not found\n", args
.interface
);
4211 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "NOTFOUND");
4215 pbx_builtin_setvar_helper(chan
, "UPQMSTATUS", "UNPAUSED");
4220 /*! \brief RemoveQueueMember application */
4221 static int rqm_exec(struct ast_channel
*chan
, void *data
)
4224 char *parse
, *temppos
= NULL
;
4225 AST_DECLARE_APP_ARGS(args
,
4226 AST_APP_ARG(queuename
);
4227 AST_APP_ARG(interface
);
4228 AST_APP_ARG(options
);
4232 if (ast_strlen_zero(data
)) {
4233 ast_log(LOG_WARNING
, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
4237 parse
= ast_strdupa(data
);
4239 AST_STANDARD_APP_ARGS(args
, parse
);
4241 if (ast_strlen_zero(args
.interface
)) {
4242 args
.interface
= ast_strdupa(chan
->name
);
4243 temppos
= strrchr(args
.interface
, '-');
4248 switch (remove_from_queue(args
.queuename
, args
.interface
)) {
4250 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "REMOVEMEMBER", "%s", "");
4251 ast_log(LOG_NOTICE
, "Removed interface '%s' from queue '%s'\n", args
.interface
, args
.queuename
);
4252 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "REMOVED");
4256 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args
.interface
, args
.queuename
);
4257 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTINQUEUE");
4260 case RES_NOSUCHQUEUE
:
4261 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': No such queue\n", args
.queuename
);
4262 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOSUCHQUEUE");
4265 case RES_NOT_DYNAMIC
:
4266 ast_log(LOG_WARNING
, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args
.queuename
, args
.interface
);
4267 pbx_builtin_setvar_helper(chan
, "RQMSTATUS", "NOTDYNAMIC");
4275 /*! \brief AddQueueMember application */
4276 static int aqm_exec(struct ast_channel
*chan
, void *data
)
4279 char *parse
, *temppos
= NULL
;
4280 AST_DECLARE_APP_ARGS(args
,
4281 AST_APP_ARG(queuename
);
4282 AST_APP_ARG(interface
);
4283 AST_APP_ARG(penalty
);
4284 AST_APP_ARG(options
);
4285 AST_APP_ARG(membername
);
4286 AST_APP_ARG(state_interface
);
4290 if (ast_strlen_zero(data
)) {
4291 ast_log(LOG_WARNING
, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
4295 parse
= ast_strdupa(data
);
4297 AST_STANDARD_APP_ARGS(args
, parse
);
4299 if (ast_strlen_zero(args
.interface
)) {
4300 args
.interface
= ast_strdupa(chan
->name
);
4301 temppos
= strrchr(args
.interface
, '-');
4306 if (!ast_strlen_zero(args
.penalty
)) {
4307 if ((sscanf(args
.penalty
, "%d", &penalty
) != 1) || penalty
< 0) {
4308 ast_log(LOG_WARNING
, "Penalty '%s' is invalid, must be an integer >= 0\n", args
.penalty
);
4313 switch (add_to_queue(args
.queuename
, args
.interface
, args
.membername
, penalty
, 0, queue_persistent_members
, args
.state_interface
)) {
4315 ast_queue_log(args
.queuename
, chan
->uniqueid
, args
.interface
, "ADDMEMBER", "%s", "");
4316 ast_log(LOG_NOTICE
, "Added interface '%s' to queue '%s'\n", args
.interface
, args
.queuename
);
4317 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "ADDED");
4321 ast_log(LOG_WARNING
, "Unable to add interface '%s' to queue '%s': Already there\n", args
.interface
, args
.queuename
);
4322 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "MEMBERALREADY");
4325 case RES_NOSUCHQUEUE
:
4326 ast_log(LOG_WARNING
, "Unable to add interface to queue '%s': No such queue\n", args
.queuename
);
4327 pbx_builtin_setvar_helper(chan
, "AQMSTATUS", "NOSUCHQUEUE");
4330 case RES_OUTOFMEMORY
:
4331 ast_log(LOG_ERROR
, "Out of memory adding member %s to queue %s\n", args
.interface
, args
.queuename
);
4338 /*! \brief QueueLog application */
4339 static int ql_exec(struct ast_channel
*chan
, void *data
)
4343 AST_DECLARE_APP_ARGS(args
,
4344 AST_APP_ARG(queuename
);
4345 AST_APP_ARG(uniqueid
);
4346 AST_APP_ARG(membername
);
4348 AST_APP_ARG(params
);
4351 if (ast_strlen_zero(data
)) {
4352 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
4356 parse
= ast_strdupa(data
);
4358 AST_STANDARD_APP_ARGS(args
, parse
);
4360 if (ast_strlen_zero(args
.queuename
) || ast_strlen_zero(args
.uniqueid
)
4361 || ast_strlen_zero(args
.membername
) || ast_strlen_zero(args
.event
)) {
4362 ast_log(LOG_WARNING
, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
4366 ast_queue_log(args
.queuename
, args
.uniqueid
, args
.membername
, args
.event
,
4367 "%s", args
.params
? args
.params
: "");
4372 /*! \brief Copy rule from global list into specified queue */
4373 static void copy_rules(struct queue_ent
*qe
, const char *rulename
)
4375 struct penalty_rule
*pr_iter
;
4376 struct rule_list
*rl_iter
;
4377 const char *tmp
= ast_strlen_zero(rulename
) ? qe
->parent
->defaultrule
: rulename
;
4378 AST_LIST_LOCK(&rule_lists
);
4379 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
4380 if (!strcasecmp(rl_iter
->name
, tmp
))
4384 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
4385 struct penalty_rule
*new_pr
= ast_calloc(1, sizeof(*new_pr
));
4387 ast_log(LOG_ERROR
, "Memory allocation error when copying penalty rules! Aborting!\n");
4388 AST_LIST_UNLOCK(&rule_lists
);
4391 new_pr
->time
= pr_iter
->time
;
4392 new_pr
->max_value
= pr_iter
->max_value
;
4393 new_pr
->min_value
= pr_iter
->min_value
;
4394 new_pr
->max_relative
= pr_iter
->max_relative
;
4395 new_pr
->min_relative
= pr_iter
->min_relative
;
4396 AST_LIST_INSERT_TAIL(&qe
->qe_rules
, new_pr
, list
);
4399 AST_LIST_UNLOCK(&rule_lists
);
4402 /*!\brief The starting point for all queue calls
4404 * The process involved here is to
4405 * 1. Parse the options specified in the call to Queue()
4407 * 3. Wait in a loop until it is our turn to try calling a queue member
4408 * 4. Attempt to call a queue member
4409 * 5. If 4. did not result in a bridged call, then check for between
4410 * call options such as periodic announcements etc.
4411 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
4414 static int queue_exec(struct ast_channel
*chan
, void *data
)
4418 const char *user_priority
;
4419 const char *max_penalty_str
;
4420 const char *min_penalty_str
;
4423 int max_penalty
, min_penalty
;
4424 enum queue_result reason
= QUEUE_UNKNOWN
;
4425 /* whether to exit Queue application after the timeout hits */
4429 int makeannouncement
= 0;
4430 AST_DECLARE_APP_ARGS(args
,
4431 AST_APP_ARG(queuename
);
4432 AST_APP_ARG(options
);
4434 AST_APP_ARG(announceoverride
);
4435 AST_APP_ARG(queuetimeoutstr
);
4441 /* Our queue entry */
4442 struct queue_ent qe
;
4444 if (ast_strlen_zero(data
)) {
4445 ast_log(LOG_WARNING
, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
4449 parse
= ast_strdupa(data
);
4450 AST_STANDARD_APP_ARGS(args
, parse
);
4452 /* Setup our queue entry */
4453 memset(&qe
, 0, sizeof(qe
));
4454 qe
.start
= time(NULL
);
4456 /* set the expire time based on the supplied timeout; */
4457 if (!ast_strlen_zero(args
.queuetimeoutstr
))
4458 qe
.expire
= qe
.start
+ atoi(args
.queuetimeoutstr
);
4462 /* Get the priority from the variable ${QUEUE_PRIO} */
4463 ast_channel_lock(chan
);
4464 user_priority
= pbx_builtin_getvar_helper(chan
, "QUEUE_PRIO");
4465 if (user_priority
) {
4466 if (sscanf(user_priority
, "%d", &prio
) == 1) {
4467 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan
->name
, prio
);
4469 ast_log(LOG_WARNING
, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
4470 user_priority
, chan
->name
);
4474 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
4478 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
4480 if ((max_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MAX_PENALTY"))) {
4481 if (sscanf(max_penalty_str
, "%d", &max_penalty
) == 1) {
4482 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan
->name
, max_penalty
);
4484 ast_log(LOG_WARNING
, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
4485 max_penalty_str
, chan
->name
);
4492 if ((min_penalty_str
= pbx_builtin_getvar_helper(chan
, "QUEUE_MIN_PENALTY"))) {
4493 if (sscanf(min_penalty_str
, "%d", &min_penalty
) == 1) {
4494 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan
->name
, min_penalty
);
4496 ast_log(LOG_WARNING
, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
4497 min_penalty_str
, chan
->name
);
4503 ast_channel_unlock(chan
);
4505 if (args
.options
&& (strchr(args
.options
, 'r')))
4508 if (args
.options
&& (strchr(args
.options
, 'c')))
4511 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
4512 args
.queuename
, args
.options
, args
.url
, args
.announceoverride
, (long)qe
.expire
, prio
);
4516 qe
.max_penalty
= max_penalty
;
4517 qe
.min_penalty
= min_penalty
;
4518 qe
.last_pos_said
= 0;
4520 qe
.last_periodic_announce_time
= time(NULL
);
4521 qe
.last_periodic_announce_sound
= 0;
4522 qe
.valid_digits
= 0;
4523 if (join_queue(args
.queuename
, &qe
, &reason
)) {
4524 ast_log(LOG_WARNING
, "Unable to join queue '%s'\n", args
.queuename
);
4525 set_queue_result(chan
, reason
);
4528 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args
.url
, ""),
4529 S_OR(chan
->cid
.cid_num
, ""));
4530 copy_rules(&qe
, args
.rule
);
4531 qe
.pr
= AST_LIST_FIRST(&qe
.qe_rules
);
4534 ast_indicate(chan
, AST_CONTROL_RINGING
);
4536 ast_moh_start(chan
, qe
.moh
, NULL
);
4539 /* This is the wait loop for callers 2 through maxlen */
4540 res
= wait_our_turn(&qe
, ringing
, &reason
);
4545 makeannouncement
= 0;
4548 /* This is the wait loop for the head caller*/
4549 /* To exit, they may get their call answered; */
4550 /* they may dial a digit from the queue context; */
4551 /* or, they may timeout. */
4553 enum queue_member_status stat
;
4555 /* Leave if we have exceeded our queuetimeout */
4556 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
4557 record_abandoned(&qe
);
4558 reason
= QUEUE_TIMEOUT
;
4560 ast_queue_log(args
.queuename
, chan
->uniqueid
,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
4561 qe
.pos
, qe
.opos
, (long) time(NULL
) - qe
.start
);
4565 if (makeannouncement
) {
4566 /* Make a position announcement, if enabled */
4567 if (qe
.parent
->announcefrequency
)
4568 if ((res
= say_position(&qe
,ringing
)))
4571 makeannouncement
= 1;
4573 /* Make a periodic announcement, if enabled */
4574 if (qe
.parent
->periodicannouncefrequency
)
4575 if ((res
= say_periodic_announcement(&qe
,ringing
)))
4578 /* see if we need to move to the next penalty level for this queue */
4579 while (qe
.pr
&& ((time(NULL
) - qe
.start
) > qe
.pr
->time
)) {
4580 update_qe_rule(&qe
);
4583 /* Try calling all queue members for 'timeout' seconds */
4584 res
= try_calling(&qe
, args
.options
, args
.announceoverride
, args
.url
, &tries
, &noption
, args
.agi
, args
.macro
, args
.gosub
, ringing
);
4589 stat
= get_member_status(qe
.parent
, qe
.max_penalty
, qe
.min_penalty
);
4591 /* exit after 'timeout' cycle if 'n' option enabled */
4592 if (noption
&& tries
>= qe
.parent
->membercount
) {
4593 ast_verb(3, "Exiting on time-out cycle\n");
4594 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHTIMEOUT", "%d", qe
.pos
);
4595 record_abandoned(&qe
);
4596 reason
= QUEUE_TIMEOUT
;
4601 /* leave the queue if no agents, if enabled */
4602 if (qe
.parent
->leavewhenempty
&& (stat
== QUEUE_NO_MEMBERS
)) {
4603 record_abandoned(&qe
);
4604 reason
= QUEUE_LEAVEEMPTY
;
4605 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
4610 /* leave the queue if no reachable agents, if enabled */
4611 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_STRICT
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
|| stat
== QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS
)) {
4612 record_abandoned(&qe
);
4613 reason
= QUEUE_LEAVEUNAVAIL
;
4614 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITEMPTY", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long)(time(NULL
) - qe
.start
));
4618 if ((qe
.parent
->leavewhenempty
== QUEUE_EMPTY_LOOSE
) && (stat
== QUEUE_NO_REACHABLE_MEMBERS
)) {
4619 record_abandoned(&qe
);
4620 reason
= QUEUE_LEAVEUNAVAIL
;
4625 /* Leave if we have exceeded our queuetimeout */
4626 if (qe
.expire
&& (time(NULL
) > qe
.expire
)) {
4627 record_abandoned(&qe
);
4628 reason
= QUEUE_TIMEOUT
;
4630 ast_queue_log(qe
.parent
->name
, qe
.chan
->uniqueid
,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe
.pos
, qe
.opos
, (long) time(NULL
) - qe
.start
);
4634 /* If using dynamic realtime members, we should regenerate the member list for this queue */
4635 update_realtime_members(qe
.parent
);
4637 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
4638 res
= wait_a_bit(&qe
);
4642 /* Since this is a priority queue and
4643 * it is not sure that we are still at the head
4644 * of the queue, go and check for our turn again.
4646 if (!is_our_turn(&qe
)) {
4647 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe
.chan
->name
);
4656 record_abandoned(&qe
);
4657 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "ABANDON",
4658 "%d|%d|%ld", qe
.pos
, qe
.opos
,
4659 (long) time(NULL
) - qe
.start
);
4662 } else if (qe
.valid_digits
) {
4663 ast_queue_log(args
.queuename
, chan
->uniqueid
, "NONE", "EXITWITHKEY",
4664 "%s|%d", qe
.digits
, qe
.pos
);
4668 /* Don't allow return code > 0 */
4669 if (res
>= 0 && res
!= AST_PBX_KEEPALIVE
) {
4672 ast_indicate(chan
, -1);
4676 ast_stopstream(chan
);
4679 set_queue_variables(&qe
);
4682 if (reason
!= QUEUE_UNKNOWN
)
4683 set_queue_result(chan
, reason
);
4689 * \brief create interface var with all queue details.
4690 * \retval 0 on success
4691 * \retval -1 on error
4693 static int queue_function_var(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4696 struct call_queue
*q
, tmpq
= {
4700 char interfacevar
[256] = "";
4703 if (ast_strlen_zero(data
)) {
4704 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4708 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4710 if (q
->setqueuevar
) {
4714 if (q
->callscompleted
> 0) {
4715 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
4718 snprintf(interfacevar
, sizeof(interfacevar
),
4719 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
4720 q
->maxlen
, int2strat(q
->strategy
), q
->count
, q
->holdtime
, q
->callscompleted
, q
->callsabandoned
, q
->servicelevel
, sl
);
4722 pbx_builtin_setvar_multiple(chan
, interfacevar
);
4728 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4731 snprintf(buf
, len
, "%d", res
);
4737 * \brief Get number either busy / free or total members of a specific queue
4738 * \retval number of members (busy / free / total)
4739 * \retval -1 on error
4741 static int queue_function_qac(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4745 struct ao2_iterator mem_iter
;
4746 struct call_queue
*q
;
4749 if (ast_strlen_zero(data
)) {
4750 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4754 if ((option
= strchr(data
, ',')))
4758 if ((q
= load_realtime_queue(data
))) {
4760 if (!strcasecmp(option
, "logged")) {
4761 mem_iter
= ao2_iterator_init(q
->members
, 0);
4762 while ((m
= ao2_iterator_next(&mem_iter
))) {
4763 /* Count the agents who are logged in and presently answering calls */
4764 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
4769 } else if (!strcasecmp(option
, "free")) {
4770 mem_iter
= ao2_iterator_init(q
->members
, 0);
4771 while ((m
= ao2_iterator_next(&mem_iter
))) {
4772 /* Count the agents who are logged in and presently answering calls */
4773 if ((m
->status
== AST_DEVICE_NOT_INUSE
) && (!m
->paused
)) {
4778 } else /* must be "count" */
4779 count
= q
->membercount
;
4783 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4785 snprintf(buf
, len
, "%d", count
);
4791 * \brief Get the total number of members in a specific queue (Deprecated)
4792 * \retval number of members
4793 * \retval -1 on error
4795 static int queue_function_qac_dep(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4799 struct call_queue
*q
;
4800 struct ao2_iterator mem_iter
;
4801 static int depflag
= 1;
4805 ast_log(LOG_NOTICE
, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
4808 if (ast_strlen_zero(data
)) {
4809 ast_log(LOG_ERROR
, "%s requires an argument: queuename\n", cmd
);
4813 if ((q
= load_realtime_queue(data
))) {
4815 mem_iter
= ao2_iterator_init(q
->members
, 0);
4816 while ((m
= ao2_iterator_next(&mem_iter
))) {
4817 /* Count the agents who are logged in and presently answering calls */
4818 if ((m
->status
!= AST_DEVICE_UNAVAILABLE
) && (m
->status
!= AST_DEVICE_INVALID
)) {
4826 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4828 snprintf(buf
, len
, "%d", count
);
4833 /*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
4834 static int queue_function_queuewaitingcount(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4837 struct call_queue
*q
, tmpq
= {
4840 struct ast_variable
*var
= NULL
;
4844 if (ast_strlen_zero(data
)) {
4845 ast_log(LOG_ERROR
, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
4849 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4854 } else if ((var
= ast_load_realtime("queues", "name", data
, NULL
))) {
4855 /* if the queue is realtime but was not found in memory, this
4856 * means that the queue had been deleted from memory since it was
4857 * "dead." This means it has a 0 waiting count
4860 ast_variables_destroy(var
);
4862 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4864 snprintf(buf
, len
, "%d", count
);
4869 /*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
4870 static int queue_function_queuememberlist(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4872 struct call_queue
*q
, tmpq
= {
4877 /* Ensure an otherwise empty list doesn't return garbage */
4880 if (ast_strlen_zero(data
)) {
4881 ast_log(LOG_ERROR
, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
4885 if ((q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
4886 int buflen
= 0, count
= 0;
4887 struct ao2_iterator mem_iter
= ao2_iterator_init(q
->members
, 0);
4890 while ((m
= ao2_iterator_next(&mem_iter
))) {
4891 /* strcat() is always faster than printf() */
4893 strncat(buf
+ buflen
, ",", len
- buflen
- 1);
4896 strncat(buf
+ buflen
, m
->membername
, len
- buflen
- 1);
4897 buflen
+= strlen(m
->membername
);
4898 /* Safeguard against overflow (negative length) */
4899 if (buflen
>= len
- 2) {
4901 ast_log(LOG_WARNING
, "Truncating list\n");
4909 ast_log(LOG_WARNING
, "queue %s was not found\n", data
);
4911 /* We should already be terminated, but let's make sure. */
4912 buf
[len
- 1] = '\0';
4917 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
4918 static int queue_function_memberpenalty_read(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
4921 AST_DECLARE_APP_ARGS(args
,
4922 AST_APP_ARG(queuename
);
4923 AST_APP_ARG(interface
);
4925 /* Make sure the returned value on error is NULL. */
4928 if (ast_strlen_zero(data
)) {
4929 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4933 AST_STANDARD_APP_ARGS(args
, data
);
4935 if (args
.argc
< 2) {
4936 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4940 penalty
= get_member_penalty (args
.queuename
, args
.interface
);
4942 if (penalty
>= 0) /* remember that buf is already '\0' */
4943 snprintf (buf
, len
, "%d", penalty
);
4948 /*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
4949 static int queue_function_memberpenalty_write(struct ast_channel
*chan
, const char *cmd
, char *data
, const char *value
)
4952 AST_DECLARE_APP_ARGS(args
,
4953 AST_APP_ARG(queuename
);
4954 AST_APP_ARG(interface
);
4957 if (ast_strlen_zero(data
)) {
4958 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4962 AST_STANDARD_APP_ARGS(args
, data
);
4964 if (args
.argc
< 2) {
4965 ast_log(LOG_ERROR
, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
4969 penalty
= atoi(value
);
4971 if (ast_strlen_zero(args
.interface
)) {
4972 ast_log (LOG_ERROR
, "<interface> parameter can't be null\n");
4976 /* if queuename = NULL then penalty will be set for interface in all the queues. */
4977 if (set_member_penalty(args
.queuename
, args
.interface
, penalty
)) {
4978 ast_log(LOG_ERROR
, "Invalid interface, queue or penalty\n");
4985 static struct ast_custom_function queuevar_function
= {
4986 .name
= "QUEUE_VARIABLES",
4987 .synopsis
= "Return Queue information in variables",
4988 .syntax
= "QUEUE_VARIABLES(<queuename>)",
4990 "Makes the following queue variables available.\n"
4991 "QUEUEMAX maxmimum number of calls allowed\n"
4992 "QUEUESTRATEGY the strategy of the queue\n"
4993 "QUEUECALLS number of calls currently in the queue\n"
4994 "QUEUEHOLDTIME current average hold time\n"
4995 "QUEUECOMPLETED number of completed calls for the queue\n"
4996 "QUEUEABANDONED number of abandoned calls\n"
4997 "QUEUESRVLEVEL queue service level\n"
4998 "QUEUESRVLEVELPERF current service level performance\n"
4999 "Returns 0 if queue is found and setqueuevar is defined, -1 otherwise",
5000 .read
= queue_function_var
,
5003 static struct ast_custom_function queuemembercount_function
= {
5004 .name
= "QUEUE_MEMBER",
5005 .synopsis
= "Count number of members answering a queue",
5006 .syntax
= "QUEUE_MEMBER(<queuename>, <option>)",
5008 "Returns the number of members currently associated with the specified queue.\n"
5009 "One of three options may be passed to determine the count returned:\n"
5010 "\"logged\" - Returns the number of logged-in members for the specified queue\n"
5011 "\"free\" - Returns the number of logged-in members for the specified queue available to take a call\n"
5012 "\"count\" - Returns the total number of members for the specified queue\n",
5013 .read
= queue_function_qac
,
5016 static struct ast_custom_function queuemembercount_dep
= {
5017 .name
= "QUEUE_MEMBER_COUNT",
5018 .synopsis
= "Count number of members answering a queue",
5019 .syntax
= "QUEUE_MEMBER_COUNT(<queuename>)",
5021 "Returns the number of members currently associated with the specified queue.\n\n"
5022 "This function has been deprecated in favor of the QUEUE_MEMBER function\n",
5023 .read
= queue_function_qac_dep
,
5026 static struct ast_custom_function queuewaitingcount_function
= {
5027 .name
= "QUEUE_WAITING_COUNT",
5028 .synopsis
= "Count number of calls currently waiting in a queue",
5029 .syntax
= "QUEUE_WAITING_COUNT(<queuename>)",
5031 "Returns the number of callers currently waiting in the specified queue.\n",
5032 .read
= queue_function_queuewaitingcount
,
5035 static struct ast_custom_function queuememberlist_function
= {
5036 .name
= "QUEUE_MEMBER_LIST",
5037 .synopsis
= "Returns a list of interfaces on a queue",
5038 .syntax
= "QUEUE_MEMBER_LIST(<queuename>)",
5040 "Returns a comma-separated list of members associated with the specified queue.\n",
5041 .read
= queue_function_queuememberlist
,
5044 static struct ast_custom_function queuememberpenalty_function
= {
5045 .name
= "QUEUE_MEMBER_PENALTY",
5046 .synopsis
= "Gets or sets queue members penalty.",
5047 .syntax
= "QUEUE_MEMBER_PENALTY(<queuename>,<interface>)",
5049 "Gets or sets queue members penalty\n",
5050 .read
= queue_function_memberpenalty_read
,
5051 .write
= queue_function_memberpenalty_write
,
5054 static int reload_queue_rules(int reload
)
5056 struct ast_config
*cfg
;
5057 struct rule_list
*rl_iter
, *new_rl
;
5058 struct penalty_rule
*pr_iter
;
5059 char *rulecat
= NULL
;
5060 struct ast_variable
*rulevar
= NULL
;
5061 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
5063 if (!(cfg
= ast_config_load("queuerules.conf", config_flags
))) {
5064 ast_log(LOG_NOTICE
, "No queuerules.conf file found, queues will not follow penalty rules\n");
5065 } else if (cfg
== CONFIG_STATUS_FILEUNCHANGED
) {
5066 ast_log(LOG_NOTICE
, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
5067 return AST_MODULE_LOAD_SUCCESS
;
5069 AST_LIST_LOCK(&rule_lists
);
5070 while ((rl_iter
= AST_LIST_REMOVE_HEAD(&rule_lists
, list
))) {
5071 while ((pr_iter
= AST_LIST_REMOVE_HEAD(&rl_iter
->rules
, list
)))
5075 while ((rulecat
= ast_category_browse(cfg
, rulecat
))) {
5076 if (!(new_rl
= ast_calloc(1, sizeof(*new_rl
)))) {
5077 ast_log(LOG_ERROR
, "Memory allocation error while loading queuerules.conf! Aborting!\n");
5078 AST_LIST_UNLOCK(&rule_lists
);
5079 return AST_MODULE_LOAD_FAILURE
;
5081 ast_copy_string(new_rl
->name
, rulecat
, sizeof(new_rl
->name
));
5082 AST_LIST_INSERT_TAIL(&rule_lists
, new_rl
, list
);
5083 for (rulevar
= ast_variable_browse(cfg
, rulecat
); rulevar
; rulevar
= rulevar
->next
)
5084 if(!strcasecmp(rulevar
->name
, "penaltychange"))
5085 insert_penaltychange(new_rl
->name
, rulevar
->value
, rulevar
->lineno
);
5087 ast_log(LOG_WARNING
, "Don't know how to handle rule type '%s' on line %d\n", rulevar
->name
, rulevar
->lineno
);
5090 AST_LIST_UNLOCK(&rule_lists
);
5093 ast_config_destroy(cfg
);
5095 return AST_MODULE_LOAD_SUCCESS
;
5099 static int reload_queues(int reload
)
5101 struct call_queue
*q
;
5102 struct ast_config
*cfg
;
5104 struct ast_variable
*var
;
5105 struct member
*cur
, *newm
;
5106 struct ao2_iterator mem_iter
;
5108 const char *general_val
= NULL
;
5110 char *interface
, *state_interface
;
5111 char *membername
= NULL
;
5113 struct ast_flags config_flags
= { reload
? CONFIG_FLAG_FILEUNCHANGED
: 0 };
5114 struct ao2_iterator queue_iter
;
5115 AST_DECLARE_APP_ARGS(args
,
5116 AST_APP_ARG(interface
);
5117 AST_APP_ARG(penalty
);
5118 AST_APP_ARG(membername
);
5119 AST_APP_ARG(state_interface
);
5122 /*First things first. Let's load queuerules.conf*/
5123 if (reload_queue_rules(reload
) == AST_MODULE_LOAD_FAILURE
)
5124 return AST_MODULE_LOAD_FAILURE
;
5126 if (!(cfg
= ast_config_load("queues.conf", config_flags
))) {
5127 ast_log(LOG_NOTICE
, "No call queueing config file (queues.conf), so no call queues\n");
5129 } else if (cfg
== CONFIG_STATUS_FILEUNCHANGED
)
5133 /* Mark all queues as dead for the moment */
5134 queue_iter
= ao2_iterator_init(queues
, F_AO2I_DONTLOCK
);
5135 while ((q
= ao2_iterator_next(&queue_iter
))) {
5143 /* Chug through config file */
5145 while ((cat
= ast_category_browse(cfg
, cat
)) ) {
5146 if (!strcasecmp(cat
, "general")) {
5147 /* Initialize global settings */
5148 queue_keep_stats
= 0;
5149 if ((general_val
= ast_variable_retrieve(cfg
, "general", "keepstats")))
5150 queue_keep_stats
= ast_true(general_val
);
5151 queue_persistent_members
= 0;
5152 if ((general_val
= ast_variable_retrieve(cfg
, "general", "persistentmembers")))
5153 queue_persistent_members
= ast_true(general_val
);
5154 autofill_default
= 0;
5155 if ((general_val
= ast_variable_retrieve(cfg
, "general", "autofill")))
5156 autofill_default
= ast_true(general_val
);
5157 montype_default
= 0;
5158 if ((general_val
= ast_variable_retrieve(cfg
, "general", "monitor-type"))) {
5159 if (!strcasecmp(general_val
, "mixmonitor"))
5160 montype_default
= 1;
5163 if ((general_val
= ast_variable_retrieve(cfg
, "general", "updatecdr")))
5164 update_cdr
= ast_true(general_val
);
5165 shared_lastcall
= 0;
5166 if ((general_val
= ast_variable_retrieve(cfg
, "general", "shared_lastcall")))
5167 shared_lastcall
= ast_true(general_val
);
5168 } else { /* Define queue */
5169 /* Look for an existing one */
5170 struct call_queue tmpq
= {
5173 if (!(q
= ao2_find(queues
, &tmpq
, OBJ_POINTER
))) {
5175 if (!(q
= alloc_queue(cat
))) {
5176 /* TODO: Handle memory allocation failure */
5182 const char *tmpvar
= NULL
;
5185 /* Check if a queue with this name already exists */
5187 ast_log(LOG_WARNING
, "Queue '%s' already defined! Skipping!\n", cat
);
5194 /* Due to the fact that the "linear" strategy will have a different allocation
5195 * scheme for queue members, we must devise the queue's strategy before other initializations
5197 if ((tmpvar
= ast_variable_retrieve(cfg
, cat
, "strategy"))) {
5198 q
->strategy
= strat2int(tmpvar
);
5199 if (q
->strategy
< 0) {
5200 ast_log(LOG_WARNING
, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
5202 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
5205 q
->strategy
= QUEUE_STRATEGY_RINGALL
;
5206 /* Re-initialize the queue, and clear statistics */
5208 if (!queue_keep_stats
)
5210 mem_iter
= ao2_iterator_init(q
->members
, 0);
5211 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5212 if (!cur
->dynamic
) {
5217 for (var
= ast_variable_browse(cfg
, cat
); var
; var
= var
->next
) {
5218 if (!strcasecmp(var
->name
, "member")) {
5219 struct member tmpmem
;
5222 /* Add a new member */
5223 ast_copy_string(parse
, var
->value
, sizeof(parse
));
5225 AST_STANDARD_APP_ARGS(args
, parse
);
5227 interface
= args
.interface
;
5228 if (!ast_strlen_zero(args
.penalty
)) {
5230 while (*tmp
&& *tmp
< 33) tmp
++;
5231 penalty
= atoi(tmp
);
5238 if (!ast_strlen_zero(args
.membername
)) {
5239 membername
= args
.membername
;
5240 while (*membername
&& *membername
< 33) membername
++;
5243 if (!ast_strlen_zero(args
.state_interface
)) {
5244 state_interface
= args
.state_interface
;
5245 while (*state_interface
&& *state_interface
< 33) state_interface
++;
5247 state_interface
= interface
;
5249 /* Find the old position in the list */
5250 ast_copy_string(tmpmem
.interface
, interface
, sizeof(tmpmem
.interface
));
5251 cur
= ao2_find(q
->members
, &tmpmem
, OBJ_POINTER
| OBJ_UNLINK
);
5252 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
5253 if (cur
&& strcasecmp(cur
->state_interface
, state_interface
)) {
5254 remove_from_interfaces(cur
->state_interface
);
5256 newm
= create_queue_member(interface
, membername
, penalty
, cur
? cur
->paused
: 0, state_interface
);
5257 if (!cur
|| (cur
&& strcasecmp(cur
->state_interface
, state_interface
)))
5258 add_to_interfaces(state_interface
);
5259 ao2_link(q
->members
, newm
);
5269 queue_set_param(q
, var
->name
, var
->value
, var
->lineno
, 1);
5273 /* Free remaining members marked as delme */
5274 mem_iter
= ao2_iterator_init(q
->members
, 0);
5275 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5281 ao2_unlink(q
->members
, cur
);
5282 remove_from_interfaces(cur
->interface
);
5287 ao2_link(queues
, q
);
5294 ast_config_destroy(cfg
);
5295 queue_iter
= ao2_iterator_init(queues
, 0);
5296 while ((q
= ao2_iterator_next(&queue_iter
))) {
5298 ao2_unlink(queues
, q
);
5301 mem_iter
= ao2_iterator_init(q
->members
, 0);
5302 while ((cur
= ao2_iterator_next(&mem_iter
))) {
5305 cur
->status
= ast_device_state(cur
->interface
);
5316 /*! \brief direct ouput to manager or cli with proper terminator */
5317 static void do_print(struct mansession
*s
, int fd
, const char *str
)
5320 astman_append(s
, "%s\r\n", str
);
5322 ast_cli(fd
, "%s\n", str
);
5326 * \brief Show queue(s) status and statistics
5328 * List the queues strategy, calls processed, members logged in,
5329 * other queue statistics such as avg hold time.
5331 static char *__queues_show(struct mansession
*s
, int fd
, int argc
, char **argv
)
5333 struct call_queue
*q
;
5334 struct ast_str
*out
= ast_str_alloca(240);
5336 time_t now
= time(NULL
);
5337 struct ao2_iterator queue_iter
;
5338 struct ao2_iterator mem_iter
;
5340 if (argc
!= 2 && argc
!= 3)
5341 return CLI_SHOWUSAGE
;
5343 /* We only want to load realtime queues when a specific queue is asked for. */
5344 if (argc
== 3) /* specific queue */
5345 load_realtime_queue(argv
[2]);
5347 queue_iter
= ao2_iterator_init(queues
, 0);
5348 while ((q
= ao2_iterator_next(&queue_iter
))) {
5352 if (argc
== 3 && strcasecmp(q
->name
, argv
[2])) {
5358 ast_str_set(&out
, 0, "%-12.12s has %d calls (max ", q
->name
, q
->count
);
5360 ast_str_append(&out
, 0, "%d", q
->maxlen
);
5362 ast_str_append(&out
, 0, "unlimited");
5364 if (q
->callscompleted
> 0)
5365 sl
= 100 * ((float) q
->callscompletedinsl
/ (float) q
->callscompleted
);
5366 ast_str_append(&out
, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
5367 int2strat(q
->strategy
), q
->holdtime
, q
->weight
,
5368 q
->callscompleted
, q
->callsabandoned
,sl
,q
->servicelevel
);
5369 do_print(s
, fd
, out
->str
);
5370 if (!ao2_container_count(q
->members
))
5371 do_print(s
, fd
, " No Members");
5375 do_print(s
, fd
, " Members: ");
5376 mem_iter
= ao2_iterator_init(q
->members
, 0);
5377 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5378 ast_str_set(&out
, 0, " %s", mem
->membername
);
5380 ast_str_append(&out
, 0, " with penalty %d", mem
->penalty
);
5381 ast_str_append(&out
, 0, "%s%s%s (%s)",
5382 mem
->dynamic
? " (dynamic)" : "",
5383 mem
->realtime
? " (realtime)" : "",
5384 mem
->paused
? " (paused)" : "",
5385 devstate2str(mem
->status
));
5387 ast_str_append(&out
, 0, " has taken %d calls (last was %ld secs ago)",
5388 mem
->calls
, (long) (time(NULL
) - mem
->lastcall
));
5390 ast_str_append(&out
, 0, " has taken no calls yet");
5391 do_print(s
, fd
, out
->str
);
5396 do_print(s
, fd
, " No Callers");
5398 struct queue_ent
*qe
;
5401 do_print(s
, fd
, " Callers: ");
5402 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5403 ast_str_set(&out
, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
5404 pos
++, qe
->chan
->name
, (long) (now
- qe
->start
) / 60,
5405 (long) (now
- qe
->start
) % 60, qe
->prio
);
5406 do_print(s
, fd
, out
->str
);
5409 do_print(s
, fd
, ""); /* blank line between entries */
5411 if (argc
== 3) { /* print a specific entry */
5419 ast_str_set(&out
, 0, "No such queue: %s.", argv
[2]);
5421 ast_str_set(&out
, 0, "No queues.");
5422 do_print(s
, fd
, out
->str
);
5427 static char *complete_queue(const char *line
, const char *word
, int pos
, int state
)
5429 struct call_queue
*q
;
5432 int wordlen
= strlen(word
);
5433 struct ao2_iterator queue_iter
;
5435 queue_iter
= ao2_iterator_init(queues
, 0);
5436 while ((q
= ao2_iterator_next(&queue_iter
))) {
5437 if (!strncasecmp(word
, q
->name
, wordlen
) && ++which
> state
) {
5438 ret
= ast_strdup(q
->name
);
5448 static char *complete_queue_show(const char *line
, const char *word
, int pos
, int state
)
5451 return complete_queue(line
, word
, pos
, state
);
5455 static char *queue_show(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
5459 e
->command
= "queue show";
5461 "Usage: queue show\n"
5462 " Provides summary information on a specified queue.\n";
5465 return complete_queue_show(a
->line
, a
->word
, a
->pos
, a
->n
);
5468 return __queues_show(NULL
, a
->fd
, a
->argc
, a
->argv
);
5471 /*!\brief callback to display queues status in manager
5472 \addtogroup Group_AMI
5474 static int manager_queues_show(struct mansession
*s
, const struct message
*m
)
5476 char *a
[] = { "queue", "show" };
5478 __queues_show(s
, -1, 2, a
);
5479 astman_append(s
, "\r\n\r\n"); /* Properly terminate Manager output */
5481 return RESULT_SUCCESS
;
5484 static int manager_queue_rule_show(struct mansession
*s
, const struct message
*m
)
5486 const char *rule
= astman_get_header(m
, "Rule");
5487 struct rule_list
*rl_iter
;
5488 struct penalty_rule
*pr_iter
;
5490 AST_LIST_LOCK(&rule_lists
);
5491 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
5492 if (ast_strlen_zero(rule
) || !strcasecmp(rule
, rl_iter
->name
)) {
5493 astman_append(s
, "RuleList: %s\r\n", rl_iter
->name
);
5494 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
5495 astman_append(s
, "Rule: %d,%s%d,%s%d\r\n", pr_iter
->time
, pr_iter
->max_relative
&& pr_iter
->max_value
>= 0 ? "+" : "", pr_iter
->max_value
, pr_iter
->min_relative
&& pr_iter
->min_value
>= 0 ? "+" : "", pr_iter
->min_value
);
5497 if (!ast_strlen_zero(rule
))
5501 AST_LIST_UNLOCK(&rule_lists
);
5503 astman_append(s
, "\r\n\r\n");
5505 return RESULT_SUCCESS
;
5508 /*! \brief Summary of queue info via the AMI */
5509 static int manager_queues_summary(struct mansession
*s
, const struct message
*m
)
5515 int qlongestholdtime
= 0;
5516 const char *id
= astman_get_header(m
, "ActionID");
5517 const char *queuefilter
= astman_get_header(m
, "Queue");
5518 char idText
[256] = "";
5519 struct call_queue
*q
;
5520 struct queue_ent
*qe
;
5522 struct ao2_iterator queue_iter
;
5523 struct ao2_iterator mem_iter
;
5525 astman_send_ack(s
, m
, "Queue summary will follow");
5527 if (!ast_strlen_zero(id
))
5528 snprintf(idText
, 256, "ActionID: %s\r\n", id
);
5529 queue_iter
= ao2_iterator_init(queues
, 0);
5530 while ((q
= ao2_iterator_next(&queue_iter
))) {
5533 /* List queue properties */
5534 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
5535 /* Reset the necessary local variables if no queuefilter is set*/
5539 qlongestholdtime
= 0;
5541 /* List Queue Members */
5542 mem_iter
= ao2_iterator_init(q
->members
, 0);
5543 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5544 if ((mem
->status
!= AST_DEVICE_UNAVAILABLE
) && (mem
->status
!= AST_DEVICE_INVALID
)) {
5546 if (((mem
->status
== AST_DEVICE_NOT_INUSE
) || (mem
->status
== AST_DEVICE_UNKNOWN
)) && !(mem
->paused
)) {
5552 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5553 if ((now
- qe
->start
) > qlongestholdtime
) {
5554 qlongestholdtime
= now
- qe
->start
;
5558 astman_append(s
, "Event: QueueSummary\r\n"
5564 "LongestHoldTime: %d\r\n"
5567 q
->name
, qmemcount
, qmemavail
, qchancount
, q
->holdtime
, qlongestholdtime
, idText
);
5573 "Event: QueueSummaryComplete\r\n"
5577 return RESULT_SUCCESS
;
5580 /*! \brief Queue status info via AMI */
5581 static int manager_queues_status(struct mansession
*s
, const struct message
*m
)
5585 const char *id
= astman_get_header(m
,"ActionID");
5586 const char *queuefilter
= astman_get_header(m
,"Queue");
5587 const char *memberfilter
= astman_get_header(m
,"Member");
5588 char idText
[256] = "";
5589 struct call_queue
*q
;
5590 struct queue_ent
*qe
;
5593 struct ao2_iterator queue_iter
;
5594 struct ao2_iterator mem_iter
;
5596 astman_send_ack(s
, m
, "Queue status will follow");
5598 if (!ast_strlen_zero(id
))
5599 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
5601 queue_iter
= ao2_iterator_init(queues
, 0);
5602 while ((q
= ao2_iterator_next(&queue_iter
))) {
5605 /* List queue properties */
5606 if (ast_strlen_zero(queuefilter
) || !strcmp(q
->name
, queuefilter
)) {
5607 sl
= ((q
->callscompleted
> 0) ? 100 * ((float)q
->callscompletedinsl
/ (float)q
->callscompleted
) : 0);
5608 astman_append(s
, "Event: QueueParams\r\n"
5616 "ServiceLevel: %d\r\n"
5617 "ServicelevelPerf: %2.1f\r\n"
5621 q
->name
, q
->maxlen
, int2strat(q
->strategy
), q
->count
, q
->holdtime
, q
->callscompleted
,
5622 q
->callsabandoned
, q
->servicelevel
, sl
, q
->weight
, idText
);
5623 /* List Queue Members */
5624 mem_iter
= ao2_iterator_init(q
->members
, 0);
5625 while ((mem
= ao2_iterator_next(&mem_iter
))) {
5626 if (ast_strlen_zero(memberfilter
) || !strcmp(mem
->interface
, memberfilter
)) {
5627 astman_append(s
, "Event: QueueMember\r\n"
5631 "Membership: %s\r\n"
5633 "CallsTaken: %d\r\n"
5639 q
->name
, mem
->membername
, mem
->interface
, mem
->dynamic
? "dynamic" : "static",
5640 mem
->penalty
, mem
->calls
, (int)mem
->lastcall
, mem
->status
, mem
->paused
, idText
);
5644 /* List Queue Entries */
5646 for (qe
= q
->head
; qe
; qe
= qe
->next
) {
5647 astman_append(s
, "Event: QueueEntry\r\n"
5651 "CallerIDNum: %s\r\n"
5652 "CallerIDName: %s\r\n"
5656 q
->name
, pos
++, qe
->chan
->name
,
5657 S_OR(qe
->chan
->cid
.cid_num
, "unknown"),
5658 S_OR(qe
->chan
->cid
.cid_name
, "unknown"),
5659 (long) (now
- qe
->start
), idText
);
5667 "Event: QueueStatusComplete\r\n"
5671 return RESULT_SUCCESS
;
5674 static int manager_add_queue_member(struct mansession
*s
, const struct message
*m
)
5676 const char *queuename
, *interface
, *penalty_s
, *paused_s
, *membername
, *state_interface
;
5677 int paused
, penalty
= 0;
5679 queuename
= astman_get_header(m
, "Queue");
5680 interface
= astman_get_header(m
, "Interface");
5681 penalty_s
= astman_get_header(m
, "Penalty");
5682 paused_s
= astman_get_header(m
, "Paused");
5683 membername
= astman_get_header(m
, "MemberName");
5684 state_interface
= astman_get_header(m
, "StateInterface");
5686 if (ast_strlen_zero(queuename
)) {
5687 astman_send_error(s
, m
, "'Queue' not specified.");
5691 if (ast_strlen_zero(interface
)) {
5692 astman_send_error(s
, m
, "'Interface' not specified.");
5696 if (ast_strlen_zero(penalty_s
))
5698 else if (sscanf(penalty_s
, "%d", &penalty
) != 1 || penalty
< 0)
5701 if (ast_strlen_zero(paused_s
))
5704 paused
= abs(ast_true(paused_s
));
5706 switch (add_to_queue(queuename
, interface
, membername
, penalty
, paused
, queue_persistent_members
, state_interface
)) {
5708 ast_queue_log(queuename
, "MANAGER", interface
, "ADDMEMBER", "%s", "");
5709 astman_send_ack(s
, m
, "Added interface to queue");
5712 astman_send_error(s
, m
, "Unable to add interface: Already there");
5714 case RES_NOSUCHQUEUE
:
5715 astman_send_error(s
, m
, "Unable to add interface to queue: No such queue");
5717 case RES_OUTOFMEMORY
:
5718 astman_send_error(s
, m
, "Out of memory");
5725 static int manager_remove_queue_member(struct mansession
*s
, const struct message
*m
)
5727 const char *queuename
, *interface
;
5729 queuename
= astman_get_header(m
, "Queue");
5730 interface
= astman_get_header(m
, "Interface");
5732 if (ast_strlen_zero(queuename
) || ast_strlen_zero(interface
)) {
5733 astman_send_error(s
, m
, "Need 'Queue' and 'Interface' parameters.");
5737 switch (remove_from_queue(queuename
, interface
)) {
5739 ast_queue_log(queuename
, "MANAGER", interface
, "REMOVEMEMBER", "%s", "");
5740 astman_send_ack(s
, m
, "Removed interface from queue");
5743 astman_send_error(s
, m
, "Unable to remove interface: Not there");
5745 case RES_NOSUCHQUEUE
:
5746 astman_send_error(s
, m
, "Unable to remove interface from queue: No such queue");
5748 case RES_OUTOFMEMORY
:
5749 astman_send_error(s
, m
, "Out of memory");
5751 case RES_NOT_DYNAMIC
:
5752 astman_send_error(s
, m
, "Member not dynamic");
5759 static int manager_pause_queue_member(struct mansession
*s
, const struct message
*m
)
5761 const char *queuename
, *interface
, *paused_s
, *reason
;
5764 interface
= astman_get_header(m
, "Interface");
5765 paused_s
= astman_get_header(m
, "Paused");
5766 queuename
= astman_get_header(m
, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
5767 reason
= astman_get_header(m
, "Reason"); /* Optional - Only used for logging purposes */
5769 if (ast_strlen_zero(interface
) || ast_strlen_zero(paused_s
)) {
5770 astman_send_error(s
, m
, "Need 'Interface' and 'Paused' parameters.");
5774 paused
= abs(ast_true(paused_s
));
5776 if (set_member_paused(queuename
, interface
, reason
, paused
))
5777 astman_send_error(s
, m
, "Interface not found");
5779 astman_send_ack(s
, m
, paused
? "Interface paused successfully" : "Interface unpaused successfully");
5783 static int manager_queue_log_custom(struct mansession
*s
, const struct message
*m
)
5785 const char *queuename
, *event
, *message
, *interface
, *uniqueid
;
5787 queuename
= astman_get_header(m
, "Queue");
5788 uniqueid
= astman_get_header(m
, "UniqueId");
5789 interface
= astman_get_header(m
, "Interface");
5790 event
= astman_get_header(m
, "Event");
5791 message
= astman_get_header(m
, "Message");
5793 if (ast_strlen_zero(queuename
) || ast_strlen_zero(event
)) {
5794 astman_send_error(s
, m
, "Need 'Queue' and 'Event' parameters.");
5798 ast_queue_log(queuename
, S_OR(uniqueid
, "NONE"), interface
, event
, "%s", message
);
5799 astman_send_ack(s
, m
, "Event added successfully");
5804 static char *complete_queue_add_member(const char *line
, const char *word
, int pos
, int state
)
5806 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
5808 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
5810 case 4: /* only one possible match, "to" */
5811 return state
== 0 ? ast_strdup("to") : NULL
;
5812 case 5: /* <queue> */
5813 return complete_queue(line
, word
, pos
, state
);
5814 case 6: /* only one possible match, "penalty" */
5815 return state
== 0 ? ast_strdup("penalty") : NULL
;
5817 if (state
< 100) { /* 0-99 */
5819 if ((num
= ast_malloc(3))) {
5820 sprintf(num
, "%d", state
);
5826 case 8: /* only one possible match, "as" */
5827 return state
== 0 ? ast_strdup("as") : NULL
;
5828 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
5835 static int manager_queue_member_penalty(struct mansession
*s
, const struct message
*m
)
5837 const char *queuename
, *interface
, *penalty_s
;
5840 interface
= astman_get_header(m
, "Interface");
5841 penalty_s
= astman_get_header(m
, "Penalty");
5842 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
5843 queuename
= astman_get_header(m
, "Queue");
5845 if (ast_strlen_zero(interface
) || ast_strlen_zero(penalty_s
)) {
5846 astman_send_error(s
, m
, "Need 'Interface' and 'Penalty' parameters.");
5850 penalty
= atoi(penalty_s
);
5852 if (set_member_penalty((char *)queuename
, (char *)interface
, penalty
))
5853 astman_send_error(s
, m
, "Invalid interface, queuename or penalty");
5855 astman_send_ack(s
, m
, "Interface penalty set successfully");
5860 static char *handle_queue_add_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
5862 char *queuename
, *interface
, *membername
= NULL
, *state_interface
= NULL
;
5867 e
->command
= "queue add member";
5869 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n";
5872 return complete_queue_add_member(a
->line
, a
->word
, a
->pos
, a
->n
);
5875 if ((a
->argc
!= 6) && (a
->argc
!= 8) && (a
->argc
!= 10) && (a
->argc
!= 12)) {
5876 return CLI_SHOWUSAGE
;
5877 } else if (strcmp(a
->argv
[4], "to")) {
5878 return CLI_SHOWUSAGE
;
5879 } else if ((a
->argc
>= 8) && strcmp(a
->argv
[6], "penalty")) {
5880 return CLI_SHOWUSAGE
;
5881 } else if ((a
->argc
>= 10) && strcmp(a
->argv
[8], "as")) {
5882 return CLI_SHOWUSAGE
;
5883 } else if ((a
->argc
== 12) && strcmp(a
->argv
[10], "state_interface")) {
5884 return CLI_SHOWUSAGE
;
5887 queuename
= a
->argv
[5];
5888 interface
= a
->argv
[3];
5890 if (sscanf(a
->argv
[7], "%d", &penalty
) == 1) {
5892 ast_cli(a
->fd
, "Penalty must be >= 0\n");
5896 ast_cli(a
->fd
, "Penalty must be an integer >= 0\n");
5903 if (a
->argc
>= 10) {
5904 membername
= a
->argv
[9];
5907 if (a
->argc
>= 12) {
5908 state_interface
= a
->argv
[11];
5911 switch (add_to_queue(queuename
, interface
, membername
, penalty
, 0, queue_persistent_members
, state_interface
)) {
5913 ast_queue_log(queuename
, "CLI", interface
, "ADDMEMBER", "%s", "");
5914 ast_cli(a
->fd
, "Added interface '%s' to queue '%s'\n", interface
, queuename
);
5917 ast_cli(a
->fd
, "Unable to add interface '%s' to queue '%s': Already there\n", interface
, queuename
);
5919 case RES_NOSUCHQUEUE
:
5920 ast_cli(a
->fd
, "Unable to add interface to queue '%s': No such queue\n", queuename
);
5922 case RES_OUTOFMEMORY
:
5923 ast_cli(a
->fd
, "Out of memory\n");
5925 case RES_NOT_DYNAMIC
:
5926 ast_cli(a
->fd
, "Member not dynamic\n");
5933 static char *complete_queue_remove_member(const char *line
, const char *word
, int pos
, int state
)
5936 struct call_queue
*q
;
5938 struct ao2_iterator queue_iter
;
5939 struct ao2_iterator mem_iter
;
5940 int wordlen
= strlen(word
);
5942 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
5943 if (pos
> 5 || pos
< 3)
5945 if (pos
== 4) /* only one possible match, 'from' */
5946 return (state
== 0 ? ast_strdup("from") : NULL
);
5948 if (pos
== 5) /* No need to duplicate code */
5949 return complete_queue(line
, word
, pos
, state
);
5951 /* here is the case for 3, <member> */
5952 queue_iter
= ao2_iterator_init(queues
, 0);
5953 while ((q
= ao2_iterator_next(&queue_iter
))) {
5955 mem_iter
= ao2_iterator_init(q
->members
, 0);
5956 while ((m
= ao2_iterator_next(&mem_iter
))) {
5957 if (!strncasecmp(word
, m
->membername
, wordlen
) && ++which
> state
) {
5960 tmp
= m
->membername
;
5963 return ast_strdup(tmp
);
5974 static char *handle_queue_remove_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
5976 char *queuename
, *interface
;
5980 e
->command
= "queue remove member";
5981 e
->usage
= "Usage: queue remove member <channel> from <queue>\n";
5984 return complete_queue_remove_member(a
->line
, a
->word
, a
->pos
, a
->n
);
5988 return CLI_SHOWUSAGE
;
5989 } else if (strcmp(a
->argv
[4], "from")) {
5990 return CLI_SHOWUSAGE
;
5993 queuename
= a
->argv
[5];
5994 interface
= a
->argv
[3];
5996 switch (remove_from_queue(queuename
, interface
)) {
5998 ast_queue_log(queuename
, "CLI", interface
, "REMOVEMEMBER", "%s", "");
5999 ast_cli(a
->fd
, "Removed interface '%s' from queue '%s'\n", interface
, queuename
);
6002 ast_cli(a
->fd
, "Unable to remove interface '%s' from queue '%s': Not there\n", interface
, queuename
);
6004 case RES_NOSUCHQUEUE
:
6005 ast_cli(a
->fd
, "Unable to remove interface from queue '%s': No such queue\n", queuename
);
6007 case RES_OUTOFMEMORY
:
6008 ast_cli(a
->fd
, "Out of memory\n");
6015 static char *complete_queue_pause_member(const char *line
, const char *word
, int pos
, int state
)
6017 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
6019 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
6021 case 4: /* only one possible match, "queue" */
6022 return state
== 0 ? ast_strdup("queue") : NULL
;
6023 case 5: /* <queue> */
6024 return complete_queue(line
, word
, pos
, state
);
6025 case 6: /* "reason" */
6026 return state
== 0 ? ast_strdup("reason") : NULL
;
6027 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
6034 static char *handle_queue_pause_member(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6036 char *queuename
, *interface
, *reason
;
6041 e
->command
= "queue {pause|unpause} member";
6043 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
6044 " Pause or unpause a queue member. Not specifying a particular queue\n"
6045 " will pause or unpause a member across all queues to which the member\n"
6049 return complete_queue_pause_member(a
->line
, a
-> word
, a
->pos
, a
->n
);
6052 if (a
->argc
< 4 || a
->argc
== 5 || a
->argc
== 7 || a
->argc
> 8) {
6053 return CLI_SHOWUSAGE
;
6054 } else if (a
->argc
>= 5 && strcmp(a
->argv
[4], "queue")) {
6055 return CLI_SHOWUSAGE
;
6056 } else if (a
->argc
== 8 && strcmp(a
->argv
[6], "reason")) {
6057 return CLI_SHOWUSAGE
;
6061 interface
= a
->argv
[3];
6062 queuename
= a
->argc
>= 6 ? a
->argv
[5] : NULL
;
6063 reason
= a
->argc
== 8 ? a
->argv
[7] : NULL
;
6064 paused
= !strcasecmp(a
->argv
[1], "pause");
6066 if (set_member_paused(queuename
, interface
, reason
, paused
) == RESULT_SUCCESS
) {
6067 ast_cli(a
->fd
, "%spaused interface '%s'", paused
? "" : "un", interface
);
6068 if (!ast_strlen_zero(queuename
))
6069 ast_cli(a
->fd
, " in queue '%s'", queuename
);
6070 if (!ast_strlen_zero(reason
))
6071 ast_cli(a
->fd
, " for reason '%s'", reason
);
6072 ast_cli(a
->fd
, "\n");
6075 ast_cli(a
->fd
, "Unable to %spause interface '%s'", paused
? "" : "un", interface
);
6076 if (!ast_strlen_zero(queuename
))
6077 ast_cli(a
->fd
, " in queue '%s'", queuename
);
6078 if (!ast_strlen_zero(reason
))
6079 ast_cli(a
->fd
, " for reason '%s'", reason
);
6080 ast_cli(a
->fd
, "\n");
6085 static char *complete_queue_set_member_penalty(const char *line
, const char *word
, int pos
, int state
)
6087 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
6091 return ast_strdup("on");
6097 return ast_strdup("in");
6102 return complete_queue(line
, word
, pos
, state
);
6108 static char *handle_queue_set_member_penalty(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6110 char *queuename
= NULL
, *interface
;
6115 e
->command
= "queue set penalty";
6117 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
6118 "Set a member's penalty in the queue specified. If no queue is specified\n"
6119 "then that interface's penalty is set in all queues to which that interface is a member\n";
6122 return complete_queue_set_member_penalty(a
->line
, a
->word
, a
->pos
, a
->n
);
6125 if (a
->argc
!= 6 && a
->argc
!= 8) {
6126 return CLI_SHOWUSAGE
;
6127 } else if (strcmp(a
->argv
[4], "on") || (a
->argc
> 6 && strcmp(a
->argv
[6], "in"))) {
6128 return CLI_SHOWUSAGE
;
6132 queuename
= a
->argv
[7];
6133 interface
= a
->argv
[5];
6134 penalty
= atoi(a
->argv
[3]);
6136 switch (set_member_penalty(queuename
, interface
, penalty
)) {
6137 case RESULT_SUCCESS
:
6138 ast_cli(a
->fd
, "Set penalty on interface '%s' from queue '%s'\n", interface
, queuename
);
6140 case RESULT_FAILURE
:
6141 ast_cli(a
->fd
, "Failed to set penalty on interface '%s' from queue '%s'\n", interface
, queuename
);
6148 static char *complete_queue_rule_show(const char *line
, const char *word
, int pos
, int state
)
6151 struct rule_list
*rl_iter
;
6152 int wordlen
= strlen(word
);
6154 if (pos
!= 3) /* Wha? */ {
6155 ast_log(LOG_DEBUG
, "Hitting this???, pos is %d\n", pos
);
6159 AST_LIST_LOCK(&rule_lists
);
6160 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
6161 if (!strncasecmp(word
, rl_iter
->name
, wordlen
) && ++which
> state
) {
6162 ret
= ast_strdup(rl_iter
->name
);
6166 AST_LIST_UNLOCK(&rule_lists
);
6171 static char *handle_queue_rule_show(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6174 struct rule_list
*rl_iter
;
6175 struct penalty_rule
*pr_iter
;
6178 e
->command
= "queue rules show";
6180 "Usage: queue rules show [rulename]\n"
6181 "Show the list of rules associated with rulename. If no\n"
6182 "rulename is specified, list all rules defined in queuerules.conf\n";
6185 return complete_queue_rule_show(a
->line
, a
->word
, a
->pos
, a
->n
);
6188 if (a
->argc
!= 3 && a
->argc
!= 4)
6189 return CLI_SHOWUSAGE
;
6191 rule
= a
->argc
== 4 ? a
->argv
[3] : "";
6192 AST_LIST_LOCK(&rule_lists
);
6193 AST_LIST_TRAVERSE(&rule_lists
, rl_iter
, list
) {
6194 if (ast_strlen_zero(rule
) || !strcasecmp(rl_iter
->name
, rule
)) {
6195 ast_cli(a
->fd
, "Rule: %s\n", rl_iter
->name
);
6196 AST_LIST_TRAVERSE(&rl_iter
->rules
, pr_iter
, list
) {
6197 ast_cli(a
->fd
, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter
->time
, pr_iter
->max_relative
? "by" : "to", pr_iter
->max_value
, pr_iter
->min_relative
? "by" : "to", pr_iter
->min_value
);
6201 AST_LIST_UNLOCK(&rule_lists
);
6205 static char *handle_queue_rule_reload(struct ast_cli_entry
*e
, int cmd
, struct ast_cli_args
*a
)
6209 e
->command
= "queue rules reload";
6211 "Usage: queue rules reload\n"
6212 "Reloads rules defined in queuerules.conf\n";
6217 reload_queue_rules(1);
6221 static const char qpm_cmd_usage
[] =
6222 "Usage: queue pause member <channel> in <queue> reason <reason>\n";
6224 static const char qum_cmd_usage
[] =
6225 "Usage: queue unpause member <channel> in <queue> reason <reason>\n";
6227 static const char qsmp_cmd_usage
[] =
6228 "Usage: queue set member penalty <channel> from <queue> <penalty>\n";
6230 static struct ast_cli_entry cli_queue
[] = {
6231 AST_CLI_DEFINE(queue_show
, "Show status of a specified queue"),
6232 AST_CLI_DEFINE(handle_queue_add_member
, "Add a channel to a specified queue"),
6233 AST_CLI_DEFINE(handle_queue_remove_member
, "Removes a channel from a specified queue"),
6234 AST_CLI_DEFINE(handle_queue_pause_member
, "Pause or unpause a queue member"),
6235 AST_CLI_DEFINE(handle_queue_set_member_penalty
, "Set penalty for a channel of a specified queue"),
6236 AST_CLI_DEFINE(handle_queue_rule_show
, "Show the rules defined in queuerules.conf"),
6237 AST_CLI_DEFINE(handle_queue_rule_reload
, "Reload the rules defined in queuerules.conf"),
6240 static int unload_module(void)
6243 struct ast_context
*con
;
6244 struct ao2_iterator q_iter
;
6245 struct call_queue
*q
= NULL
;
6247 ast_cli_unregister_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
6248 res
= ast_manager_unregister("QueueStatus");
6249 res
|= ast_manager_unregister("Queues");
6250 res
|= ast_manager_unregister("QueueRule");
6251 res
|= ast_manager_unregister("QueueSummary");
6252 res
|= ast_manager_unregister("QueueAdd");
6253 res
|= ast_manager_unregister("QueueRemove");
6254 res
|= ast_manager_unregister("QueuePause");
6255 res
|= ast_manager_unregister("QueueLog");
6256 res
|= ast_manager_unregister("QueuePenalty");
6257 res
|= ast_unregister_application(app_aqm
);
6258 res
|= ast_unregister_application(app_rqm
);
6259 res
|= ast_unregister_application(app_pqm
);
6260 res
|= ast_unregister_application(app_upqm
);
6261 res
|= ast_unregister_application(app_ql
);
6262 res
|= ast_unregister_application(app
);
6263 res
|= ast_custom_function_unregister(&queuevar_function
);
6264 res
|= ast_custom_function_unregister(&queuemembercount_function
);
6265 res
|= ast_custom_function_unregister(&queuemembercount_dep
);
6266 res
|= ast_custom_function_unregister(&queuememberlist_function
);
6267 res
|= ast_custom_function_unregister(&queuewaitingcount_function
);
6268 res
|= ast_custom_function_unregister(&queuememberpenalty_function
);
6270 if (device_state_sub
)
6271 ast_event_unsubscribe(device_state_sub
);
6273 if ((con
= ast_context_find("app_queue_gosub_virtual_context"))) {
6274 ast_context_remove_extension2(con
, "s", 1, NULL
);
6275 ast_context_destroy(con
, "app_queue"); /* leave no trace */
6278 clear_and_free_interfaces();
6280 q_iter
= ao2_iterator_init(queues
, 0);
6281 while ((q
= ao2_iterator_next(&q_iter
))) {
6282 ao2_unlink(queues
, q
);
6285 ao2_ref(queues
, -1);
6286 devicestate_tps
= ast_taskprocessor_unreference(devicestate_tps
);
6290 static int load_module(void)
6293 struct ast_context
*con
;
6295 queues
= ao2_container_alloc(MAX_QUEUE_BUCKETS
, queue_hash_cb
, queue_cmp_cb
);
6297 if (!reload_queues(0))
6298 return AST_MODULE_LOAD_DECLINE
;
6300 con
= ast_context_find_or_create(NULL
, NULL
, "app_queue_gosub_virtual_context", "app_queue");
6302 ast_log(LOG_ERROR
, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
6304 ast_add_extension2(con
, 1, "s", 1, NULL
, NULL
, "KeepAlive", ast_strdup(""), ast_free_ptr
, "app_queue");
6306 if (queue_persistent_members
)
6307 reload_queue_members();
6309 ast_cli_register_multiple(cli_queue
, sizeof(cli_queue
) / sizeof(struct ast_cli_entry
));
6310 res
= ast_register_application(app
, queue_exec
, synopsis
, descrip
);
6311 res
|= ast_register_application(app_aqm
, aqm_exec
, app_aqm_synopsis
, app_aqm_descrip
);
6312 res
|= ast_register_application(app_rqm
, rqm_exec
, app_rqm_synopsis
, app_rqm_descrip
);
6313 res
|= ast_register_application(app_pqm
, pqm_exec
, app_pqm_synopsis
, app_pqm_descrip
);
6314 res
|= ast_register_application(app_upqm
, upqm_exec
, app_upqm_synopsis
, app_upqm_descrip
);
6315 res
|= ast_register_application(app_ql
, ql_exec
, app_ql_synopsis
, app_ql_descrip
);
6316 res
|= ast_manager_register("Queues", 0, manager_queues_show
, "Queues");
6317 res
|= ast_manager_register("QueueStatus", 0, manager_queues_status
, "Queue Status");
6318 res
|= ast_manager_register("QueueSummary", 0, manager_queues_summary
, "Queue Summary");
6319 res
|= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT
, manager_add_queue_member
, "Add interface to queue.");
6320 res
|= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT
, manager_remove_queue_member
, "Remove interface from queue.");
6321 res
|= ast_manager_register("QueuePause", EVENT_FLAG_AGENT
, manager_pause_queue_member
, "Makes a queue member temporarily unavailable");
6322 res
|= ast_manager_register("QueueLog", EVENT_FLAG_AGENT
, manager_queue_log_custom
, "Adds custom entry in queue_log");
6323 res
|= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT
, manager_queue_member_penalty
, "Set the penalty for a queue member");
6324 res
|= ast_manager_register("QueueRule", 0, manager_queue_rule_show
, "Queue Rules");
6325 res
|= ast_custom_function_register(&queuevar_function
);
6326 res
|= ast_custom_function_register(&queuemembercount_function
);
6327 res
|= ast_custom_function_register(&queuemembercount_dep
);
6328 res
|= ast_custom_function_register(&queuememberlist_function
);
6329 res
|= ast_custom_function_register(&queuewaitingcount_function
);
6330 res
|= ast_custom_function_register(&queuememberpenalty_function
);
6332 if (!(devicestate_tps
= ast_taskprocessor_get("app_queue", 0))) {
6333 ast_log(LOG_WARNING
, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
6336 if (!(device_state_sub
= ast_event_subscribe(AST_EVENT_DEVICE_STATE
, device_state_cb
, NULL
, AST_EVENT_IE_END
)))
6339 return res
? AST_MODULE_LOAD_DECLINE
: 0;
6342 static int reload(void)
6348 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "True Call Queueing",
6349 .load
= load_module
,
6350 .unload
= unload_module
,