Asterisk - The Open Source Telephony Project GIT-master-25686a5
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 struct ao2_container *members; /*!< Head of the list of members */
1886 struct queue_ent *head; /*!< Head of the list of callers */
1887 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
1888 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
1889};
1890
1892 char name[80];
1895};
1896
1898
1899static struct ao2_container *queues;
1900
1901static void update_realtime_members(struct call_queue *q);
1902static struct member *interface_exists(struct call_queue *q, const char *interface);
1903static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
1904static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime);
1905
1906static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface);
1907/*! \brief sets the QUEUESTATUS channel variable */
1908static void set_queue_result(struct ast_channel *chan, enum queue_result res)
1909{
1910 int i;
1911
1912 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
1913 if (queue_results[i].id == res) {
1914 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
1915 return;
1916 }
1917 }
1918}
1919
1920static const char *int2strat(int strategy)
1921{
1922 int x;
1923
1924 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1925 if (strategy == strategies[x].strategy) {
1926 return strategies[x].name;
1927 }
1928 }
1929
1930 return "<unknown>";
1931}
1932
1933static int strat2int(const char *strategy)
1934{
1935 int x;
1936
1937 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1938 if (!strcasecmp(strategy, strategies[x].name)) {
1939 return strategies[x].strategy;
1940 }
1941 }
1942
1943 return -1;
1944}
1945
1946static int autopause2int(const char *autopause)
1947{
1948 int x;
1949 /*This 'double check' that default value is OFF */
1951 return QUEUE_AUTOPAUSE_OFF;
1952 }
1953
1954 /*This 'double check' is to ensure old values works */
1955 if(ast_true(autopause)) {
1956 return QUEUE_AUTOPAUSE_ON;
1957 }
1958
1959 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
1960 if (!strcasecmp(autopause, autopausesmodes[x].name)) {
1961 return autopausesmodes[x].autopause;
1962 }
1963 }
1964
1965 /*This 'double check' that default value is OFF */
1966 return QUEUE_AUTOPAUSE_OFF;
1967}
1968
1969static int queue_hash_cb(const void *obj, const int flags)
1970{
1971 const struct call_queue *q = obj;
1972
1973 return ast_str_case_hash(q->name);
1974}
1975
1976static int queue_cmp_cb(void *obj, void *arg, int flags)
1977{
1978 struct call_queue *q = obj, *q2 = arg;
1979 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
1980}
1981
1982/*!
1983 * \brief Return wrapuptime
1984 *
1985 * This function checks if wrapuptime in member is set and return this value.
1986 * Otherwise return value the wrapuptime in the queue configuration
1987 * \return integer value
1988 */
1989static int get_wrapuptime(struct call_queue *q, struct member *member)
1990{
1991 if (member->wrapuptime) {
1992 return member->wrapuptime;
1993 }
1994 return q->wrapuptime;
1995}
1996
1997/*! \internal
1998 * \brief ao2_callback, Decreases queuepos of all followers with a queuepos greater than arg.
1999 * \param obj the member being acted on
2000 * \param arg pointer to an integer containing the position value that was removed and requires reduction for anything above
2001 * \param flag unused
2002 */
2003static int queue_member_decrement_followers(void *obj, void *arg, int flag)
2004{
2005 struct member *mem = obj;
2006 int *decrement_followers_after = arg;
2007
2008 if (mem->queuepos > *decrement_followers_after) {
2009 mem->queuepos--;
2010 }
2011
2012 return 0;
2013}
2014
2015/*! \internal
2016 * \brief ao2_callback, finds members in a queue marked for deletion and in a cascading fashion runs queue_member_decrement_followers
2017 * on them. This callback should always be ran before performing mass unlinking of delmarked members from queues.
2018 * \param obj member being acted on
2019 * \param arg pointer to the queue members are being removed from
2020 * \param flag unused
2021 */
2022static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
2023{
2024 struct member *mem = obj;
2025 struct call_queue *queue = arg;
2026 int rrpos = mem->queuepos;
2027
2028 if (mem->delme) {
2030 }
2031
2032 return 0;
2033}
2034
2035/*! \internal
2036 * \brief Use this to decrement followers during removal of a member
2037 * \param queue which queue the member is being removed from
2038 * \param mem which member is being removed from the queue
2039 */
2040static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
2041{
2042 int pos = mem->queuepos;
2043
2044 /* 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
2045 * who would have been next otherwise. */
2046 if (pos < queue->rrpos) {
2047 queue->rrpos--;
2048 }
2049
2051}
2052
2053#define queue_ref(q) ao2_bump(q)
2054#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2055#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2056#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2057#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2058#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2059
2060/*! \brief Set variables of queue */
2061static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
2062{
2063 char interfacevar[256]="";
2064 float sl = 0;
2065
2066 ao2_lock(q);
2067
2068 if (q->setqueuevar) {
2069 sl = 0;
2070 if (q->callscompleted > 0) {
2071 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
2072 }
2073
2074 snprintf(interfacevar, sizeof(interfacevar),
2075 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2077
2078 ao2_unlock(q);
2079
2080 pbx_builtin_setvar_multiple(chan, interfacevar);
2081 } else {
2082 ao2_unlock(q);
2083 }
2084}
2085
2086/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
2087static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
2088{
2089 struct queue_ent *cur;
2090
2091 if (!q || !new)
2092 return;
2093 if (prev) {
2094 cur = prev->next;
2095 prev->next = new;
2096 } else {
2097 cur = q->head;
2098 q->head = new;
2099 }
2100 new->next = cur;
2101
2102 /* every queue_ent must have a reference to it's parent call_queue, this
2103 * reference does not go away until the end of the queue_ent's life, meaning
2104 * that even when the queue_ent leaves the call_queue this ref must remain. */
2105 if (!new->parent) {
2106 queue_ref(q);
2107 new->parent = q;
2108 }
2109 new->pos = ++(*pos);
2110 new->opos = *pos;
2111}
2112
2114{
2116 RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
2117 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2118
2119 channel_string = ast_manager_build_channel_state_string(obj->snapshot);
2120 event_string = ast_manager_str_from_json_object(obj->blob, NULL);
2121 if (!channel_string || !event_string) {
2122 return NULL;
2123 }
2124
2126 "%s"
2127 "%s",
2128 ast_str_buffer(channel_string),
2129 ast_str_buffer(event_string));
2130}
2131
2133{
2134 return queue_channel_to_ami("QueueCallerJoin", message);
2135}
2136
2138{
2139 return queue_channel_to_ami("QueueCallerLeave", message);
2140}
2141
2143{
2144 return queue_channel_to_ami("QueueCallerAbandon", message);
2145}
2146
2147STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_join_type,
2149 );
2150STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_leave_type,
2152 );
2153STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_abandon_type,
2155 );
2156
2158{
2159 struct ast_json_payload *payload = stasis_message_data(message);
2160 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2161
2162 event_string = ast_manager_str_from_json_object(payload->json, NULL);
2163 if (!event_string) {
2164 return NULL;
2165 }
2166
2168 "%s",
2169 ast_str_buffer(event_string));
2170}
2171
2173{
2174 return queue_member_to_ami("QueueMemberStatus", message);
2175}
2176
2178{
2179 return queue_member_to_ami("QueueMemberAdded", message);
2180}
2181
2183{
2184 return queue_member_to_ami("QueueMemberRemoved", message);
2185}
2186
2188{
2189 return queue_member_to_ami("QueueMemberPause", message);
2190}
2191
2193{
2194 return queue_member_to_ami("QueueMemberPenalty", message);
2195}
2196
2198{
2199 return queue_member_to_ami("QueueMemberRinginuse", message);
2200}
2201
2202STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_status_type,
2204 );
2205STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_added_type,
2207 );
2208STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_removed_type,
2210 );
2211STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_pause_type,
2213 );
2214STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_penalty_type,
2216 );
2217STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_ringinuse_type,
2219 );
2220
2222{
2225 struct ast_channel_snapshot *agent;
2226 RAII_VAR(struct ast_str *, caller_event_string, NULL, ast_free);
2227 RAII_VAR(struct ast_str *, agent_event_string, NULL, ast_free);
2228 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2229
2231 if (caller) {
2232 caller_event_string = ast_manager_build_channel_state_string(caller);
2233 if (!caller_event_string) {
2234 ast_log(LOG_NOTICE, "No caller event string, bailing\n");
2235 return NULL;
2236 }
2237 }
2238
2239 agent = ast_multi_channel_blob_get_channel(obj, "agent");
2240 if (agent) {
2241 agent_event_string = ast_manager_build_channel_state_string_prefix(agent, "Dest");
2242 if (!agent_event_string) {
2243 ast_log(LOG_NOTICE, "No agent event string, bailing\n");
2244 return NULL;
2245 }
2246 }
2247
2249 if (!event_string) {
2250 return NULL;
2251 }
2252
2254 "%s"
2255 "%s"
2256 "%s",
2257 caller_event_string ? ast_str_buffer(caller_event_string) : "",
2258 agent_event_string ? ast_str_buffer(agent_event_string) : "",
2259 ast_str_buffer(event_string));
2260}
2261
2263{
2264 return queue_multi_channel_to_ami("AgentCalled", message);
2265}
2266
2268{
2269 return queue_multi_channel_to_ami("AgentConnect", message);
2270}
2271
2273{
2274 return queue_multi_channel_to_ami("AgentComplete", message);
2275}
2276
2278{
2279 return queue_multi_channel_to_ami("AgentDump", message);
2280}
2281
2283{
2284 return queue_multi_channel_to_ami("AgentRingNoAnswer", message);
2285}
2286
2287STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_called_type,
2289 );
2290STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_connect_type,
2292 );
2293STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_complete_type,
2295 );
2298 );
2299STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_ringnoanswer_type,
2301 );
2302
2304 struct ast_channel_snapshot *caller_snapshot,
2305 struct ast_channel_snapshot *agent_snapshot,
2306 struct stasis_message_type *type, struct ast_json *blob)
2307{
2308 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
2309 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2310
2311 if (!type) {
2312 return;
2313 }
2314
2315 payload = ast_multi_channel_blob_create(blob);
2316 if (!payload) {
2317 return;
2318 }
2319
2320 if (caller_snapshot) {
2321 ast_multi_channel_blob_add_channel(payload, "caller", caller_snapshot);
2322 } else {
2323 ast_debug(1, "Empty caller_snapshot; sending incomplete event\n");
2324 }
2325
2326 if (agent_snapshot) {
2327 ast_multi_channel_blob_add_channel(payload, "agent", agent_snapshot);
2328 }
2329
2330 msg = stasis_message_create(type, payload);
2331 if (!msg) {
2332 return;
2333 }
2334
2335 stasis_publish(topic, msg);
2336}
2337
2338static void queue_publish_multi_channel_blob(struct ast_channel *caller, struct ast_channel *agent,
2339 struct stasis_message_type *type, struct ast_json *blob)
2340{
2341 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
2342 RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
2343
2344 ast_channel_lock(caller);
2345 caller_snapshot = ast_channel_snapshot_create(caller);
2346 ast_channel_unlock(caller);
2347 ast_channel_lock(agent);
2348 agent_snapshot = ast_channel_snapshot_create(agent);
2349 ast_channel_unlock(agent);
2350
2351 if (!caller_snapshot || !agent_snapshot) {
2352 return;
2353 }
2354
2356 agent_snapshot, type, blob);
2357}
2358
2359/*!
2360 * \internal
2361 * \brief Publish the member blob.
2362 * \since 12.0.0
2363 *
2364 * \param type Stasis message type to publish.
2365 * \param blob The information being published.
2366 *
2367 * \note The json blob reference is passed to this function.
2368 */
2370{
2371 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
2372 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2373
2374 if (!blob || !type) {
2375 ast_json_unref(blob);
2376 return;
2377 }
2378
2379 payload = ast_json_payload_create(blob);
2380 ast_json_unref(blob);
2381 if (!payload) {
2382 return;
2383 }
2384
2385 msg = stasis_message_create(type, payload);
2386 if (!msg) {
2387 return;
2388 }
2389
2391}
2392
2393static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
2394{
2395 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}",
2396 "Queue", q->name,
2397 "MemberName", mem->membername,
2398 "Interface", mem->interface,
2399 "StateInterface", mem->state_interface,
2400 "Membership", (mem->dynamic ? "dynamic" : (mem->realtime ? "realtime" : "static")),
2401 "Penalty", mem->penalty,
2402 "CallsTaken", mem->calls,
2403 "LastCall", (int)mem->lastcall,
2404 "LastPause", (int)mem->lastpause,
2405 "LoginTime", (int)mem->logintime,
2406 "InCall", mem->starttime ? 1 : 0,
2407 "Status", mem->status,
2408 "Paused", mem->paused,
2409 "PausedReason", mem->reason_paused,
2410 "Ringinuse", mem->ringinuse,
2411 "Wrapuptime", mem->wrapuptime);
2412}
2413
2414/*! \brief Check if members are available
2415 *
2416 * This function checks to see if members are available to be called. If any member
2417 * is available, the function immediately returns 0. If no members are available,
2418 * then -1 is returned.
2419 */
2420static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
2421{
2422 struct member *member;
2423 struct ao2_iterator mem_iter;
2424
2425 ao2_lock(q);
2426 mem_iter = ao2_iterator_init(q->members, 0);
2427 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
2428 int penalty = member->penalty;
2429 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2430 ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
2431 penalty = raise_penalty;
2432 }
2433 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2434 if (conditions & QUEUE_EMPTY_PENALTY) {
2435 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
2436 continue;
2437 }
2438 }
2439
2440 switch (devstate ? ast_device_state(member->state_interface) : member->status) {
2441 case AST_DEVICE_INVALID:
2442 if (conditions & QUEUE_EMPTY_INVALID) {
2443 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
2444 break;
2445 }
2446 goto default_case;
2448 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2449 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
2450 break;
2451 }
2452 goto default_case;
2453 case AST_DEVICE_INUSE:
2454 if (conditions & QUEUE_EMPTY_INUSE) {
2455 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
2456 break;
2457 }
2458 goto default_case;
2459 case AST_DEVICE_RINGING:
2460 if (conditions & QUEUE_EMPTY_RINGING) {
2461 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
2462 break;
2463 }
2464 goto default_case;
2465 case AST_DEVICE_UNKNOWN:
2466 if (conditions & QUEUE_EMPTY_UNKNOWN) {
2467 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
2468 break;
2469 }
2470 /* Fall-through */
2471 default:
2472 default_case:
2473 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2474 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
2475 break;
2476 } else if ((conditions & QUEUE_EMPTY_WRAPUP)
2477 && member->lastcall
2478 && get_wrapuptime(q, member)
2479 && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
2480 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2481 member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
2482 break;
2483 } else {
2484 ao2_ref(member, -1);
2485 ao2_iterator_destroy(&mem_iter);
2486 ao2_unlock(q);
2487 ast_debug(4, "%s is available.\n", member->membername);
2488 return 0;
2489 }
2490 break;
2491 }
2492 }
2493 ao2_iterator_destroy(&mem_iter);
2494 ao2_unlock(q);
2495
2496 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2497 /* member state still may be RINGING due to lag in event message - check again with device state */
2498 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2499 }
2500 return -1;
2501}
2502
2503/*
2504 * A "pool" of member objects that calls are currently pending on. If an
2505 * agent is a member of multiple queues it's possible for that agent to be
2506 * called by each of the queues at the same time. This happens because device
2507 * state is slow to notify the queue app of one of it's member's being rung.
2508 * This "pool" allows us to track which members are currently being rung while
2509 * we wait on the device state change.
2510 */
2512#define MAX_CALL_ATTEMPT_BUCKETS 353
2513
2514static int pending_members_hash(const void *obj, const int flags)
2515{
2516 const struct member *object;
2517 const char *key;
2518
2519 switch (flags & OBJ_SEARCH_MASK) {
2520 case OBJ_SEARCH_KEY:
2521 key = obj;
2522 break;
2523 case OBJ_SEARCH_OBJECT:
2524 object = obj;
2525 key = object->interface;
2526 break;
2527 default:
2528 ast_assert(0);
2529 return 0;
2530 }
2531 return ast_str_case_hash(key);
2532}
2533
2534static int pending_members_cmp(void *obj, void *arg, int flags)
2535{
2536 const struct member *object_left = obj;
2537 const struct member *object_right = arg;
2538 const char *right_key = arg;
2539 int cmp;
2540
2541 switch (flags & OBJ_SEARCH_MASK) {
2542 case OBJ_SEARCH_OBJECT:
2543 right_key = object_right->interface;
2544 /* Fall through */
2545 case OBJ_SEARCH_KEY:
2546 cmp = strcasecmp(object_left->interface, right_key);
2547 break;
2549 /* Not supported by container. */
2550 ast_assert(0);
2551 return 0;
2552 default:
2553 cmp = 0;
2554 break;
2555 }
2556 if (cmp) {
2557 return 0;
2558 }
2559 return CMP_MATCH;
2560}
2561
2562static void pending_members_remove(struct member *mem)
2563{
2564 ast_debug(3, "Removed %s from pending_members\n", mem->membername);
2566}
2567
2568/*! \brief set a member's status based on device state of that member's state_interface.
2569 *
2570 * Lock interface list find sc, iterate through each queues queue_member list for member to
2571 * update state inside queues
2572*/
2573static void update_status(struct call_queue *q, struct member *m, const int status)
2574{
2575 if (m->status != status) {
2576 /* If this member has transitioned to being available then update their queue
2577 * information. If they are currently in a call then the leg to the agent will be
2578 * considered done and the call finished.
2579 */
2582 }
2583
2584 m->status = status;
2585
2586 /* Remove the member from the pending members pool only when the status changes.
2587 * This is not done unconditionally because we can occasionally see multiple
2588 * device state notifications of not in use after a previous call has ended,
2589 * including after we have initiated a new call. This is more likely to
2590 * happen when there is latency in the connection to the member.
2591 */
2593
2594 queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2595 }
2596}
2597
2598/*!
2599 * \internal
2600 * \brief Determine if a queue member is available
2601 * \retval 1 if the member is available
2602 * \retval 0 if the member is not available
2603 */
2604static int is_member_available(struct call_queue *q, struct member *mem)
2605{
2606 int available = 0;
2607 int wrapuptime;
2608
2609 switch (mem->status) {
2610 case AST_DEVICE_INVALID:
2612 break;
2613 case AST_DEVICE_INUSE:
2614 case AST_DEVICE_BUSY:
2615 case AST_DEVICE_RINGING:
2617 case AST_DEVICE_ONHOLD:
2618 if (!mem->ringinuse) {
2619 break;
2620 }
2621 /* else fall through */
2623 case AST_DEVICE_UNKNOWN:
2624 if (!mem->paused) {
2625 available = 1;
2626 }
2627 break;
2628 }
2629
2630 /* Let wrapuptimes override device state availability */
2631 wrapuptime = get_wrapuptime(q, mem);
2632 if (mem->lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->lastcall)) {
2633 available = 0;
2634 }
2635 return available;
2636}
2637
2638/*! \brief set a member's status based on device state of that member's interface*/
2639static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
2640{
2641 struct ao2_iterator miter, qiter;
2642 struct ast_device_state_message *dev_state;
2643 struct member *m;
2644 struct call_queue *q;
2645 char interface[80], *slash_pos;
2646 int found = 0; /* Found this member in any queue */
2647 int found_member; /* Found this member in this queue */
2648 int avail = 0; /* Found an available member in this queue */
2649
2651 return;
2652 }
2653
2654 dev_state = stasis_message_data(msg);
2655 if (dev_state->eid) {
2656 /* ignore non-aggregate states */
2657 return;
2658 }
2659
2660 qiter = ao2_iterator_init(queues, 0);
2661 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
2662 ao2_lock(q);
2663
2664 avail = 0;
2665 found_member = 0;
2666 miter = ao2_iterator_init(q->members, 0);
2667 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2668 if (!found_member) {
2669 ast_copy_string(interface, m->state_interface, sizeof(interface));
2670
2671 if ((slash_pos = strchr(interface, '/'))) {
2672 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) {
2673 *slash_pos = '\0';
2674 }
2675 }
2676
2677 if (!strcasecmp(interface, dev_state->device)) {
2678 found_member = 1;
2679 update_status(q, m, dev_state->state);
2680 }
2681 }
2682
2683 /* check every member until we find one NOT_INUSE */
2684 if (!avail) {
2685 avail = is_member_available(q, m);
2686 }
2687 if (avail && found_member) {
2688 /* early exit as we've found an available member and the member of interest */
2689 ao2_ref(m, -1);
2690 break;
2691 }
2692 }
2693
2694 if (found_member) {
2695 found = 1;
2696 if (avail) {
2698 } else {
2700 }
2701 }
2702
2703 ao2_iterator_destroy(&miter);
2704
2705 ao2_unlock(q);
2706 queue_t_unref(q, "Done with iterator");
2707 }
2708 ao2_iterator_destroy(&qiter);
2709
2710 if (found) {
2711 ast_debug(1, "Device '%s' changed to state '%u' (%s)\n",
2712 dev_state->device,
2713 dev_state->state,
2714 ast_devstate2str(dev_state->state));
2715 } else {
2716 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",
2717 dev_state->device,
2718 dev_state->state,
2719 ast_devstate2str(dev_state->state));
2720 }
2721
2722 return;
2723}
2724
2725/*! \brief Helper function which converts from extension state to device state values */
2727{
2728 switch (state) {
2731 break;
2734 break;
2735 case AST_EXTENSION_BUSY:
2737 break;
2740 break;
2743 break;
2746 break;
2749 break;
2752 break;
2755 default:
2757 break;
2758 }
2759
2760 return state;
2761}
2762
2763/*!
2764 * \brief Returns if one context includes another context
2765 *
2766 * \param parent Parent context to search for child
2767 * \param child Context to check for inclusion in parent
2768 *
2769 * This function recursively checks if the context child is included in the context parent.
2770 *
2771 * \retval 1 if child is included in parent
2772 * \retval 0 if not
2773 */
2774static int context_included(const char *parent, const char *child);
2775static int context_included(const char *parent, const char *child)
2776{
2777 struct ast_context *c = NULL;
2778
2779 c = ast_context_find(parent);
2780 if (!c) {
2781 /* well, if parent doesn't exist, how can the child be included in it? */
2782 return 0;
2783 }
2784 if (!strcmp(ast_get_context_name(c), parent)) {
2785 /* found the context of the hint app_queue is using. Now, see
2786 if that context includes the one that just changed state */
2787 struct ast_include *inc = NULL;
2788
2789 while ((inc = (struct ast_include*) ast_walk_context_includes(c, inc))) {
2790 const char *includename = ast_get_include_name(inc);
2791 if (!strcasecmp(child, includename)) {
2792 return 1;
2793 }
2794 /* recurse on this context, for nested includes. The
2795 PBX extension parser will prevent infinite recursion. */
2796 if (context_included(includename, child)) {
2797 return 1;
2798 }
2799 }
2800 }
2801 return 0;
2802}
2803
2804static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
2805{
2806 struct ao2_iterator miter, qiter;
2807 struct member *m;
2808 struct call_queue *q;
2809 int state = info->exten_state;
2810 int found = 0, device_state = extensionstate2devicestate(state);
2811
2812 /* only interested in extension state updates involving device states */
2813 if (info->reason != AST_HINT_UPDATE_DEVICE) {
2814 return 0;
2815 }
2816
2817 qiter = ao2_iterator_init(queues, 0);
2818 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
2819 ao2_lock(q);
2820
2821 miter = ao2_iterator_init(q->members, 0);
2822 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2823 if (!strcmp(m->state_exten, exten) &&
2825 /* context could be included in m->state_context. We need to check. */
2826 found = 1;
2827 update_status(q, m, device_state);
2828 }
2829 }
2830 ao2_iterator_destroy(&miter);
2831
2832 ao2_unlock(q);
2833 queue_t_unref(q, "Done with iterator");
2834 }
2835 ao2_iterator_destroy(&qiter);
2836
2837 if (found) {
2838 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
2839 } else {
2840 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",
2841 exten, context, device_state, ast_devstate2str(device_state));
2842 }
2843
2844 return 0;
2845}
2846
2847/*! \brief Return the current state of a member */
2848static int get_queue_member_status(struct member *cur)
2849{
2851}
2852
2853static void destroy_queue_member_cb(void *obj)
2854{
2855 struct member *mem = obj;
2856
2857 if (mem->state_id != -1) {
2859 }
2860}
2861
2862/*! \brief allocate space for new queue member and set fields based on parameters passed */
2863static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
2864{
2865 struct member *cur;
2866
2867 if ((cur = ao2_alloc(sizeof(*cur), destroy_queue_member_cb))) {
2868 cur->ringinuse = ringinuse;
2869 cur->penalty = penalty;
2870 cur->paused = paused;
2871 cur->wrapuptime = wrapuptime;
2872 if (paused) {
2873 time(&cur->lastpause); /* Update time of last pause */
2874 }
2875 time(&cur->logintime);
2876 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
2879 } else {
2881 }
2883 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
2884 } else {
2885 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
2886 }
2887 if (!strchr(cur->interface, '/')) {
2888 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
2889 }
2890 if (!strncmp(cur->state_interface, "hint:", 5)) {
2891 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
2892 char *exten = strsep(&context, "@") + 5;
2893
2894 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
2895 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
2896
2898 } else {
2899 cur->state_id = -1;
2900 }
2901 cur->status = get_queue_member_status(cur);
2902 }
2903
2904 return cur;
2905}
2906
2907
2908static int compress_char(const char c)
2909{
2910 if (c < 32) {
2911 return 0;
2912 } else if (c > 96) {
2913 return c - 64;
2914 }
2915 return c - 32;
2916}
2917
2918static int member_hash_fn(const void *obj, const int flags)
2919{
2920 const struct member *mem = obj;
2921 const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
2922 const char *chname = strchr(interface, '/');
2923 int ret = 0, i;
2924
2925 if (!chname) {
2926 chname = interface;
2927 }
2928 for (i = 0; i < 5 && chname[i]; i++) {
2929 ret += compress_char(chname[i]) << (i * 6);
2930 }
2931 return ret;
2932}
2933
2934static int member_cmp_fn(void *obj1, void *obj2, int flags)
2935{
2936 struct member *mem1 = obj1;
2937 struct member *mem2 = obj2;
2938 const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;
2939
2940 return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
2941}
2942
2943/*!
2944 * \brief Initialize Queue default values.
2945 * \note the queue's lock must be held before executing this function
2946*/
2947static void init_queue(struct call_queue *q)
2948{
2949 int i;
2950 struct penalty_rule *pr_iter;
2951
2952 q->dead = 0;
2953 q->retry = DEFAULT_RETRY;
2955 q->maxlen = 0;
2956
2957 ast_string_field_set(q, announce, "");
2959 ast_string_field_set(q, membergosub, "");
2960 ast_string_field_set(q, defaultrule, "");
2961
2962 q->announcefrequency = 0;
2964 q->announceholdtime = 1;
2966 q->announcepositionlimit = 10; /* Default 10 positions */
2967 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
2968 q->roundingseconds = 0; /* Default - don't announce seconds */
2969 q->servicelevel = 0;
2970 q->ringinuse = 1;
2972 q->setinterfacevar = 0;
2973 q->setqueuevar = 0;
2974 q->setqueueentryvar = 0;
2976 q->monfmt[0] = '\0';
2977 q->reportholdtime = 0;
2978 q->wrapuptime = 0;
2979 q->penaltymemberslimit = 0;
2980 q->joinempty = 0;
2981 q->leavewhenempty = 0;
2982 q->memberdelay = 0;
2983 q->weight = 0;
2984 q->timeoutrestart = 0;
2988 q->numperiodicannounce = 0;
2991 q->autopausebusy = 0;
2992 q->autopauseunavail = 0;
2994 q->autopausedelay = 0;
2995 if (!q->members) {
2997 /* linear strategy depends on order, so we have to place all members in a list */
2999 } else {
3002 }
3003 }
3004 q->found = 1;
3005
3006 ast_string_field_set(q, moh, "");
3007 ast_string_field_set(q, sound_next, "queue-youarenext");
3008 ast_string_field_set(q, sound_thereare, "queue-thereare");
3009 ast_string_field_set(q, sound_calls, "queue-callswaiting");
3010 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
3011 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
3012 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
3013 ast_string_field_set(q, sound_minutes, "queue-minutes");
3014 ast_string_field_set(q, sound_minute, "queue-minute");
3015 ast_string_field_set(q, sound_seconds, "queue-seconds");
3016 ast_string_field_set(q, sound_thanks, "queue-thankyou");
3017 ast_string_field_set(q, sound_callerannounce, "");
3018 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
3019
3020 if (!q->sound_periodicannounce[0]) {
3022 }
3023
3024 if (q->sound_periodicannounce[0]) {
3025 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
3026 }
3027
3028 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3029 if (q->sound_periodicannounce[i]) {
3030 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
3031 }
3032 }
3033
3034 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
3035 ast_free(pr_iter);
3036 }
3037
3038 /* On restart assume no members are available.
3039 * The queue_avail hint is a boolean state to indicate whether a member is available or not.
3040 *
3041 * This seems counter intuitive, but is required to light a BLF
3042 * AST_DEVICE_INUSE indicates no members are available.
3043 * AST_DEVICE_NOT_INUSE indicates a member is available.
3044 */
3046}
3047
3048static void clear_queue(struct call_queue *q)
3049{
3050 q->holdtime = 0;
3051 q->callscompleted = 0;
3052 q->callsabandoned = 0;
3053 q->callscompletedinsl = 0;
3054 q->callsabandonedinsl = 0;
3055 q->talktime = 0;
3056
3057 if (q->members) {
3058 struct member *mem;
3059 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3060 while ((mem = ao2_iterator_next(&mem_iter))) {
3061 mem->calls = 0;
3062 mem->callcompletedinsl = 0;
3063 mem->lastcall = 0;
3064 mem->starttime = 0;
3065 ao2_ref(mem, -1);
3066 }
3067 ao2_iterator_destroy(&mem_iter);
3068 }
3069}
3070
3071/*!
3072 * \brief Change queue penalty by adding rule.
3073 *
3074 * Check rule for errors with time or formatting, see if rule is relative to rest
3075 * of queue, iterate list of rules to find correct insertion point, insert and return.
3076 * \retval -1 on failure
3077 * \retval 0 on success
3078 * \note Call this with the rule_lists locked
3079*/
3080static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
3081{
3082 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3083 struct penalty_rule *rule = NULL, *rule_iter;
3084 struct rule_list *rl_iter;
3085 int penaltychangetime, inserted = 0;
3086
3087 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
3088 return -1;
3089 }
3090
3091 contentdup = ast_strdupa(content);
3092
3093 if (!(maxstr = strchr(contentdup, ','))) {
3094 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3095 ast_free(rule);
3096 return -1;
3097 }
3098
3099 *maxstr++ = '\0';
3100 if ((minstr = strchr(maxstr,','))) {
3101 *minstr++ = '\0';
3102 if ((raisestr = strchr(minstr,','))) {
3103 *raisestr++ = '\0';
3104 }
3105 } else {
3106 raisestr = NULL;
3107 }
3108
3109 timestr = contentdup;
3110 if ((penaltychangetime = atoi(timestr)) < 0) {
3111 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3112 ast_free(rule);
3113 return -1;
3114 }
3115
3116 rule->time = penaltychangetime;
3117
3118 /* The last check will evaluate true if either no penalty change is indicated for a given rule
3119 * OR if a min penalty change is indicated but no max penalty change is */
3120 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
3121 rule->max_relative = 1;
3122 }
3123
3124 rule->max_value = atoi(maxstr);
3125
3126 if (!ast_strlen_zero(minstr)) {
3127 if (*minstr == '+' || *minstr == '-') {
3128 rule->min_relative = 1;
3129 }
3130 rule->min_value = atoi(minstr);
3131 } else { /*there was no minimum specified, so assume this means no change*/
3132 rule->min_relative = 1;
3133 }
3134
3135 if (!ast_strlen_zero(raisestr)) {
3136 if (*raisestr == '+' || *raisestr == '-') {
3137 rule->raise_relative = 1;
3138 }
3139 rule->raise_value = atoi(raisestr);
3140 } else { /*there was no raise specified, so assume this means no change*/
3141 rule->raise_relative = 1;
3142 }
3143
3144 /*We have the rule made, now we need to insert it where it belongs*/
3145 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
3146 if (strcasecmp(rl_iter->name, list_name)) {
3147 continue;
3148 }
3149
3150 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
3151 if (rule->time < rule_iter->time) {
3153 inserted = 1;
3154 break;
3155 }
3156 }
3158
3159 if (!inserted) {
3160 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
3161 inserted = 1;
3162 }
3163
3164 break;
3165 }
3166
3167 if (!inserted) {
3168 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
3169 ast_free(rule);
3170 return -1;
3171 }
3172 return 0;
3173}
3174
3175/*!
3176 * \brief Load queue rules from realtime.
3177 *
3178 * Check rule for errors with time or formatting, see if rule is relative to rest
3179 * of queue, iterate list of rules to find correct insertion point, insert and return.
3180 * \retval -1 on failure
3181 * \retval 0 on success
3182 * \note Call this with the rule_lists locked
3183*/
3184static int load_realtime_rules(void)
3185{
3186 struct ast_config *cfg;
3187 struct rule_list *rl_iter, *new_rl;
3188 struct penalty_rule *pr_iter;
3189 char *rulecat = NULL;
3190
3191 if (!ast_check_realtime("queue_rules")) {
3192 ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
3193 return 0;
3194 }
3195 if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
3196 ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
3197 return 0;
3198 }
3199 while ((rulecat = ast_category_browse(cfg, rulecat))) {
3200 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3201 int penaltychangetime, rule_exists = 0, inserted = 0;
3202 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3203 int min_relative = 0, max_relative = 0, raise_relative = 0;
3204 struct penalty_rule *new_penalty_rule = NULL;
3205
3206 rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
3207 if (ast_strlen_zero(rule_name)) {
3208 continue;
3209 }
3210
3211 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
3212 if (!(strcasecmp(rl_iter->name, rule_name))) {
3213 rule_exists = 1;
3214 new_rl = rl_iter;
3215 break;
3216 }
3217 }
3218 if (!rule_exists) {
3219 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
3220 ast_config_destroy(cfg);
3221 return -1;
3222 }
3223 ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
3225 }
3226 timestr = ast_variable_retrieve(cfg, rulecat, "time");
3227 if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
3228 ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
3229 (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
3230 continue;
3231 }
3232 if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
3233 ast_config_destroy(cfg);
3234 return -1;
3235 }
3236 if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
3237 ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
3238 max_penalty = 0;
3239 max_relative = 1;
3240 } else {
3241 if (*maxstr == '+' || *maxstr == '-') {
3242 max_relative = 1;
3243 }
3244 }
3245 if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
3246 ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
3247 min_penalty = 0;
3248 min_relative = 1;
3249 } else {
3250 if (*minstr == '+' || *minstr == '-') {
3251 min_relative = 1;
3252 }
3253 }
3254 if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
3255 ast_strlen_zero(raisestr) || sscanf(raisestr, "%30d", &raise_penalty) != 1) {
3256 raise_penalty = 0;
3257 raise_relative = 1;
3258 } else {
3259 if (*raisestr == '+' || *raisestr == '-') {
3260 raise_relative = 1;
3261 }
3262 }
3263 new_penalty_rule->time = penaltychangetime;
3264 new_penalty_rule->max_relative = max_relative;
3265 new_penalty_rule->max_value = max_penalty;
3266 new_penalty_rule->min_relative = min_relative;
3267 new_penalty_rule->min_value = min_penalty;
3268 new_penalty_rule->raise_relative = raise_relative;
3269 new_penalty_rule->raise_value = raise_penalty;
3270 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
3271 if (new_penalty_rule->time < pr_iter->time) {
3272 AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
3273 inserted = 1;
3274 }
3275 }
3277 if (!inserted) {
3278 AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
3279 }
3280 }
3281
3282 ast_config_destroy(cfg);
3283 return 0;
3284}
3285
3286static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
3287{
3288 char *value_copy = ast_strdupa(value);
3289 char *option = NULL;
3290 while ((option = strsep(&value_copy, ","))) {
3291 if (!strcasecmp(option, "paused")) {
3292 *empty |= QUEUE_EMPTY_PAUSED;
3293 } else if (!strcasecmp(option, "penalty")) {
3294 *empty |= QUEUE_EMPTY_PENALTY;
3295 } else if (!strcasecmp(option, "inuse")) {
3296 *empty |= QUEUE_EMPTY_INUSE;
3297 } else if (!strcasecmp(option, "ringing")) {
3298 *empty |= QUEUE_EMPTY_RINGING;
3299 } else if (!strcasecmp(option, "invalid")) {
3300 *empty |= QUEUE_EMPTY_INVALID;
3301 } else if (!strcasecmp(option, "wrapup")) {
3302 *empty |= QUEUE_EMPTY_WRAPUP;
3303 } else if (!strcasecmp(option, "unavailable")) {
3304 *empty |= QUEUE_EMPTY_UNAVAILABLE;
3305 } else if (!strcasecmp(option, "unknown")) {
3306 *empty |= QUEUE_EMPTY_UNKNOWN;
3307 } else if (!strcasecmp(option, "loose")) {
3309 } else if (!strcasecmp(option, "strict")) {
3311 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
3313 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
3314 *empty = 0;
3315 } else {
3316 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
3317 }
3318 }
3319}
3320
3321/*! \brief Configure a queue parameter.
3322 *
3323 * The failunknown flag is set for config files (and static realtime) to show
3324 * errors for unknown parameters. It is cleared for dynamic realtime to allow
3325 * extra fields in the tables.
3326 * \note For error reporting, line number is passed for .conf static configuration,
3327 * for Realtime queues, linenum is -1.
3328*/
3329static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
3330{
3331 if (!strcasecmp(param, "musicclass") ||
3332 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
3333 ast_string_field_set(q, moh, val);
3334 } else if (!strcasecmp(param, "announce")) {
3335 ast_string_field_set(q, announce, val);
3336 } else if (!strcasecmp(param, "context")) {
3338 } else if (!strcasecmp(param, "timeout")) {
3339 q->timeout = atoi(val);
3340 if (q->timeout < 0) {
3342 }
3343 } else if (!strcasecmp(param, "ringinuse")) {
3344 q->ringinuse = ast_true(val);
3345 } else if (!strcasecmp(param, "setinterfacevar")) {
3347 } else if (!strcasecmp(param, "setqueuevar")) {
3348 q->setqueuevar = ast_true(val);
3349 } else if (!strcasecmp(param, "setqueueentryvar")) {
3351 } else if (!strcasecmp(param, "monitor-format")) {
3352 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
3353 } else if (!strcasecmp(param, "membergosub")) {
3354 ast_string_field_set(q, membergosub, val);
3355 } else if (!strcasecmp(param, "queue-youarenext")) {
3356 ast_string_field_set(q, sound_next, val);
3357 } else if (!strcasecmp(param, "queue-thereare")) {
3358 ast_string_field_set(q, sound_thereare, val);
3359 } else if (!strcasecmp(param, "queue-callswaiting")) {
3360 ast_string_field_set(q, sound_calls, val);
3361 } else if (!strcasecmp(param, "queue-quantity1")) {
3362 ast_string_field_set(q, queue_quantity1, val);
3363 } else if (!strcasecmp(param, "queue-quantity2")) {
3364 ast_string_field_set(q, queue_quantity2, val);
3365 } else if (!strcasecmp(param, "queue-holdtime")) {
3366 ast_string_field_set(q, sound_holdtime, val);
3367 } else if (!strcasecmp(param, "queue-minutes")) {
3368 ast_string_field_set(q, sound_minutes, val);
3369 } else if (!strcasecmp(param, "queue-minute")) {
3370 ast_string_field_set(q, sound_minute, val);
3371 } else if (!strcasecmp(param, "queue-seconds")) {
3372 ast_string_field_set(q, sound_seconds, val);
3373 } else if (!strcasecmp(param, "queue-thankyou")) {
3374 ast_string_field_set(q, sound_thanks, val);
3375 } else if (!strcasecmp(param, "queue-callerannounce")) {
3376 ast_string_field_set(q, sound_callerannounce, val);
3377 } else if (!strcasecmp(param, "queue-reporthold")) {
3378 ast_string_field_set(q, sound_reporthold, val);
3379 } else if (!strcasecmp(param, "announce-frequency")) {
3380 q->announcefrequency = atoi(val);
3381 } else if (!strcasecmp(param, "announce-to-first-user")) {
3383 } else if (!strcasecmp(param, "min-announce-frequency")) {
3384 q->minannouncefrequency = atoi(val);
3385 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
3386 } else if (!strcasecmp(param, "announce-round-seconds")) {
3387 q->roundingseconds = atoi(val);
3388 /* Rounding to any other values just doesn't make sense... */
3389 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
3390 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
3391 if (linenum >= 0) {
3392 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3393 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3394 val, param, q->name, linenum);
3395 } else {
3396 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3397 "using 0 instead for queue '%s'\n", val, param, q->name);
3398 }
3399 q->roundingseconds=0;
3400 }
3401 } else if (!strcasecmp(param, "announce-holdtime")) {
3402 if (!strcasecmp(val, "once")) {
3404 } else if (ast_true(val)) {
3406 } else {
3407 q->announceholdtime = 0;
3408 }
3409 } else if (!strcasecmp(param, "announce-position")) {
3410 if (!strcasecmp(val, "limit")) {
3412 } else if (!strcasecmp(val, "more")) {
3414 } else if (ast_true(val)) {
3416 } else {
3418 }
3419 } else if (!strcasecmp(param, "announce-position-only-up")) {
3421 } else if (!strcasecmp(param, "announce-position-limit")) {
3422 q->announcepositionlimit = atoi(val);
3423 } else if (!strcasecmp(param, "periodic-announce")) {
3424 if (strchr(val, ',')) {
3425 char *s, *buf = ast_strdupa(val);
3426 unsigned int i = 0;
3427
3428 while ((s = strsep(&buf, ",|"))) {
3429 if (!q->sound_periodicannounce[i]) {
3431 }
3432 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
3433 i++;
3434 if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
3435 break;
3436 }
3437 }
3438 q->numperiodicannounce = i;
3439 } else {
3440 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
3441 q->numperiodicannounce = 1;
3442 }
3443 } else if (!strcasecmp(param, "periodic-announce-startdelay")) {
3445 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
3446 q->periodicannouncefrequency = atoi(val);
3447 } else if (!strcasecmp(param, "relative-periodic-announce")) {
3449 } else if (!strcasecmp(param, "random-periodic-announce")) {
3451 } else if (!strcasecmp(param, "retry")) {
3452 q->retry = atoi(val);
3453 if (q->retry <= 0) {
3454 q->retry = DEFAULT_RETRY;
3455 }
3456 } else if (!strcasecmp(param, "wrapuptime")) {
3457 q->wrapuptime = atoi(val);
3458 } else if (!strcasecmp(param, "penaltymemberslimit")) {
3459 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
3460 q->penaltymemberslimit = 0;
3461 }
3462 } else if (!strcasecmp(param, "autofill")) {
3463 q->autofill = ast_true(val);
3464 } else if (!strcasecmp(param, "autopause")) {
3466 } else if (!strcasecmp(param, "autopausedelay")) {
3467 q->autopausedelay = atoi(val);
3468 } else if (!strcasecmp(param, "autopausebusy")) {
3470 } else if (!strcasecmp(param, "autopauseunavail")) {
3472 } else if (!strcasecmp(param, "maxlen")) {
3473 q->maxlen = atoi(val);
3474 if (q->maxlen < 0) {
3475 q->maxlen = 0;
3476 }
3477 } else if (!strcasecmp(param, "servicelevel")) {
3478 q->servicelevel= atoi(val);
3479 } else if (!strcasecmp(param, "strategy")) {
3480 int strategy;
3481
3482 /* We are a static queue and already have set this, no need to do it again */
3483 if (failunknown) {
3484 return;
3485 }
3487 if (strategy < 0) {
3488 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3489 val, q->name);
3491 }
3492 if (strategy == q->strategy) {
3493 return;
3494 }
3496 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
3497 return;
3498 }
3499 q->strategy = strategy;
3500 } else if (!strcasecmp(param, "joinempty")) {
3502 } else if (!strcasecmp(param, "leavewhenempty")) {
3504 } else if (!strcasecmp(param, "reportholdtime")) {
3506 } else if (!strcasecmp(param, "memberdelay")) {
3507 q->memberdelay = atoi(val);
3508 } else if (!strcasecmp(param, "weight")) {
3509 q->weight = atoi(val);
3510 } else if (!strcasecmp(param, "timeoutrestart")) {
3512 } else if (!strcasecmp(param, "defaultrule")) {
3513 ast_string_field_set(q, defaultrule, val);
3514 } else if (!strcasecmp(param, "timeoutpriority")) {
3515 if (!strcasecmp(val, "conf")) {
3517 } else {
3519 }
3520 } else if (failunknown) {
3521 if (linenum >= 0) {
3522 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3523 q->name, param, linenum);
3524 } else {
3525 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
3526 }
3527 }
3528}
3529
3530
3531#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3532#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3533#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3534
3535/*! \internal
3536 * \brief If adding a single new member to a queue, use this function instead of ao2_linking.
3537 * This adds round robin queue position data for a fresh member as well as links it.
3538 * \param queue Which queue the member is being added to
3539 * \param mem Which member is being added to the queue
3540 */
3541static void member_add_to_queue(struct call_queue *queue, struct member *mem)
3542{
3543 ao2_lock(queue->members);
3544 mem->queuepos = ao2_container_count(queue->members);
3545 ao2_link(queue->members, mem);
3547 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", queue->name, mem->interface);
3548 ao2_unlock(queue->members);
3549}
3550
3551/*! \internal
3552 * \brief If removing a single member from a queue, use this function instead of ao2_unlinking.
3553 * This will perform round robin queue position reordering for the remaining members.
3554 * \param queue Which queue the member is being removed from
3555 * \param mem Which member is being removed from the queue
3556 */
3557static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
3558{
3560 ao2_lock(queue->members);
3563 ao2_unlink(queue->members, mem);
3564 ao2_unlock(queue->members);
3565}
3566
3567/*!
3568 * \brief Find rt member record to update otherwise create one.
3569 *
3570 * Search for member in queue, if found update penalty/paused state,
3571 * if no member exists create one flag it as a RT member and add to queue member list.
3572*/
3573static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
3574{
3575 struct member *m;
3576 struct ao2_iterator mem_iter;
3577 int penalty = 0;
3578 int paused = 0;
3579 int found = 0;
3580 int wrapuptime = 0;
3581 int ringinuse = q->ringinuse;
3582
3583 const char *config_val;
3584 const char *interface = ast_variable_retrieve(member_config, category, "interface");
3585 const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
3586 const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
3587 const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
3588 const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
3589 const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
3590 const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
3591 const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
3592
3593 if (ast_strlen_zero(rt_uniqueid)) {
3594 ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
3595 S_OR(membername, "NULL"));
3596 return;
3597 }
3598
3599 if (ast_strlen_zero(interface)) {
3600 ast_log(LOG_WARNING, "Realtime field 'interface' is empty for member %s\n",
3601 S_OR(membername, "NULL"));
3602 return;
3603 }
3604
3605 if (penalty_str) {
3606 penalty = atoi(penalty_str);
3607 if ((penalty < 0) && negative_penalty_invalid) {
3608 return;
3609 } else if (penalty < 0) {
3610 penalty = 0;
3611 }
3612 }
3613
3614 if (paused_str) {
3615 paused = atoi(paused_str);
3616 if (paused < 0) {
3617 paused = 0;
3618 }
3619 }
3620
3621 if (wrapuptime_str) {
3622 wrapuptime = atoi(wrapuptime_str);
3623 if (wrapuptime < 0) {
3624 wrapuptime = 0;
3625 }
3626 }
3627
3628 if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3629 if (ast_true(config_val)) {
3630 ringinuse = 1;
3631 } else if (ast_false(config_val)) {
3632 ringinuse = 0;
3633 } else {
3634 ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
3635 }
3636 }
3637
3638 /* Find member by realtime uniqueid and update */
3639 mem_iter = ao2_iterator_init(q->members, 0);
3640 while ((m = ao2_iterator_next(&mem_iter))) {
3641 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
3642 m->dead = 0; /* Do not delete this one. */
3643 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3644 if (paused_str) {
3645 m->paused = paused;
3646 if (paused && m->lastpause == 0) {
3647 time(&m->lastpause); /* XXX: Should this come from realtime? */
3648 }
3650 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, m->interface);
3651 }
3652 if (strcasecmp(state_interface, m->state_interface)) {
3653 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
3654 }
3655 m->penalty = penalty;
3656 m->ringinuse = ringinuse;
3657 m->wrapuptime = wrapuptime;
3659 ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
3660 }
3661 found = 1;
3662 ao2_ref(m, -1);
3663 break;
3664 }
3665 ao2_ref(m, -1);
3666 }
3667 ao2_iterator_destroy(&mem_iter);
3668
3669 /* Create a new member */
3670 if (!found) {
3671 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3672 m->dead = 0;
3673 m->realtime = 1;
3674 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3675 if (!ast_strlen_zero(reason_paused)) {
3676 ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
3677 }
3679 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3680 } else {
3681 ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3682 }
3683 member_add_to_queue(q, m);
3684 ao2_ref(m, -1);
3685 m = NULL;
3686 }
3687 }
3688}
3689
3690/*! \brief Iterate through queue's member list and delete them */
3691static void free_members(struct call_queue *q, int all)
3692{
3693 /* Free non-dynamic members */
3694 struct member *cur;
3695 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3696
3697 while ((cur = ao2_iterator_next(&mem_iter))) {
3698 if (all || !cur->dynamic) {
3700 }
3701 ao2_ref(cur, -1);
3702 }
3703 ao2_iterator_destroy(&mem_iter);
3704}
3705
3706/*! \brief Free queue's member list then its string fields */
3707static void destroy_queue(void *obj)
3708{
3709 struct call_queue *q = obj;
3710 int i;
3711
3712 free_members(q, 1);
3714 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3715 if (q->sound_periodicannounce[i]) {
3717 }
3718 }
3719 ao2_ref(q->members, -1);
3720}
3721
3722static struct call_queue *alloc_queue(const char *queuename)
3723{
3724 struct call_queue *q;
3725
3726 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
3727 if (ast_string_field_init(q, 64)) {
3728 queue_t_unref(q, "String field allocation failed");
3729 return NULL;
3730 }
3731 ast_string_field_set(q, name, queuename);
3732 }
3733 return q;
3734}
3735
3736/*!
3737 * \brief Reload a single queue via realtime.
3738 *
3739 * Check for statically defined queue first, check if deleted RT queue,
3740 * check for new RT queue, if queue vars are not defined init them with defaults.
3741 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
3742 * \retval the queue,
3743 * \retval NULL if it doesn't exist.
3744 * \note Should be called with the "queues" container locked.
3745*/
3746static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
3747{
3748 struct ast_variable *v;
3749 struct call_queue *q, tmpq = {
3750 .name = queuename,
3751 };
3752 struct member *m;
3753 struct ao2_iterator mem_iter;
3754 char *category = NULL;
3755 const char *tmp_name;
3756 char *tmp;
3757 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
3758
3759 /* Static queues override realtime. */
3760 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
3761 ao2_lock(q);
3762 if (!q->realtime) {
3763 if (q->dead) {
3764 ao2_unlock(q);
3765 queue_t_unref(q, "Queue is dead; can't return it");
3766 return NULL;
3767 }
3768 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
3769 ao2_unlock(q);
3770 return q;
3771 }
3772 } else if (!member_config) {
3773 /* Not found in the list, and it's not realtime ... */
3774 return NULL;
3775 }
3776 /* Check if queue is defined in realtime. */
3777 if (!queue_vars) {
3778 /* Delete queue from in-core list if it has been deleted in realtime. */
3779 if (q) {
3780 /*! \note Hmm, can't seem to distinguish a DB failure from a not
3781 found condition... So we might delete an in-core queue
3782 in case of DB failure. */
3783 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
3784
3785 q->dead = 1;
3786 /* Delete if unused (else will be deleted when last caller leaves). */
3787 queues_t_unlink(queues, q, "Unused; removing from container");
3788 ao2_unlock(q);
3789 queue_t_unref(q, "Queue is dead; can't return it");
3790 }
3791 return NULL;
3792 }
3793
3794 /* Create a new queue if an in-core entry does not exist yet. */
3795 if (!q) {
3796 struct ast_variable *tmpvar = NULL;
3797 if (!(q = alloc_queue(queuename))) {
3798 return NULL;
3799 }
3800 ao2_lock(q);
3801 clear_queue(q);
3802 q->realtime = 1;
3803 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
3804 * will allocate the members properly
3805 */
3806 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
3807 if (!strcasecmp(tmpvar->name, "strategy")) {
3808 q->strategy = strat2int(tmpvar->value);
3809 if (q->strategy < 0) {
3810 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3811 tmpvar->value, q->name);
3813 }
3814 break;
3815 }
3816 }
3817 /* We traversed all variables and didn't find a strategy */
3818 if (!tmpvar) {
3820 }
3821 queues_t_link(queues, q, "Add queue to container");
3822 }
3823 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
3824
3825 memset(tmpbuf, 0, sizeof(tmpbuf));
3826 for (v = queue_vars; v; v = v->next) {
3827 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
3828 if (strchr(v->name, '_')) {
3829 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
3830 tmp_name = tmpbuf;
3831 tmp = tmpbuf;
3832 while ((tmp = strchr(tmp, '_'))) {
3833 *tmp++ = '-';
3834 }
3835 } else {
3836 tmp_name = v->name;
3837 }
3838
3839 /* NULL values don't get returned from realtime; blank values should
3840 * still get set. If someone doesn't want a value to be set, they
3841 * should set the realtime column to NULL, not blank. */
3842 queue_set_param(q, tmp_name, v->value, -1, 0);
3843 }
3844
3845 /* Temporarily set realtime members dead so we can detect deleted ones. */
3846 mem_iter = ao2_iterator_init(q->members, 0);
3847 while ((m = ao2_iterator_next(&mem_iter))) {
3848 if (m->realtime) {
3849 m->dead = 1;
3850 }
3851 ao2_ref(m, -1);
3852 }
3853 ao2_iterator_destroy(&mem_iter);
3854
3855 while ((category = ast_category_browse(member_config, category))) {
3856 rt_handle_member_record(q, category, member_config);
3857 }
3858
3859 /* Delete all realtime members that have been deleted in DB. */
3860 mem_iter = ao2_iterator_init(q->members, 0);
3861 while ((m = ao2_iterator_next(&mem_iter))) {
3862 if (m->dead) {
3864 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
3865 } else {
3866 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
3867 }
3869 }
3870 ao2_ref(m, -1);
3871 }
3872 ao2_iterator_destroy(&mem_iter);
3873
3874 ao2_unlock(q);
3875
3876 return q;
3877}
3878
3879/*!
3880 * note */
3881
3882/*!
3883 * \internal
3884 * \brief Returns reference to the named queue. If the queue is realtime, it will load the queue as well.
3885 * \param queuename - name of the desired queue
3886 *
3887 * \retval the queue
3888 * \retval NULL if it doesn't exist
3889 */
3890static struct call_queue *find_load_queue_rt_friendly(const char *queuename)
3891{
3892 struct ast_variable *queue_vars;
3893 struct ast_config *member_config = NULL;
3894 struct call_queue *q = NULL, tmpq = {
3895 .name = queuename,
3896 };
3897 int prev_weight = 0;
3898
3899 /* Find the queue in the in-core list first. */
3900 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
3901
3902 if (!q || q->realtime) {
3903 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
3904 queue operations while waiting for the DB.
3905
3906 This will be two separate database transactions, so we might
3907 see queue parameters as they were before another process
3908 changed the queue and member list as it was after the change.
3909 Thus we might see an empty member list when a queue is
3910 deleted. In practise, this is unlikely to cause a problem. */
3911
3912 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
3913 if (queue_vars) {
3914 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
3915 if (!member_config) {
3916 ast_debug(1, "No queue_members defined in config extconfig.conf\n");
3917 member_config = ast_config_new();
3918 }
3919 }
3920 if (q) {
3921 prev_weight = q->weight ? 1 : 0;
3922 queue_t_unref(q, "Need to find realtime queue");
3923 }
3924
3925 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
3926 ast_config_destroy(member_config);
3927 ast_variables_destroy(queue_vars);
3928
3929 /* update the use_weight value if the queue's has gained or lost a weight */
3930 if (q) {
3931 if (!q->weight && prev_weight) {
3933 }
3934 if (q->weight && !prev_weight) {
3936 }
3937 }
3938 /* Other cases will end up with the proper value for use_weight */
3939 } else {
3941 }
3942 return q;
3943}
3944
3945/*!
3946 * \internal
3947 * \brief Load queues and members from realtime.
3948 *
3949 * \param queuename - name of the desired queue to load or empty if need to load all queues
3950*/
3951static void load_realtime_queues(const char *queuename)
3952{
3953 struct ast_config *cfg = NULL;
3954 char *category = NULL;
3955 const char *name = NULL;
3956 struct call_queue *q = NULL;
3957
3958 if (!ast_check_realtime("queues")) {
3959 return;
3960 }
3961
3962 if (ast_strlen_zero(queuename)) {
3963 if ((cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
3964 while ((category = ast_category_browse(cfg, category))) {
3965 name = ast_variable_retrieve(cfg, category, "name");
3967 queue_unref(q);
3968 }
3969 }
3970 ast_config_destroy(cfg);
3971 }
3972 } else {
3973 if ((q = find_load_queue_rt_friendly(queuename))) {
3974 queue_unref(q);
3975 }
3976 }
3977}
3978
3979static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
3980{
3981 int ret = -1;
3982
3983 if (ast_strlen_zero(mem->rt_uniqueid)) {
3984 return ret;
3985 }
3986
3987 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) >= 0) {
3988 ret = 0;
3989 }
3990
3991 return ret;
3992}
3993
3994
3996{
3997 struct ast_config *member_config = NULL;
3998 struct member *m;
3999 char *category = NULL;
4000 struct ao2_iterator mem_iter;
4001
4002 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
4003 /* This queue doesn't have realtime members. If the queue still has any realtime
4004 * members in memory, they need to be removed.
4005 */
4006 ao2_lock(q);
4007 mem_iter = ao2_iterator_init(q->members, 0);
4008 while ((m = ao2_iterator_next(&mem_iter))) {
4009 if (m->realtime) {
4011 }
4012 ao2_ref(m, -1);
4013 }
4014 ao2_iterator_destroy(&mem_iter);
4015 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
4016 ao2_unlock(q);
4017 return;
4018 }
4019
4020 ao2_lock(q);
4021
4022 /* Temporarily set realtime members dead so we can detect deleted ones.*/
4023 mem_iter = ao2_iterator_init(q->members, 0);
4024 while ((m = ao2_iterator_next(&mem_iter))) {
4025 if (m->realtime) {
4026 m->dead = 1;
4027 }
4028 ao2_ref(m, -1);
4029 }
4030 ao2_iterator_destroy(&mem_iter);
4031
4032 while ((category = ast_category_browse(member_config, category))) {
4033 rt_handle_member_record(q, category, member_config);
4034 }
4035
4036 /* Delete all realtime members that have been deleted in DB. */
4037 mem_iter = ao2_iterator_init(q->members, 0);
4038 while ((m = ao2_iterator_next(&mem_iter))) {
4039 if (m->dead) {
4041 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4042 } else {
4043 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4044 }
4046 }
4047 ao2_ref(m, -1);
4048 }
4049 ao2_iterator_destroy(&mem_iter);
4050 ao2_unlock(q);
4051 ast_config_destroy(member_config);
4052}
4053
4054static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
4055{
4056 struct call_queue *q;
4057 struct queue_ent *cur, *prev = NULL;
4058 int res = -1;
4059 int pos = 0;
4060 int inserted = 0;
4061
4062 if (!(q = find_load_queue_rt_friendly(queuename))) {
4063 return res;
4064 }
4065 ao2_lock(q);
4066
4067 /* This is our one */
4068 if (q->joinempty) {
4069 int status = 0;
4070 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, qe->raise_penalty, q->joinempty, 0))) {
4071 *reason = QUEUE_JOINEMPTY;
4072 ao2_unlock(q);
4073 queue_t_unref(q, "Done with realtime queue");
4074 return res;
4075 }
4076 }
4077 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) {
4078 *reason = QUEUE_FULL;
4079 } else if (*reason == QUEUE_UNKNOWN) {
4080 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4081
4082 /* There's space for us, put us at the right position inside
4083 * the queue.
4084 * Take into account the priority of the calling user */
4085 inserted = 0;
4086 prev = NULL;
4087 cur = q->head;
4088 while (cur) {
4089 /* We have higher priority than the current user, enter
4090 * before him, after all the other users with priority
4091 * higher or equal to our priority. */
4092 if ((!inserted) && (qe->prio > cur->prio)) {
4093 insert_entry(q, prev, qe, &pos);
4094 inserted = 1;
4095 }
4096 /* <= is necessary for the position comparison because it may not be possible to enter
4097 * at our desired position since higher-priority callers may have taken the position we want
4098 */
4099 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
4100 insert_entry(q, prev, qe, &pos);
4101 inserted = 1;
4102 /*pos is incremented inside insert_entry, so don't need to add 1 here*/
4103 if (position < pos) {
4104 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
4105 }
4106 }
4107 cur->pos = ++pos;
4108 prev = cur;
4109 cur = cur->next;
4110 }
4111 /* No luck, join at the end of the queue */
4112 if (!inserted) {
4113 insert_entry(q, prev, qe, &pos);
4114 }
4115 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
4116 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
4117 ast_copy_string(qe->context, q->context, sizeof(qe->context));
4118 q->count++;
4119 if (q->count == 1) {
4121 }
4122
4123 res = 0;
4124
4125 blob = ast_json_pack("{s: s, s: i, s: i}",
4126 "Queue", q->name,
4127 "Position", qe->pos,
4128 "Count", q->count);
4129 ast_channel_publish_cached_blob(qe->chan, queue_caller_join_type(), blob);
4130 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, ast_channel_name(qe->chan), qe->pos );
4131 }
4132 ao2_unlock(q);
4133 queue_t_unref(q, "Done with realtime queue");
4134
4135 return res;
4136}
4137
4138static int play_file(struct ast_channel *chan, const char *filename)
4139{
4140 int res;
4141
4142 if (ast_strlen_zero(filename)) {
4143 return 0;
4144 }
4145
4146 if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
4147 return 0;
4148 }
4149
4151
4152 res = ast_streamfile(chan, filename, ast_channel_language(chan));
4153 if (!res) {
4155 }
4156
4158
4159 return res;
4160}
4161
4162/*!
4163 * \brief Check for valid exit from queue via goto
4164 * \retval 0 if failure
4165 * \retval 1 if successful
4166*/
4167static int valid_exit(struct queue_ent *qe, char digit)
4168{
4169 int digitlen = strlen(qe->digits);
4170
4171 /* Prevent possible buffer overflow */
4172 if (digitlen < sizeof(qe->digits) - 2) {
4173 qe->digits[digitlen] = digit;
4174 qe->digits[digitlen + 1] = '\0';
4175 } else {
4176 qe->digits[0] = '\0';
4177 return 0;
4178 }
4179
4180 /* If there's no context to goto, short-circuit */
4181 if (ast_strlen_zero(qe->context)) {
4182 return 0;
4183 }
4184
4185 /* If the extension is bad, then reset the digits to blank */
4186 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
4188 qe->digits[0] = '\0';
4189 return 0;
4190 }
4191
4192 /* We have an exact match */
4193 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
4194 qe->valid_digits = 1;
4195 /* Return 1 on a successful goto */
4196 return 1;
4197 }
4198
4199 return 0;
4200}
4201
4202static int say_position(struct queue_ent *qe, int ringing)
4203{
4204 int res = 0, say_thanks = 0;
4205 long avgholdmins, avgholdsecs;
4206 time_t now;
4207
4208 /* Let minannouncefrequency seconds pass between the start of each position announcement */
4209 time(&now);
4210 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) {
4211 return 0;
4212 }
4213
4214 /* If either our position has changed, or we are over the freq timer, say position */
4215 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) {
4216 return 0;
4217 }
4218
4219 /* Only announce if the caller's queue position has improved since last time */
4220 if (qe->parent->announceposition_only_up && qe->last_pos_said <= qe->pos) {
4221 return 0;
4222 }
4223
4224 if (ringing) {
4225 ast_indicate(qe->chan,-1);
4226 } else {
4227 ast_moh_stop(qe->chan);
4228 }
4229
4233 qe->pos <= qe->parent->announcepositionlimit)) {
4234 say_thanks = 1;
4235 /* Say we're next, if we are */
4236 if (qe->pos == 1) {
4237 res = play_file(qe->chan, qe->parent->sound_next);
4238 if (!res) {
4239 goto posout;
4240 }
4241 /* Say there are more than N callers */
4243 res = (
4244 play_file(qe->chan, qe->parent->queue_quantity1) ||
4246 ast_channel_language(qe->chan), NULL) || /* Needs gender */
4248 /* Say there are currently N callers waiting */
4249 } else {
4250 res = (
4251 play_file(qe->chan, qe->parent->sound_thereare) ||
4253 ast_channel_language(qe->chan), "n") || /* Needs gender */
4254 play_file(qe->chan, qe->parent->sound_calls));
4255 }
4256 if (res) {
4257 goto playout;
4258 }
4259 }
4260 /* Round hold time to nearest minute */
4261 avgholdmins = labs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
4262
4263 /* If they have specified a rounding then round the seconds as well */
4264 if (qe->parent->roundingseconds) {
4265 avgholdsecs = (labs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
4266 avgholdsecs *= qe->parent->roundingseconds;
4267 } else {
4268 avgholdsecs = 0;
4269 }
4270
4271 ast_verb(3, "Hold time for %s is %ld minute(s) %ld seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
4272
4273 /* If the hold time is >1 min, if it's enabled, and if it's not
4274 supposed to be only once and we have already said it, say it */
4275 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
4278 say_thanks = 1;
4279 res = play_file(qe->chan, qe->parent->sound_holdtime);
4280 if (res) {
4281 goto playout;
4282 }
4283
4284 if (avgholdmins >= 1) {
4285 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4286 if (res) {
4287 goto playout;
4288 }
4289
4290 if (avgholdmins == 1) {
4291 res = play_file(qe->chan, qe->parent->sound_minute);
4292 if (res) {
4293 goto playout;
4294 }
4295 } else {
4296 res = play_file(qe->chan, qe->parent->sound_minutes);
4297 if (res) {
4298 goto playout;
4299 }
4300 }
4301 }
4302 if (avgholdsecs >= 1) {
4303 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4304 if (res) {
4305 goto playout;
4306 }
4307
4308 res = play_file(qe->chan, qe->parent->sound_seconds);
4309 if (res) {
4310 goto playout;
4311 }
4312 }
4313 }
4314
4315posout:
4316 if (qe->parent->announceposition) {
4317 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
4318 ast_channel_name(qe->chan), qe->parent->name, qe->pos);
4319 }
4320 if (say_thanks) {
4321 res = play_file(qe->chan, qe->parent->sound_thanks);
4322 }
4323playout:
4324
4325 if ((res > 0 && !valid_exit(qe, res))) {
4326 res = 0;
4327 }
4328
4329 /* Set our last_pos indicators */
4330 qe->last_pos = now;
4331 qe->last_pos_said = qe->pos;
4332
4333 /* Don't restart music on hold if we're about to exit the caller from the queue */
4334 if (!res) {
4335 if (ringing) {
4337 } else {
4338 ast_moh_start(qe->chan, qe->moh, NULL);
4339 }
4340 }
4341 return res;
4342}
4343
4344static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
4345{
4346 int oldvalue;
4347
4348 /* Calculate holdtime using an exponential average */
4349 /* Thanks to SRT for this contribution */
4350 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
4351
4352 ao2_lock(qe->parent);
4353 if ((qe->parent->callscompleted + qe->parent->callsabandoned) == 0) {
4354 qe->parent->holdtime = newholdtime;
4355 } else {
4356 oldvalue = qe->parent->holdtime;
4357 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4358 }
4359 ao2_unlock(qe->parent);
4360}
4361
4362/*! \brief Caller leaving queue.
4363 *
4364 * Search the queue to find the leaving client, if found remove from queue
4365 * create manager event, move others up the queue.
4366*/
4367static void leave_queue(struct queue_ent *qe)
4368{
4369 struct call_queue *q;
4370 struct queue_ent *current, *prev = NULL;
4371 struct penalty_rule *pr_iter;
4372 int pos = 0;
4373
4374 if (!(q = qe->parent)) {
4375 return;
4376 }
4377 queue_t_ref(q, "Copy queue pointer from queue entry");
4378 ao2_lock(q);
4379
4380 prev = NULL;
4381 for (current = q->head; current; current = current->next) {
4382 if (current == qe) {
4383 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4384 char posstr[20];
4385 q->count--;
4386 if (!q->count) {
4388 }
4389
4390 blob = ast_json_pack("{s: s, s: i, s: i}",
4391 "Queue", q->name,
4392 "Position", qe->pos,
4393 "Count", q->count);
4394 ast_channel_publish_cached_blob(qe->chan, queue_caller_leave_type(), blob);
4395 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
4396 /* Take us out of the queue */
4397 if (prev) {
4398 prev->next = current->next;
4399 } else {
4400 q->head = current->next;
4401 }
4402 /* Free penalty rules */
4403 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
4404 ast_free(pr_iter);
4405 }
4406 qe->pr = NULL;
4407 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
4408 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
4409 } else {
4410 /* Renumber the people after us in the queue based on a new count */
4411 current->pos = ++pos;
4412 prev = current;
4413 }
4414 }
4415 ao2_unlock(q);
4416
4417 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
4418 if (q->realtime) {
4419 struct ast_variable *var;
4420 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
4421 q->dead = 1;
4422 } else {
4424 }
4425 }
4426
4427 if (q->dead) {
4428 /* It's dead and nobody is in it, so kill it */
4429 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
4430 }
4431 /* unref the explicit ref earlier in the function */
4432 queue_t_unref(q, "Expire copied reference");
4433}
4434
4435/*!
4436 * \internal
4437 * \brief Destroy the given callattempt structure and free it.
4438 * \since 1.8
4439 *
4440 * \param doomed callattempt structure to destroy.
4441 */
4442static void callattempt_free(struct callattempt *doomed)
4443{
4444 if (doomed->member) {
4445 ao2_ref(doomed->member, -1);
4446 }
4448 ast_free(doomed->orig_chan_name);
4449 ast_free(doomed);
4450}
4451
4452static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
4453{
4454 struct callattempt *cur;
4455
4456 for (cur = outgoing; cur; cur = cur->q_next) {
4457 if (cur->chan && cur->chan != exception) {
4459 }
4460 }
4461}
4462
4463/*! \brief Hang up a list of outgoing calls */
4464static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
4465{
4466 struct callattempt *oo;
4467
4468 while (outgoing) {
4469 /* If someone else answered the call we should indicate this in the CANCEL */
4470 /* Hangup any existing lines we have open */
4471 if (outgoing->chan && (outgoing->chan != exception)) {
4472 if (exception || cancel_answered_elsewhere) {
4474 }
4475 ast_channel_publish_dial(qe->chan, outgoing->chan, outgoing->interface, "CANCEL");
4476
4477 /* When dialing channels it is possible that they may not ever
4478 * leave the not in use state (Local channels in particular) by
4479 * the time we cancel them. If this occurs but we know they were
4480 * dialed we explicitly remove them from the pending members
4481 * container so that subsequent call attempts occur.
4482 */
4483 if (outgoing->member->status == AST_DEVICE_NOT_INUSE) {
4485 }
4486
4487 ast_hangup(outgoing->chan);
4488 }
4489 oo = outgoing;
4490 outgoing = outgoing->q_next;
4492 callattempt_free(oo);
4493 }
4494}
4495
4496/*!
4497 * \brief Get the number of members available to accept a call.
4498 *
4499 * \note The queue passed in should be locked prior to this function call
4500 *
4501 * \param[in] q The queue for which we are counting the number of available members
4502 * \return Return the number of available members in queue q
4503 */
4505{
4506 struct member *mem;
4507 int avl = 0;
4508 struct ao2_iterator mem_iter;
4509
4510 mem_iter = ao2_iterator_init(q->members, 0);
4511 while ((mem = ao2_iterator_next(&mem_iter))) {
4512
4513 avl += is_member_available(q, mem);
4514 ao2_ref(mem, -1);
4515
4516 /* If autofill is not enabled or if the queue's strategy is ringall, then
4517 * we really don't care about the number of available members so much as we
4518 * do that there is at least one available.
4519 *
4520 * In fact, we purposely will return from this function stating that only
4521 * one member is available if either of those conditions hold. That way,
4522 * functions which determine what action to take based on the number of available
4523 * members will operate properly. The reasoning is that even if multiple
4524 * members are available, only the head caller can actually be serviced.
4525 */
4526 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4527 break;
4528 }
4529 }
4530 ao2_iterator_destroy(&mem_iter);
4531
4532 return avl;
4533}
4534
4535/* traverse all defined queues which have calls waiting and contain this member
4536 return 0 if no other queue has precedence (higher weight) or 1 if found */
4537static int compare_weight(struct call_queue *rq, struct member *member)
4538{
4539 struct call_queue *q;
4540 struct member *mem;
4541 int found = 0;
4542 struct ao2_iterator queue_iter;
4543
4544 queue_iter = ao2_iterator_init(queues, 0);
4545 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4546 if (q == rq) { /* don't check myself, could deadlock */
4547 queue_t_unref(q, "Done with iterator");
4548 continue;
4549 }
4550