Asterisk - The Open Source Telephony Project GIT-master-0644429
app_queue.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2018, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
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.
13 *
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.
17 */
18
19/*! \file
20 *
21 * \brief True call queues with optional send URL on answer
22 *
23 * \author Mark Spencer <markster@digium.com>
24 *
25 * \par Development notes
26 * \note 2004-11-25: Persistent Dynamic Members added by:
27 * NetNation Communications (www.netnation.com)
28 * Kevin Lindsay <kevinl@netnation.com>
29 *
30 * Each dynamic agent in each queue is now stored in the astdb.
31 * When asterisk is restarted, each agent will be automatically
32 * readded into their recorded queues. This feature can be
33 * configured with the 'persistent_members=<1|0>' setting in the
34 * '[general]' category in queues.conf. The default is on.
35 *
36 * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
37 *
38 * \note These features added by David C. Troy <dave@toad.net>:
39 * - Per-queue holdtime calculation
40 * - Estimated holdtime announcement
41 * - Position announcement
42 * - Abandoned/completed call counters
43 * - Failout timer passed as optional app parameter
44 *
45 * Patch Version 1.07 2003-12-24 01
46 *
47 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
48 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
49 *
50 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
51 * by Matthew Enger <m.enger@xi.com.au>
52 *
53 * \ingroup applications
54 */
55
56/*! \li \ref app_queues.c uses configuration file \ref queues.conf
57 * \addtogroup configuration_file
58 */
59
60/*! \page queues.conf queues.conf
61 * \verbinclude queues.conf.sample
62 */
63
64/*** MODULEINFO
65 <support_level>core</support_level>
66 ***/
67
68#include "asterisk.h"
69
70#include <sys/time.h>
71#include <signal.h>
72#include <netinet/in.h>
73#include <ctype.h>
74
75#include "asterisk/lock.h"
76#include "asterisk/file.h"
77#include "asterisk/channel.h"
78#include "asterisk/pbx.h"
79#include "asterisk/app.h"
81#include "asterisk/module.h"
82#include "asterisk/translate.h"
83#include "asterisk/say.h"
84#include "asterisk/features.h"
86#include "asterisk/cli.h"
87#include "asterisk/manager.h"
88#include "asterisk/config.h"
89#include "asterisk/utils.h"
90#include "asterisk/causes.h"
91#include "asterisk/astdb.h"
94#include "asterisk/astobj2.h"
95#include "asterisk/strings.h"
97#include "asterisk/aoc.h"
98#include "asterisk/callerid.h"
99#include "asterisk/term.h"
100#include "asterisk/dial.h"
105#include "asterisk/core_local.h"
106#include "asterisk/mixmonitor.h"
109
110/*!
111 * \par Please read before modifying this file.
112 * There are three locks which are regularly used
113 * throughout this file, the queue list lock, the lock
114 * for each individual queue, and the interface list lock.
115 * Please be extra careful to always lock in the following order
116 * 1) queue list lock
117 * 2) individual queue lock
118 * 3) interface list lock
119 * This order has sort of "evolved" over the lifetime of this
120 * application, but it is now in place this way, so please adhere
121 * to this order!
122 */
123
124/*** DOCUMENTATION
125 <application name="Queue" language="en_US">
126 <synopsis>
127 Queue a call for a call queue.
128 </synopsis>
129 <syntax>
130 <parameter name="queuename" required="true" />
131 <parameter name="options">
132 <optionlist>
133 <option name="b" argsep="^">
134 <para>Before initiating an outgoing call, <literal>Gosub</literal> to the specified
135 location using the newly created channel. The <literal>Gosub</literal> will be
136 executed for each destination channel.</para>
137 <argument name="context" required="false" />
138 <argument name="exten" required="false" />
139 <argument name="priority" required="true" hasparams="optional" argsep="^">
140 <argument name="arg1" multiple="true" required="true" />
141 <argument name="argN" />
142 </argument>
143 </option>
144 <option name="B" argsep="^">
145 <para>Before initiating the outgoing call(s), <literal>Gosub</literal> to the
146 specified location using the current channel.</para>
147 <argument name="context" required="false" />
148 <argument name="exten" required="false" />
149 <argument name="priority" required="true" hasparams="optional" argsep="^">
150 <argument name="arg1" multiple="true" required="true" />
151 <argument name="argN" />
152 </argument>
153 </option>
154 <option name="C">
155 <para>Mark all calls as "answered elsewhere" when cancelled.</para>
156 </option>
157 <option name="c">
158 <para>Continue in the dialplan if the callee hangs up.</para>
159 </option>
160 <option name="d">
161 <para>Data-quality (modem) call (minimum delay).</para>
162 <para>This option only applies to DAHDI channels. By default,
163 DTMF is verified by muting audio TX/RX to verify the tone
164 is still present. This option disables that behavior.</para>
165 </option>
166 <option name="F" argsep="^">
167 <argument name="context" required="false" />
168 <argument name="exten" required="false" />
169 <argument name="priority" required="true" />
170 <para>When the caller hangs up, transfer the <emphasis>called member</emphasis>
171 to the specified destination and <emphasis>start</emphasis> execution at that location.</para>
172 <para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
173 prefixed with one or two underbars ('_').</para>
174 <para>NOTE: Using this option from a GoSub() might not make sense as there would be no return points.</para>
175 </option>
176 <option name="h">
177 <para>Allow <emphasis>callee</emphasis> to hang up by pressing <literal>*</literal>.</para>
178 </option>
179 <option name="H">
180 <para>Allow <emphasis>caller</emphasis> to hang up by pressing <literal>*</literal>.</para>
181 </option>
182 <option name="i">
183 <para>Ignore call forward requests from queue members and do nothing
184 when they are requested.</para>
185 </option>
186 <option name="I">
187 <para>Asterisk will ignore any connected line update requests or any redirecting party
188 update requests it may receive on this dial attempt.</para>
189 </option>
190 <option name="k">
191 <para>Allow the <emphasis>called</emphasis> party to enable parking of the call by sending
192 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
193 </option>
194 <option name="K">
195 <para>Allow the <emphasis>calling</emphasis> party to enable parking of the call by sending
196 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
197 </option>
198 <option name="m">
199 <para>Custom music on hold class to use, which will override the music on hold class configured
200 in <filename>queues.conf</filename>, if specified.</para>
201 <para>Note that CHANNEL(musicclass), if set, will still override this option.</para>
202 </option>
203 <option name="n">
204 <para>No retries on the timeout; will exit this application and
205 go to the next step.</para>
206 </option>
207 <option name="r">
208 <para>Ring instead of playing MOH. Periodic Announcements are still made, if applicable.</para>
209 </option>
210 <option name="R">
211 <para>Ring instead of playing MOH when a member channel is actually ringing.</para>
212 </option>
213 <option name="t">
214 <para>Allow the <emphasis>called</emphasis> user to transfer the calling user.</para>
215 </option>
216 <option name="T">
217 <para>Allow the <emphasis>calling</emphasis> user to transfer the call.</para>
218 </option>
219 <option name="x">
220 <para>Allow the <emphasis>called</emphasis> user to write the conversation
221 to disk via MixMonitor.</para>
222 </option>
223 <option name="X">
224 <para>Allow the <emphasis>calling</emphasis> user to write the conversation to
225 disk via MixMonitor.</para>
226 </option>
227 </optionlist>
228 </parameter>
229 <parameter name="URL">
230 <para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
231 </parameter>
232 <parameter name="announceoverride" argsep="&amp;">
233 <para>Announcement file(s) to play to agent before bridging
234 call, overriding the announcement(s) configured in
235 <filename>queues.conf</filename>, if any.</para>
236 <para>Ampersand separated list of filenames. If the filename
237 is a relative filename (it does not begin with a slash), it
238 will be searched for in the Asterisk sounds directory. If the
239 filename is able to be parsed as a URL, Asterisk will
240 download the file and then begin playback on it. To include a
241 literal <literal>&amp;</literal> in the URL you can enclose
242 the URL in single quotes.</para>
243 <argument name="announceoverride" required="true" />
244 <argument name="announceoverride2" multiple="true" />
245 </parameter>
246 <parameter name="timeout">
247 <para>Will cause the queue to fail out after a specified number of
248 seconds, checked between each <filename>queues.conf</filename> <replaceable>timeout</replaceable> and
249 <replaceable>retry</replaceable> cycle.</para>
250 </parameter>
251 <parameter name="AGI">
252 <para>Will setup an AGI script to be executed on the calling party's channel once they are
253 connected to a queue member.</para>
254 </parameter>
255 <parameter name="gosub">
256 <para>Will run a gosub on the called party's channel (the queue member)
257 once the parties are connected. The subroutine execution starts in the
258 named context at the s exten and priority 1.</para>
259 </parameter>
260 <parameter name="rule">
261 <para>Will cause the queue's defaultrule to be overridden by the rule specified.</para>
262 </parameter>
263 <parameter name="position">
264 <para>Attempt to enter the caller into the queue at the numerical position specified. <literal>1</literal>
265 would attempt to enter the caller at the head of the queue, and <literal>3</literal> would attempt to place
266 the caller third in the queue.</para>
267 </parameter>
268 </syntax>
269 <description>
270 <para>In addition to transferring the call, a call may be parked and then picked
271 up by another user.</para>
272 <para>This application will return to the dialplan if the queue does not exist, or
273 any of the join options cause the caller to not enter the queue.</para>
274 <para>This application does not automatically answer and should be preceeded
275 by an application such as Answer(), Progress(), or Ringing().</para>
276 <para>This application sets the following channel variables upon completion:</para>
277 <variablelist>
278 <variable name="QUEUESTATUS">
279 <para>The status of the call as a text string.</para>
280 <value name="TIMEOUT" />
281 <value name="FULL" />
282 <value name="JOINEMPTY" />
283 <value name="LEAVEEMPTY" />
284 <value name="JOINUNAVAIL" />
285 <value name="LEAVEUNAVAIL" />
286 <value name="CONTINUE" />
287 <value name="WITHDRAW" />
288 </variable>
289 <variable name="ABANDONED">
290 <para>If the call was not answered by an agent this variable will be TRUE.</para>
291 <value name="TRUE" />
292 </variable>
293 <variable name="DIALEDPEERNUMBER">
294 <para>Resource of the agent that was dialed set on the outbound channel.</para>
295 </variable>
296 <variable name="QUEUE_WITHDRAW_INFO">
297 <para>If the call was successfully withdrawn from the queue, and the withdraw request was provided with optional withdraw info, the withdraw info will be stored in this variable.</para>
298 </variable>
299 </variablelist>
300 </description>
301 <see-also>
302 <ref type="application">Queue</ref>
303 <ref type="application">QueueLog</ref>
304 <ref type="application">AddQueueMember</ref>
305 <ref type="application">RemoveQueueMember</ref>
306 <ref type="application">PauseQueueMember</ref>
307 <ref type="application">UnpauseQueueMember</ref>
308 <ref type="function">QUEUE_VARIABLES</ref>
309 <ref type="function">QUEUE_MEMBER</ref>
310 <ref type="function">QUEUE_MEMBER_COUNT</ref>
311 <ref type="function">QUEUE_EXISTS</ref>
312 <ref type="function">QUEUE_GET_CHANNEL</ref>
313 <ref type="function">QUEUE_WAITING_COUNT</ref>
314 <ref type="function">QUEUE_MEMBER_LIST</ref>
315 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
316 </see-also>
317 </application>
318 <application name="AddQueueMember" language="en_US">
319 <synopsis>
320 Dynamically adds queue members.
321 </synopsis>
322 <syntax>
323 <parameter name="queuename" required="true" />
324 <parameter name="interface" />
325 <parameter name="penalty" />
326 <parameter name="options" />
327 <parameter name="membername" />
328 <parameter name="stateinterface" />
329 <parameter name="wrapuptime" />
330 </syntax>
331 <description>
332 <para>Dynamically adds interface to an existing queue. If the interface is
333 already in the queue it will return an error.</para>
334 <para>This application sets the following channel variable upon completion:</para>
335 <variablelist>
336 <variable name="AQMSTATUS">
337 <para>The status of the attempt to add a queue member as a text string.</para>
338 <value name="ADDED" />
339 <value name="MEMBERALREADY" />
340 <value name="NOSUCHQUEUE" />
341 </variable>
342 </variablelist>
343 </description>
344 <see-also>
345 <ref type="application">Queue</ref>
346 <ref type="application">QueueLog</ref>
347 <ref type="application">AddQueueMember</ref>
348 <ref type="application">RemoveQueueMember</ref>
349 <ref type="application">PauseQueueMember</ref>
350 <ref type="application">UnpauseQueueMember</ref>
351 <ref type="function">QUEUE_VARIABLES</ref>
352 <ref type="function">QUEUE_MEMBER</ref>
353 <ref type="function">QUEUE_MEMBER_COUNT</ref>
354 <ref type="function">QUEUE_EXISTS</ref>
355 <ref type="function">QUEUE_GET_CHANNEL</ref>
356 <ref type="function">QUEUE_WAITING_COUNT</ref>
357 <ref type="function">QUEUE_MEMBER_LIST</ref>
358 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
359 </see-also>
360 </application>
361 <application name="RemoveQueueMember" language="en_US">
362 <synopsis>
363 Dynamically removes queue members.
364 </synopsis>
365 <syntax>
366 <parameter name="queuename" required="true" />
367 <parameter name="interface" />
368 </syntax>
369 <description>
370 <para>If the interface is <emphasis>NOT</emphasis> in the queue it will return an error.</para>
371 <para>This application sets the following channel variable upon completion:</para>
372 <variablelist>
373 <variable name="RQMSTATUS">
374 <value name="REMOVED" />
375 <value name="NOTINQUEUE" />
376 <value name="NOSUCHQUEUE" />
377 <value name="NOTDYNAMIC" />
378 </variable>
379 </variablelist>
380 <example title="Remove queue member">
381 same => n,RemoveQueueMember(techsupport,SIP/3000)
382 </example>
383 </description>
384 <see-also>
385 <ref type="application">Queue</ref>
386 <ref type="application">QueueLog</ref>
387 <ref type="application">AddQueueMember</ref>
388 <ref type="application">RemoveQueueMember</ref>
389 <ref type="application">PauseQueueMember</ref>
390 <ref type="application">UnpauseQueueMember</ref>
391 <ref type="function">QUEUE_VARIABLES</ref>
392 <ref type="function">QUEUE_MEMBER</ref>
393 <ref type="function">QUEUE_MEMBER_COUNT</ref>
394 <ref type="function">QUEUE_EXISTS</ref>
395 <ref type="function">QUEUE_GET_CHANNEL</ref>
396 <ref type="function">QUEUE_WAITING_COUNT</ref>
397 <ref type="function">QUEUE_MEMBER_LIST</ref>
398 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
399 </see-also>
400 </application>
401 <application name="PauseQueueMember" language="en_US">
402 <synopsis>
403 Pauses a queue member.
404 </synopsis>
405 <syntax>
406 <parameter name="queuename" />
407 <parameter name="interface" required="true" />
408 <parameter name="options" />
409 <parameter name="reason">
410 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
411 </parameter>
412 </syntax>
413 <description>
414 <para>Pauses (blocks calls for) a queue member. The given interface will be paused in the given queue.
415 This prevents any calls from being sent from the queue to the interface until it is
416 unpaused with UnpauseQueueMember or the manager interface. If no queuename is given,
417 the interface is paused in every queue it is a member of. The application will fail if the
418 interface is not found.</para>
419 <para>This application sets the following channel variable upon completion:</para>
420 <variablelist>
421 <variable name="PQMSTATUS">
422 <para>The status of the attempt to pause a queue member as a text string.</para>
423 <value name="PAUSED" />
424 <value name="NOTFOUND" />
425 </variable>
426 </variablelist>
427 <example title="Pause queue member">
428 same => n,PauseQueueMember(,SIP/3000)
429 </example>
430 </description>
431 <see-also>
432 <ref type="application">Queue</ref>
433 <ref type="application">QueueLog</ref>
434 <ref type="application">AddQueueMember</ref>
435 <ref type="application">RemoveQueueMember</ref>
436 <ref type="application">PauseQueueMember</ref>
437 <ref type="application">UnpauseQueueMember</ref>
438 <ref type="function">QUEUE_VARIABLES</ref>
439 <ref type="function">QUEUE_MEMBER</ref>
440 <ref type="function">QUEUE_MEMBER_COUNT</ref>
441 <ref type="function">QUEUE_EXISTS</ref>
442 <ref type="function">QUEUE_GET_CHANNEL</ref>
443 <ref type="function">QUEUE_WAITING_COUNT</ref>
444 <ref type="function">QUEUE_MEMBER_LIST</ref>
445 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
446 </see-also>
447 </application>
448 <application name="UnpauseQueueMember" language="en_US">
449 <synopsis>
450 Unpauses a queue member.
451 </synopsis>
452 <syntax>
453 <parameter name="queuename" />
454 <parameter name="interface" required="true" />
455 <parameter name="options" />
456 <parameter name="reason">
457 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
458 </parameter>
459 </syntax>
460 <description>
461 <para>Unpauses (resumes calls to) a queue member. This is the counterpart to <literal>PauseQueueMember()</literal>
462 and operates exactly the same way, except it unpauses instead of pausing the given interface.</para>
463 <para>This application sets the following channel variable upon completion:</para>
464 <variablelist>
465 <variable name="UPQMSTATUS">
466 <para>The status of the attempt to unpause a queue member as a text string.</para>
467 <value name="UNPAUSED" />
468 <value name="NOTFOUND" />
469 </variable>
470 </variablelist>
471 <example title="Unpause queue member">
472 same => n,UnpauseQueueMember(,SIP/3000)
473 </example>
474 </description>
475 <see-also>
476 <ref type="application">Queue</ref>
477 <ref type="application">QueueLog</ref>
478 <ref type="application">AddQueueMember</ref>
479 <ref type="application">RemoveQueueMember</ref>
480 <ref type="application">PauseQueueMember</ref>
481 <ref type="application">UnpauseQueueMember</ref>
482 <ref type="function">QUEUE_VARIABLES</ref>
483 <ref type="function">QUEUE_MEMBER</ref>
484 <ref type="function">QUEUE_MEMBER_COUNT</ref>
485 <ref type="function">QUEUE_EXISTS</ref>
486 <ref type="function">QUEUE_GET_CHANNEL</ref>
487 <ref type="function">QUEUE_WAITING_COUNT</ref>
488 <ref type="function">QUEUE_MEMBER_LIST</ref>
489 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
490 </see-also>
491 </application>
492 <application name="QueueLog" language="en_US">
493 <synopsis>
494 Writes to the queue_log file.
495 </synopsis>
496 <syntax>
497 <parameter name="queuename" required="true" />
498 <parameter name="uniqueid" required="true" />
499 <parameter name="agent" required="true" />
500 <parameter name="event" required="true" />
501 <parameter name="additionalinfo" />
502 </syntax>
503 <description>
504 <para>Allows you to write your own events into the queue log.</para>
505 <example title="Log custom queue event">
506 same => n,QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)
507 </example>
508 </description>
509 <see-also>
510 <ref type="application">Queue</ref>
511 <ref type="application">QueueLog</ref>
512 <ref type="application">AddQueueMember</ref>
513 <ref type="application">RemoveQueueMember</ref>
514 <ref type="application">PauseQueueMember</ref>
515 <ref type="application">UnpauseQueueMember</ref>
516 <ref type="function">QUEUE_VARIABLES</ref>
517 <ref type="function">QUEUE_MEMBER</ref>
518 <ref type="function">QUEUE_MEMBER_COUNT</ref>
519 <ref type="function">QUEUE_EXISTS</ref>
520 <ref type="function">QUEUE_GET_CHANNEL</ref>
521 <ref type="function">QUEUE_WAITING_COUNT</ref>
522 <ref type="function">QUEUE_MEMBER_LIST</ref>
523 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
524 </see-also>
525 </application>
526 <application name="QueueUpdate" language="en_US">
527 <synopsis>
528 Writes to the queue_log file for outbound calls and updates Realtime Data.
529 Is used at h extension to be able to have all the parameters.
530 </synopsis>
531 <syntax>
532 <parameter name="queuename" required="true" />
533 <parameter name="uniqueid" required="true" />
534 <parameter name="agent" required="true" />
535 <parameter name="status" required="true" />
536 <parameter name="talktime" required="true" />
537 <parameter name="params" required="false" />
538 </syntax>
539 <description>
540 <para>Allows you to write Outbound events into the queue log.</para>
541 <example title="Write outbound event into queue log">
542 exten => h,1,QueueUpdate(${QUEUE}, ${UNIQUEID}, ${AGENT}, ${DIALSTATUS}, ${ANSWEREDTIME}, ${DIALEDTIME} | ${DIALEDNUMBER})
543 </example>
544 </description>
545 </application>
546 <function name="QUEUE_VARIABLES" language="en_US">
547 <synopsis>
548 Return Queue information in variables.
549 </synopsis>
550 <syntax>
551 <parameter name="queuename" required="true">
552 <enumlist>
553 <enum name="QUEUEMAX">
554 <para>Maxmimum number of calls allowed.</para>
555 </enum>
556 <enum name="QUEUESTRATEGY">
557 <para>The strategy of the queue.</para>
558 </enum>
559 <enum name="QUEUECALLS">
560 <para>Number of calls currently in the queue.</para>
561 </enum>
562 <enum name="QUEUEHOLDTIME">
563 <para>Current average hold time.</para>
564 </enum>
565 <enum name="QUEUECOMPLETED">
566 <para>Number of completed calls for the queue.</para>
567 </enum>
568 <enum name="QUEUEABANDONED">
569 <para>Number of abandoned calls.</para>
570 </enum>
571 <enum name="QUEUESRVLEVEL">
572 <para>Queue service level.</para>
573 </enum>
574 <enum name="QUEUESRVLEVELPERF">
575 <para>Current service level performance.</para>
576 </enum>
577 </enumlist>
578 </parameter>
579 </syntax>
580 <description>
581 <para>Makes the following queue variables available.</para>
582 <para>Returns <literal>0</literal> if queue is found and setqueuevar is defined, <literal>-1</literal> otherwise.</para>
583 </description>
584 <see-also>
585 <ref type="application">Queue</ref>
586 <ref type="application">QueueLog</ref>
587 <ref type="application">AddQueueMember</ref>
588 <ref type="application">RemoveQueueMember</ref>
589 <ref type="application">PauseQueueMember</ref>
590 <ref type="application">UnpauseQueueMember</ref>
591 <ref type="function">QUEUE_VARIABLES</ref>
592 <ref type="function">QUEUE_MEMBER</ref>
593 <ref type="function">QUEUE_MEMBER_COUNT</ref>
594 <ref type="function">QUEUE_EXISTS</ref>
595 <ref type="function">QUEUE_GET_CHANNEL</ref>
596 <ref type="function">QUEUE_WAITING_COUNT</ref>
597 <ref type="function">QUEUE_MEMBER_LIST</ref>
598 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
599 </see-also>
600 </function>
601 <function name="QUEUE_MEMBER" language="en_US">
602 <synopsis>
603 Provides a count of queue members based on the provided criteria, or updates a
604 queue member's settings.
605 </synopsis>
606 <syntax>
607 <parameter name="queuename" required="false" />
608 <parameter name="option" required="true">
609 <enumlist>
610 <enum name="logged">
611 <para>Returns the number of logged-in members for the specified queue.</para>
612 </enum>
613 <enum name="free">
614 <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>
615 </enum>
616 <enum name="ready">
617 <para>Returns the number of logged-in members for the specified queue that are immediately available to answer a call.</para>
618 </enum>
619 <enum name="count">
620 <para>Returns the total number of members for the specified queue.</para>
621 </enum>
622 <enum name="penalty">
623 <para>Gets or sets queue member penalty. If
624 <replaceable>queuename</replaceable> is not specified
625 when setting the penalty then the penalty is set in all queues
626 the interface is a member.</para>
627 </enum>
628 <enum name="paused">
629 <para>Gets or sets queue member paused status. If
630 <replaceable>queuename</replaceable> is not specified
631 when setting the paused status then the paused status is set
632 in all queues the interface is a member.</para>
633 </enum>
634 <enum name="ringinuse">
635 <para>Gets or sets queue member ringinuse. If
636 <replaceable>queuename</replaceable> is not specified
637 when setting ringinuse then ringinuse is set
638 in all queues the interface is a member.</para>
639 </enum>
640 </enumlist>
641 </parameter>
642 <parameter name="interface" required="false" />
643 </syntax>
644 <description>
645 <para>Allows access to queue counts [R] and member information [R/W].</para>
646 <para><replaceable>queuename</replaceable> is required for all read operations.</para>
647 <para><replaceable>interface</replaceable> is required for all member operations.</para>
648 </description>
649 <see-also>
650 <ref type="application">Queue</ref>
651 <ref type="application">QueueLog</ref>
652 <ref type="application">AddQueueMember</ref>
653 <ref type="application">RemoveQueueMember</ref>
654 <ref type="application">PauseQueueMember</ref>
655 <ref type="application">UnpauseQueueMember</ref>
656 <ref type="function">QUEUE_VARIABLES</ref>
657 <ref type="function">QUEUE_MEMBER</ref>
658 <ref type="function">QUEUE_MEMBER_COUNT</ref>
659 <ref type="function">QUEUE_EXISTS</ref>
660 <ref type="function">QUEUE_GET_CHANNEL</ref>
661 <ref type="function">QUEUE_WAITING_COUNT</ref>
662 <ref type="function">QUEUE_MEMBER_LIST</ref>
663 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
664 </see-also>
665 </function>
666 <function name="QUEUE_MEMBER_COUNT" language="en_US">
667 <synopsis>
668 Count number of members answering a queue.
669 </synopsis>
670 <syntax>
671 <parameter name="queuename" required="true" />
672 </syntax>
673 <description>
674 <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
675 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
676 </description>
677 <see-also>
678 <ref type="application">Queue</ref>
679 <ref type="application">QueueLog</ref>
680 <ref type="application">AddQueueMember</ref>
681 <ref type="application">RemoveQueueMember</ref>
682 <ref type="application">PauseQueueMember</ref>
683 <ref type="application">UnpauseQueueMember</ref>
684 <ref type="function">QUEUE_VARIABLES</ref>
685 <ref type="function">QUEUE_MEMBER</ref>
686 <ref type="function">QUEUE_MEMBER_COUNT</ref>
687 <ref type="function">QUEUE_EXISTS</ref>
688 <ref type="function">QUEUE_GET_CHANNEL</ref>
689 <ref type="function">QUEUE_WAITING_COUNT</ref>
690 <ref type="function">QUEUE_MEMBER_LIST</ref>
691 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
692 </see-also>
693 </function>
694 <function name="QUEUE_EXISTS" language="en_US">
695 <synopsis>
696 Check if a named queue exists on this server
697 </synopsis>
698 <syntax>
699 <parameter name="queuename" />
700 </syntax>
701 <description>
702 <para>Returns 1 if the specified queue exists, 0 if it does not</para>
703 </description>
704 <see-also>
705 <ref type="application">Queue</ref>
706 <ref type="application">QueueLog</ref>
707 <ref type="application">AddQueueMember</ref>
708 <ref type="application">RemoveQueueMember</ref>
709 <ref type="application">PauseQueueMember</ref>
710 <ref type="application">UnpauseQueueMember</ref>
711 <ref type="function">QUEUE_VARIABLES</ref>
712 <ref type="function">QUEUE_MEMBER</ref>
713 <ref type="function">QUEUE_MEMBER_COUNT</ref>
714 <ref type="function">QUEUE_EXISTS</ref>
715 <ref type="function">QUEUE_GET_CHANNEL</ref>
716 <ref type="function">QUEUE_WAITING_COUNT</ref>
717 <ref type="function">QUEUE_MEMBER_LIST</ref>
718 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
719 </see-also>
720 </function>
721 <function name="QUEUE_GET_CHANNEL" language="en_US">
722 <synopsis>
723 Return caller at the specified position in a queue.
724 </synopsis>
725 <syntax>
726 <parameter name="queuename" required="true" />
727 <parameter name="position" />
728 </syntax>
729 <description>
730 <para>Returns the caller channel at <replaceable>position</replaceable> in the specified <replaceable>queuename</replaceable>.</para>
731 <para>If <replaceable>position</replaceable> is unspecified the first channel is returned.</para>
732 </description>
733 <see-also>
734 <ref type="application">Queue</ref>
735 <ref type="application">QueueLog</ref>
736 <ref type="application">AddQueueMember</ref>
737 <ref type="application">RemoveQueueMember</ref>
738 <ref type="application">PauseQueueMember</ref>
739 <ref type="application">UnpauseQueueMember</ref>
740 <ref type="function">QUEUE_VARIABLES</ref>
741 <ref type="function">QUEUE_MEMBER</ref>
742 <ref type="function">QUEUE_MEMBER_COUNT</ref>
743 <ref type="function">QUEUE_EXISTS</ref>
744 <ref type="function">QUEUE_WAITING_COUNT</ref>
745 <ref type="function">QUEUE_MEMBER_LIST</ref>
746 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
747 </see-also>
748 </function>
749 <function name="QUEUE_WAITING_COUNT" language="en_US">
750 <synopsis>
751 Count number of calls currently waiting in a queue.
752 </synopsis>
753 <syntax>
754 <parameter name="queuename" />
755 </syntax>
756 <description>
757 <para>Returns the number of callers currently waiting in the specified <replaceable>queuename</replaceable>.</para>
758 </description>
759 <see-also>
760 <ref type="application">Queue</ref>
761 <ref type="application">QueueLog</ref>
762 <ref type="application">AddQueueMember</ref>
763 <ref type="application">RemoveQueueMember</ref>
764 <ref type="application">PauseQueueMember</ref>
765 <ref type="application">UnpauseQueueMember</ref>
766 <ref type="function">QUEUE_VARIABLES</ref>
767 <ref type="function">QUEUE_MEMBER</ref>
768 <ref type="function">QUEUE_MEMBER_COUNT</ref>
769 <ref type="function">QUEUE_EXISTS</ref>
770 <ref type="function">QUEUE_GET_CHANNEL</ref>
771 <ref type="function">QUEUE_WAITING_COUNT</ref>
772 <ref type="function">QUEUE_MEMBER_LIST</ref>
773 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
774 </see-also>
775 </function>
776 <function name="QUEUE_MEMBER_LIST" language="en_US">
777 <synopsis>
778 Returns a list of interfaces on a queue.
779 </synopsis>
780 <syntax>
781 <parameter name="queuename" required="true" />
782 </syntax>
783 <description>
784 <para>Returns a comma-separated list of members associated with the specified <replaceable>queuename</replaceable>.</para>
785 </description>
786 <see-also>
787 <ref type="application">Queue</ref>
788 <ref type="application">QueueLog</ref>
789 <ref type="application">AddQueueMember</ref>
790 <ref type="application">RemoveQueueMember</ref>
791 <ref type="application">PauseQueueMember</ref>
792 <ref type="application">UnpauseQueueMember</ref>
793 <ref type="function">QUEUE_VARIABLES</ref>
794 <ref type="function">QUEUE_MEMBER</ref>
795 <ref type="function">QUEUE_MEMBER_COUNT</ref>
796 <ref type="function">QUEUE_EXISTS</ref>
797 <ref type="function">QUEUE_GET_CHANNEL</ref>
798 <ref type="function">QUEUE_WAITING_COUNT</ref>
799 <ref type="function">QUEUE_MEMBER_LIST</ref>
800 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
801 </see-also>
802 </function>
803 <function name="QUEUE_MEMBER_PENALTY" language="en_US">
804 <synopsis>
805 Gets or sets queue members penalty.
806 </synopsis>
807 <syntax>
808 <parameter name="queuename" required="true" />
809 <parameter name="interface" required="true" />
810 </syntax>
811 <description>
812 <para>Gets or sets queue members penalty.</para>
813 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
814 </description>
815 <see-also>
816 <ref type="application">Queue</ref>
817 <ref type="application">QueueLog</ref>
818 <ref type="application">AddQueueMember</ref>
819 <ref type="application">RemoveQueueMember</ref>
820 <ref type="application">PauseQueueMember</ref>
821 <ref type="application">UnpauseQueueMember</ref>
822 <ref type="function">QUEUE_VARIABLES</ref>
823 <ref type="function">QUEUE_MEMBER</ref>
824 <ref type="function">QUEUE_MEMBER_COUNT</ref>
825 <ref type="function">QUEUE_EXISTS</ref>
826 <ref type="function">QUEUE_GET_CHANNEL</ref>
827 <ref type="function">QUEUE_WAITING_COUNT</ref>
828 <ref type="function">QUEUE_MEMBER_LIST</ref>
829 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
830 </see-also>
831 </function>
832 <manager name="QueueStatus" language="en_US">
833 <synopsis>
834 Show queue status.
835 </synopsis>
836 <syntax>
837 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
838 <parameter name="Queue">
839 <para>Limit the response to the status of the specified queue.</para>
840 </parameter>
841 <parameter name="Member">
842 <para>Limit the response to the status of the specified member.</para>
843 </parameter>
844 </syntax>
845 <description>
846 <para>Check the status of one or more queues.</para>
847 </description>
848 </manager>
849 <manager name="QueueSummary" language="en_US">
850 <synopsis>
851 Show queue summary.
852 </synopsis>
853 <syntax>
854 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
855 <parameter name="Queue">
856 <para>Queue for which the summary is requested.</para>
857 </parameter>
858 </syntax>
859 <description>
860 <para>Request the manager to send a QueueSummary event.</para>
861 </description>
862 </manager>
863 <manager name="QueueAdd" language="en_US">
864 <synopsis>
865 Add interface to queue.
866 </synopsis>
867 <syntax>
868 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
869 <parameter name="Queue" required="true">
870 <para>Queue's name.</para>
871 </parameter>
872 <parameter name="Interface" required="true">
873 <para>The name of the interface (tech/name) to add to the queue.</para>
874 </parameter>
875 <parameter name="Penalty">
876 <para>A penalty (number) to apply to this member. Asterisk will distribute calls to members with higher penalties only after attempting to distribute calls to those with lower penalty.</para>
877 </parameter>
878 <parameter name="Paused">
879 <para>To pause or not the member initially (true/false or 1/0).</para>
880 </parameter>
881 <parameter name="MemberName">
882 <para>Text alias for the interface.</para>
883 </parameter>
884 <parameter name="StateInterface" />
885 </syntax>
886 <description>
887 </description>
888 </manager>
889 <manager name="QueueRemove" language="en_US">
890 <synopsis>
891 Remove interface from queue.
892 </synopsis>
893 <syntax>
894 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
895 <parameter name="Queue" required="true">
896 <para>The name of the queue to take action on.</para>
897 </parameter>
898 <parameter name="Interface" required="true">
899 <para>The interface (tech/name) to remove from queue.</para>
900 </parameter>
901 </syntax>
902 <description>
903 </description>
904 </manager>
905 <manager name="QueuePause" language="en_US">
906 <synopsis>
907 Makes a queue member temporarily unavailable.
908 </synopsis>
909 <syntax>
910 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
911 <parameter name="Interface" required="true">
912 <para>The name of the interface (tech/name) to pause or unpause.</para>
913 </parameter>
914 <parameter name="Paused" required="true">
915 <para>Pause or unpause the interface. Set to 'true' to pause the member or 'false' to unpause.</para>
916 </parameter>
917 <parameter name="Queue">
918 <para>The name of the queue in which to pause or unpause this member. If not specified, the member will be paused or unpaused in all the queues it is a member of.</para>
919 </parameter>
920 <parameter name="Reason">
921 <para>Text description, returned in the event QueueMemberPaused.</para>
922 </parameter>
923 </syntax>
924 <description>
925 <para>Pause or unpause a member in a queue.</para>
926 </description>
927 </manager>
928 <manager name="QueueLog" language="en_US">
929 <synopsis>
930 Adds custom entry in queue_log.
931 </synopsis>
932 <syntax>
933 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
934 <parameter name="Queue" required="true" />
935 <parameter name="Event" required="true" />
936 <parameter name="Uniqueid" />
937 <parameter name="Interface" />
938 <parameter name="Message" />
939 </syntax>
940 <description>
941 </description>
942 </manager>
943 <manager name="QueuePenalty" language="en_US">
944 <synopsis>
945 Set the penalty for a queue member.
946 </synopsis>
947 <syntax>
948 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
949 <parameter name="Interface" required="true">
950 <para>The interface (tech/name) of the member whose penalty to change.</para>
951 </parameter>
952 <parameter name="Penalty" required="true">
953 <para>The new penalty (number) for the member. Must be nonnegative.</para>
954 </parameter>
955 <parameter name="Queue">
956 <para>If specified, only set the penalty for the member of this queue. Otherwise, set the penalty for the member in all queues to which the member belongs.</para>
957 </parameter>
958 </syntax>
959 <description>
960 <para>Change the penalty of a queue member</para>
961 </description>
962 </manager>
963 <manager name="QueueMemberRingInUse" language="en_US">
964 <synopsis>
965 Set the ringinuse value for a queue member.
966 </synopsis>
967 <syntax>
968 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
969 <parameter name="Interface" required="true" />
970 <parameter name="RingInUse" required="true" />
971 <parameter name="Queue" />
972 </syntax>
973 <description>
974 </description>
975 </manager>
976 <manager name="QueueRule" language="en_US">
977 <synopsis>
978 Queue Rules.
979 </synopsis>
980 <syntax>
981 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
982 <parameter name="Rule">
983 <para>The name of the rule in queuerules.conf whose contents to list.</para>
984 </parameter>
985 </syntax>
986 <description>
987 <para>List queue rules defined in queuerules.conf</para>
988 </description>
989 </manager>
990 <manager name="QueueReload" language="en_US">
991 <synopsis>
992 Reload a queue, queues, or any sub-section of a queue or queues.
993 </synopsis>
994 <syntax>
995 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
996 <parameter name="Queue">
997 <para>The name of the queue to take action on. If no queue name is specified, then all queues are affected.</para>
998 </parameter>
999 <parameter name="Members">
1000 <para>Whether to reload the queue's members.</para>
1001 <enumlist>
1002 <enum name="yes" />
1003 <enum name="no" />
1004 </enumlist>
1005 </parameter>
1006 <parameter name="Rules">
1007 <para>Whether to reload queuerules.conf</para>
1008 <enumlist>
1009 <enum name="yes" />
1010 <enum name="no" />
1011 </enumlist>
1012 </parameter>
1013 <parameter name="Parameters">
1014 <para>Whether to reload the other queue options.</para>
1015 <enumlist>
1016 <enum name="yes" />
1017 <enum name="no" />
1018 </enumlist>
1019 </parameter>
1020 </syntax>
1021 <description>
1022 </description>
1023 </manager>
1024 <manager name="QueueReset" language="en_US">
1025 <synopsis>
1026 Reset queue statistics.
1027 </synopsis>
1028 <syntax>
1029 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1030 <parameter name="Queue">
1031 <para>The name of the queue on which to reset statistics.</para>
1032 </parameter>
1033 </syntax>
1034 <description>
1035 <para>Reset the statistics for a queue.</para>
1036 </description>
1037 </manager>
1038 <manager name="QueueChangePriorityCaller" language="en_US">
1039 <synopsis>
1040 Change priority of a caller on queue.
1041 </synopsis>
1042 <syntax>
1043 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1044 <parameter name="Queue" required="true">
1045 <para>The name of the queue to take action on.</para>
1046 </parameter>
1047 <parameter name="Caller" required="true">
1048 <para>The caller (channel) to change priority on queue.</para>
1049 </parameter>
1050
1051 <parameter name="Priority" required="true">
1052 <para>Priority value for change for caller on queue.</para>
1053 </parameter>
1054 <parameter name="Immediate">
1055 <para>When set to yes will cause the priority change to be reflected immediately, causing the channel to change position within the queue.</para>
1056 </parameter>
1057 </syntax>
1058 <description>
1059 </description>
1060 </manager>
1061 <manager name="QueueWithdrawCaller" language="en_US">
1062 <synopsis>
1063 Request to withdraw a caller from the queue back to the dialplan.
1064 </synopsis>
1065 <syntax>
1066 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1067 <parameter name="Queue" required="true">
1068 <para>The name of the queue to take action on.</para>
1069 </parameter>
1070 <parameter name="Caller" required="true">
1071 <para>The caller (channel) to withdraw from the queue.</para>
1072 </parameter>
1073 <parameter name="WithdrawInfo" required="false">
1074 <para>Optional info to store. If the call is successfully withdrawn from the queue, this information will be available in the QUEUE_WITHDRAW_INFO variable.</para>
1075 </parameter>
1076 </syntax>
1077 <description>
1078 </description>
1079 </manager>
1080
1081 <managerEvent language="en_US" name="QueueParams">
1082 <managerEventInstance class="EVENT_FLAG_AGENT">
1083 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1084 <syntax>
1085 <parameter name="Max">
1086 <para>The name of the queue.</para>
1087 </parameter>
1088 <parameter name="Strategy">
1089 <para>The strategy of the queue.</para>
1090 </parameter>
1091 <parameter name="Calls">
1092 <para>The queue member's channel technology or location.</para>
1093 </parameter>
1094 <parameter name="Holdtime">
1095 <para>The queue's hold time.</para>
1096 </parameter>
1097 <parameter name="TalkTime">
1098 <para>The queue's talk time.</para>
1099 </parameter>
1100 <parameter name="Completed">
1101 <para>The queue's completion time.</para>
1102 </parameter>
1103 <parameter name="Abandoned">
1104 <para>The queue's call abandonment metric.</para>
1105 </parameter>
1106 <parameter name="ServiceLevelPerf">
1107 <para>Primary service level performance metric.</para>
1108 </parameter>
1109 <parameter name="ServiceLevelPerf2">
1110 <para>Secondary service level performance metric.</para>
1111 </parameter>
1112 </syntax>
1113 <see-also>
1114 <ref type="managerEvent">QueueMember</ref>
1115 <ref type="managerEvent">QueueEntry</ref>
1116 </see-also>
1117 </managerEventInstance>
1118 </managerEvent>
1119 <managerEvent language="en_US" name="QueueEntry">
1120 <managerEventInstance class="EVENT_FLAG_AGENT">
1121 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1122 <syntax>
1123 <parameter name="Queue">
1124 <para>The name of the queue.</para>
1125 </parameter>
1126 <parameter name="Position">
1127 <para>The caller's position within the queue.</para>
1128 </parameter>
1129 <parameter name="Channel">
1130 <para>The name of the caller's channel.</para>
1131 </parameter>
1132 <parameter name="Uniqueid">
1133 <para>The unique ID of the channel.</para>
1134 </parameter>
1135 <parameter name="CallerIDNum">
1136 <para>The Caller ID number.</para>
1137 </parameter>
1138 <parameter name="CallerIDName">
1139 <para>The Caller ID name.</para>
1140 </parameter>
1141 <parameter name="ConnectedLineNum">
1142 <para>The bridged party's number.</para>
1143 </parameter>
1144 <parameter name="ConnectedLineName">
1145 <para>The bridged party's name.</para>
1146 </parameter>
1147 <parameter name="Wait">
1148 <para>The caller's wait time.</para>
1149 </parameter>
1150 <parameter name="Priority">
1151 <para>The caller's priority within the queue.</para>
1152 </parameter>
1153 </syntax>
1154 <see-also>
1155 <ref type="managerEvent">QueueParams</ref>
1156 <ref type="managerEvent">QueueMember</ref>
1157 </see-also>
1158 </managerEventInstance>
1159 </managerEvent>
1160 <managerEvent language="en_US" name="QueueMemberStatus">
1161 <managerEventInstance class="EVENT_FLAG_AGENT">
1162 <synopsis>Raised when a Queue member's status has changed.</synopsis>
1163 <syntax>
1164 <parameter name="Queue">
1165 <para>The name of the queue.</para>
1166 </parameter>
1167 <parameter name="MemberName">
1168 <para>The name of the queue member.</para>
1169 </parameter>
1170 <parameter name="Interface">
1171 <para>The queue member's channel technology or location.</para>
1172 </parameter>
1173 <parameter name="StateInterface">
1174 <para>Channel technology or location from which to read device state changes.</para>
1175 </parameter>
1176 <parameter name="Membership">
1177 <enumlist>
1178 <enum name="dynamic"/>
1179 <enum name="realtime"/>
1180 <enum name="static"/>
1181 </enumlist>
1182 </parameter>
1183 <parameter name="Penalty">
1184 <para>The penalty associated with the queue member.</para>
1185 </parameter>
1186 <parameter name="CallsTaken">
1187 <para>The number of calls this queue member has serviced.</para>
1188 </parameter>
1189 <parameter name="LastCall">
1190 <para>The time this member last took a call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1191 </parameter>
1192 <parameter name="LastPause">
1193 <para>The time when started last paused the queue member.</para>
1194 </parameter>
1195 <parameter name="LoginTime">
1196 <para>The time this member logged in to the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1197 </parameter>
1198 <parameter name="InCall">
1199 <para>Set to 1 if member is in call. Set to 0 after LastCall time is updated.</para>
1200 <enumlist>
1201 <enum name="0"/>
1202 <enum name="1"/>
1203 </enumlist>
1204 </parameter>
1205 <parameter name="Status">
1206 <para>The numeric device state status of the queue member.</para>
1207 <enumlist>
1208 <enum name="0"><para>AST_DEVICE_UNKNOWN</para></enum>
1209 <enum name="1"><para>AST_DEVICE_NOT_INUSE</para></enum>
1210 <enum name="2"><para>AST_DEVICE_INUSE</para></enum>
1211 <enum name="3"><para>AST_DEVICE_BUSY</para></enum>
1212 <enum name="4"><para>AST_DEVICE_INVALID</para></enum>
1213 <enum name="5"><para>AST_DEVICE_UNAVAILABLE</para></enum>
1214 <enum name="6"><para>AST_DEVICE_RINGING</para></enum>
1215 <enum name="7"><para>AST_DEVICE_RINGINUSE</para></enum>
1216 <enum name="8"><para>AST_DEVICE_ONHOLD</para></enum>
1217 </enumlist>
1218 </parameter>
1219 <parameter name="Paused">
1220 <enumlist>
1221 <enum name="0"/>
1222 <enum name="1"/>
1223 </enumlist>
1224 </parameter>
1225 <parameter name="PausedReason">
1226 <para>If set when paused, the reason the queue member was paused.</para>
1227 </parameter>
1228 <parameter name="Ringinuse">
1229 <enumlist>
1230 <enum name="0"/>
1231 <enum name="1"/>
1232 </enumlist>
1233 </parameter>
1234 <parameter name="Wrapuptime">
1235 <para>The Wrapup Time of the queue member. If this value is set will override the wrapup time of queue.</para>
1236 </parameter>
1237 </syntax>
1238 </managerEventInstance>
1239 </managerEvent>
1240 <managerEvent language="en_US" name="QueueMemberAdded">
1241 <managerEventInstance class="EVENT_FLAG_AGENT">
1242 <synopsis>Raised when a member is added to the queue.</synopsis>
1243 <syntax>
1244 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1245 </syntax>
1246 <see-also>
1247 <ref type="managerEvent">QueueMemberRemoved</ref>
1248 <ref type="application">AddQueueMember</ref>
1249 </see-also>
1250 </managerEventInstance>
1251 </managerEvent>
1252 <managerEvent language="en_US" name="QueueMemberRemoved">
1253 <managerEventInstance class="EVENT_FLAG_AGENT">
1254 <synopsis>Raised when a member is removed from the queue.</synopsis>
1255 <syntax>
1256 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1257 </syntax>
1258 <see-also>
1259 <ref type="managerEvent">QueueMemberAdded</ref>
1260 <ref type="application">RemoveQueueMember</ref>
1261 </see-also>
1262 </managerEventInstance>
1263 </managerEvent>
1264 <managerEvent language="en_US" name="QueueMemberPause">
1265 <managerEventInstance class="EVENT_FLAG_AGENT">
1266 <synopsis>Raised when a member is paused/unpaused in the queue.</synopsis>
1267 <syntax>
1268 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1269 </syntax>
1270 <see-also>
1271 <ref type="application">PauseQueueMember</ref>
1272 <ref type="application">UnpauseQueueMember</ref>
1273 </see-also>
1274 </managerEventInstance>
1275 </managerEvent>
1276 <managerEvent language="en_US" name="QueueMemberPenalty">
1277 <managerEventInstance class="EVENT_FLAG_AGENT">
1278 <synopsis>Raised when a member's penalty is changed.</synopsis>
1279 <syntax>
1280 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1281 </syntax>
1282 <see-also>
1283 <ref type="function">QUEUE_MEMBER</ref>
1284 </see-also>
1285 </managerEventInstance>
1286 </managerEvent>
1287 <managerEvent language="en_US" name="QueueMemberRinginuse">
1288 <managerEventInstance class="EVENT_FLAG_AGENT">
1289 <synopsis>Raised when a member's ringinuse setting is changed.</synopsis>
1290 <syntax>
1291 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1292 </syntax>
1293 <see-also>
1294 <ref type="function">QUEUE_MEMBER</ref>
1295 </see-also>
1296 </managerEventInstance>
1297 </managerEvent>
1298 <managerEvent language="en_US" name="QueueCallerJoin">
1299 <managerEventInstance class="EVENT_FLAG_AGENT">
1300 <synopsis>Raised when a caller joins a Queue.</synopsis>
1301 <syntax>
1302 <channel_snapshot/>
1303 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1304 <parameter name="Position">
1305 <para>This channel's current position in the queue.</para>
1306 </parameter>
1307 <parameter name="Count">
1308 <para>The total number of channels in the queue.</para>
1309 </parameter>
1310 </syntax>
1311 <see-also>
1312 <ref type="managerEvent">QueueCallerLeave</ref>
1313 <ref type="application">Queue</ref>
1314 </see-also>
1315 </managerEventInstance>
1316 </managerEvent>
1317 <managerEvent language="en_US" name="QueueCallerLeave">
1318 <managerEventInstance class="EVENT_FLAG_AGENT">
1319 <synopsis>Raised when a caller leaves a Queue.</synopsis>
1320 <syntax>
1321 <channel_snapshot/>
1322 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1323 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Count'])" />
1324 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1325 </syntax>
1326 <see-also>
1327 <ref type="managerEvent">QueueCallerJoin</ref>
1328 </see-also>
1329 </managerEventInstance>
1330 </managerEvent>
1331 <managerEvent language="en_US" name="QueueCallerAbandon">
1332 <managerEventInstance class="EVENT_FLAG_AGENT">
1333 <synopsis>Raised when a caller abandons the queue.</synopsis>
1334 <syntax>
1335 <channel_snapshot/>
1336 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1337 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1338 <parameter name="OriginalPosition">
1339 <para>The channel's original position in the queue.</para>
1340 </parameter>
1341 <parameter name="HoldTime">
1342 <para>The time the channel was in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1343 </parameter>
1344 </syntax>
1345 </managerEventInstance>
1346 </managerEvent>
1347 <managerEvent language="en_US" name="AgentCalled">
1348 <managerEventInstance class="EVENT_FLAG_AGENT">
1349 <synopsis>Raised when an queue member is notified of a caller in the queue.</synopsis>
1350 <syntax>
1351 <channel_snapshot/>
1352 <channel_snapshot prefix="Dest"/>
1353 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1354 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1355 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1356 </syntax>
1357 <see-also>
1358 <ref type="managerEvent">AgentRingNoAnswer</ref>
1359 <ref type="managerEvent">AgentComplete</ref>
1360 <ref type="managerEvent">AgentConnect</ref>
1361 </see-also>
1362 </managerEventInstance>
1363 </managerEvent>
1364 <managerEvent language="en_US" name="AgentRingNoAnswer">
1365 <managerEventInstance class="EVENT_FLAG_AGENT">
1366 <synopsis>Raised when a queue member is notified of a caller in the queue and fails to answer.</synopsis>
1367 <syntax>
1368 <channel_snapshot/>
1369 <channel_snapshot prefix="Dest"/>
1370 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1371 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1372 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1373 <parameter name="RingTime">
1374 <para>The time the queue member was rung, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1375 </parameter>
1376 </syntax>
1377 <see-also>
1378 <ref type="managerEvent">AgentCalled</ref>
1379 </see-also>
1380 </managerEventInstance>
1381 </managerEvent>
1382 <managerEvent language="en_US" name="AgentComplete">
1383 <managerEventInstance class="EVENT_FLAG_AGENT">
1384 <synopsis>Raised when a queue member has finished servicing a caller in the queue.</synopsis>
1385 <syntax>
1386 <channel_snapshot/>
1387 <channel_snapshot prefix="Dest"/>
1388 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1389 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1390 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1391 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1392 <parameter name="TalkTime">
1393 <para>The time the queue member talked with the caller in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1394 </parameter>
1395 <parameter name="Reason">
1396 <enumlist>
1397 <enum name="caller"/>
1398 <enum name="agent"/>
1399 <enum name="transfer"/>
1400 </enumlist>
1401 </parameter>
1402 </syntax>
1403 <see-also>
1404 <ref type="managerEvent">AgentCalled</ref>
1405 <ref type="managerEvent">AgentConnect</ref>
1406 </see-also>
1407 </managerEventInstance>
1408 </managerEvent>
1409 <managerEvent language="en_US" name="AgentDump">
1410 <managerEventInstance class="EVENT_FLAG_AGENT">
1411 <synopsis>Raised when a queue member hangs up on a caller in the queue.</synopsis>
1412 <syntax>
1413 <channel_snapshot/>
1414 <channel_snapshot prefix="Dest"/>
1415 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1416 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1417 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1418 </syntax>
1419 <see-also>
1420 <ref type="managerEvent">AgentCalled</ref>
1421 <ref type="managerEvent">AgentConnect</ref>
1422 </see-also>
1423 </managerEventInstance>
1424 </managerEvent>
1425 <managerEvent language="en_US" name="AgentConnect">
1426 <managerEventInstance class="EVENT_FLAG_AGENT">
1427 <synopsis>Raised when a queue member answers and is bridged to a caller in the queue.</synopsis>
1428 <syntax>
1429 <channel_snapshot/>
1430 <channel_snapshot prefix="Dest"/>
1431 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1432 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1433 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1434 <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='RingTime'])" />
1435 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1436 </syntax>
1437 <see-also>
1438 <ref type="managerEvent">AgentCalled</ref>
1439 <ref type="managerEvent">AgentComplete</ref>
1440 <ref type="managerEvent">AgentDump</ref>
1441 </see-also>
1442 </managerEventInstance>
1443 </managerEvent>
1444 ***/
1445
1446enum {
1448 OPT_GO_ON = (1 << 1),
1457 OPT_NO_RETRY = (1 << 10),
1458 OPT_RINGING = (1 << 11),
1469};
1470
1471enum {
1476 /* note: this entry _MUST_ be the last one in the enum */
1479
1504
1505enum {
1514};
1515
1516enum {
1521
1527};
1528
1529static const struct strategy {
1531 const char *name;
1532} strategies[] = {
1533 { QUEUE_STRATEGY_RINGALL, "ringall" },
1534 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
1535 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
1536 { QUEUE_STRATEGY_RANDOM, "random" },
1537 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
1538 { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
1539 { QUEUE_STRATEGY_LINEAR, "linear" },
1540 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
1541 { QUEUE_STRATEGY_RRORDERED, "rrordered"},
1543
1544static const struct autopause {
1546 const char *name;
1547} autopausesmodes [] = {
1548 { QUEUE_AUTOPAUSE_OFF,"no" },
1549 { QUEUE_AUTOPAUSE_ON, "yes" },
1550 { QUEUE_AUTOPAUSE_ALL,"all" },
1552
1553#define DEFAULT_RETRY 5
1554#define DEFAULT_TIMEOUT 15
1555#define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
1556#define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
1557/*!
1558 * \brief The minimum number of seconds between position announcements.
1559 * \note The default value of 15 provides backwards compatibility.
1560 */
1561#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
1562
1563#define MAX_QUEUE_BUCKETS 53
1564
1565#define RES_OKAY 0 /*!< Action completed */
1566#define RES_EXISTS (-1) /*!< Entry already exists */
1567#define RES_OUTOFMEMORY (-2) /*!< Out of memory */
1568#define RES_NOSUCHQUEUE (-3) /*!< No such queue */
1569#define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
1570#define RES_NOT_CALLER (-5) /*!< Caller not found */
1571
1572static char *app = "Queue";
1573
1574static char *app_aqm = "AddQueueMember" ;
1575
1576static char *app_rqm = "RemoveQueueMember" ;
1577
1578static char *app_pqm = "PauseQueueMember" ;
1579
1580static char *app_upqm = "UnpauseQueueMember" ;
1581
1582static char *app_ql = "QueueLog" ;
1583
1584static char *app_qupd = "QueueUpdate";
1585
1586/*! \brief Persistent Members astdb family */
1587static const char * const pm_family = "Queue/PersistentMembers";
1588
1589/*! \brief queues.conf [general] option */
1591
1592/*! \brief Records that one or more queues use weight */
1593static int use_weight;
1594
1595/*! \brief queues.conf [general] option */
1597
1598/*! \brief queues.conf [general] option */
1600
1601/*! \brief queues.conf [general] option */
1603
1604/*! \brief queuerules.conf [general] option */
1606
1607/*! \brief Subscription to device state change messages */
1609
1610/*! \brief queues.conf [general] option */
1612
1613/*! \brief queues.conf [general] option */
1615
1616/*! \brief queues.conf [general] option */
1618
1619/*! \brief name of the ringinuse field in the realtime database */
1621
1622/*! \brief does realtime backend support reason_paused */
1624
1635};
1636
1637static const struct {
1639 char *text;
1640} queue_results[] = {
1641 { QUEUE_UNKNOWN, "UNKNOWN" },
1642 { QUEUE_TIMEOUT, "TIMEOUT" },
1643 { QUEUE_JOINEMPTY,"JOINEMPTY" },
1644 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
1645 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
1646 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
1647 { QUEUE_FULL, "FULL" },
1648 { QUEUE_CONTINUE, "CONTINUE" },
1649 { QUEUE_WITHDRAW, "WITHDRAW" },
1651
1655};
1656
1657/*! \brief We define a custom "local user" structure because we
1658 * use it not only for keeping track of what is in use but
1659 * also for keeping track of who we're dialing.
1660 *
1661 * There are two "links" defined in this structure, q_next and call_next.
1662 * q_next links ALL defined callattempt structures into a linked list. call_next is
1663 * a link which allows for a subset of the callattempts to be traversed. This subset
1664 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
1665 * also is helpful so that queue logs are always accurate in the case where a call to
1666 * a member times out, especially if using the ringall strategy.
1667*/
1668
1673 char interface[256]; /*!< An Asterisk dial string (not a channel name) */
1676 /*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
1678 /*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
1680 /*! TRUE if the connected line update is blocked. */
1682 /*! TRUE if caller id is not available for connected line */
1683 unsigned int dial_callerid_absent:1;
1684 /*! TRUE if the call is still active */
1685 unsigned int stillgoing:1;
1687 /*! Original channel name. Must be freed. Could be NULL if allocation failed. */
1689};
1690
1691
1693 struct call_queue *parent; /*!< What queue is our parent */
1694 char moh[MAX_MUSICCLASS]; /*!< Name of musiconhold to be used */
1695 char announce[PATH_MAX]; /*!< Announcement to play for member when call is answered */
1696 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
1697 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
1698 const char *predial_callee; /*!< Gosub app arguments for outgoing calls. NULL if not supplied. */
1699 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
1700 int pos; /*!< Where we are in the queue */
1701 int prio; /*!< Our priority */
1702 int last_pos_said; /*!< Last position we told the user */
1703 int ring_when_ringing; /*!< Should we only use ring indication when a channel is ringing? */
1704 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
1705 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
1706 time_t last_pos; /*!< Last time we told the user their position */
1707 int opos; /*!< Where we started in the queue */
1708 int handled; /*!< Whether our call was handled */
1709 int pending; /*!< Non-zero if we are attempting to call a member */
1710 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
1711 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
1712 int raise_penalty; /*!< Float lower penalty members to a minimum penalty */
1713 int linpos; /*!< If using linear strategy, what position are we at? */
1714 int linwrapped; /*!< Is the linpos wrapped? */
1715 time_t start; /*!< When we started holding */
1716 time_t expire; /*!< When this entry should expire (time out of queue) */
1717 int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
1718 unsigned int withdraw:1; /*!< Should this call exit the queue at its next iteration? Used for QueueWithdrawCaller */
1719 char *withdraw_info; /*!< Optional info passed by the caller of QueueWithdrawCaller */
1720 struct ast_channel *chan; /*!< Our channel */
1721 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
1722 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
1723 struct queue_ent *next; /*!< The next queue entry */
1724};
1725
1726struct member {
1727 char interface[AST_CHANNEL_NAME]; /*!< Technology/Location to dial to reach this member*/
1728 char state_exten[AST_MAX_EXTENSION]; /*!< Extension to get state from (if using hint) */
1729 char state_context[AST_MAX_CONTEXT]; /*!< Context to use when getting state (if using hint) */
1730 char state_interface[AST_CHANNEL_NAME]; /*!< Technology/Location from which to read devicestate changes */
1731 int state_id; /*!< Extension state callback id (if using hint) */
1732 char membername[80]; /*!< Member name to use in queue logs */
1733 int penalty; /*!< Are we a last resort? */
1734 int calls; /*!< Number of calls serviced by this member */
1735 int dynamic; /*!< Are we dynamically added? */
1736 int realtime; /*!< Is this member realtime? */
1737 int status; /*!< Status of queue member */
1738 int paused; /*!< Are we paused (not accepting calls)? */
1739 char reason_paused[80]; /*!< Reason of paused if member is paused */
1740 int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
1741 int callcompletedinsl; /*!< Whether the current call was completed within service level */
1742 int wrapuptime; /*!< Wrapup Time */
1743 time_t starttime; /*!< The time at which the member answered the current caller. */
1744 time_t lastcall; /*!< When last successful call was hungup */
1745 time_t lastpause; /*!< When started the last pause */
1746 time_t logintime; /*!< The time when started the login */
1747 struct call_queue *lastqueue; /*!< Last queue we received a call */
1748 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
1749 unsigned int delme:1; /*!< Flag to delete entry on reload */
1750 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
1751 unsigned int ringinuse:1; /*!< Flag to ring queue members even if their status is 'inuse' */
1752};
1753
1763};
1764
1768};
1769
1770/* values used in multi-bit flags in call_queue */
1771#define ANNOUNCEHOLDTIME_ALWAYS 1
1772#define ANNOUNCEHOLDTIME_ONCE 2
1773#define QUEUE_EVENT_VARIABLES 3
1774
1776 int time; /*!< Number of seconds that need to pass before applying this rule */
1777 int max_value; /*!< The amount specified in the penalty rule for max penalty */
1778 int min_value; /*!< The amount specified in the penalty rule for min penalty */
1779 int raise_value; /*!< The amount specified in the penalty rule for min penalty */
1780 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
1781 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1782 int raise_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1783 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
1784};
1785
1786#define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
1787#define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
1788#define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
1789#define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
1790
1793 /*! Queue name */
1795 /*! Music on Hold class */
1797 /*! Announcement to play when call is answered */
1799 /*! Exit context */
1801 /*! Gosub to run upon member connection */
1803 /*! Default rule to use if none specified in call to Queue() */
1805 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
1807 /*! Sound file: "There are currently" (def. queue-thereare) */
1809 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
1811 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
1813 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
1815 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
1817 /*! Sound file: "minutes." (def. queue-minutes) */
1819 /*! Sound file: "minute." (def. queue-minute) */
1821 /*! Sound file: "seconds." (def. queue-seconds) */
1823 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
1825 /*! Sound file: Custom announce for caller, no default */
1827 /*! Sound file: "Hold time" (def. queue-reporthold) */
1830 /*! Sound files: Custom announce, no default */
1832 unsigned int dead:1;
1833 unsigned int ringinuse:1;
1834 unsigned int announce_to_first_user:1; /*!< Whether or not we announce to the first user in a queue */
1835 unsigned int setinterfacevar:1;
1836 unsigned int setqueuevar:1;
1837 unsigned int setqueueentryvar:1;
1838 unsigned int reportholdtime:1;
1839 unsigned int wrapped:1;
1840 unsigned int timeoutrestart:1;
1841 unsigned int announceholdtime:2;
1842 unsigned int announceposition:3;
1843 unsigned int announceposition_only_up:1; /*!< Only announce position if it has improved */
1845 unsigned int realtime:1;
1846 unsigned int found:1;
1848 unsigned int autopausebusy:1;
1849 unsigned int autopauseunavail:1;
1852 int announcepositionlimit; /*!< How many positions we announce? */
1853 int announcefrequency; /*!< How often to announce their position */
1854 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
1855 int periodicannouncestartdelay; /*!< How long into the queue should the periodic accouncement start */
1856 int periodicannouncefrequency; /*!< How often to play periodic announcement */
1857 int numperiodicannounce; /*!< The number of periodic announcements configured */
1858 int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
1859 int roundingseconds; /*!< How many seconds do we round to? */
1860 int holdtime; /*!< Current avg holdtime, based on an exponential average */
1861 int talktime; /*!< Current avg talktime, based on the same exponential average */
1862 int callscompleted; /*!< Number of queue calls completed */
1863 int callsabandoned; /*!< Number of queue calls abandoned */
1864 int callsabandonedinsl; /*!< Number of queue calls abandoned in servicelevel */
1865 int servicelevel; /*!< seconds setting for servicelevel*/
1866 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
1867 char monfmt[8]; /*!< Format to use when recording calls */
1868 int count; /*!< How many entries */
1869 int maxlen; /*!< Max number of entries */
1870 int wrapuptime; /*!< Wrapup Time */
1871 int penaltymemberslimit; /*!< Disregard penalty when queue has fewer than this many members */
1872
1873 int retry; /*!< Retry calling everyone after this amount of time */
1874 int timeout; /*!< How long to wait for an answer */
1875 int weight; /*!< Respective weight */
1876 int autopause; /*!< Auto pause queue members if they fail to answer */
1877 int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
1878 int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
1879
1880 /* Queue strategy things */
1881 int rrpos; /*!< Round Robin - position */
1882 int memberdelay; /*!< Seconds to delay connecting member to caller */
1883 int autofill; /*!< Ignore the head call status and ring an available agent */
1884
1885 int log_restricted_caller_id:1; /*!< Whether log Restricted Caller ID */
1886
1887 struct ao2_container *members; /*!< Head of the list of members */
1888 struct queue_ent *head; /*!< Head of the list of callers */
1889 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
1890 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
1891};
1892
1894 char name[80];
1897};
1898
1900
1901static struct ao2_container *queues;
1902
1903static void update_realtime_members(struct call_queue *q);
1904static struct member *interface_exists(struct call_queue *q, const char *interface);
1905static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
1906static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime);
1907
1908static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface);
1909/*! \brief sets the QUEUESTATUS channel variable */
1910static void set_queue_result(struct ast_channel *chan, enum queue_result res)
1911{
1912 int i;
1913
1914 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
1915 if (queue_results[i].id == res) {
1916 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
1917 return;
1918 }
1919 }
1920}
1921
1922static const char *int2strat(int strategy)
1923{
1924 int x;
1925
1926 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1927 if (strategy == strategies[x].strategy) {
1928 return strategies[x].name;
1929 }
1930 }
1931
1932 return "<unknown>";
1933}
1934
1935static int strat2int(const char *strategy)
1936{
1937 int x;
1938
1939 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1940 if (!strcasecmp(strategy, strategies[x].name)) {
1941 return strategies[x].strategy;
1942 }
1943 }
1944
1945 return -1;
1946}
1947
1948static int autopause2int(const char *autopause)
1949{
1950 int x;
1951 /*This 'double check' that default value is OFF */
1953 return QUEUE_AUTOPAUSE_OFF;
1954 }
1955
1956 /*This 'double check' is to ensure old values works */
1957 if(ast_true(autopause)) {
1958 return QUEUE_AUTOPAUSE_ON;
1959 }
1960
1961 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
1962 if (!strcasecmp(autopause, autopausesmodes[x].name)) {
1963 return autopausesmodes[x].autopause;
1964 }
1965 }
1966
1967 /*This 'double check' that default value is OFF */
1968 return QUEUE_AUTOPAUSE_OFF;
1969}
1970
1971static int queue_hash_cb(const void *obj, const int flags)
1972{
1973 const struct call_queue *q = obj;
1974
1975 return ast_str_case_hash(q->name);
1976}
1977
1978static int queue_cmp_cb(void *obj, void *arg, int flags)
1979{
1980 struct call_queue *q = obj, *q2 = arg;
1981 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
1982}
1983
1984/*!
1985 * \brief Return wrapuptime
1986 *
1987 * This function checks if wrapuptime in member is set and return this value.
1988 * Otherwise return value the wrapuptime in the queue configuration
1989 * \return integer value
1990 */
1991static int get_wrapuptime(struct call_queue *q, struct member *member)
1992{
1993 if (member->wrapuptime) {
1994 return member->wrapuptime;
1995 }
1996 return q->wrapuptime;
1997}
1998
1999/*! \internal
2000 * \brief ao2_callback, Decreases queuepos of all followers with a queuepos greater than arg.
2001 * \param obj the member being acted on
2002 * \param arg pointer to an integer containing the position value that was removed and requires reduction for anything above
2003 * \param flag unused
2004 */
2005static int queue_member_decrement_followers(void *obj, void *arg, int flag)
2006{
2007 struct member *mem = obj;
2008 int *decrement_followers_after = arg;
2009
2010 if (mem->queuepos > *decrement_followers_after) {
2011 mem->queuepos--;
2012 }
2013
2014 return 0;
2015}
2016
2017/*! \internal
2018 * \brief ao2_callback, finds members in a queue marked for deletion and in a cascading fashion runs queue_member_decrement_followers
2019 * on them. This callback should always be ran before performing mass unlinking of delmarked members from queues.
2020 * \param obj member being acted on
2021 * \param arg pointer to the queue members are being removed from
2022 * \param flag unused
2023 */
2024static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
2025{
2026 struct member *mem = obj;
2027 struct call_queue *queue = arg;
2028 int rrpos = mem->queuepos;
2029
2030 if (mem->delme) {
2032 }
2033
2034 return 0;
2035}
2036
2037/*! \internal
2038 * \brief Use this to decrement followers during removal of a member
2039 * \param queue which queue the member is being removed from
2040 * \param mem which member is being removed from the queue
2041 */
2042static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
2043{
2044 int pos = mem->queuepos;
2045
2046 /* If the position being removed is less than the current place in the queue, reduce the queue position by one so that we don't skip the member
2047 * who would have been next otherwise. */
2048 if (pos < queue->rrpos) {
2049 queue->rrpos--;
2050 }
2051
2053}
2054
2055#define queue_ref(q) ao2_bump(q)
2056#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2057#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2058#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2059#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2060#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2061
2062/*! \brief Set variables of queue */
2063static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
2064{
2065 char interfacevar[256]="";
2066 float sl = 0;
2067
2068 ao2_lock(q);
2069
2070 if (q->setqueuevar) {
2071 sl = 0;
2072 if (q->callscompleted > 0) {
2073 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
2074 }
2075
2076 snprintf(interfacevar, sizeof(interfacevar),
2077 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2079
2080 ao2_unlock(q);
2081
2082 pbx_builtin_setvar_multiple(chan, interfacevar);
2083 } else {
2084 ao2_unlock(q);
2085 }
2086}
2087
2088/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
2089static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
2090{
2091 struct queue_ent *cur;
2092
2093 if (!q || !new)
2094 return;
2095 if (prev) {
2096 cur = prev->next;
2097 prev->next = new;
2098 } else {
2099 cur = q->head;
2100 q->head = new;
2101 }
2102 new->next = cur;
2103
2104 /* every queue_ent must have a reference to it's parent call_queue, this
2105 * reference does not go away until the end of the queue_ent's life, meaning
2106 * that even when the queue_ent leaves the call_queue this ref must remain. */
2107 if (!new->parent) {
2108 queue_ref(q);
2109 new->parent = q;
2110 }
2111 new->pos = ++(*pos);
2112 new->opos = *pos;
2113}
2114
2116{
2118 RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
2119 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2120
2121 channel_string = ast_manager_build_channel_state_string(obj->snapshot);
2122 event_string = ast_manager_str_from_json_object(obj->blob, NULL);
2123 if (!channel_string || !event_string) {
2124 return NULL;
2125 }
2126
2128 "%s"
2129 "%s",
2130 ast_str_buffer(channel_string),
2131 ast_str_buffer(event_string));
2132}
2133
2135{
2136 return queue_channel_to_ami("QueueCallerJoin", message);
2137}
2138
2140{
2141 return queue_channel_to_ami("QueueCallerLeave", message);
2142}
2143
2145{
2146 return queue_channel_to_ami("QueueCallerAbandon", message);
2147}
2148
2149STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_join_type,
2151 );
2152STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_leave_type,
2154 );
2155STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_abandon_type,
2157 );
2158
2160{
2161 struct ast_json_payload *payload = stasis_message_data(message);
2162 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2163
2164 event_string = ast_manager_str_from_json_object(payload->json, NULL);
2165 if (!event_string) {
2166 return NULL;
2167 }
2168
2170 "%s",
2171 ast_str_buffer(event_string));
2172}
2173
2175{
2176 return queue_member_to_ami("QueueMemberStatus", message);
2177}
2178
2180{
2181 return queue_member_to_ami("QueueMemberAdded", message);
2182}
2183
2185{
2186 return queue_member_to_ami("QueueMemberRemoved", message);
2187}
2188
2190{
2191 return queue_member_to_ami("QueueMemberPause", message);
2192}
2193
2195{
2196 return queue_member_to_ami("QueueMemberPenalty", message);
2197}
2198
2200{
2201 return queue_member_to_ami("QueueMemberRinginuse", message);
2202}
2203
2204STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_status_type,
2206 );
2207STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_added_type,
2209 );
2210STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_removed_type,
2212 );
2213STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_pause_type,
2215 );
2216STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_penalty_type,
2218 );
2219STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_ringinuse_type,
2221 );
2222
2224{
2227 struct ast_channel_snapshot *agent;
2228 RAII_VAR(struct ast_str *, caller_event_string, NULL, ast_free);
2229 RAII_VAR(struct ast_str *, agent_event_string, NULL, ast_free);
2230 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2231
2233 if (caller) {
2234 caller_event_string = ast_manager_build_channel_state_string(caller);
2235 if (!caller_event_string) {
2236 ast_log(LOG_NOTICE, "No caller event string, bailing\n");
2237 return NULL;
2238 }
2239 }
2240
2241 agent = ast_multi_channel_blob_get_channel(obj, "agent");
2242 if (agent) {
2243 agent_event_string = ast_manager_build_channel_state_string_prefix(agent, "Dest");
2244 if (!agent_event_string) {
2245 ast_log(LOG_NOTICE, "No agent event string, bailing\n");
2246 return NULL;
2247 }
2248 }
2249
2251 if (!event_string) {
2252 return NULL;
2253 }
2254
2256 "%s"
2257 "%s"
2258 "%s",
2259 caller_event_string ? ast_str_buffer(caller_event_string) : "",
2260 agent_event_string ? ast_str_buffer(agent_event_string) : "",
2261 ast_str_buffer(event_string));
2262}
2263
2265{
2266 return queue_multi_channel_to_ami("AgentCalled", message);
2267}
2268
2270{
2271 return queue_multi_channel_to_ami("AgentConnect", message);
2272}
2273
2275{
2276 return queue_multi_channel_to_ami("AgentComplete", message);
2277}
2278
2280{
2281 return queue_multi_channel_to_ami("AgentDump", message);
2282}
2283
2285{
2286 return queue_multi_channel_to_ami("AgentRingNoAnswer", message);
2287}
2288
2289STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_called_type,
2291 );
2292STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_connect_type,
2294 );
2295STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_complete_type,
2297 );
2300 );
2301STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_ringnoanswer_type,
2303 );
2304
2306 struct ast_channel_snapshot *caller_snapshot,
2307 struct ast_channel_snapshot *agent_snapshot,
2308 struct stasis_message_type *type, struct ast_json *blob)
2309{
2310 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
2311 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2312
2313 if (!type) {
2314 return;
2315 }
2316
2317 payload = ast_multi_channel_blob_create(blob);
2318 if (!payload) {
2319 return;
2320 }
2321
2322 if (caller_snapshot) {
2323 ast_multi_channel_blob_add_channel(payload, "caller", caller_snapshot);
2324 } else {
2325 ast_debug(1, "Empty caller_snapshot; sending incomplete event\n");
2326 }
2327
2328 if (agent_snapshot) {
2329 ast_multi_channel_blob_add_channel(payload, "agent", agent_snapshot);
2330 }
2331
2332 msg = stasis_message_create(type, payload);
2333 if (!msg) {
2334 return;
2335 }
2336
2337 stasis_publish(topic, msg);
2338}
2339
2340static void queue_publish_multi_channel_blob(struct ast_channel *caller, struct ast_channel *agent,
2341 struct stasis_message_type *type, struct ast_json *blob)
2342{
2343 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
2344 RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
2345
2346 ast_channel_lock(caller);
2347 caller_snapshot = ast_channel_snapshot_create(caller);
2348 ast_channel_unlock(caller);
2349 ast_channel_lock(agent);
2350 agent_snapshot = ast_channel_snapshot_create(agent);
2351 ast_channel_unlock(agent);
2352
2353 if (!caller_snapshot || !agent_snapshot) {
2354 return;
2355 }
2356
2358 agent_snapshot, type, blob);
2359}
2360
2361/*!
2362 * \internal
2363 * \brief Publish the member blob.
2364 * \since 12.0.0
2365 *
2366 * \param type Stasis message type to publish.
2367 * \param blob The information being published.
2368 *
2369 * \note The json blob reference is passed to this function.
2370 */
2372{
2373 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
2374 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2375
2376 if (!blob || !type) {
2377 ast_json_unref(blob);
2378 return;
2379 }
2380
2381 payload = ast_json_payload_create(blob);
2382 ast_json_unref(blob);
2383 if (!payload) {
2384 return;
2385 }
2386
2387 msg = stasis_message_create(type, payload);
2388 if (!msg) {
2389 return;
2390 }
2391
2393}
2394
2395static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
2396{
2397 return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: i, s: i, s: i, s: s, s: i, s: i}",
2398 "Queue", q->name,
2399 "MemberName", mem->membername,
2400 "Interface", mem->interface,
2401 "StateInterface", mem->state_interface,
2402 "Membership", (mem->dynamic ? "dynamic" : (mem->realtime ? "realtime" : "static")),
2403 "Penalty", mem->penalty,
2404 "CallsTaken", mem->calls,
2405 "LastCall", (int)mem->lastcall,
2406 "LastPause", (int)mem->lastpause,
2407 "LoginTime", (int)mem->logintime,
2408 "InCall", mem->starttime ? 1 : 0,
2409 "Status", mem->status,
2410 "Paused", mem->paused,
2411 "PausedReason", mem->reason_paused,
2412 "Ringinuse", mem->ringinuse,
2413 "Wrapuptime", mem->wrapuptime);
2414}
2415
2416/*! \brief Check if members are available
2417 *
2418 * This function checks to see if members are available to be called. If any member
2419 * is available, the function immediately returns 0. If no members are available,
2420 * then -1 is returned.
2421 */
2422static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
2423{
2424 struct member *member;
2425 struct ao2_iterator mem_iter;
2426
2427 ao2_lock(q);
2428 mem_iter = ao2_iterator_init(q->members, 0);
2429 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
2430 int penalty = member->penalty;
2431 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2432 ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
2433 penalty = raise_penalty;
2434 }
2435 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2436 if (conditions & QUEUE_EMPTY_PENALTY) {
2437 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
2438 continue;
2439 }
2440 }
2441
2442 switch (devstate ? ast_device_state(member->state_interface) : member->status) {
2443 case AST_DEVICE_INVALID:
2444 if (conditions & QUEUE_EMPTY_INVALID) {
2445 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
2446 break;
2447 }
2448 goto default_case;
2450 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2451 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
2452 break;
2453 }
2454 goto default_case;
2455 case AST_DEVICE_INUSE:
2456 if (conditions & QUEUE_EMPTY_INUSE) {
2457 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
2458 break;
2459 }
2460 goto default_case;
2461 case AST_DEVICE_RINGING:
2462 if (conditions & QUEUE_EMPTY_RINGING) {
2463 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
2464 break;
2465 }
2466 goto default_case;
2467 case AST_DEVICE_UNKNOWN:
2468 if (conditions & QUEUE_EMPTY_UNKNOWN) {
2469 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
2470 break;
2471 }
2472 /* Fall-through */
2473 default:
2474 default_case:
2475 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2476 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
2477 break;
2478 } else if ((conditions & QUEUE_EMPTY_WRAPUP)
2479 && member->lastcall
2480 && get_wrapuptime(q, member)
2481 && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
2482 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2483 member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
2484 break;
2485 } else {
2486 ao2_ref(member, -1);
2487 ao2_iterator_destroy(&mem_iter);
2488 ao2_unlock(q);
2489 ast_debug(4, "%s is available.\n", member->membername);
2490 return 0;
2491 }
2492 break;
2493 }
2494 }
2495 ao2_iterator_destroy(&mem_iter);
2496 ao2_unlock(q);
2497
2498 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2499 /* member state still may be RINGING due to lag in event message - check again with device state */
2500 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2501 }
2502 return -1;
2503}
2504
2505/*
2506 * A "pool" of member objects that calls are currently pending on. If an
2507 * agent is a member of multiple queues it's possible for that agent to be
2508 * called by each of the queues at the same time. This happens because device
2509 * state is slow to notify the queue app of one of it's member's being rung.
2510 * This "pool" allows us to track which members are currently being rung while
2511 * we wait on the device state change.
2512 */
2514#define MAX_CALL_ATTEMPT_BUCKETS 353
2515
2516static int pending_members_hash(const void *obj, const int flags)
2517{
2518 const struct member *object;
2519 const char *key;
2520
2521 switch (flags & OBJ_SEARCH_MASK) {
2522 case OBJ_SEARCH_KEY:
2523 key = obj;
2524 break;
2525 case OBJ_SEARCH_OBJECT:
2526 object = obj;
2527 key = object->interface;
2528 break;
2529 default:
2530 ast_assert(0);
2531 return 0;
2532 }
2533 return ast_str_case_hash(key);
2534}
2535
2536static int pending_members_cmp(void *obj, void *arg, int flags)
2537{
2538 const struct member *object_left = obj;
2539 const struct member *object_right = arg;
2540 const char *right_key = arg;
2541 int cmp;
2542
2543 switch (flags & OBJ_SEARCH_MASK) {
2544 case OBJ_SEARCH_OBJECT:
2545 right_key = object_right->interface;
2546 /* Fall through */
2547 case OBJ_SEARCH_KEY:
2548 cmp = strcasecmp(object_left->interface, right_key);
2549 break;
2551 /* Not supported by container. */
2552 ast_assert(0);
2553 return 0;
2554 default:
2555 cmp = 0;
2556 break;
2557 }
2558 if (cmp) {
2559 return 0;
2560 }
2561 return CMP_MATCH;
2562}
2563
2564static void pending_members_remove(struct member *mem)
2565{
2566 ast_debug(3, "Removed %s from pending_members\n", mem->membername);
2568}
2569
2570/*! \brief set a member's status based on device state of that member's state_interface.
2571 *
2572 * Lock interface list find sc, iterate through each queues queue_member list for member to
2573 * update state inside queues
2574*/
2575static void update_status(struct call_queue *q, struct member *m, const int status)
2576{
2577 if (m->status != status) {
2578 /* If this member has transitioned to being available then update their queue
2579 * information. If they are currently in a call then the leg to the agent will be
2580 * considered done and the call finished.
2581 */
2584 }
2585
2586 m->status = status;
2587
2588 /* Remove the member from the pending members pool only when the status changes.
2589 * This is not done unconditionally because we can occasionally see multiple
2590 * device state notifications of not in use after a previous call has ended,
2591 * including after we have initiated a new call. This is more likely to
2592 * happen when there is latency in the connection to the member.
2593 */
2595
2596 queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2597 }
2598}
2599
2600/*!
2601 * \internal
2602 * \brief Determine if a queue member is available
2603 * \retval 1 if the member is available
2604 * \retval 0 if the member is not available
2605 */
2606static int is_member_available(struct call_queue *q, struct member *mem)
2607{
2608 int available = 0;
2609 int wrapuptime;
2610
2611 switch (mem->status) {
2612 case AST_DEVICE_INVALID:
2614 break;
2615 case AST_DEVICE_INUSE:
2616 case AST_DEVICE_BUSY:
2617 case AST_DEVICE_RINGING:
2619 case AST_DEVICE_ONHOLD:
2620 if (!mem->ringinuse) {
2621 break;
2622 }
2623 /* else fall through */
2625 case AST_DEVICE_UNKNOWN:
2626 if (!mem->paused) {
2627 available = 1;
2628 }
2629 break;
2630 }
2631
2632 /* Let wrapuptimes override device state availability */
2633 wrapuptime = get_wrapuptime(q, mem);
2634 if (mem->lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->lastcall)) {
2635 available = 0;
2636 }
2637 return available;
2638}
2639
2640/*! \brief set a member's status based on device state of that member's interface*/
2641static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
2642{
2643 struct ao2_iterator miter, qiter;
2644 struct ast_device_state_message *dev_state;
2645 struct member *m;
2646 struct call_queue *q;
2647 char interface[80], *slash_pos;
2648 int found = 0; /* Found this member in any queue */
2649 int found_member; /* Found this member in this queue */
2650 int avail = 0; /* Found an available member in this queue */
2651
2653 return;
2654 }
2655
2656 dev_state = stasis_message_data(msg);
2657 if (dev_state->eid) {
2658 /* ignore non-aggregate states */
2659 return;
2660 }
2661
2662 qiter = ao2_iterator_init(queues, 0);
2663 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
2664 ao2_lock(q);
2665
2666 avail = 0;
2667 found_member = 0;
2668 miter = ao2_iterator_init(q->members, 0);
2669 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2670 if (!found_member) {
2671 ast_copy_string(interface, m->state_interface, sizeof(interface));
2672
2673 if ((slash_pos = strchr(interface, '/'))) {
2674 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) {
2675 *slash_pos = '\0';
2676 }
2677 }
2678
2679 if (!strcasecmp(interface, dev_state->device)) {
2680 found_member = 1;
2681 update_status(q, m, dev_state->state);
2682 }
2683 }
2684
2685 /* check every member until we find one NOT_INUSE */
2686 if (!avail) {
2687 avail = is_member_available(q, m);
2688 }
2689 if (avail && found_member) {
2690 /* early exit as we've found an available member and the member of interest */
2691 ao2_ref(m, -1);
2692 break;
2693 }
2694 }
2695
2696 if (found_member) {
2697 found = 1;
2698 if (avail) {
2700 } else {
2702 }
2703 }
2704
2705 ao2_iterator_destroy(&miter);
2706
2707 ao2_unlock(q);
2708 queue_t_unref(q, "Done with iterator");
2709 }
2710 ao2_iterator_destroy(&qiter);
2711
2712 if (found) {
2713 ast_debug(1, "Device '%s' changed to state '%u' (%s)\n",
2714 dev_state->device,
2715 dev_state->state,
2716 ast_devstate2str(dev_state->state));
2717 } else {
2718 ast_debug(3, "Device '%s' changed to state '%u' (%s) but we don't care because they're not a member of any queue.\n",
2719 dev_state->device,
2720 dev_state->state,
2721 ast_devstate2str(dev_state->state));
2722 }
2723
2724 return;
2725}
2726
2727/*! \brief Helper function which converts from extension state to device state values */
2729{
2730 switch (state) {
2733 break;
2736 break;
2737 case AST_EXTENSION_BUSY:
2739 break;
2742 break;
2745 break;
2748 break;
2751 break;
2754 break;
2757 default:
2759 break;
2760 }
2761
2762 return state;
2763}
2764
2765/*!
2766 * \brief Returns if one context includes another context
2767 *
2768 * \param parent Parent context to search for child
2769 * \param child Context to check for inclusion in parent
2770 *
2771 * This function recursively checks if the context child is included in the context parent.
2772 *
2773 * \retval 1 if child is included in parent
2774 * \retval 0 if not
2775 */
2776static int context_included(const char *parent, const char *child);
2777static int context_included(const char *parent, const char *child)
2778{
2779 struct ast_context *c = NULL;
2780
2781 c = ast_context_find(parent);
2782 if (!c) {
2783 /* well, if parent doesn't exist, how can the child be included in it? */
2784 return 0;
2785 }
2786 if (!strcmp(ast_get_context_name(c), parent)) {
2787 /* found the context of the hint app_queue is using. Now, see
2788 if that context includes the one that just changed state */
2789 struct ast_include *inc = NULL;
2790
2791 while ((inc = (struct ast_include*) ast_walk_context_includes(c, inc))) {
2792 const char *includename = ast_get_include_name(inc);
2793 if (!strcasecmp(child, includename)) {
2794 return 1;
2795 }
2796 /* recurse on this context, for nested includes. The
2797 PBX extension parser will prevent infinite recursion. */
2798 if (context_included(includename, child)) {
2799 return 1;
2800 }
2801 }
2802 }
2803 return 0;
2804}
2805
2806static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
2807{
2808 struct ao2_iterator miter, qiter;
2809 struct member *m;
2810 struct call_queue *q;
2811 int state = info->exten_state;
2812 int found = 0, device_state = extensionstate2devicestate(state);
2813
2814 /* only interested in extension state updates involving device states */
2815 if (info->reason != AST_HINT_UPDATE_DEVICE) {
2816 return 0;
2817 }
2818
2819 qiter = ao2_iterator_init(queues, 0);
2820 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
2821 ao2_lock(q);
2822
2823 miter = ao2_iterator_init(q->members, 0);
2824 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2825 if (!strcmp(m->state_exten, exten) &&
2827 /* context could be included in m->state_context. We need to check. */
2828 found = 1;
2829 update_status(q, m, device_state);
2830 }
2831 }
2832 ao2_iterator_destroy(&miter);
2833
2834 ao2_unlock(q);
2835 queue_t_unref(q, "Done with iterator");
2836 }
2837 ao2_iterator_destroy(&qiter);
2838
2839 if (found) {
2840 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
2841 } else {
2842 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",
2843 exten, context, device_state, ast_devstate2str(device_state));
2844 }
2845
2846 return 0;
2847}
2848
2849/*! \brief Return the current state of a member */
2850static int get_queue_member_status(struct member *cur)
2851{
2853}
2854
2855static void destroy_queue_member_cb(void *obj)
2856{
2857 struct member *mem = obj;
2858
2859 if (mem->state_id != -1) {
2861 }
2862}
2863
2864/*! \brief allocate space for new queue member and set fields based on parameters passed */
2865static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
2866{
2867 struct member *cur;
2868
2869 if ((cur = ao2_alloc(sizeof(*cur), destroy_queue_member_cb))) {
2870 cur->ringinuse = ringinuse;
2871 cur->penalty = penalty;
2872 cur->paused = paused;
2873 cur->wrapuptime = wrapuptime;
2874 if (paused) {
2875 time(&cur->lastpause); /* Update time of last pause */
2876 }
2877 time(&cur->logintime);
2878 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
2881 } else {
2883 }
2885 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
2886 } else {
2887 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
2888 }
2889 if (!strchr(cur->interface, '/')) {
2890 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
2891 }
2892 if (!strncmp(cur->state_interface, "hint:", 5)) {
2893 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
2894 char *exten = strsep(&context, "@") + 5;
2895
2896 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
2897 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
2898
2900 } else {
2901 cur->state_id = -1;
2902 }
2903 cur->status = get_queue_member_status(cur);
2904 }
2905
2906 return cur;
2907}
2908
2909
2910static int compress_char(const char c)
2911{
2912 if (c < 32) {
2913 return 0;
2914 } else if (c > 96) {
2915 return c - 64;
2916 }
2917 return c - 32;
2918}
2919
2920static int member_hash_fn(const void *obj, const int flags)
2921{
2922 const struct member *mem = obj;
2923 const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
2924 const char *chname = strchr(interface, '/');
2925 int ret = 0, i;
2926
2927 if (!chname) {
2928 chname = interface;
2929 }
2930 for (i = 0; i < 5 && chname[i]; i++) {
2931 ret += compress_char(chname[i]) << (i * 6);
2932 }
2933 return ret;
2934}
2935
2936static int member_cmp_fn(void *obj1, void *obj2, int flags)
2937{
2938 struct member *mem1 = obj1;
2939 struct member *mem2 = obj2;
2940 const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;
2941
2942 return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
2943}
2944
2945/*!
2946 * \brief Initialize Queue default values.
2947 * \note the queue's lock must be held before executing this function
2948*/
2949static void init_queue(struct call_queue *q)
2950{
2951 int i;
2952 struct penalty_rule *pr_iter;
2953
2954 q->dead = 0;
2955 q->retry = DEFAULT_RETRY;
2957 q->maxlen = 0;
2958
2959 ast_string_field_set(q, announce, "");
2961 ast_string_field_set(q, membergosub, "");
2962 ast_string_field_set(q, defaultrule, "");
2963
2964 q->announcefrequency = 0;
2966 q->announceholdtime = 1;
2968 q->announcepositionlimit = 10; /* Default 10 positions */
2969 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
2970 q->roundingseconds = 0; /* Default - don't announce seconds */
2971 q->servicelevel = 0;
2972 q->ringinuse = 1;
2974 q->setinterfacevar = 0;
2975 q->setqueuevar = 0;
2976 q->setqueueentryvar = 0;
2978 q->monfmt[0] = '\0';
2979 q->reportholdtime = 0;
2980 q->wrapuptime = 0;
2981 q->penaltymemberslimit = 0;
2982 q->joinempty = 0;
2983 q->leavewhenempty = 0;
2984 q->memberdelay = 0;
2985 q->weight = 0;
2986 q->timeoutrestart = 0;
2990 q->numperiodicannounce = 0;
2993 q->autopausebusy = 0;
2994 q->autopauseunavail = 0;
2996 q->autopausedelay = 0;
2998 if (!q->members) {
3000 /* linear strategy depends on order, so we have to place all members in a list */
3002 } else {
3005 }
3006 }
3007 q->found = 1;
3008
3009 ast_string_field_set(q, moh, "");
3010 ast_string_field_set(q, sound_next, "queue-youarenext");
3011 ast_string_field_set(q, sound_thereare, "queue-thereare");
3012 ast_string_field_set(q, sound_calls, "queue-callswaiting");
3013 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
3014 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
3015 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
3016 ast_string_field_set(q, sound_minutes, "queue-minutes");
3017 ast_string_field_set(q, sound_minute, "queue-minute");
3018 ast_string_field_set(q, sound_seconds, "queue-seconds");
3019 ast_string_field_set(q, sound_thanks, "queue-thankyou");
3020 ast_string_field_set(q, sound_callerannounce, "");
3021 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
3022
3023 if (!q->sound_periodicannounce[0]) {
3025 }
3026
3027 if (q->sound_periodicannounce[0]) {
3028 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
3029 }
3030
3031 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3032 if (q->sound_periodicannounce[i]) {
3033 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
3034 }
3035 }
3036
3037 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
3038 ast_free(pr_iter);
3039 }
3040
3041 /* On restart assume no members are available.
3042 * The queue_avail hint is a boolean state to indicate whether a member is available or not.
3043 *
3044 * This seems counter intuitive, but is required to light a BLF
3045 * AST_DEVICE_INUSE indicates no members are available.
3046 * AST_DEVICE_NOT_INUSE indicates a member is available.
3047 */
3049}
3050
3051static void clear_queue(struct call_queue *q)
3052{
3053 q->holdtime = 0;
3054 q->callscompleted = 0;
3055 q->callsabandoned = 0;
3056 q->callscompletedinsl = 0;
3057 q->callsabandonedinsl = 0;
3058 q->talktime = 0;
3059
3060 if (q->members) {
3061 struct member *mem;
3062 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3063 while ((mem = ao2_iterator_next(&mem_iter))) {
3064 mem->calls = 0;
3065 mem->callcompletedinsl = 0;
3066 mem->lastcall = 0;
3067 mem->starttime = 0;
3068 ao2_ref(mem, -1);
3069 }
3070 ao2_iterator_destroy(&mem_iter);
3071 }
3072}
3073
3074/*!
3075 * \brief Change queue penalty by adding rule.
3076 *
3077 * Check rule for errors with time or formatting, see if rule is relative to rest
3078 * of queue, iterate list of rules to find correct insertion point, insert and return.
3079 * \retval -1 on failure
3080 * \retval 0 on success
3081 * \note Call this with the rule_lists locked
3082*/
3083static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
3084{
3085 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3086 struct penalty_rule *rule = NULL, *rule_iter;
3087 struct rule_list *rl_iter;
3088 int penaltychangetime, inserted = 0;
3089
3090 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
3091 return -1;
3092 }
3093
3094 contentdup = ast_strdupa(content);
3095
3096 if (!(maxstr = strchr(contentdup, ','))) {
3097 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3098 ast_free(rule);
3099 return -1;
3100 }
3101
3102 *maxstr++ = '\0';
3103 if ((minstr = strchr(maxstr,','))) {
3104 *minstr++ = '\0';
3105 if ((raisestr = strchr(minstr,','))) {
3106 *raisestr++ = '\0';
3107 }
3108 } else {
3109 raisestr = NULL;
3110 }
3111
3112 timestr = contentdup;
3113 if ((penaltychangetime = atoi(timestr)) < 0) {
3114 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3115 ast_free(rule);
3116 return -1;
3117 }
3118
3119 rule->time = penaltychangetime;
3120
3121 /* The last check will evaluate true if either no penalty change is indicated for a given rule
3122 * OR if a min penalty change is indicated but no max penalty change is */
3123 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
3124 rule->max_relative = 1;
3125 }
3126
3127 rule->max_value = atoi(maxstr);
3128
3129 if (!ast_strlen_zero(minstr)) {
3130 if (*minstr == '+' || *minstr == '-') {
3131 rule->min_relative = 1;
3132 }
3133 rule->min_value = atoi(minstr);
3134 } else { /*there was no minimum specified, so assume this means no change*/
3135 rule->min_relative = 1;
3136 }
3137
3138 if (!ast_strlen_zero(raisestr)) {
3139 if (*raisestr == '+' || *raisestr == '-') {
3140 rule->raise_relative = 1;
3141 }
3142 rule->raise_value = atoi(raisestr);
3143 } else { /*there was no raise specified, so assume this means no change*/
3144 rule->raise_relative = 1;
3145 }
3146
3147 /*We have the rule made, now we need to insert it where it belongs*/
3148 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
3149 if (strcasecmp(rl_iter->name, list_name)) {
3150 continue;
3151 }
3152
3153 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
3154 if (rule->time < rule_iter->time) {
3156 inserted = 1;
3157 break;
3158 }
3159 }
3161
3162 if (!inserted) {
3163 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
3164 inserted = 1;
3165 }
3166
3167 break;
3168 }
3169
3170 if (!inserted) {
3171 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
3172 ast_free(rule);
3173 return -1;
3174 }
3175 return 0;
3176}
3177
3178/*!
3179 * \brief Load queue rules from realtime.
3180 *
3181 * Check rule for errors with time or formatting, see if rule is relative to rest
3182 * of queue, iterate list of rules to find correct insertion point, insert and return.
3183 * \retval -1 on failure
3184 * \retval 0 on success
3185 * \note Call this with the rule_lists locked
3186*/
3187static int load_realtime_rules(void)
3188{
3189 struct ast_config *cfg;
3190 struct rule_list *rl_iter, *new_rl;
3191 struct penalty_rule *pr_iter;
3192 char *rulecat = NULL;
3193
3194 if (!ast_check_realtime("queue_rules")) {
3195 ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
3196 return 0;
3197 }
3198 if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
3199 ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
3200 return 0;
3201 }
3202 while ((rulecat = ast_category_browse(cfg, rulecat))) {
3203 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3204 int penaltychangetime, rule_exists = 0, inserted = 0;
3205 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3206 int min_relative = 0, max_relative = 0, raise_relative = 0;
3207 struct penalty_rule *new_penalty_rule = NULL;
3208
3209 rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
3210 if (ast_strlen_zero(rule_name)) {
3211 continue;
3212 }
3213
3214 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
3215 if (!(strcasecmp(rl_iter->name, rule_name))) {
3216 rule_exists = 1;
3217 new_rl = rl_iter;
3218 break;
3219 }
3220 }
3221 if (!rule_exists) {
3222 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
3223 ast_config_destroy(cfg);
3224 return -1;
3225 }
3226 ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
3228 }
3229 timestr = ast_variable_retrieve(cfg, rulecat, "time");
3230 if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
3231 ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
3232 (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
3233 continue;
3234 }
3235 if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
3236 ast_config_destroy(cfg);
3237 return -1;
3238 }
3239 if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
3240 ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
3241 max_penalty = 0;
3242 max_relative = 1;
3243 } else {
3244 if (*maxstr == '+' || *maxstr == '-') {
3245 max_relative = 1;
3246 }
3247 }
3248 if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
3249 ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
3250 min_penalty = 0;
3251 min_relative = 1;
3252 } else {
3253 if (*minstr == '+' || *minstr == '-') {
3254 min_relative = 1;
3255 }
3256 }
3257 if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
3258 ast_strlen_zero(raisestr) || sscanf(raisestr, "%30d", &raise_penalty) != 1) {
3259 raise_penalty = 0;
3260 raise_relative = 1;
3261 } else {
3262 if (*raisestr == '+' || *raisestr == '-') {
3263 raise_relative = 1;
3264 }
3265 }
3266 new_penalty_rule->time = penaltychangetime;
3267 new_penalty_rule->max_relative = max_relative;
3268 new_penalty_rule->max_value = max_penalty;
3269 new_penalty_rule->min_relative = min_relative;
3270 new_penalty_rule->min_value = min_penalty;
3271 new_penalty_rule->raise_relative = raise_relative;
3272 new_penalty_rule->raise_value = raise_penalty;
3273 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
3274 if (new_penalty_rule->time < pr_iter->time) {
3275 AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
3276 inserted = 1;
3277 }
3278 }
3280 if (!inserted) {
3281 AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
3282 }
3283 }
3284
3285 ast_config_destroy(cfg);
3286 return 0;
3287}
3288
3289static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
3290{
3291 char *value_copy = ast_strdupa(value);
3292 char *option = NULL;
3293 while ((option = strsep(&value_copy, ","))) {
3294 if (!strcasecmp(option, "paused")) {
3295 *empty |= QUEUE_EMPTY_PAUSED;
3296 } else if (!strcasecmp(option, "penalty")) {
3297 *empty |= QUEUE_EMPTY_PENALTY;
3298 } else if (!strcasecmp(option, "inuse")) {
3299 *empty |= QUEUE_EMPTY_INUSE;
3300 } else if (!strcasecmp(option, "ringing")) {
3301 *empty |= QUEUE_EMPTY_RINGING;
3302 } else if (!strcasecmp(option, "invalid")) {
3303 *empty |= QUEUE_EMPTY_INVALID;
3304 } else if (!strcasecmp(option, "wrapup")) {
3305 *empty |= QUEUE_EMPTY_WRAPUP;
3306 } else if (!strcasecmp(option, "unavailable")) {
3307 *empty |= QUEUE_EMPTY_UNAVAILABLE;
3308 } else if (!strcasecmp(option, "unknown")) {
3309 *empty |= QUEUE_EMPTY_UNKNOWN;
3310 } else if (!strcasecmp(option, "loose")) {
3312 } else if (!strcasecmp(option, "strict")) {
3314 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
3316 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
3317 *empty = 0;
3318 } else {
3319 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
3320 }
3321 }
3322}
3323
3324/*! \brief Configure a queue parameter.
3325 *
3326 * The failunknown flag is set for config files (and static realtime) to show
3327 * errors for unknown parameters. It is cleared for dynamic realtime to allow
3328 * extra fields in the tables.
3329 * \note For error reporting, line number is passed for .conf static configuration,
3330 * for Realtime queues, linenum is -1.
3331*/
3332static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
3333{
3334 if (!strcasecmp(param, "musicclass") ||
3335 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
3336 ast_string_field_set(q, moh, val);
3337 } else if (!strcasecmp(param, "announce")) {
3338 ast_string_field_set(q, announce, val);
3339 } else if (!strcasecmp(param, "context")) {
3341 } else if (!strcasecmp(param, "timeout")) {
3342 q->timeout = atoi(val);
3343 if (q->timeout < 0) {
3345 }
3346 } else if (!strcasecmp(param, "ringinuse")) {
3347 q->ringinuse = ast_true(val);
3348 } else if (!strcasecmp(param, "setinterfacevar")) {
3350 } else if (!strcasecmp(param, "setqueuevar")) {
3351 q->setqueuevar = ast_true(val);
3352 } else if (!strcasecmp(param, "setqueueentryvar")) {
3354 } else if (!strcasecmp(param, "monitor-format")) {
3355 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
3356 } else if (!strcasecmp(param, "membergosub")) {
3357 ast_string_field_set(q, membergosub, val);
3358 } else if (!strcasecmp(param, "queue-youarenext")) {
3359 ast_string_field_set(q, sound_next, val);
3360 } else if (!strcasecmp(param, "queue-thereare")) {
3361 ast_string_field_set(q, sound_thereare, val);
3362 } else if (!strcasecmp(param, "queue-callswaiting")) {
3363 ast_string_field_set(q, sound_calls, val);
3364 } else if (!strcasecmp(param, "queue-quantity1")) {
3365 ast_string_field_set(q, queue_quantity1, val);
3366 } else if (!strcasecmp(param, "queue-quantity2")) {
3367 ast_string_field_set(q, queue_quantity2, val);
3368 } else if (!strcasecmp(param, "queue-holdtime")) {
3369 ast_string_field_set(q, sound_holdtime, val);
3370 } else if (!strcasecmp(param, "queue-minutes")) {
3371 ast_string_field_set(q, sound_minutes, val);
3372 } else if (!strcasecmp(param, "queue-minute")) {
3373 ast_string_field_set(q, sound_minute, val);
3374 } else if (!strcasecmp(param, "queue-seconds")) {
3375 ast_string_field_set(q, sound_seconds, val);
3376 } else if (!strcasecmp(param, "queue-thankyou")) {
3377 ast_string_field_set(q, sound_thanks, val);
3378 } else if (!strcasecmp(param, "queue-callerannounce")) {
3379 ast_string_field_set(q, sound_callerannounce, val);
3380 } else if (!strcasecmp(param, "queue-reporthold")) {
3381 ast_string_field_set(q, sound_reporthold, val);
3382 } else if (!strcasecmp(param, "announce-frequency")) {
3383 q->announcefrequency = atoi(val);
3384 } else if (!strcasecmp(param, "announce-to-first-user")) {
3386 } else if (!strcasecmp(param, "min-announce-frequency")) {
3387 q->minannouncefrequency = atoi(val);
3388 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
3389 } else if (!strcasecmp(param, "announce-round-seconds")) {
3390 q->roundingseconds = atoi(val);
3391 /* Rounding to any other values just doesn't make sense... */
3392 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
3393 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
3394 if (linenum >= 0) {
3395 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3396 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3397 val, param, q->name, linenum);
3398 } else {
3399 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3400 "using 0 instead for queue '%s'\n", val, param, q->name);
3401 }
3402 q->roundingseconds=0;
3403 }
3404 } else if (!strcasecmp(param, "announce-holdtime")) {
3405 if (!strcasecmp(val, "once")) {
3407 } else if (ast_true(val)) {
3409 } else {
3410 q->announceholdtime = 0;
3411 }
3412 } else if (!strcasecmp(param, "announce-position")) {
3413 if (!strcasecmp(val, "limit")) {
3415 } else if (!strcasecmp(val, "more")) {
3417 } else if (ast_true(val)) {
3419 } else {
3421 }
3422 } else if (!strcasecmp(param, "announce-position-only-up")) {
3424 } else if (!strcasecmp(param, "announce-position-limit")) {
3425 q->announcepositionlimit = atoi(val);
3426 } else if (!strcasecmp(param, "periodic-announce")) {
3427 if (strchr(val, ',')) {
3428 char *s, *buf = ast_strdupa(val);
3429 unsigned int i = 0;
3430
3431 while ((s = strsep(&buf, ",|"))) {
3432 if (!q->sound_periodicannounce[i]) {
3434 }
3435 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
3436 i++;
3437 if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
3438 break;
3439 }
3440 }
3441 q->numperiodicannounce = i;
3442 } else {
3443 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
3444 q->numperiodicannounce = 1;
3445 }
3446 } else if (!strcasecmp(param, "periodic-announce-startdelay")) {
3448 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
3449 q->periodicannouncefrequency = atoi(val);
3450 } else if (!strcasecmp(param, "relative-periodic-announce")) {
3452 } else if (!strcasecmp(param, "random-periodic-announce")) {
3454 } else if (!strcasecmp(param, "retry")) {
3455 q->retry = atoi(val);
3456 if (q->retry <= 0) {
3457 q->retry = DEFAULT_RETRY;
3458 }
3459 } else if (!strcasecmp(param, "wrapuptime")) {
3460 q->wrapuptime = atoi(val);
3461 } else if (!strcasecmp(param, "penaltymemberslimit")) {
3462 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
3463 q->penaltymemberslimit = 0;
3464 }
3465 } else if (!strcasecmp(param, "autofill")) {
3466 q->autofill = ast_true(val);
3467 } else if (!strcasecmp(param, "autopause")) {
3469 } else if (!strcasecmp(param, "autopausedelay")) {
3470 q->autopausedelay = atoi(val);
3471 } else if (!strcasecmp(param, "autopausebusy")) {
3473 } else if (!strcasecmp(param, "autopauseunavail")) {
3475 } else if (!strcasecmp(param, "maxlen")) {
3476 q->maxlen = atoi(val);
3477 if (q->maxlen < 0) {
3478 q->maxlen = 0;
3479 }
3480 } else if (!strcasecmp(param, "servicelevel")) {
3481 q->servicelevel= atoi(val);
3482 } else if (!strcasecmp(param, "strategy")) {
3483 int strategy;
3484
3485 /* We are a static queue and already have set this, no need to do it again */
3486 if (failunknown) {
3487 return;
3488 }
3490 if (strategy < 0) {
3491 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3492 val, q->name);
3494 }
3495 if (strategy == q->strategy) {
3496 return;
3497 }
3499 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
3500 return;
3501 }
3502 q->strategy = strategy;
3503 } else if (!strcasecmp(param, "joinempty")) {
3505 } else if (!strcasecmp(param, "leavewhenempty")) {
3507 } else if (!strcasecmp(param, "reportholdtime")) {
3509 } else if (!strcasecmp(param, "memberdelay")) {
3510 q->memberdelay = atoi(val);
3511 } else if (!strcasecmp(param, "weight")) {
3512 q->weight = atoi(val);
3513 } else if (!strcasecmp(param, "timeoutrestart")) {
3515 } else if (!strcasecmp(param, "defaultrule")) {
3516 ast_string_field_set(q, defaultrule, val);
3517 } else if (!strcasecmp(param, "timeoutpriority")) {
3518 if (!strcasecmp(val, "conf")) {
3520 } else {
3522 }
3523 } else if (!strcasecmp(param, "log-restricted-caller-id")) {
3525 } else if (failunknown) {
3526 if (linenum >= 0) {
3527 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3528 q->name, param, linenum);
3529 } else {
3530 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
3531 }
3532 }
3533}
3534
3535
3536#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3537#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3538#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3539
3540/*! \internal
3541 * \brief If adding a single new member to a queue, use this function instead of ao2_linking.
3542 * This adds round robin queue position data for a fresh member as well as links it.
3543 * \param queue Which queue the member is being added to
3544 * \param mem Which member is being added to the queue
3545 */
3546static void member_add_to_queue(struct call_queue *queue, struct member *mem)
3547{
3548 ao2_lock(queue->members);
3549 mem->queuepos = ao2_container_count(queue->members);
3550 ao2_link(queue->members, mem);
3552 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", queue->name, mem->interface);
3553 ao2_unlock(queue->members);
3554}
3555
3556/*! \internal
3557 * \brief If removing a single member from a queue, use this function instead of ao2_unlinking.
3558 * This will perform round robin queue position reordering for the remaining members.
3559 * \param queue Which queue the member is being removed from
3560 * \param mem Which member is being removed from the queue
3561 */
3562static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
3563{
3565 ao2_lock(queue->members);
3568 ao2_unlink(queue->members, mem);
3569 ao2_unlock(queue->members);
3570}
3571
3572/*!
3573 * \brief Find rt member record to update otherwise create one.
3574 *
3575 * Search for member in queue, if found update penalty/paused state,
3576 * if no member exists create one flag it as a RT member and add to queue member list.
3577*/
3578static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
3579{
3580 struct member *m;
3581 struct ao2_iterator mem_iter;
3582 int penalty = 0;
3583 int paused = 0;
3584 int found = 0;
3585 int wrapuptime = 0;
3586 int ringinuse = q->ringinuse;
3587
3588 const char *config_val;
3589 const char *interface = ast_variable_retrieve(member_config, category, "interface");
3590 const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
3591 const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
3592 const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
3593 const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
3594 const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
3595 const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
3596 const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
3597
3598 if (ast_strlen_zero(rt_uniqueid)) {
3599 ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
3600 S_OR(membername, "NULL"));
3601 return;
3602 }
3603
3604 if (ast_strlen_zero(interface)) {
3605 ast_log(LOG_WARNING, "Realtime field 'interface' is empty for member %s\n",
3606 S_OR(membername, "NULL"));
3607 return;
3608 }
3609
3610 if (penalty_str) {
3611 penalty = atoi(penalty_str);
3612 if ((penalty < 0) && negative_penalty_invalid) {
3613 return;
3614 } else if (penalty < 0) {
3615 penalty = 0;
3616 }
3617 }
3618
3619 if (paused_str) {
3620 paused = atoi(paused_str);
3621 if (paused < 0) {
3622 paused = 0;
3623 }
3624 }
3625
3626 if (wrapuptime_str) {
3627 wrapuptime = atoi(wrapuptime_str);
3628 if (wrapuptime < 0) {
3629 wrapuptime = 0;
3630 }
3631 }
3632
3633 if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3634 if (ast_true(config_val)) {
3635 ringinuse = 1;
3636 } else if (ast_false(config_val)) {
3637 ringinuse = 0;
3638 } else {
3639 ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
3640 }
3641 }
3642
3643 /* Find member by realtime uniqueid and update */
3644 mem_iter = ao2_iterator_init(q->members, 0);
3645 while ((m = ao2_iterator_next(&mem_iter))) {
3646 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
3647 m->dead = 0; /* Do not delete this one. */
3648 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3649 if (paused_str) {
3650 m->paused = paused;
3651 if (paused && m->lastpause == 0) {
3652 time(&m->lastpause); /* XXX: Should this come from realtime? */
3653 }
3655 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, m->interface);
3656 }
3657 if (strcasecmp(state_interface, m->state_interface)) {
3658 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
3659 }
3660 m->penalty = penalty;
3661 m->ringinuse = ringinuse;
3662 m->wrapuptime = wrapuptime;
3664 ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
3665 }
3666 found = 1;
3667 ao2_ref(m, -1);
3668 break;
3669 }
3670 ao2_ref(m, -1);
3671 }
3672 ao2_iterator_destroy(&mem_iter);
3673
3674 /* Create a new member */
3675 if (!found) {
3676 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3677 m->dead = 0;
3678 m->realtime = 1;
3679 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3680 if (!ast_strlen_zero(reason_paused)) {
3681 ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
3682 }
3684 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3685 } else {
3686 ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3687 }
3688 member_add_to_queue(q, m);
3689 ao2_ref(m, -1);
3690 m = NULL;
3691 }
3692 }
3693}
3694
3695/*! \brief Iterate through queue's member list and delete them */
3696static void free_members(struct call_queue *q, int all)
3697{
3698 /* Free non-dynamic members */
3699 struct member *cur;
3700 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3701
3702 while ((cur = ao2_iterator_next(&mem_iter))) {
3703 if (all || !cur->dynamic) {
3705 }
3706 ao2_ref(cur, -1);
3707 }
3708 ao2_iterator_destroy(&mem_iter);
3709}
3710
3711/*! \brief Free queue's member list then its string fields */
3712static void destroy_queue(void *obj)
3713{
3714 struct call_queue *q = obj;
3715 int i;
3716
3717 free_members(q, 1);
3719 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3720 if (q->sound_periodicannounce[i]) {
3722 }
3723 }
3724 ao2_ref(q->members, -1);
3725}
3726
3727static struct call_queue *alloc_queue(const char *queuename)
3728{
3729 struct call_queue *q;
3730
3731 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
3732 if (ast_string_field_init(q, 64)) {
3733 queue_t_unref(q, "String field allocation failed");
3734 return NULL;
3735 }
3736 ast_string_field_set(q, name, queuename);
3737 }
3738 return q;
3739}
3740
3741/*!
3742 * \brief Reload a single queue via realtime.
3743 *
3744 * Check for statically defined queue first, check if deleted RT queue,
3745 * check for new RT queue, if queue vars are not defined init them with defaults.
3746 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
3747 * \retval the queue,
3748 * \retval NULL if it doesn't exist.
3749 * \note Should be called with the "queues" container locked.
3750*/
3751static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
3752{
3753 struct ast_variable *v;
3754 struct call_queue *q, tmpq = {
3755 .name = queuename,
3756 };
3757 struct member *m;
3758 struct ao2_iterator mem_iter;
3759 char *category = NULL;
3760 const char *tmp_name;
3761 char *tmp;
3762 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
3763
3764 /* Static queues override realtime. */
3765 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
3766 ao2_lock(q);
3767 if (!q->realtime) {
3768 if (q->dead) {
3769 ao2_unlock(q);
3770 queue_t_unref(q, "Queue is dead; can't return it");
3771 return NULL;
3772 }
3773 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
3774 ao2_unlock(q);
3775 return q;
3776 }
3777 } else if (!member_config) {
3778 /* Not found in the list, and it's not realtime ... */
3779 return NULL;
3780 }
3781 /* Check if queue is defined in realtime. */
3782 if (!queue_vars) {
3783 /* Delete queue from in-core list if it has been deleted in realtime. */
3784 if (q) {
3785 /*! \note Hmm, can't seem to distinguish a DB failure from a not
3786 found condition... So we might delete an in-core queue
3787 in case of DB failure. */
3788 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
3789
3790 q->dead = 1;
3791 /* Delete if unused (else will be deleted when last caller leaves). */
3792 queues_t_unlink(queues, q, "Unused; removing from container");
3793 ao2_unlock(q);
3794 queue_t_unref(q, "Queue is dead; can't return it");
3795 }
3796 return NULL;
3797 }
3798
3799 /* Create a new queue if an in-core entry does not exist yet. */
3800 if (!q) {
3801 struct ast_variable *tmpvar = NULL;
3802 if (!(q = alloc_queue(queuename))) {
3803 return NULL;
3804 }
3805 ao2_lock(q);
3806 clear_queue(q);
3807 q->realtime = 1;
3808 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
3809 * will allocate the members properly
3810 */
3811 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
3812 if (!strcasecmp(tmpvar->name, "strategy")) {
3813 q->strategy = strat2int(tmpvar->value);
3814 if (q->strategy < 0) {
3815 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3816 tmpvar->value, q->name);
3818 }
3819 break;
3820 }
3821 }
3822 /* We traversed all variables and didn't find a strategy */
3823 if (!tmpvar) {
3825 }
3826 queues_t_link(queues, q, "Add queue to container");
3827 }
3828 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
3829
3830 memset(tmpbuf, 0, sizeof(tmpbuf));
3831 for (v = queue_vars; v; v = v->next) {
3832 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
3833 if (strchr(v->name, '_')) {
3834 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
3835 tmp_name = tmpbuf;
3836 tmp = tmpbuf;
3837 while ((tmp = strchr(tmp, '_'))) {
3838 *tmp++ = '-';
3839 }
3840 } else {
3841 tmp_name = v->name;
3842 }
3843
3844 /* NULL values don't get returned from realtime; blank values should
3845 * still get set. If someone doesn't want a value to be set, they
3846 * should set the realtime column to NULL, not blank. */
3847 queue_set_param(q, tmp_name, v->value, -1, 0);
3848 }
3849
3850 /* Temporarily set realtime members dead so we can detect deleted ones. */
3851 mem_iter = ao2_iterator_init(q->members, 0);
3852 while ((m = ao2_iterator_next(&mem_iter))) {
3853 if (m->realtime) {
3854 m->dead = 1;
3855 }
3856 ao2_ref(m, -1);
3857 }
3858 ao2_iterator_destroy(&mem_iter);
3859
3860 while ((category = ast_category_browse(member_config, category))) {
3861 rt_handle_member_record(q, category, member_config);
3862 }
3863
3864 /* Delete all realtime members that have been deleted in DB. */
3865 mem_iter = ao2_iterator_init(q->members, 0);
3866 while ((m = ao2_iterator_next(&mem_iter))) {
3867 if (m->dead) {
3869 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
3870 } else {
3871 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
3872 }
3874 }
3875 ao2_ref(m, -1);
3876 }
3877 ao2_iterator_destroy(&mem_iter);
3878
3879 ao2_unlock(q);
3880
3881 return q;
3882}
3883
3884/*!
3885 * note */
3886
3887/*!
3888 * \internal
3889 * \brief Returns reference to the named queue. If the queue is realtime, it will load the queue as well.
3890 * \param queuename - name of the desired queue
3891 *
3892 * \retval the queue
3893 * \retval NULL if it doesn't exist
3894 */
3895static struct call_queue *find_load_queue_rt_friendly(const char *queuename)
3896{
3897 struct ast_variable *queue_vars;
3898 struct ast_config *member_config = NULL;
3899 struct call_queue *q = NULL, tmpq = {
3900 .name = queuename,
3901 };
3902 int prev_weight = 0;
3903
3904 /* Find the queue in the in-core list first. */
3905 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
3906
3907 if (!q || q->realtime) {
3908 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
3909 queue operations while waiting for the DB.
3910
3911 This will be two separate database transactions, so we might
3912 see queue parameters as they were before another process
3913 changed the queue and member list as it was after the change.
3914 Thus we might see an empty member list when a queue is
3915 deleted. In practise, this is unlikely to cause a problem. */
3916
3917 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
3918 if (queue_vars) {
3919 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
3920 if (!member_config) {
3921 ast_debug(1, "No queue_members defined in config extconfig.conf\n");
3922 member_config = ast_config_new();
3923 }
3924 }
3925 if (q) {
3926 prev_weight = q->weight ? 1 : 0;
3927 queue_t_unref(q, "Need to find realtime queue");
3928 }
3929
3930 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
3931 ast_config_destroy(member_config);
3932 ast_variables_destroy(queue_vars);
3933
3934 /* update the use_weight value if the queue's has gained or lost a weight */
3935 if (q) {
3936 if (!q->weight && prev_weight) {
3938 }
3939 if (q->weight && !prev_weight) {
3941 }
3942 }
3943 /* Other cases will end up with the proper value for use_weight */
3944 } else {
3946 }
3947 return q;
3948}
3949
3950/*!
3951 * \internal
3952 * \brief Load queues and members from realtime.
3953 *
3954 * \param queuename - name of the desired queue to load or empty if need to load all queues
3955*/
3956static void load_realtime_queues(const char *queuename)
3957{
3958 struct ast_config *cfg = NULL;
3959 char *category = NULL;
3960 const char *name = NULL;
3961 struct call_queue *q = NULL;
3962
3963 if (!ast_check_realtime("queues")) {
3964 return;
3965 }
3966
3967 if (ast_strlen_zero(queuename)) {
3968 if ((cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
3969 while ((category = ast_category_browse(cfg, category))) {
3970 name = ast_variable_retrieve(cfg, category, "name");
3972 queue_unref(q);
3973 }
3974 }
3975 ast_config_destroy(cfg);
3976 }
3977 } else {
3978 if ((q = find_load_queue_rt_friendly(queuename))) {
3979 queue_unref(q);
3980 }
3981 }
3982}
3983
3984static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
3985{
3986 int ret = -1;
3987
3988 if (ast_strlen_zero(mem->rt_uniqueid)) {
3989 return ret;
3990 }
3991
3992 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) >= 0) {
3993 ret = 0;
3994 }
3995
3996 return ret;
3997}
3998
3999
4001{
4002 struct ast_config *member_config = NULL;
4003 struct member *m;
4004 char *category = NULL;
4005 struct ao2_iterator mem_iter;
4006
4007 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
4008 /* This queue doesn't have realtime members. If the queue still has any realtime
4009 * members in memory, they need to be removed.
4010 */
4011 ao2_lock(q);
4012 mem_iter = ao2_iterator_init(q->members, 0);
4013 while ((m = ao2_iterator_next(&mem_iter))) {
4014 if (m->realtime) {
4016 }
4017 ao2_ref(m, -1);
4018 }
4019 ao2_iterator_destroy(&mem_iter);
4020 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
4021 ao2_unlock(q);
4022 return;
4023 }
4024
4025 ao2_lock(q);
4026
4027 /* Temporarily set realtime members dead so we can detect deleted ones.*/
4028 mem_iter = ao2_iterator_init(q->members, 0);
4029 while ((m = ao2_iterator_next(&mem_iter))) {
4030 if (m->realtime) {
4031 m->dead = 1;
4032 }
4033 ao2_ref(m, -1);
4034 }
4035 ao2_iterator_destroy(&mem_iter);
4036
4037 while ((category = ast_category_browse(member_config, category))) {
4038 rt_handle_member_record(q, category, member_config);
4039 }
4040
4041 /* Delete all realtime members that have been deleted in DB. */
4042 mem_iter = ao2_iterator_init(q->members, 0);
4043 while ((m = ao2_iterator_next(&mem_iter))) {
4044 if (m->dead) {
4046 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4047 } else {
4048 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4049 }
4051 }
4052 ao2_ref(m, -1);
4053 }
4054 ao2_iterator_destroy(&mem_iter);
4055 ao2_unlock(q);
4056 ast_config_destroy(member_config);
4057}
4058
4059static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
4060{
4061 struct call_queue *q;
4062 struct queue_ent *cur, *prev = NULL;
4063 int res = -1;
4064 int pos = 0;
4065 int inserted = 0;
4066
4067 if (!(q = find_load_queue_rt_friendly(queuename))) {
4068 return res;
4069 }
4070 ao2_lock(q);
4071
4072 /* This is our one */
4073 if (q->joinempty) {
4074 int status = 0;
4075 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, qe->raise_penalty, q->joinempty, 0))) {
4076 *reason = QUEUE_JOINEMPTY;
4077 ao2_unlock(q);
4078 queue_t_unref(q, "Done with realtime queue");
4079 return res;
4080 }
4081 }
4082 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) {
4083 *reason = QUEUE_FULL;
4084 } else if (*reason == QUEUE_UNKNOWN) {
4085 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4086
4087 /* There's space for us, put us at the right position inside
4088 * the queue.
4089 * Take into account the priority of the calling user */
4090 inserted = 0;
4091 prev = NULL;
4092 cur = q->head;
4093 while (cur) {
4094 /* We have higher priority than the current user, enter
4095 * before him, after all the other users with priority
4096 * higher or equal to our priority. */
4097 if ((!inserted) && (qe->prio > cur->prio)) {
4098 insert_entry(q, prev, qe, &pos);
4099 inserted = 1;
4100 }
4101 /* <= is necessary for the position comparison because it may not be possible to enter
4102 * at our desired position since higher-priority callers may have taken the position we want
4103 */
4104 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
4105 insert_entry(q, prev, qe, &pos);
4106 inserted = 1;
4107 /*pos is incremented inside insert_entry, so don't need to add 1 here*/
4108 if (position < pos) {
4109 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
4110 }
4111 }
4112 cur->pos = ++pos;
4113 prev = cur;
4114 cur = cur->next;
4115 }
4116 /* No luck, join at the end of the queue */
4117 if (!inserted) {
4118 insert_entry(q, prev, qe, &pos);
4119 }
4120 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
4121 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
4122 ast_copy_string(qe->context, q->context, sizeof(qe->context));
4123 q->count++;
4124 if (q->count == 1) {
4126 }
4127
4128 res = 0;
4129
4130 blob = ast_json_pack("{s: s, s: i, s: i}",
4131 "Queue", q->name,
4132 "Position", qe->pos,
4133 "Count", q->count);
4134 ast_channel_publish_cached_blob(qe->chan, queue_caller_join_type(), blob);
4135 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, ast_channel_name(qe->chan), qe->pos );
4136 }
4137 ao2_unlock(q);
4138 queue_t_unref(q, "Done with realtime queue");
4139
4140 return res;
4141}
4142
4143static int play_file(struct ast_channel *chan, const char *filename)
4144{
4145 int res;
4146
4147 if (ast_strlen_zero(filename)) {
4148 return 0;
4149 }
4150
4151 if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
4152 return 0;
4153 }
4154
4156
4157 res = ast_streamfile(chan, filename, ast_channel_language(chan));
4158 if (!res) {
4160 }
4161
4163
4164 return res;
4165}
4166
4167/*!
4168 * \brief Check for valid exit from queue via goto
4169 * \retval 0 if failure
4170 * \retval 1 if successful
4171*/
4172static int valid_exit(struct queue_ent *qe, char digit)
4173{
4174 int digitlen = strlen(qe->digits);
4175
4176 /* Prevent possible buffer overflow */
4177 if (digitlen < sizeof(qe->digits) - 2) {
4178 qe->digits[digitlen] = digit;
4179 qe->digits[digitlen + 1] = '\0';
4180 } else {
4181 qe->digits[0] = '\0';
4182 return 0;
4183 }
4184
4185 /* If there's no context to goto, short-circuit */
4186 if (ast_strlen_zero(qe->context)) {
4187 return 0;
4188 }
4189
4190 /* If the extension is bad, then reset the digits to blank */
4191 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
4193 qe->digits[0] = '\0';
4194 return 0;
4195 }
4196
4197 /* We have an exact match */
4198 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
4199 qe->valid_digits = 1;
4200 /* Return 1 on a successful goto */
4201 return 1;
4202 }
4203
4204 return 0;
4205}
4206
4207static int say_position(struct queue_ent *qe, int ringing)
4208{
4209 int res = 0, say_thanks = 0;
4210 long avgholdmins, avgholdsecs;
4211 time_t now;
4212
4213 /* Let minannouncefrequency seconds pass between the start of each position announcement */
4214 time(&now);
4215 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) {
4216 return 0;
4217 }
4218
4219 /* If either our position has changed, or we are over the freq timer, say position */
4220 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) {
4221 return 0;
4222 }
4223
4224 /* Only announce if the caller's queue position has improved since last time */
4225 if (qe->parent->announceposition_only_up && qe->last_pos_said <= qe->pos) {
4226 return 0;
4227 }
4228
4229 if (ringing) {
4230 ast_indicate(qe->chan,-1);
4231 } else {
4232 ast_moh_stop(qe->chan);
4233 }
4234
4238 qe->pos <= qe->parent->announcepositionlimit)) {
4239 say_thanks = 1;
4240 /* Say we're next, if we are */
4241 if (qe->pos == 1) {
4242 res = play_file(qe->chan, qe->parent->sound_next);
4243 if (!res) {
4244 goto posout;
4245 }
4246 /* Say there are more than N callers */
4248 res = (
4249 play_file(qe->chan, qe->parent->queue_quantity1) ||
4251 ast_channel_language(qe->chan), NULL) || /* Needs gender */
4253 /* Say there are currently N callers waiting */
4254 } else {
4255 res = (
4256 play_file(qe->chan, qe->parent->sound_thereare) ||
4258 ast_channel_language(qe->chan), "n") || /* Needs gender */
4259 play_file(qe->chan, qe->parent->sound_calls));
4260 }
4261 if (res) {
4262 goto playout;
4263 }
4264 }
4265 /* Round hold time to nearest minute */
4266 avgholdmins = labs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
4267
4268 /* If they have specified a rounding then round the seconds as well */
4269 if (qe->parent->roundingseconds) {
4270 avgholdsecs = (labs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
4271 avgholdsecs *= qe->parent->roundingseconds;
4272 } else {
4273 avgholdsecs = 0;
4274 }
4275
4276 ast_verb(3, "Hold time for %s is %ld minute(s) %ld seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
4277
4278 /* If the hold time is >1 min, if it's enabled, and if it's not
4279 supposed to be only once and we have already said it, say it */
4280 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
4283 say_thanks = 1;
4284 res = play_file(qe->chan, qe->parent->sound_holdtime);
4285 if (res) {
4286 goto playout;
4287 }
4288
4289 if (avgholdmins >= 1) {
4290 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4291 if (res) {
4292 goto playout;
4293 }
4294
4295 if (avgholdmins == 1) {
4296 res = play_file(qe->chan, qe->parent->sound_minute);
4297 if (res) {
4298 goto playout;
4299 }
4300 } else {
4301 res = play_file(qe->chan, qe->parent->sound_minutes);
4302 if (res) {
4303 goto playout;
4304 }
4305 }
4306 }
4307 if (avgholdsecs >= 1) {
4308 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4309 if (res) {
4310 goto playout;
4311 }
4312
4313 res = play_file(qe->chan, qe->parent->sound_seconds);
4314 if (res) {
4315 goto playout;
4316 }
4317 }
4318 }
4319
4320posout:
4321 if (qe->parent->announceposition) {
4322 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
4323 ast_channel_name(qe->chan), qe->parent->name, qe->pos);
4324 }
4325 if (say_thanks) {
4326 res = play_file(qe->chan, qe->parent->sound_thanks);
4327 }
4328playout:
4329
4330 if ((res > 0 && !valid_exit(qe, res))) {
4331 res = 0;
4332 }
4333
4334 /* Set our last_pos indicators */
4335 qe->last_pos = now;
4336 qe->last_pos_said = qe->pos;
4337
4338 /* Don't restart music on hold if we're about to exit the caller from the queue */
4339 if (!res) {
4340 if (ringing) {
4342 } else {
4343 ast_moh_start(qe->chan, qe->moh, NULL);
4344 }
4345 }
4346 return res;
4347}
4348
4349static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
4350{
4351 int oldvalue;
4352
4353 /* Calculate holdtime using an exponential average */
4354 /* Thanks to SRT for this contribution */
4355 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
4356
4357 ao2_lock(qe->parent);
4358 if ((qe->parent->callscompleted + qe->parent->callsabandoned) == 0) {
4359 qe->parent->holdtime = newholdtime;
4360 } else {
4361 oldvalue = qe->parent->holdtime;
4362 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4363 }
4364 ao2_unlock(qe->parent);
4365}
4366
4367/*! \brief Caller leaving queue.
4368 *
4369 * Search the queue to find the leaving client, if found remove from queue
4370 * create manager event, move others up the queue.
4371*/
4372static void leave_queue(struct queue_ent *qe)
4373{
4374 struct call_queue *q;
4375 struct queue_ent *current, *prev = NULL;
4376 struct penalty_rule *pr_iter;
4377 int pos = 0;
4378
4379 if (!(q = qe->parent)) {
4380 return;
4381 }
4382 queue_t_ref(q, "Copy queue pointer from queue entry");
4383 ao2_lock(q);
4384
4385 prev = NULL;
4386 for (current = q->head; current; current = current->next) {
4387 if (current == qe) {
4388 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4389 char posstr[20];
4390 q->count--;
4391 if (!q->count) {
4393 }
4394
4395 blob = ast_json_pack("{s: s, s: i, s: i}",
4396 "Queue", q->name,
4397 "Position", qe->pos,
4398 "Count", q->count);
4399 ast_channel_publish_cached_blob(qe->chan, queue_caller_leave_type(), blob);
4400 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
4401 /* Take us out of the queue */
4402 if (prev) {
4403 prev->next = current->next;
4404 } else {
4405 q->head = current->next;
4406 }
4407 /* Free penalty rules */
4408 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
4409 ast_free(pr_iter);
4410 }
4411 qe->pr = NULL;
4412 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
4413 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
4414 } else {
4415 /* Renumber the people after us in the queue based on a new count */
4416 current->pos = ++pos;
4417 prev = current;
4418 }
4419 }
4420 ao2_unlock(q);
4421
4422 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
4423 if (q->realtime) {
4424 struct ast_variable *var;
4425 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
4426 q->dead = 1;
4427 } else {
4429 }
4430 }
4431
4432 if (q->dead) {
4433 /* It's dead and nobody is in it, so kill it */
4434 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
4435 }
4436 /* unref the explicit ref earlier in the function */
4437 queue_t_unref(q, "Expire copied reference");
4438}
4439
4440/*!
4441 * \internal
4442 * \brief Destroy the given callattempt structure and free it.
4443 * \since 1.8
4444 *
4445 * \param doomed callattempt structure to destroy.
4446 */
4447static void callattempt_free(struct callattempt *doomed)
4448{
4449 if (doomed->member) {
4450 ao2_ref(doomed->member, -1);
4451 }
4453 ast_free(doomed->orig_chan_name);
4454 ast_free(doomed);
4455}
4456
4457static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
4458{
4459 struct callattempt *cur;
4460
4461 for (cur = outgoing; cur; cur = cur->q_next) {
4462 if (cur->chan && cur->chan != exception) {
4464 }
4465 }
4466}
4467
4468/*! \brief Hang up a list of outgoing calls */
4469static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
4470{
4471 struct callattempt *oo;
4472
4473 while (outgoing) {
4474 /* If someone else answered the call we should indicate this in the CANCEL */
4475 /* Hangup any existing lines we have open */
4476 if (outgoing->chan && (outgoing->chan != exception)) {
4477 if (exception || cancel_answered_elsewhere) {
4479 }
4480 ast_channel_publish_dial(qe->chan, outgoing->chan, outgoing->interface, "CANCEL");
4481
4482 /* When dialing channels it is possible that they may not ever
4483 * leave the not in use state (Local channels in particular) by
4484 * the time we cancel them. If this occurs but we know they were
4485 * dialed we explicitly remove them from the pending members
4486 * container so that subsequent call attempts occur.
4487 */
4488 if (outgoing->member->status == AST_DEVICE_NOT_INUSE) {
4490 }
4491
4492 ast_hangup(outgoing->chan);
4493 }
4494 oo = outgoing;
4495 outgoing = outgoing->q_next;
4497 callattempt_free(oo);
4498 }
4499}
4500
4501/*!
4502 * \brief Get the number of members available to accept a call.
4503 *
4504 * \note The queue passed in should be locked prior to this function call
4505 *
4506 * \param[in] q The queue for which we are counting the number of available members
4507 * \return Return the number of available members in queue q
4508 */
4510{
4511 struct member *mem;
4512 int avl = 0;
4513 struct ao2_iterator mem_iter;
4514
4515 mem_iter = ao2_iterator_init(q->members, 0);
4516 while ((mem = ao2_iterator_next(&mem_iter))) {
4517
4518 avl += is_member_available(q, mem);
4519 ao2_ref(mem, -1);
4520
4521 /* If autofill is not enabled or if the queue's strategy is ringall, then
4522 * we really don't care about the number of available members so much as we
4523 * do that there is at least one available.
4524 *
4525 * In fact, we purposely will return from this function stating that only
4526 * one member is available if either of those conditions hold. That way,
4527 * functions which determine what action to take based on the number of available
4528 * members will operate properly. The reasoning is that even if multiple
4529 * members are available, only the head caller can actually be serviced.
4530 */
4531 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4532 break;
4533 }
4534 }
4535 ao2_iterator_destroy(&mem_iter);
4536
4537 return avl;
4538}
4539
4540/* traverse all defined queues which have calls waiting and contain this member
4541 return 0 if no other queue has precedence (higher weight) or 1 if found */
4542static int compare_weight(struct call_queue *rq, struct member *member)
4543{
4544 struct call_queue *q;
4545 struct member *mem;
4546 int found = 0;
4547 struct ao2_iterator queue_iter;
4548
4549 queue_iter =