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>
72 #include "asterisk/lock.h"
73 #include "asterisk/file.h"
74 #include "asterisk/channel.h"
75 #include "asterisk/pbx.h"
76 #include "asterisk/app.h"
77 #include "asterisk/linkedlists.h"
78 #include "asterisk/module.h"
79 #include "asterisk/translate.h"
80 #include "asterisk/say.h"
81 #include "asterisk/features.h"
82 #include "asterisk/musiconhold.h"
83 #include "asterisk/cli.h"
84 #include "asterisk/manager.h"
85 #include "asterisk/config.h"
86 #include "asterisk/monitor.h"
87 #include "asterisk/utils.h"
88 #include "asterisk/causes.h"
89 #include "asterisk/astdb.h"
90 #include "asterisk/devicestate.h"
91 #include "asterisk/stringfields.h"
92 #include "asterisk/event.h"
93 #include "asterisk/astobj2.h"
94 #include "asterisk/strings.h"
95 #include "asterisk/global_datastores.h"
96 #include "asterisk/taskprocessor.h"
97 #include "asterisk/callerid.h"
98 #include "asterisk/cel.h"
99 #include "asterisk/data.h"
101 /* Define, to debug reference counts on queues, without debugging reference counts on queue members */
102 /* #define REF_DEBUG_ONLY_QUEUES */
105 * \par Please read before modifying this file.
106 * There are three locks which are regularly used
107 * throughout this file, the queue list lock, the lock
108 * for each individual queue, and the interface list lock.
109 * Please be extra careful to always lock in the following order
111 * 2) individual queue lock
112 * 3) interface list lock
113 * This order has sort of "evolved" over the lifetime of this
114 * application, but it is now in place this way, so please adhere
119 <application name="Queue" language="en_US">
121 Queue a call for a call queue.
124 <parameter name="queuename" required="true" />
125 <parameter name="options">
128 <para>Mark all calls as "answered elsewhere" when cancelled.</para>
131 <para>Continue in the dialplan if the callee hangs up.</para>
134 <para>data-quality (modem) call (minimum delay).</para>
137 <para>Allow <emphasis>callee</emphasis> to hang up by pressing <literal>*</literal>.</para>
140 <para>Allow <emphasis>caller</emphasis> to hang up by pressing <literal>*</literal>.</para>
143 <para>No retries on the timeout; will exit this application and
144 go to the next step.</para>
147 <para>Ignore call forward requests from queue members and do nothing
148 when they are requested.</para>
151 <para>Asterisk will ignore any connected line update requests or any redirecting party
152 update requests it may receive on this dial attempt.</para>
155 <para>Ring instead of playing MOH. Periodic Announcements are still made, if applicable.</para>
158 <para>Ring instead of playing MOH when a member channel is actually ringing.</para>
161 <para>Allow the <emphasis>called</emphasis> user to transfer the calling user.</para>
164 <para>Allow the <emphasis>calling</emphasis> user to transfer the call.</para>
167 <para>Allow the <emphasis>called</emphasis> user to write the conversation to
168 disk via Monitor.</para>
171 <para>Allow the <emphasis>calling</emphasis> user to write the conversation to
172 disk via Monitor.</para>
175 <para>Allow the <emphasis>called</emphasis> party to enable parking of the call by sending
176 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
179 <para>Allow the <emphasis>calling</emphasis> party to enable parking of the call by sending
180 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
183 <para>Allow the <emphasis>called</emphasis> user to write the conversation
184 to disk via MixMonitor.</para>
187 <para>Allow the <emphasis>calling</emphasis> user to write the conversation to
188 disk via MixMonitor.</para>
192 <parameter name="URL">
193 <para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
195 <parameter name="announceoverride" />
196 <parameter name="timeout">
197 <para>Will cause the queue to fail out after a specified number of
198 seconds, checked between each <filename>queues.conf</filename> <replaceable>timeout</replaceable> and
199 <replaceable>retry</replaceable> cycle.</para>
201 <parameter name="AGI">
202 <para>Will setup an AGI script to be executed on the calling party's channel once they are
203 connected to a queue member.</para>
205 <parameter name="macro">
206 <para>Will run a macro on the calling party's channel once they are connected to a queue member.</para>
208 <parameter name="gosub">
209 <para>Will run a gosub on the calling party's channel once they are connected to a queue member.</para>
211 <parameter name="rule">
212 <para>Will cause the queue's defaultrule to be overridden by the rule specified.</para>
214 <parameter name="position">
215 <para>Attempt to enter the caller into the queue at the numerical position specified. <literal>1</literal>
216 would attempt to enter the caller at the head of the queue, and <literal>3</literal> would attempt to place
217 the caller third in the queue.</para>
221 <para>In addition to transferring the call, a call may be parked and then picked
222 up by another user.</para>
223 <para>This application will return to the dialplan if the queue does not exist, or
224 any of the join options cause the caller to not enter the queue.</para>
225 <para>This application sets the following channel variable upon completion:</para>
227 <variable name="QUEUESTATUS">
228 <para>The status of the call as a text string.</para>
229 <value name="TIMEOUT" />
230 <value name="FULL" />
231 <value name="JOINEMPTY" />
232 <value name="LEAVEEMPTY" />
233 <value name="JOINUNAVAIL" />
234 <value name="LEAVEUNAVAIL" />
235 <value name="CONTINUE" />
240 <ref type="application">AddQueueMember</ref>
241 <ref type="application">RemoveQueueMember</ref>
242 <ref type="application">PauseQueueMember</ref>
243 <ref type="application">UnpauseQueueMember</ref>
244 <ref type="application">AgentLogin</ref>
245 <ref type="function">QUEUE_MEMBER_COUNT</ref>
246 <ref type="function">QUEUE_MEMBER_LIST</ref>
247 <ref type="function">QUEUE_WAITING_COUNT</ref>
250 <application name="AddQueueMember" language="en_US">
252 Dynamically adds queue members.
255 <parameter name="queuename" required="true" />
256 <parameter name="interface" />
257 <parameter name="penalty" />
258 <parameter name="options" />
259 <parameter name="membername" />
260 <parameter name="stateinterface" />
263 <para>Dynamically adds interface to an existing queue. If the interface is
264 already in the queue it will return an error.</para>
265 <para>This application sets the following channel variable upon completion:</para>
267 <variable name="AQMSTATUS">
268 <para>The status of the attempt to add a queue member as a text string.</para>
269 <value name="ADDED" />
270 <value name="MEMBERALREADY" />
271 <value name="NOSUCHQUEUE" />
276 <ref type="application">RemoveQueueMember</ref>
277 <ref type="application">PauseQueueMember</ref>
278 <ref type="application">UnpauseQueueMember</ref>
279 <ref type="application">AgentLogin</ref>
282 <application name="RemoveQueueMember" language="en_US">
284 Dynamically removes queue members.
287 <parameter name="queuename" required="true" />
288 <parameter name="interface" />
289 <parameter name="options" />
292 <para>If the interface is <emphasis>NOT</emphasis> in the queue it will return an error.</para>
293 <para>This application sets the following channel variable upon completion:</para>
295 <variable name="RQMSTATUS">
296 <value name="REMOVED" />
297 <value name="NOTINQUEUE" />
298 <value name="NOSUCHQUEUE" />
301 <para>Example: RemoveQueueMember(techsupport,SIP/3000)</para>
304 <ref type="application">Queue</ref>
305 <ref type="application">AddQueueMember</ref>
306 <ref type="application">PauseQueueMember</ref>
307 <ref type="application">UnpauseQueueMember</ref>
310 <application name="PauseQueueMember" language="en_US">
312 Pauses a queue member.
315 <parameter name="queuename" />
316 <parameter name="interface" required="true" />
317 <parameter name="options" />
318 <parameter name="reason">
319 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
323 <para>Pauses (blocks calls for) a queue member. The given interface will be paused in the given queue.
324 This prevents any calls from being sent from the queue to the interface until it is
325 unpaused with UnpauseQueueMember or the manager interface. If no queuename is given,
326 the interface is paused in every queue it is a member of. The application will fail if the
327 interface is not found.</para>
328 <para>This application sets the following channel variable upon completion:</para>
330 <variable name="PQMSTATUS">
331 <para>The status of the attempt to pause a queue member as a text string.</para>
332 <value name="PAUSED" />
333 <value name="NOTFOUND" />
336 <para>Example: PauseQueueMember(,SIP/3000)</para>
339 <ref type="application">UnpauseQueueMember</ref>
342 <application name="UnpauseQueueMember" language="en_US">
344 Unpauses a queue member.
347 <parameter name="queuename" />
348 <parameter name="interface" required="true" />
349 <parameter name="options" />
350 <parameter name="reason">
351 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
355 <para>Unpauses (resumes calls to) a queue member. This is the counterpart to <literal>PauseQueueMember()</literal>
356 and operates exactly the same way, except it unpauses instead of pausing the given interface.</para>
357 <para>This application sets the following channel variable upon completion:</para>
359 <variable name="UPQMSTATUS">
360 <para>The status of the attempt to unpause a queue member as a text string.</para>
361 <value name="UNPAUSED" />
362 <value name="NOTFOUND" />
365 <para>Example: UnpauseQueueMember(,SIP/3000)</para>
368 <ref type="application">PauseQueueMember</ref>
371 <application name="QueueLog" language="en_US">
373 Writes to the queue_log file.
376 <parameter name="queuename" required="true" />
377 <parameter name="uniqueid" required="true" />
378 <parameter name="agent" required="true" />
379 <parameter name="event" required="true" />
380 <parameter name="additionalinfo" />
383 <para>Allows you to write your own events into the queue log.</para>
384 <para>Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)</para>
387 <ref type="application">Queue</ref>
390 <function name="QUEUE_VARIABLES" language="en_US">
392 Return Queue information in variables.
395 <parameter name="queuename" required="true">
397 <enum name="QUEUEMAX">
398 <para>Maxmimum number of calls allowed.</para>
400 <enum name="QUEUESTRATEGY">
401 <para>The strategy of the queue.</para>
403 <enum name="QUEUECALLS">
404 <para>Number of calls currently in the queue.</para>
406 <enum name="QUEUEHOLDTIME">
407 <para>Current average hold time.</para>
409 <enum name="QUEUECOMPLETED">
410 <para>Number of completed calls for the queue.</para>
412 <enum name="QUEUEABANDONED">
413 <para>Number of abandoned calls.</para>
415 <enum name="QUEUESRVLEVEL">
416 <para>Queue service level.</para>
418 <enum name="QUEUESRVLEVELPERF">
419 <para>Current service level performance.</para>
425 <para>Makes the following queue variables available.</para>
426 <para>Returns <literal>0</literal> if queue is found and setqueuevar is defined, <literal>-1</literal> otherwise.</para>
429 <function name="QUEUE_MEMBER" language="en_US">
431 Count number of members answering a queue.
434 <parameter name="queuename" required="true" />
435 <parameter name="option" required="true">
438 <para>Returns the number of logged-in members for the specified queue.</para>
441 <para>Returns the number of logged-in members for the specified queue that either can take calls or are currently wrapping up after a previous call.</para>
444 <para>Returns the number of logged-in members for the specified queue that are immediately available to answer a call.</para>
447 <para>Returns the total number of members for the specified queue.</para>
453 <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
456 <function name="QUEUE_MEMBER_COUNT" language="en_US">
458 Count number of members answering a queue.
461 <parameter name="queuename" required="true" />
464 <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
465 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
468 <ref type="function">QUEUE_MEMBER_LIST</ref>
471 <function name="QUEUE_WAITING_COUNT" language="en_US">
473 Count number of calls currently waiting in a queue.
476 <parameter name="queuename" />
479 <para>Returns the number of callers currently waiting in the specified <replaceable>queuename</replaceable>.</para>
482 <function name="QUEUE_MEMBER_LIST" language="en_US">
484 Returns a list of interfaces on a queue.
487 <parameter name="queuename" required="true" />
490 <para>Returns a comma-separated list of members associated with the specified <replaceable>queuename</replaceable>.</para>
493 <ref type="function">QUEUE_MEMBER_COUNT</ref>
496 <function name="QUEUE_MEMBER_PENALTY" language="en_US">
498 Gets or sets queue members penalty.
501 <parameter name="queuename" required="true" />
502 <parameter name="interface" required="true" />
505 <para>Gets or sets queue members penalty.</para>
508 <manager name="Queues" language="en_US">
513 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
518 <manager name="QueueStatus" language="en_US">
523 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
524 <parameter name="Queue" />
525 <parameter name="Member" />
530 <manager name="QueueSummary" language="en_US">
535 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
536 <parameter name="Queue" />
541 <manager name="QueueAdd" language="en_US">
543 Add interface to queue.
546 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
547 <parameter name="Queue" required="true" />
548 <parameter name="Interface" required="true" />
549 <parameter name="Penalty" />
550 <parameter name="Paused" />
551 <parameter name="MemberName" />
552 <parameter name="StateInterface" />
557 <manager name="QueueRemove" language="en_US">
559 Remove interface from queue.
562 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
563 <parameter name="Queue" required="true" />
564 <parameter name="Interface" required="true" />
569 <manager name="QueuePause" language="en_US">
571 Makes a queue member temporarily unavailable.
574 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
575 <parameter name="Interface" required="true" />
576 <parameter name="Paused" required="true" />
577 <parameter name="Queue" />
578 <parameter name="Reason" />
583 <manager name="QueueLog" language="en_US">
585 Adds custom entry in queue_log.
588 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
589 <parameter name="Queue" required="true" />
590 <parameter name="Event" required="true" />
591 <parameter name="Uniqueid" />
592 <parameter name="Interface" />
593 <parameter name="Message" />
598 <manager name="QueuePenalty" language="en_US">
600 Set the penalty for a queue member.
603 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
604 <parameter name="Interface" required="true" />
605 <parameter name="Penalty" required="true" />
606 <parameter name="Queue" />
611 <manager name="QueueRule" language="en_US">
616 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
617 <parameter name="Rule" />
622 <manager name="QueueReload" language="en_US">
624 Reload a queue, queues, or any sub-section of a queue or queues.
627 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
628 <parameter name="Queue" />
629 <parameter name="Members">
635 <parameter name="Rules">
641 <parameter name="Parameters">
651 <manager name="QueueReset" language="en_US">
653 Reset queue statistics.
656 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
657 <parameter name="Queue" />
665 QUEUE_STRATEGY_RINGALL = 0,
666 QUEUE_STRATEGY_LEASTRECENT,
667 QUEUE_STRATEGY_FEWESTCALLS,
668 QUEUE_STRATEGY_RANDOM,
669 QUEUE_STRATEGY_RRMEMORY,
670 QUEUE_STRATEGY_LINEAR,
671 QUEUE_STRATEGY_WRANDOM
674 enum queue_reload_mask {
675 QUEUE_RELOAD_PARAMETERS = (1 << 0),
676 QUEUE_RELOAD_MEMBER = (1 << 1),
677 QUEUE_RELOAD_RULES = (1 << 2),
678 QUEUE_RESET_STATS = (1 << 3),
681 static const struct strategy {
685 { QUEUE_STRATEGY_RINGALL, "ringall" },
686 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
687 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
688 { QUEUE_STRATEGY_RANDOM, "random" },
689 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
690 { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
691 { QUEUE_STRATEGY_LINEAR, "linear" },
692 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
695 static struct ast_taskprocessor *devicestate_tps;
697 #define DEFAULT_RETRY 5
698 #define DEFAULT_TIMEOUT 15
699 #define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
700 #define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
701 #define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 /*!< The minimum number of seconds between position announcements \
702 The default value of 15 provides backwards compatibility */
703 #define MAX_QUEUE_BUCKETS 53
705 #define RES_OKAY 0 /*!< Action completed */
706 #define RES_EXISTS (-1) /*!< Entry already exists */
707 #define RES_OUTOFMEMORY (-2) /*!< Out of memory */
708 #define RES_NOSUCHQUEUE (-3) /*!< No such queue */
709 #define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
711 static char *app = "Queue";
713 static char *app_aqm = "AddQueueMember" ;
715 static char *app_rqm = "RemoveQueueMember" ;
717 static char *app_pqm = "PauseQueueMember" ;
719 static char *app_upqm = "UnpauseQueueMember" ;
721 static char *app_ql = "QueueLog" ;
723 /*! \brief Persistent Members astdb family */
724 static const char * const pm_family = "Queue/PersistentMembers";
725 /* The maximum length of each persistent member queue database entry */
726 #define PM_MAX_LEN 8192
728 /*! \brief queues.conf [general] option */
729 static int queue_persistent_members = 0;
731 /*! \brief queues.conf per-queue weight option */
732 static int use_weight = 0;
734 /*! \brief queues.conf [general] option */
735 static int autofill_default = 0;
737 /*! \brief queues.conf [general] option */
738 static int montype_default = 0;
740 /*! \brief queues.conf [general] option */
741 static int shared_lastcall = 0;
743 /*! \brief Subscription to device state change events */
744 static struct ast_event_sub *device_state_sub;
746 /*! \brief queues.conf [general] option */
747 static int update_cdr = 0;
753 QUEUE_LEAVEEMPTY = 3,
754 QUEUE_JOINUNAVAIL = 4,
755 QUEUE_LEAVEUNAVAIL = 5,
760 static const struct {
761 enum queue_result id;
763 } queue_results[] = {
764 { QUEUE_UNKNOWN, "UNKNOWN" },
765 { QUEUE_TIMEOUT, "TIMEOUT" },
766 { QUEUE_JOINEMPTY,"JOINEMPTY" },
767 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
768 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
769 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
770 { QUEUE_FULL, "FULL" },
771 { QUEUE_CONTINUE, "CONTINUE" },
774 enum queue_timeout_priority {
775 TIMEOUT_PRIORITY_APP,
776 TIMEOUT_PRIORITY_CONF,
779 /*! \brief We define a custom "local user" structure because we
780 * use it not only for keeping track of what is in use but
781 * also for keeping track of who we're dialing.
783 * There are two "links" defined in this structure, q_next and call_next.
784 * q_next links ALL defined callattempt structures into a linked list. call_next is
785 * a link which allows for a subset of the callattempts to be traversed. This subset
786 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
787 * also is helpful so that queue logs are always accurate in the case where a call to
788 * a member times out, especially if using the ringall strategy.
792 struct callattempt *q_next;
793 struct callattempt *call_next;
794 struct ast_channel *chan;
799 struct call_queue *lastqueue;
800 struct member *member;
801 unsigned int update_connectedline:1;
802 struct ast_party_connected_line connected;
807 struct call_queue *parent; /*!< What queue is our parent */
808 char moh[80]; /*!< Name of musiconhold to be used */
809 char announce[80]; /*!< Announcement to play for member when call is answered */
810 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
811 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
812 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
813 int pos; /*!< Where we are in the queue */
814 int prio; /*!< Our priority */
815 int last_pos_said; /*!< Last position we told the user */
816 int ring_when_ringing; /*!< Should we only use ring indication when a channel is ringing? */
817 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
818 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
819 time_t last_pos; /*!< Last time we told the user their position */
820 int opos; /*!< Where we started in the queue */
821 int handled; /*!< Whether our call was handled */
822 int pending; /*!< Non-zero if we are attempting to call a member */
823 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
824 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
825 int linpos; /*!< If using linear strategy, what position are we at? */
826 int linwrapped; /*!< Is the linpos wrapped? */
827 time_t start; /*!< When we started holding */
828 time_t expire; /*!< When this entry should expire (time out of queue) */
829 int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
830 struct ast_channel *chan; /*!< Our channel */
831 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
832 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
833 struct queue_ent *next; /*!< The next queue entry */
837 char interface[80]; /*!< Technology/Location to dial to reach this member*/
838 char state_exten[AST_MAX_EXTENSION]; /*!< Extension to get state from (if using hint) */
839 char state_context[AST_MAX_CONTEXT]; /*!< Context to use when getting state (if using hint) */
840 char state_interface[80]; /*!< Technology/Location from which to read devicestate changes */
841 char membername[80]; /*!< Member name to use in queue logs */
842 int penalty; /*!< Are we a last resort? */
843 int calls; /*!< Number of calls serviced by this member */
844 int dynamic; /*!< Are we dynamically added? */
845 int realtime; /*!< Is this member realtime? */
846 int status; /*!< Status of queue member */
847 int paused; /*!< Are we paused (not accepting calls)? */
848 time_t lastcall; /*!< When last successful call was hungup */
849 struct call_queue *lastqueue; /*!< Last queue we received a call */
850 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
851 unsigned int delme:1; /*!< Flag to delete entry on reload */
852 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
855 enum empty_conditions {
856 QUEUE_EMPTY_PENALTY = (1 << 0),
857 QUEUE_EMPTY_PAUSED = (1 << 1),
858 QUEUE_EMPTY_INUSE = (1 << 2),
859 QUEUE_EMPTY_RINGING = (1 << 3),
860 QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
861 QUEUE_EMPTY_INVALID = (1 << 5),
862 QUEUE_EMPTY_UNKNOWN = (1 << 6),
863 QUEUE_EMPTY_WRAPUP = (1 << 7),
866 /* values used in multi-bit flags in call_queue */
867 #define ANNOUNCEHOLDTIME_ALWAYS 1
868 #define ANNOUNCEHOLDTIME_ONCE 2
869 #define QUEUE_EVENT_VARIABLES 3
871 struct penalty_rule {
872 int time; /*!< Number of seconds that need to pass before applying this rule */
873 int max_value; /*!< The amount specified in the penalty rule for max penalty */
874 int min_value; /*!< The amount specified in the penalty rule for min penalty */
875 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
876 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
877 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
880 #define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
881 #define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
882 #define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
883 #define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
886 AST_DECLARE_STRING_FIELDS(
888 AST_STRING_FIELD(name);
889 /*! Music on Hold class */
890 AST_STRING_FIELD(moh);
891 /*! Announcement to play when call is answered */
892 AST_STRING_FIELD(announce);
894 AST_STRING_FIELD(context);
895 /*! Macro to run upon member connection */
896 AST_STRING_FIELD(membermacro);
897 /*! Gosub to run upon member connection */
898 AST_STRING_FIELD(membergosub);
899 /*! Default rule to use if none specified in call to Queue() */
900 AST_STRING_FIELD(defaultrule);
901 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
902 AST_STRING_FIELD(sound_next);
903 /*! Sound file: "There are currently" (def. queue-thereare) */
904 AST_STRING_FIELD(sound_thereare);
905 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
906 AST_STRING_FIELD(sound_calls);
907 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
908 AST_STRING_FIELD(queue_quantity1);
909 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
910 AST_STRING_FIELD(queue_quantity2);
911 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
912 AST_STRING_FIELD(sound_holdtime);
913 /*! Sound file: "minutes." (def. queue-minutes) */
914 AST_STRING_FIELD(sound_minutes);
915 /*! Sound file: "minute." (def. queue-minute) */
916 AST_STRING_FIELD(sound_minute);
917 /*! Sound file: "seconds." (def. queue-seconds) */
918 AST_STRING_FIELD(sound_seconds);
919 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
920 AST_STRING_FIELD(sound_thanks);
921 /*! Sound file: Custom announce for caller, no default */
922 AST_STRING_FIELD(sound_callerannounce);
923 /*! Sound file: "Hold time" (def. queue-reporthold) */
924 AST_STRING_FIELD(sound_reporthold);
926 /*! Sound files: Custom announce, no default */
927 struct ast_str *sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS];
929 unsigned int eventwhencalled:2;
930 unsigned int ringinuse:1;
931 unsigned int setinterfacevar:1;
932 unsigned int setqueuevar:1;
933 unsigned int setqueueentryvar:1;
934 unsigned int reportholdtime:1;
935 unsigned int wrapped:1;
936 unsigned int timeoutrestart:1;
937 unsigned int announceholdtime:2;
938 unsigned int announceposition:3;
940 unsigned int maskmemberstatus:1;
941 unsigned int realtime:1;
942 unsigned int found:1;
943 unsigned int relativeperiodicannounce:1;
944 enum empty_conditions joinempty;
945 enum empty_conditions leavewhenempty;
946 int announcepositionlimit; /*!< How many positions we announce? */
947 int announcefrequency; /*!< How often to announce their position */
948 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
949 int periodicannouncefrequency; /*!< How often to play periodic announcement */
950 int numperiodicannounce; /*!< The number of periodic announcements configured */
951 int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
952 int roundingseconds; /*!< How many seconds do we round to? */
953 int holdtime; /*!< Current avg holdtime, based on an exponential average */
954 int talktime; /*!< Current avg talktime, based on the same exponential average */
955 int callscompleted; /*!< Number of queue calls completed */
956 int callsabandoned; /*!< Number of queue calls abandoned */
957 int servicelevel; /*!< seconds setting for servicelevel*/
958 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
959 char monfmt[8]; /*!< Format to use when recording calls */
960 int montype; /*!< Monitor type Monitor vs. MixMonitor */
961 int count; /*!< How many entries */
962 int maxlen; /*!< Max number of entries */
963 int wrapuptime; /*!< Wrapup Time */
964 int penaltymemberslimit; /*!< Disregard penalty when queue has fewer than this many members */
966 int retry; /*!< Retry calling everyone after this amount of time */
967 int timeout; /*!< How long to wait for an answer */
968 int weight; /*!< Respective weight */
969 int autopause; /*!< Auto pause queue members if they fail to answer */
970 int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
972 /* Queue strategy things */
973 int rrpos; /*!< Round Robin - position */
974 int memberdelay; /*!< Seconds to delay connecting member to caller */
975 int autofill; /*!< Ignore the head call status and ring an available agent */
977 struct ao2_container *members; /*!< Head of the list of members */
979 * \brief Number of members _logged in_
980 * \note There will be members in the members container that are not logged
981 * in, so this can not simply be replaced with ao2_container_count().
984 struct queue_ent *head; /*!< Head of the list of callers */
985 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
986 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
991 AST_LIST_HEAD_NOLOCK(,penalty_rule) rules;
992 AST_LIST_ENTRY(rule_list) list;
995 static AST_LIST_HEAD_STATIC(rule_lists, rule_list);
997 static struct ao2_container *queues;
999 static void update_realtime_members(struct call_queue *q);
1000 static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
1002 static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
1003 /*! \brief sets the QUEUESTATUS channel variable */
1004 static void set_queue_result(struct ast_channel *chan, enum queue_result res)
1008 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
1009 if (queue_results[i].id == res) {
1010 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
1016 static const char *int2strat(int strategy)
1020 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1021 if (strategy == strategies[x].strategy)
1022 return strategies[x].name;
1028 static int strat2int(const char *strategy)
1032 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1033 if (!strcasecmp(strategy, strategies[x].name))
1034 return strategies[x].strategy;
1040 static int queue_hash_cb(const void *obj, const int flags)
1042 const struct call_queue *q = obj;
1044 return ast_str_case_hash(q->name);
1047 static int queue_cmp_cb(void *obj, void *arg, int flags)
1049 struct call_queue *q = obj, *q2 = arg;
1050 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
1053 #ifdef REF_DEBUG_ONLY_QUEUES
1054 #define queue_ref(a) __ao2_ref_debug(a,1,"",__FILE__,__LINE__,__PRETTY_FUNCTION__)
1055 #define queue_unref(a) __ao2_ref_debug(a,-1,"",__FILE__,__LINE__,__PRETTY_FUNCTION__)
1056 #define queue_t_ref(a,b) __ao2_ref_debug(a,1,b,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1057 #define queue_t_unref(a,b) __ao2_ref_debug(a,-1,b,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1058 #define queues_t_link(c,q,tag) __ao2_link_debug(c,q,tag,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1059 #define queues_t_unlink(c,q,tag) __ao2_unlink_debug(c,q,tag,__FILE__,__LINE__,__PRETTY_FUNCTION__)
1061 #define queue_t_ref(a,b) queue_ref(a)
1062 #define queue_t_unref(a,b) queue_unref(a)
1063 #define queues_t_link(c,q,tag) ao2_t_link(c,q,tag)
1064 #define queues_t_unlink(c,q,tag) ao2_t_unlink(c,q,tag)
1065 static inline struct call_queue *queue_ref(struct call_queue *q)
1071 static inline struct call_queue *queue_unref(struct call_queue *q)
1078 /*! \brief Set variables of queue */
1079 static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
1081 char interfacevar[256]="";
1084 if (q->setqueuevar) {
1086 if (q->callscompleted > 0)
1087 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
1089 snprintf(interfacevar, sizeof(interfacevar),
1090 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
1091 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
1093 pbx_builtin_setvar_multiple(chan, interfacevar);
1097 /*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
1098 static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
1100 struct queue_ent *cur;
1113 /* every queue_ent must have a reference to it's parent call_queue, this
1114 * reference does not go away until the end of the queue_ent's life, meaning
1115 * that even when the queue_ent leaves the call_queue this ref must remain. */
1118 new->pos = ++(*pos);
1122 /*! \brief Check if members are available
1124 * This function checks to see if members are available to be called. If any member
1125 * is available, the function immediately returns 0. If no members are available,
1126 * then -1 is returned.
1128 static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions)
1130 struct member *member;
1131 struct ao2_iterator mem_iter;
1134 mem_iter = ao2_iterator_init(q->members, 0);
1135 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
1136 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
1137 if (conditions & QUEUE_EMPTY_PENALTY) {
1138 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
1143 switch (member->status) {
1144 case AST_DEVICE_INVALID:
1145 if (conditions & QUEUE_EMPTY_INVALID) {
1146 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
1149 case AST_DEVICE_UNAVAILABLE:
1150 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
1151 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
1154 case AST_DEVICE_INUSE:
1155 if (conditions & QUEUE_EMPTY_INUSE) {
1156 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
1159 case AST_DEVICE_UNKNOWN:
1160 if (conditions & QUEUE_EMPTY_UNKNOWN) {
1161 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
1165 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
1166 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
1168 } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
1169 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
1173 ao2_ref(member, -1);
1174 ao2_iterator_destroy(&mem_iter);
1175 ast_debug(4, "%s is available.\n", member->membername);
1181 ao2_iterator_destroy(&mem_iter);
1187 struct statechange {
1188 AST_LIST_ENTRY(statechange) entry;
1193 /*! \brief set a member's status based on device state of that member's state_interface.
1195 * Lock interface list find sc, iterate through each queues queue_member list for member to
1196 * update state inside queues
1198 static int update_status(struct call_queue *q, struct member *m, const int status)
1202 if (q->maskmemberstatus)
1205 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
1208 "MemberName: %s\r\n"
1209 "Membership: %s\r\n"
1211 "CallsTaken: %d\r\n"
1215 q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
1216 m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
1222 /*! \brief set a member's status based on device state of that member's interface*/
1223 static int handle_statechange(void *datap)
1225 struct statechange *sc = datap;
1226 struct ao2_iterator miter, qiter;
1228 struct call_queue *q;
1229 char interface[80], *slash_pos;
1232 qiter = ao2_iterator_init(queues, 0);
1233 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
1236 miter = ao2_iterator_init(q->members, 0);
1237 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
1238 ast_copy_string(interface, m->state_interface, sizeof(interface));
1240 if ((slash_pos = strchr(interface, '/')))
1241 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
1244 if (!strcasecmp(interface, sc->dev)) {
1246 update_status(q, m, sc->state);
1251 ao2_iterator_destroy(&miter);
1254 queue_t_unref(q, "Done with iterator");
1256 ao2_iterator_destroy(&qiter);
1259 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
1261 ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, ast_devstate2str(sc->state));
1267 static void device_state_cb(const struct ast_event *event, void *unused)
1269 enum ast_device_state state;
1271 struct statechange *sc;
1274 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
1275 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
1277 if (ast_strlen_zero(device)) {
1278 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
1281 datapsize = sizeof(*sc) + strlen(device) + 1;
1282 if (!(sc = ast_calloc(1, datapsize))) {
1283 ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
1287 strcpy(sc->dev, device);
1288 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
1293 /*! \brief Helper function which converts from extension state to device state values */
1294 static int extensionstate2devicestate(int state)
1297 case AST_EXTENSION_NOT_INUSE:
1298 state = AST_DEVICE_NOT_INUSE;
1300 case AST_EXTENSION_INUSE:
1301 state = AST_DEVICE_INUSE;
1303 case AST_EXTENSION_BUSY:
1304 state = AST_DEVICE_BUSY;
1306 case AST_EXTENSION_RINGING:
1307 state = AST_DEVICE_RINGING;
1309 case AST_EXTENSION_ONHOLD:
1310 state = AST_DEVICE_ONHOLD;
1312 case AST_EXTENSION_UNAVAILABLE:
1313 state = AST_DEVICE_UNAVAILABLE;
1315 case AST_EXTENSION_REMOVED:
1316 case AST_EXTENSION_DEACTIVATED:
1318 state = AST_DEVICE_INVALID;
1325 static int extension_state_cb(char *context, char *exten, enum ast_extension_states state, void *data)
1327 struct ao2_iterator miter, qiter;
1329 struct call_queue *q;
1330 int found = 0, device_state = extensionstate2devicestate(state);
1332 qiter = ao2_iterator_init(queues, 0);
1333 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
1336 miter = ao2_iterator_init(q->members, 0);
1337 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
1338 if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
1339 update_status(q, m, device_state);
1345 ao2_iterator_destroy(&miter);
1348 queue_t_unref(q, "Done with iterator");
1350 ao2_iterator_destroy(&qiter);
1353 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
1355 ast_debug(3, "Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n",
1356 exten, context, device_state, ast_devstate2str(device_state));
1362 /*! \brief Return the current state of a member */
1363 static int get_queue_member_status(struct member *cur)
1365 return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten));
1368 /*! \brief allocate space for new queue member and set fields based on parameters passed */
1369 static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
1373 if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
1374 cur->penalty = penalty;
1375 cur->paused = paused;
1376 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
1377 if (!ast_strlen_zero(state_interface))
1378 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
1380 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
1381 if (!ast_strlen_zero(membername))
1382 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
1384 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
1385 if (!strchr(cur->interface, '/'))
1386 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
1387 if (!strncmp(cur->state_interface, "hint:", 5)) {
1388 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
1389 char *exten = strsep(&context, "@") + 5;
1391 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
1392 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
1394 cur->status = get_queue_member_status(cur);
1401 static int compress_char(const char c)
1411 static int member_hash_fn(const void *obj, const int flags)
1413 const struct member *mem = obj;
1414 const char *chname = strchr(mem->interface, '/');
1417 chname = mem->interface;
1418 for (i = 0; i < 5 && chname[i]; i++)
1419 ret += compress_char(chname[i]) << (i * 6);
1423 static int member_cmp_fn(void *obj1, void *obj2, int flags)
1425 struct member *mem1 = obj1, *mem2 = obj2;
1426 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
1430 * \brief Initialize Queue default values.
1431 * \note the queue's lock must be held before executing this function
1433 static void init_queue(struct call_queue *q)
1436 struct penalty_rule *pr_iter;
1439 q->retry = DEFAULT_RETRY;
1440 q->timeout = DEFAULT_TIMEOUT;
1442 q->announcefrequency = 0;
1443 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
1444 q->announceholdtime = 1;
1445 q->announcepositionlimit = 10; /* Default 10 positions */
1446 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
1447 q->roundingseconds = 0; /* Default - don't announce seconds */
1448 q->servicelevel = 0;
1450 q->setinterfacevar = 0;
1452 q->setqueueentryvar = 0;
1453 q->autofill = autofill_default;
1454 q->montype = montype_default;
1455 q->monfmt[0] = '\0';
1456 q->reportholdtime = 0;
1458 q->penaltymemberslimit = 0;
1460 q->leavewhenempty = 0;
1462 q->maskmemberstatus = 0;
1463 q->eventwhencalled = 0;
1465 q->timeoutrestart = 0;
1466 q->periodicannouncefrequency = 0;
1467 q->randomperiodicannounce = 0;
1468 q->numperiodicannounce = 0;
1469 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
1471 if (q->strategy == QUEUE_STRATEGY_LINEAR)
1472 /* linear strategy depends on order, so we have to place all members in a single bucket */
1473 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
1475 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
1479 ast_string_field_set(q, sound_next, "queue-youarenext");
1480 ast_string_field_set(q, sound_thereare, "queue-thereare");
1481 ast_string_field_set(q, sound_calls, "queue-callswaiting");
1482 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
1483 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
1484 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
1485 ast_string_field_set(q, sound_minutes, "queue-minutes");
1486 ast_string_field_set(q, sound_minute, "queue-minute");
1487 ast_string_field_set(q, sound_seconds, "queue-seconds");
1488 ast_string_field_set(q, sound_thanks, "queue-thankyou");
1489 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
1491 if ((q->sound_periodicannounce[0] = ast_str_create(32)))
1492 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
1494 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
1495 if (q->sound_periodicannounce[i])
1496 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
1499 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
1503 static void clear_queue(struct call_queue *q)
1506 q->callscompleted = 0;
1507 q->callsabandoned = 0;
1508 q->callscompletedinsl = 0;
1514 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
1515 while ((mem = ao2_iterator_next(&mem_iter))) {
1519 ao2_iterator_destroy(&mem_iter);
1524 * \brief Change queue penalty by adding rule.
1526 * Check rule for errors with time or fomatting, see if rule is relative to rest
1527 * of queue, iterate list of rules to find correct insertion point, insert and return.
1528 * \retval -1 on failure
1529 * \retval 0 on success
1530 * \note Call this with the rule_lists locked
1532 static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
1534 char *timestr, *maxstr, *minstr, *contentdup;
1535 struct penalty_rule *rule = NULL, *rule_iter;
1536 struct rule_list *rl_iter;
1537 int penaltychangetime, inserted = 0;
1539 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
1543 contentdup = ast_strdupa(content);
1545 if (!(maxstr = strchr(contentdup, ','))) {
1546 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
1552 timestr = contentdup;
1554 if ((penaltychangetime = atoi(timestr)) < 0) {
1555 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
1560 rule->time = penaltychangetime;
1562 if ((minstr = strchr(maxstr,',')))
1565 /* The last check will evaluate true if either no penalty change is indicated for a given rule
1566 * OR if a min penalty change is indicated but no max penalty change is */
1567 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
1568 rule->max_relative = 1;
1571 rule->max_value = atoi(maxstr);
1573 if (!ast_strlen_zero(minstr)) {
1574 if (*minstr == '+' || *minstr == '-')
1575 rule->min_relative = 1;
1576 rule->min_value = atoi(minstr);
1577 } else /*there was no minimum specified, so assume this means no change*/
1578 rule->min_relative = 1;
1580 /*We have the rule made, now we need to insert it where it belongs*/
1581 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
1582 if (strcasecmp(rl_iter->name, list_name))
1585 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
1586 if (rule->time < rule_iter->time) {
1587 AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
1592 AST_LIST_TRAVERSE_SAFE_END;
1595 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
1602 static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
1604 char *value_copy = ast_strdupa(value);
1605 char *option = NULL;
1606 while ((option = strsep(&value_copy, ","))) {
1607 if (!strcasecmp(option, "paused")) {
1608 *empty |= QUEUE_EMPTY_PAUSED;
1609 } else if (!strcasecmp(option, "penalty")) {
1610 *empty |= QUEUE_EMPTY_PENALTY;
1611 } else if (!strcasecmp(option, "inuse")) {
1612 *empty |= QUEUE_EMPTY_INUSE;
1613 } else if (!strcasecmp(option, "ringing")) {
1614 *empty |= QUEUE_EMPTY_RINGING;
1615 } else if (!strcasecmp(option, "invalid")) {
1616 *empty |= QUEUE_EMPTY_INVALID;
1617 } else if (!strcasecmp(option, "wrapup")) {
1618 *empty |= QUEUE_EMPTY_WRAPUP;
1619 } else if (!strcasecmp(option, "unavailable")) {
1620 *empty |= QUEUE_EMPTY_UNAVAILABLE;
1621 } else if (!strcasecmp(option, "unknown")) {
1622 *empty |= QUEUE_EMPTY_UNKNOWN;
1623 } else if (!strcasecmp(option, "loose")) {
1624 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
1625 } else if (!strcasecmp(option, "strict")) {
1626 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
1627 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
1628 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
1629 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
1632 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
1637 /*! \brief Configure a queue parameter.
1639 * The failunknown flag is set for config files (and static realtime) to show
1640 * errors for unknown parameters. It is cleared for dynamic realtime to allow
1641 * extra fields in the tables.
1642 * \note For error reporting, line number is passed for .conf static configuration,
1643 * for Realtime queues, linenum is -1.
1645 static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
1647 if (!strcasecmp(param, "musicclass") ||
1648 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
1649 ast_string_field_set(q, moh, val);
1650 } else if (!strcasecmp(param, "announce")) {
1651 ast_string_field_set(q, announce, val);
1652 } else if (!strcasecmp(param, "context")) {
1653 ast_string_field_set(q, context, val);
1654 } else if (!strcasecmp(param, "timeout")) {
1655 q->timeout = atoi(val);
1657 q->timeout = DEFAULT_TIMEOUT;
1658 } else if (!strcasecmp(param, "ringinuse")) {
1659 q->ringinuse = ast_true(val);
1660 } else if (!strcasecmp(param, "setinterfacevar")) {
1661 q->setinterfacevar = ast_true(val);
1662 } else if (!strcasecmp(param, "setqueuevar")) {
1663 q->setqueuevar = ast_true(val);
1664 } else if (!strcasecmp(param, "setqueueentryvar")) {
1665 q->setqueueentryvar = ast_true(val);
1666 } else if (!strcasecmp(param, "monitor-format")) {
1667 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
1668 } else if (!strcasecmp(param, "membermacro")) {
1669 ast_string_field_set(q, membermacro, val);
1670 } else if (!strcasecmp(param, "membergosub")) {
1671 ast_string_field_set(q, membergosub, val);
1672 } else if (!strcasecmp(param, "queue-youarenext")) {
1673 ast_string_field_set(q, sound_next, val);
1674 } else if (!strcasecmp(param, "queue-thereare")) {
1675 ast_string_field_set(q, sound_thereare, val);
1676 } else if (!strcasecmp(param, "queue-callswaiting")) {
1677 ast_string_field_set(q, sound_calls, val);
1678 } else if (!strcasecmp(param, "queue-quantity1")) {
1679 ast_string_field_set(q, queue_quantity1, val);
1680 } else if (!strcasecmp(param, "queue-quantity2")) {
1681 ast_string_field_set(q, queue_quantity2, val);
1682 } else if (!strcasecmp(param, "queue-holdtime")) {
1683 ast_string_field_set(q, sound_holdtime, val);
1684 } else if (!strcasecmp(param, "queue-minutes")) {
1685 ast_string_field_set(q, sound_minutes, val);
1686 } else if (!strcasecmp(param, "queue-minute")) {
1687 ast_string_field_set(q, sound_minute, val);
1688 } else if (!strcasecmp(param, "queue-seconds")) {
1689 ast_string_field_set(q, sound_seconds, val);
1690 } else if (!strcasecmp(param, "queue-thankyou")) {
1691 ast_string_field_set(q, sound_thanks, val);
1692 } else if (!strcasecmp(param, "queue-callerannounce")) {
1693 ast_string_field_set(q, sound_callerannounce, val);
1694 } else if (!strcasecmp(param, "queue-reporthold")) {
1695 ast_string_field_set(q, sound_reporthold, val);
1696 } else if (!strcasecmp(param, "announce-frequency")) {
1697 q->announcefrequency = atoi(val);
1698 } else if (!strcasecmp(param, "min-announce-frequency")) {
1699 q->minannouncefrequency = atoi(val);
1700 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
1701 } else if (!strcasecmp(param, "announce-round-seconds")) {
1702 q->roundingseconds = atoi(val);
1703 /* Rounding to any other values just doesn't make sense... */
1704 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
1705 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
1707 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1708 "using 0 instead for queue '%s' at line %d of queues.conf\n",
1709 val, param, q->name, linenum);
1711 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
1712 "using 0 instead for queue '%s'\n", val, param, q->name);
1714 q->roundingseconds=0;
1716 } else if (!strcasecmp(param, "announce-holdtime")) {
1717 if (!strcasecmp(val, "once"))
1718 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
1719 else if (ast_true(val))
1720 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
1722 q->announceholdtime = 0;
1723 } else if (!strcasecmp(param, "announce-position")) {
1724 if (!strcasecmp(val, "limit"))
1725 q->announceposition = ANNOUNCEPOSITION_LIMIT;
1726 else if (!strcasecmp(val, "more"))
1727 q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
1728 else if (ast_true(val))
1729 q->announceposition = ANNOUNCEPOSITION_YES;
1731 q->announceposition = ANNOUNCEPOSITION_NO;
1732 } else if (!strcasecmp(param, "announce-position-limit")) {
1733 q->announcepositionlimit = atoi(val);
1734 } else if (!strcasecmp(param, "periodic-announce")) {
1735 if (strchr(val, ',')) {
1736 char *s, *buf = ast_strdupa(val);
1739 while ((s = strsep(&buf, ",|"))) {
1740 if (!q->sound_periodicannounce[i])
1741 q->sound_periodicannounce[i] = ast_str_create(16);
1742 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
1744 if (i == MAX_PERIODIC_ANNOUNCEMENTS)
1747 q->numperiodicannounce = i;
1749 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
1750 q->numperiodicannounce = 1;
1752 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
1753 q->periodicannouncefrequency = atoi(val);
1754 } else if (!strcasecmp(param, "relative-periodic-announce")) {
1755 q->relativeperiodicannounce = ast_true(val);
1756 } else if (!strcasecmp(param, "random-periodic-announce")) {
1757 q->randomperiodicannounce = ast_true(val);
1758 } else if (!strcasecmp(param, "retry")) {
1759 q->retry = atoi(val);
1761 q->retry = DEFAULT_RETRY;
1762 } else if (!strcasecmp(param, "wrapuptime")) {
1763 q->wrapuptime = atoi(val);
1764 } else if (!strcasecmp(param, "penaltymemberslimit")) {
1765 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
1766 q->penaltymemberslimit = 0;
1768 } else if (!strcasecmp(param, "autofill")) {
1769 q->autofill = ast_true(val);
1770 } else if (!strcasecmp(param, "monitor-type")) {
1771 if (!strcasecmp(val, "mixmonitor"))
1773 } else if (!strcasecmp(param, "autopause")) {
1774 q->autopause = ast_true(val);
1775 } else if (!strcasecmp(param, "maxlen")) {
1776 q->maxlen = atoi(val);
1779 } else if (!strcasecmp(param, "servicelevel")) {
1780 q->servicelevel= atoi(val);
1781 } else if (!strcasecmp(param, "strategy")) {
1784 /* We are a static queue and already have set this, no need to do it again */
1788 strategy = strat2int(val);
1790 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
1792 q->strategy = QUEUE_STRATEGY_RINGALL;
1794 if (strategy == q->strategy) {
1797 if (strategy == QUEUE_STRATEGY_LINEAR) {
1798 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
1801 q->strategy = strategy;
1802 } else if (!strcasecmp(param, "joinempty")) {
1803 parse_empty_options(val, &q->joinempty, 1);
1804 } else if (!strcasecmp(param, "leavewhenempty")) {
1805 parse_empty_options(val, &q->leavewhenempty, 0);
1806 } else if (!strcasecmp(param, "eventmemberstatus")) {
1807 q->maskmemberstatus = !ast_true(val);
1808 } else if (!strcasecmp(param, "eventwhencalled")) {
1809 if (!strcasecmp(val, "vars")) {
1810 q->eventwhencalled = QUEUE_EVENT_VARIABLES;
1812 q->eventwhencalled = ast_true(val) ? 1 : 0;
1814 } else if (!strcasecmp(param, "reportholdtime")) {
1815 q->reportholdtime = ast_true(val);
1816 } else if (!strcasecmp(param, "memberdelay")) {
1817 q->memberdelay = atoi(val);
1818 } else if (!strcasecmp(param, "weight")) {
1819 q->weight = atoi(val);
1820 } else if (!strcasecmp(param, "timeoutrestart")) {
1821 q->timeoutrestart = ast_true(val);
1822 } else if (!strcasecmp(param, "defaultrule")) {
1823 ast_string_field_set(q, defaultrule, val);
1824 } else if (!strcasecmp(param, "timeoutpriority")) {
1825 if (!strcasecmp(val, "conf")) {
1826 q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
1828 q->timeoutpriority = TIMEOUT_PRIORITY_APP;
1830 } else if (failunknown) {
1832 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
1833 q->name, param, linenum);
1835 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
1841 * \brief Find rt member record to update otherwise create one.
1843 * Search for member in queue, if found update penalty/paused state,
1844 * if no member exists create one flag it as a RT member and add to queue member list.
1846 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)
1849 struct ao2_iterator mem_iter;
1854 if (ast_strlen_zero(rt_uniqueid)) {
1855 ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
1860 penalty = atoi(penalty_str);
1866 paused = atoi(paused_str);
1871 /* Find member by realtime uniqueid and update */
1872 mem_iter = ao2_iterator_init(q->members, 0);
1873 while ((m = ao2_iterator_next(&mem_iter))) {
1874 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
1875 m->dead = 0; /* Do not delete this one. */
1876 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1879 if (strcasecmp(state_interface, m->state_interface)) {
1880 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
1882 m->penalty = penalty;
1889 ao2_iterator_destroy(&mem_iter);
1891 /* Create a new member */
1893 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
1896 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
1897 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
1898 ao2_link(q->members, m);
1906 /*! \brief Iterate through queue's member list and delete them */
1907 static void free_members(struct call_queue *q, int all)
1909 /* Free non-dynamic members */
1911 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
1913 while ((cur = ao2_iterator_next(&mem_iter))) {
1914 if (all || !cur->dynamic) {
1915 ao2_unlink(q->members, cur);
1920 ao2_iterator_destroy(&mem_iter);
1923 /*! \brief Free queue's member list then its string fields */
1924 static void destroy_queue(void *obj)
1926 struct call_queue *q = obj;
1930 ast_string_field_free_memory(q);
1931 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
1932 if (q->sound_periodicannounce[i])
1933 free(q->sound_periodicannounce[i]);
1935 ao2_ref(q->members, -1);
1938 static struct call_queue *alloc_queue(const char *queuename)
1940 struct call_queue *q;
1942 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
1943 if (ast_string_field_init(q, 64)) {
1944 queue_t_unref(q, "String field allocation failed");
1947 ast_string_field_set(q, name, queuename);
1953 * \brief Reload a single queue via realtime.
1955 * Check for statically defined queue first, check if deleted RT queue,
1956 * check for new RT queue, if queue vars are not defined init them with defaults.
1957 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
1958 * \retval the queue,
1959 * \retval NULL if it doesn't exist.
1960 * \note Should be called with the "queues" container locked.
1962 static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
1964 struct ast_variable *v;
1965 struct call_queue *q, tmpq = {
1969 struct ao2_iterator mem_iter;
1970 char *interface = NULL;
1971 const char *tmp_name;
1973 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
1975 /* Static queues override realtime. */
1976 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
1981 queue_t_unref(q, "Queue is dead; can't return it");
1984 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
1989 } else if (!member_config)
1990 /* Not found in the list, and it's not realtime ... */
1993 /* Check if queue is defined in realtime. */
1995 /* Delete queue from in-core list if it has been deleted in realtime. */
1997 /*! \note Hmm, can't seem to distinguish a DB failure from a not
1998 found condition... So we might delete an in-core queue
1999 in case of DB failure. */
2000 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
2003 /* Delete if unused (else will be deleted when last caller leaves). */
2004 queues_t_unlink(queues, q, "Unused; removing from container");
2006 queue_t_unref(q, "Queue is dead; can't return it");
2011 /* Create a new queue if an in-core entry does not exist yet. */
2013 struct ast_variable *tmpvar = NULL;
2014 if (!(q = alloc_queue(queuename)))
2020 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
2021 * will allocate the members properly
2023 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
2024 if (!strcasecmp(tmpvar->name, "strategy")) {
2025 q->strategy = strat2int(tmpvar->value);
2026 if (q->strategy < 0) {
2027 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
2028 tmpvar->value, q->name);
2029 q->strategy = QUEUE_STRATEGY_RINGALL;
2034 /* We traversed all variables and didn't find a strategy */
2036 q->strategy = QUEUE_STRATEGY_RINGALL;
2037 queues_t_link(queues, q, "Add queue to container");
2039 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
2041 memset(tmpbuf, 0, sizeof(tmpbuf));
2042 for (v = queue_vars; v; v = v->next) {
2043 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
2044 if ((tmp = strchr(v->name, '_'))) {
2045 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
2048 while ((tmp = strchr(tmp, '_')))
2053 if (!ast_strlen_zero(v->value)) {
2054 /* Don't want to try to set the option if the value is empty */
2055 queue_set_param(q, tmp_name, v->value, -1, 0);
2059 /* Temporarily set realtime members dead so we can detect deleted ones.
2060 * Also set the membercount correctly for realtime*/
2061 mem_iter = ao2_iterator_init(q->members, 0);
2062 while ((m = ao2_iterator_next(&mem_iter))) {
2068 ao2_iterator_destroy(&mem_iter);
2070 while ((interface = ast_category_browse(member_config, interface))) {
2071 rt_handle_member_record(q, interface,
2072 ast_variable_retrieve(member_config, interface, "uniqueid"),
2073 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
2074 ast_variable_retrieve(member_config, interface, "penalty"),
2075 ast_variable_retrieve(member_config, interface, "paused"),
2076 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
2079 /* Delete all realtime members that have been deleted in DB. */
2080 mem_iter = ao2_iterator_init(q->members, 0);
2081 while ((m = ao2_iterator_next(&mem_iter))) {
2083 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
2084 ao2_unlink(q->members, m);
2089 ao2_iterator_destroy(&mem_iter);
2096 static struct call_queue *load_realtime_queue(const char *queuename)
2098 struct ast_variable *queue_vars;
2099 struct ast_config *member_config = NULL;
2100 struct call_queue *q = NULL, tmpq = {
2103 int prev_weight = 0;
2105 /* Find the queue in the in-core list first. */
2106 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
2108 if (!q || q->realtime) {
2109 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
2110 queue operations while waiting for the DB.
2112 This will be two separate database transactions, so we might
2113 see queue parameters as they were before another process
2114 changed the queue and member list as it was after the change.
2115 Thus we might see an empty member list when a queue is
2116 deleted. In practise, this is unlikely to cause a problem. */
2118 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
2120 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
2121 if (!member_config) {
2122 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
2123 ast_variables_destroy(queue_vars);
2128 prev_weight = q->weight ? 1 : 0;
2133 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
2134 if (member_config) {
2135 ast_config_destroy(member_config);
2138 ast_variables_destroy(queue_vars);
2140 /* update the use_weight value if the queue's has gained or lost a weight */
2142 if (!q->weight && prev_weight) {
2143 ast_atomic_fetchadd_int(&use_weight, -1);
2145 if (q->weight && !prev_weight) {
2146 ast_atomic_fetchadd_int(&use_weight, +1);
2149 /* Other cases will end up with the proper value for use_weight */
2153 update_realtime_members(q);
2158 static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
2162 if (ast_strlen_zero(mem->rt_uniqueid))
2165 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
2172 static void update_realtime_members(struct call_queue *q)
2174 struct ast_config *member_config = NULL;
2176 char *interface = NULL;
2177 struct ao2_iterator mem_iter;
2179 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
2180 /*This queue doesn't have realtime members*/
2181 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
2188 /* Temporarily set realtime members dead so we can detect deleted ones.*/
2189 mem_iter = ao2_iterator_init(q->members, 0);
2190 while ((m = ao2_iterator_next(&mem_iter))) {
2195 ao2_iterator_destroy(&mem_iter);
2197 while ((interface = ast_category_browse(member_config, interface))) {
2198 rt_handle_member_record(q, interface,
2199 ast_variable_retrieve(member_config, interface, "uniqueid"),
2200 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
2201 ast_variable_retrieve(member_config, interface, "penalty"),
2202 ast_variable_retrieve(member_config, interface, "paused"),
2203 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
2206 /* Delete all realtime members that have been deleted in DB. */
2207 mem_iter = ao2_iterator_init(q->members, 0);
2208 while ((m = ao2_iterator_next(&mem_iter))) {
2210 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
2211 ao2_unlink(q->members, m);
2216 ao2_iterator_destroy(&mem_iter);
2219 ast_config_destroy(member_config);
2222 static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
2224 struct call_queue *q;
2225 struct queue_ent *cur, *prev = NULL;
2230 if (!(q = load_realtime_queue(queuename)))
2236 /* This is our one */
2239 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
2240 *reason = QUEUE_JOINEMPTY;
2246 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
2247 *reason = QUEUE_FULL;
2248 else if (*reason == QUEUE_UNKNOWN) {
2249 /* There's space for us, put us at the right position inside
2251 * Take into account the priority of the calling user */
2256 /* We have higher priority than the current user, enter
2257 * before him, after all the other users with priority
2258 * higher or equal to our priority. */
2259 if ((!inserted) && (qe->prio > cur->prio)) {
2260 insert_entry(q, prev, qe, &pos);
2263 /* <= is necessary for the position comparison because it may not be possible to enter
2264 * at our desired position since higher-priority callers may have taken the position we want
2266 if (!inserted && (qe->prio <= cur->prio) && position && (position <= pos + 1)) {
2267 insert_entry(q, prev, qe, &pos);
2268 /*pos is incremented inside insert_entry, so don't need to add 1 here*/
2269 if (position < pos) {
2270 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
2278 /* No luck, join at the end of the queue */
2280 insert_entry(q, prev, qe, &pos);
2281 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
2282 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
2283 ast_copy_string(qe->context, q->context, sizeof(qe->context));
2286 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
2287 "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
2289 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
2290 S_OR(qe->chan->cid.cid_name, "unknown"),
2291 q->name, qe->pos, q->count, qe->chan->uniqueid );
2292 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
2300 static int play_file(struct ast_channel *chan, const char *filename)
2304 if (ast_strlen_zero(filename)) {
2308 ast_stopstream(chan);
2310 res = ast_streamfile(chan, filename, chan->language);
2312 res = ast_waitstream(chan, AST_DIGIT_ANY);
2314 ast_stopstream(chan);
2320 * \brief Check for valid exit from queue via goto
2321 * \retval 0 if failure
2322 * \retval 1 if successful
2324 static int valid_exit(struct queue_ent *qe, char digit)
2326 int digitlen = strlen(qe->digits);
2328 /* Prevent possible buffer overflow */
2329 if (digitlen < sizeof(qe->digits) - 2) {
2330 qe->digits[digitlen] = digit;
2331 qe->digits[digitlen + 1] = '\0';
2333 qe->digits[0] = '\0';
2337 /* If there's no context to goto, short-circuit */
2338 if (ast_strlen_zero(qe->context))
2341 /* If the extension is bad, then reset the digits to blank */
2342 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
2343 qe->digits[0] = '\0';
2347 /* We have an exact match */
2348 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
2349 qe->valid_digits = 1;
2350 /* Return 1 on a successful goto */
2357 static int say_position(struct queue_ent *qe, int ringing)
2359 int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
2363 /* Let minannouncefrequency seconds pass between the start of each position announcement */
2365 if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
2368 /* If either our position has changed, or we are over the freq timer, say position */
2369 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
2373 ast_indicate(qe->chan,-1);
2375 ast_moh_stop(qe->chan);
2378 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
2379 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
2380 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
2381 qe->pos <= qe->parent->announcepositionlimit))
2382 announceposition = 1;
2385 if (announceposition == 1) {
2386 /* Say we're next, if we are */
2388 res = play_file(qe->chan, qe->parent->sound_next);
2394 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
2396 res = play_file(qe->chan, qe->parent->queue_quantity1);
2399 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
2404 res = play_file(qe->chan, qe->parent->sound_thereare);
2407 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
2411 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
2413 res = play_file(qe->chan, qe->parent->queue_quantity2);
2417 res = play_file(qe->chan, qe->parent->sound_calls);
2423 /* Round hold time to nearest minute */
2424 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
2426 /* If they have specified a rounding then round the seconds as well */
2427 if (qe->parent->roundingseconds) {
2428 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
2429 avgholdsecs *= qe->parent->roundingseconds;
2434 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
2436 /* If the hold time is >1 min, if it's enabled, and if it's not
2437 supposed to be only once and we have already said it, say it */
2438 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
2439 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
2440 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
2441 res = play_file(qe->chan, qe->parent->sound_holdtime);
2445 if (avgholdmins >= 1) {
2446 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
2450 if (avgholdmins == 1) {
2451 res = play_file(qe->chan, qe->parent->sound_minute);
2455 res = play_file(qe->chan, qe->parent->sound_minutes);
2460 if (avgholdsecs >= 1) {
2461 res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
2465 res = play_file(qe->chan, qe->parent->sound_seconds);
2469 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
2474 if (qe->parent->announceposition) {
2475 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
2476 qe->chan->name, qe->parent->name, qe->pos);
2479 res = play_file(qe->chan, qe->parent->sound_thanks);
2483 if ((res > 0 && !valid_exit(qe, res)))
2486 /* Set our last_pos indicators */
2488 qe->last_pos_said = qe->pos;
2490 /* Don't restart music on hold if we're about to exit the caller from the queue */
2493 ast_indicate(qe->chan, AST_CONTROL_RINGING);
2495 ast_moh_start(qe->chan, qe->moh, NULL);
2501 static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
2505 /* Calculate holdtime using an exponential average */
2506 /* Thanks to SRT for this contribution */
2507 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
2509 ao2_lock(qe->parent);
2510 oldvalue = qe->parent->holdtime;
2511 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
2512 ao2_unlock(qe->parent);
2515 /*! \brief Caller leaving queue.
2517 * Search the queue to find the leaving client, if found remove from queue
2518 * create manager event, move others up the queue.
2520 static void leave_queue(struct queue_ent *qe)
2522 struct call_queue *q;
2523 struct queue_ent *current, *prev = NULL;
2524 struct penalty_rule *pr_iter;
2527 if (!(q = qe->parent))
2529 queue_t_ref(q, "Copy queue pointer from queue entry");
2533 for (current = q->head; current; current = current->next) {
2534 if (current == qe) {
2538 /* Take us out of the queue */
2539 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
2540 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
2541 qe->chan->name, q->name, q->count, qe->pos, qe->chan->uniqueid);
2542 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
2543 /* Take us out of the queue */
2545 prev->next = current->next;
2547 q->head = current->next;
2548 /* Free penalty rules */
2549 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
2551 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
2552 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
2554 /* Renumber the people after us in the queue based on a new count */
2555 current->pos = ++pos;
2561 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
2563 struct ast_variable *var;
2564 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
2567 ast_variables_destroy(var);
2572 /* It's dead and nobody is in it, so kill it */
2573 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
2575 /* unref the explicit ref earlier in the function */
2576 queue_t_unref(q, "Expire copied reference");
2579 /*! \brief Hang up a list of outgoing calls */
2580 static void hangupcalls(struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
2582 struct callattempt *oo;
2585 /* If someone else answered the call we should indicate this in the CANCEL */
2586 /* Hangup any existing lines we have open */
2587 if (outgoing->chan && (outgoing->chan != exception)) {
2588 if (exception || cancel_answered_elsewhere)
2589 ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
2590 ast_hangup(outgoing->chan);
2593 outgoing = outgoing->q_next;
2595 ao2_ref(oo->member, -1);
2601 * \brief Get the number of members available to accept a call.
2603 * \note The queue passed in should be locked prior to this function call
2605 * \param[in] q The queue for which we are couting the number of available members
2606 * \return Return the number of available members in queue q
2608 static int num_available_members(struct call_queue *q)
2612 struct ao2_iterator mem_iter;
2614 mem_iter = ao2_iterator_init(q->members, 0);
2615 while ((mem = ao2_iterator_next(&mem_iter))) {
2616 switch (mem->status) {
2617 case AST_DEVICE_INUSE:
2620 /* else fall through */
2621 case AST_DEVICE_NOT_INUSE:
2622 case AST_DEVICE_UNKNOWN:
2630 /* If autofill is not enabled or if the queue's strategy is ringall, then
2631 * we really don't care about the number of available members so much as we
2632 * do that there is at least one available.
2634 * In fact, we purposely will return from this function stating that only
2635 * one member is available if either of those conditions hold. That way,
2636 * functions which determine what action to take based on the number of available
2637 * members will operate properly. The reasoning is that even if multiple
2638 * members are available, only the head caller can actually be serviced.
2640 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
2644 ao2_iterator_destroy(&mem_iter);
2649 /* traverse all defined queues which have calls waiting and contain this member
2650 return 0 if no other queue has precedence (higher weight) or 1 if found */
2651 static int compare_weight(struct call_queue *rq, struct member *member)
2653 struct call_queue *q;
2656 struct ao2_iterator queue_iter;
2658 /* q's lock and rq's lock already set by try_calling()
2659 * to solve deadlock */
2660 queue_iter = ao2_iterator_init(queues, 0);
2661 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
2662 if (q == rq) { /* don't check myself, could deadlock */
2663 queue_t_unref(q, "Done with iterator");
2667 if (q->count && q->members) {
2668 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
2669 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
2670 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
2671 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);
2678 queue_t_unref(q, "Done with iterator");
2683 ao2_iterator_destroy(&queue_iter);
2687 /*! \brief common hangup actions */
2688 static void do_hang(struct callattempt *o)
2691 ast_hangup(o->chan);
2695 /*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2696 static char *vars2manager(struct ast_channel *chan, char *vars, size_t len)
2698 struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
2701 if (pbx_builtin_serialize_variables(chan, &buf)) {
2704 /* convert "\n" to "\nVariable: " */
2705 strcpy(vars, "Variable: ");
2706 tmp = ast_str_buffer(buf);
2708 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
2711 if (tmp[i + 1] == '\0')
2713 if (tmp[i] == '\n') {
2717 ast_copy_string(&(vars[j]), "Variable: ", len - j);
2727 /* there are no channel variables; leave it blank */
2734 * \brief Part 2 of ring_one
2736 * Does error checking before attempting to request a channel and call a member.
2737 * This function is only called from ring_one().
2738 * Failure can occur if:
2741 * - Wrapup time not expired
2742 * - Priority by another queue
2744 * \retval 1 on success to reach a free agent
2745 * \retval 0 on failure to get agent.
2747 static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
2753 const char *macrocontext, *macroexten;
2755 /* on entry here, we know that tmp->chan == NULL */
2756 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
2757 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
2758 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
2759 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
2761 ast_cdr_busy(qe->chan->cdr);
2762 tmp->stillgoing = 0;
2767 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
2768 ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
2770 ast_cdr_busy(qe->chan->cdr);
2771 tmp->stillgoing = 0;
2775 if (tmp->member->paused) {
2776 ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
2778 ast_cdr_busy(qe->chan->cdr);
2779 tmp->stillgoing = 0;
2782 if (use_weight && compare_weight(qe->parent,tmp->member)) {
2783 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
2785 ast_cdr_busy(qe->chan->cdr);
2786 tmp->stillgoing = 0;
2791 ast_copy_string(tech, tmp->interface, sizeof(tech));
2792 if ((location = strchr(tech, '/')))
2797 /* Request the peer */
2798 tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
2799 if (!tmp->chan) { /* If we can't, just go on to the next call */
2801 ast_cdr_busy(qe->chan->cdr);
2802 tmp->stillgoing = 0;
2804 ao2_lock(qe->parent);
2805 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
2806 qe->parent->rrpos++;
2808 ao2_unlock(qe->parent);
2814 ast_channel_lock(tmp->chan);
2815 while (ast_channel_trylock(qe->chan)) {
2816 CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan);
2819 if (qe->cancel_answered_elsewhere) {
2820 ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
2822 tmp->chan->appl = "AppQueue";
2823 tmp->chan->data = "(Outgoing Line)";
2824 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
2826 /* If the new channel has no callerid, try to guess what it should be */
2827 if (ast_strlen_zero(tmp->chan->cid.cid_num)) {
2828 if (!ast_strlen_zero(qe->chan->connected.id.number)) {
2829 ast_set_callerid(tmp->chan, qe->chan->connected.id.number, qe->chan->connected.id.name, qe->chan->connected.ani);
2830 tmp->chan->cid.cid_pres = qe->chan->connected.id.number_presentation;
2831 } else if (!ast_strlen_zero(qe->chan->cid.cid_dnid)) {
2832 ast_set_callerid(tmp->chan, qe->chan->cid.cid_dnid, NULL, NULL);
2833 } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
2834 ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL);
2836 tmp->update_connectedline = 0;
2839 ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
2841 tmp->chan->cid.cid_tns = qe->chan->cid.cid_tns;
2843 ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->cid);
2845 /* Inherit specially named variables from parent channel */
2846 ast_channel_inherit_variables(qe->chan, tmp->chan);
2847 ast_channel_datastore_inherit(qe->chan, tmp->chan);
2849 /* Presense of ADSI CPE on outgoing channel follows ours */
2850 tmp->chan->adsicpe = qe->chan->adsicpe;
2852 /* Inherit context and extension */
2853 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
2854 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
2855 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
2856 if (!ast_strlen_zero(macroexten))
2857 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
2859 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
2860 if (ast_cdr_isset_unanswered()) {
2861 /* they want to see the unanswered dial attempts! */
2862 /* set up the CDR fields on all the CDRs to give sensical information */
2863 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
2864 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
2865 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
2866 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
2867 strcpy(tmp->chan->cdr->dst, qe->chan->exten);
2868 strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
2869 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
2870 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
2871 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
2872 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
2873 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
2876 /* Place the call, but don't wait on the answer */
2877 if ((res = ast_call(tmp->chan, location, 0))) {
2878 /* Again, keep going even if there's an error */
2879 ast_debug(1, "ast call on peer returned %d\n", res);
2880 ast_verb(3, "Couldn't call %s\n", tmp->interface);
2881 ast_channel_unlock(tmp->chan);
2882 ast_channel_unlock(qe->chan);
2885 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
2887 } else if (qe->parent->eventwhencalled) {
2890 manager_event(EVENT_FLAG_AGENT, "AgentCalled",
2892 "AgentCalled: %s\r\n"
2894 "ChannelCalling: %s\r\n"
2895 "DestinationChannel: %s\r\n"
2896 "CallerIDNum: %s\r\n"
2897 "CallerIDName: %s\r\n"
2903 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
2904 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
2905 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
2906 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
2907 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
2908 ast_verb(3, "Called %s\n", tmp->interface);
2910 ast_channel_unlock(tmp->chan);
2911 ast_channel_unlock(qe->chan);
2913 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
2917 /*! \brief find the entry with the best metric, or NULL */
2918 static struct callattempt *find_best(struct callattempt *outgoing)
2920 struct callattempt *best = NULL, *cur;
2922 for (cur = outgoing; cur; cur = cur->q_next) {
2923 if (cur->stillgoing && /* Not already done */
2924 !cur->chan && /* Isn't already going */
2925 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
2934 * \brief Place a call to a queue member.
2936 * Once metrics have been calculated for each member, this function is used
2937 * to place a call to the appropriate member (or members). The low-level
2938 * channel-handling and error detection is handled in ring_entry
2940 * \retval 1 if a member was called successfully
2941 * \retval 0 otherwise