Asterisk - The Open Source Telephony Project GIT-master-7988d11
Loading...
Searching...
No Matches
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#include <inttypes.h>
75
76#include "asterisk/lock.h"
77#include "asterisk/file.h"
78#include "asterisk/channel.h"
79#include "asterisk/pbx.h"
80#include "asterisk/app.h"
82#include "asterisk/module.h"
83#include "asterisk/translate.h"
84#include "asterisk/say.h"
85#include "asterisk/features.h"
87#include "asterisk/cli.h"
88#include "asterisk/manager.h"
89#include "asterisk/config.h"
90#include "asterisk/utils.h"
91#include "asterisk/causes.h"
92#include "asterisk/astdb.h"
95#include "asterisk/astobj2.h"
96#include "asterisk/strings.h"
98#include "asterisk/aoc.h"
99#include "asterisk/callerid.h"
100#include "asterisk/term.h"
101#include "asterisk/dial.h"
106#include "asterisk/core_local.h"
107#include "asterisk/mixmonitor.h"
110
111/*!
112 * \par Please read before modifying this file.
113 * There are three locks which are regularly used
114 * throughout this file, the queue list lock, the lock
115 * for each individual queue, and the interface list lock.
116 * Please be extra careful to always lock in the following order
117 * 1) queue list lock
118 * 2) individual queue lock
119 * 3) interface list lock
120 * This order has sort of "evolved" over the lifetime of this
121 * application, but it is now in place this way, so please adhere
122 * to this order!
123 */
124
125/*** DOCUMENTATION
126 <application name="Queue" language="en_US">
127 <since>
128 <version>0.2.0</version>
129 </since>
130 <synopsis>
131 Queue a call for a call queue.
132 </synopsis>
133 <syntax>
134 <parameter name="queuename" required="true" />
135 <parameter name="options">
136 <optionlist>
137 <option name="b" argsep="^">
138 <para>Before initiating an outgoing call, <literal>Gosub</literal> to the specified
139 location using the newly created channel. The <literal>Gosub</literal> will be
140 executed for each destination channel.</para>
141 <argument name="context" required="false" />
142 <argument name="exten" required="false" />
143 <argument name="priority" required="true" hasparams="optional" argsep="^">
144 <argument name="arg1" multiple="true" required="true" />
145 <argument name="argN" />
146 </argument>
147 </option>
148 <option name="B" argsep="^">
149 <para>Before initiating the outgoing call(s), <literal>Gosub</literal> to the
150 specified location using the current channel.</para>
151 <argument name="context" required="false" />
152 <argument name="exten" required="false" />
153 <argument name="priority" required="true" hasparams="optional" argsep="^">
154 <argument name="arg1" multiple="true" required="true" />
155 <argument name="argN" />
156 </argument>
157 </option>
158 <option name="C">
159 <para>Mark all calls as "answered elsewhere" when cancelled.</para>
160 </option>
161 <option name="c">
162 <para>Continue in the dialplan if the callee hangs up.</para>
163 </option>
164 <option name="d">
165 <para>Data-quality (modem) call (minimum delay).</para>
166 <para>This option only applies to DAHDI channels. By default,
167 DTMF is verified by muting audio TX/RX to verify the tone
168 is still present. This option disables that behavior.</para>
169 </option>
170 <option name="F" argsep="^">
171 <argument name="context" required="false" />
172 <argument name="exten" required="false" />
173 <argument name="priority" required="true" />
174 <para>When the caller hangs up, transfer the <emphasis>called member</emphasis>
175 to the specified destination and <emphasis>start</emphasis> execution at that location.</para>
176 <para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
177 prefixed with one or two underbars ('_').</para>
178 <para>NOTE: Using this option from a Gosub() might not make sense as there would be no return points.</para>
179 </option>
180 <option name="h">
181 <para>Allow <emphasis>callee</emphasis> to hang up by pressing <literal>*</literal>.</para>
182 </option>
183 <option name="H">
184 <para>Allow <emphasis>caller</emphasis> to hang up by pressing <literal>*</literal>.</para>
185 </option>
186 <option name="i">
187 <para>Ignore call forward requests from queue members and do nothing
188 when they are requested.</para>
189 </option>
190 <option name="I">
191 <para>Asterisk will ignore any connected line update requests or any redirecting party
192 update requests it may receive on this dial attempt.</para>
193 </option>
194 <option name="k">
195 <para>Allow the <emphasis>called</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="K">
199 <para>Allow the <emphasis>calling</emphasis> party to enable parking of the call by sending
200 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
201 </option>
202 <option name="m">
203 <para>Custom music on hold class to use, which will override the music on hold class configured
204 in <filename>queues.conf</filename>, if specified.</para>
205 <para>Note that CHANNEL(musicclass), if set, will still override this option.</para>
206 </option>
207 <option name="n">
208 <para>No retries on the timeout; will exit this application and
209 go to the next step.</para>
210 </option>
211 <option name="r">
212 <para>Ring instead of playing MOH. Periodic Announcements are still made, if applicable.</para>
213 </option>
214 <option name="R">
215 <para>Ring instead of playing MOH when a member channel is actually ringing.</para>
216 </option>
217 <option name="t">
218 <para>Allow the <emphasis>called</emphasis> user to transfer the calling user.</para>
219 </option>
220 <option name="T">
221 <para>Allow the <emphasis>calling</emphasis> user to transfer the call.</para>
222 </option>
223 <option name="x">
224 <para>Allow the <emphasis>called</emphasis> user to write the conversation
225 to disk via MixMonitor.</para>
226 </option>
227 <option name="X">
228 <para>Allow the <emphasis>calling</emphasis> user to write the conversation to
229 disk via MixMonitor.</para>
230 </option>
231 </optionlist>
232 </parameter>
233 <parameter name="URL">
234 <para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
235 </parameter>
236 <parameter name="announceoverride" argsep="&amp;">
237 <para>Announcement file(s) to play to agent before bridging
238 call, overriding the announcement(s) configured in
239 <filename>queues.conf</filename>, if any.</para>
240 <para>Ampersand separated list of filenames. If the filename
241 is a relative filename (it does not begin with a slash), it
242 will be searched for in the Asterisk sounds directory. If the
243 filename is able to be parsed as a URL, Asterisk will
244 download the file and then begin playback on it. To include a
245 literal <literal>&amp;</literal> in the URL you can enclose
246 the URL in single quotes.</para>
247 <argument name="announceoverride" required="true" />
248 <argument name="announceoverride2" multiple="true" />
249 </parameter>
250 <parameter name="timeout">
251 <para>Will cause the queue to fail out after a specified number of
252 seconds, checked between each <filename>queues.conf</filename> <replaceable>timeout</replaceable> and
253 <replaceable>retry</replaceable> cycle.</para>
254 </parameter>
255 <parameter name="AGI">
256 <para>Will setup an AGI script to be executed on the calling party's channel once they are
257 connected to a queue member.</para>
258 </parameter>
259 <parameter name="gosub">
260 <para>Will run a gosub on the called party's channel (the queue member)
261 once the parties are connected. The subroutine execution starts in the
262 named context at the s exten and priority 1.</para>
263 <note><para>Macro was removed in Asterisk 21 which resulted in an
264 argument ordering change. The upgrade notice was missed for this,
265 so a note is being made here to provide a record of the change
266 for users who have not upgraded yet.</para></note>
267 </parameter>
268 <parameter name="rule">
269 <para>Will cause the queue's defaultrule to be overridden by the rule specified.</para>
270 </parameter>
271 <parameter name="position">
272 <para>Attempt to enter the caller into the queue at the numerical position specified. <literal>1</literal>
273 would attempt to enter the caller at the head of the queue, and <literal>3</literal> would attempt to place
274 the caller third in the queue.</para>
275 </parameter>
276 </syntax>
277 <description>
278 <para>In addition to transferring the call, a call may be parked and then picked
279 up by another user.</para>
280 <para>This application will return to the dialplan if the queue does not exist, or
281 any of the join options cause the caller to not enter the queue.</para>
282 <para>This application does not automatically answer and should be preceded
283 by an application such as Answer(), Progress(), or Ringing().</para>
284 <para>This application sets the following channel variables upon completion:</para>
285 <variablelist>
286 <variable name="QUEUESTATUS">
287 <para>The status of the call as a text string.</para>
288 <value name="TIMEOUT" />
289 <value name="FULL" />
290 <value name="JOINEMPTY" />
291 <value name="LEAVEEMPTY" />
292 <value name="JOINUNAVAIL" />
293 <value name="LEAVEUNAVAIL" />
294 <value name="CONTINUE" />
295 <value name="WITHDRAW" />
296 </variable>
297 <variable name="ABANDONED">
298 <para>If the call was not answered by an agent this variable will be TRUE.</para>
299 <value name="TRUE" />
300 </variable>
301 <variable name="DIALEDPEERNUMBER">
302 <para>Resource of the agent that was dialed set on the outbound channel.</para>
303 </variable>
304 <variable name="QUEUE_WITHDRAW_INFO">
305 <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>
306 </variable>
307 <variable name="QUEUEWAIT">
308 <para>The total amount of time, in seconds, that the caller spent waiting in the queue before being connected to an agent.</para>
309 </variable>
310 <variable name="QUEUEWAIT_MS">
311 <para>The total amount of time, in milliseconds, that the caller spent waiting in the queue before being connected to an agent.</para>
312 </variable>
313 <variable name="ANSWEREDTIME">
314 <para>The amount of time, in seconds, that the caller spent connected to an agent. If the call was never answered, this will be set to 0.</para>
315 </variable>
316 <variable name="ANSWEREDTIME_MS">
317 <para>The amount of time, in milliseconds, that the caller spent connected to an agent.</para>
318 </variable>
319 <variable name="DIALEDTIME">
320 <para>The total amount of time, in seconds, from the start of the call until it ends. This matches the behavior of Dial().</para>
321 </variable>
322 <variable name="DIALEDTIME_MS">
323 <para>The total amount of time, in milliseconds, from the start of the call until it ends.</para>
324 </variable>
325 </variablelist>
326 </description>
327 <see-also>
328 <ref type="application">Queue</ref>
329 <ref type="application">QueueLog</ref>
330 <ref type="application">AddQueueMember</ref>
331 <ref type="application">RemoveQueueMember</ref>
332 <ref type="application">PauseQueueMember</ref>
333 <ref type="application">UnpauseQueueMember</ref>
334 <ref type="function">QUEUE_VARIABLES</ref>
335 <ref type="function">QUEUE_MEMBER</ref>
336 <ref type="function">QUEUE_EXISTS</ref>
337 <ref type="function">QUEUE_GET_CHANNEL</ref>
338 <ref type="function">QUEUE_WAITING_COUNT</ref>
339 <ref type="function">QUEUE_MEMBER_LIST</ref>
340 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
341 </see-also>
342 </application>
343 <application name="AddQueueMember" language="en_US">
344 <since>
345 <version>0.5.0</version>
346 </since>
347 <synopsis>
348 Dynamically adds queue members.
349 </synopsis>
350 <syntax>
351 <parameter name="queuename" required="true" />
352 <parameter name="interface" />
353 <parameter name="penalty" />
354 <parameter name="options">
355 <optionlist>
356 <option name="p">
357 <para>Add queue member in paused state.</para>
358 </option>
359 <option name="r">
360 <argument name="reason" required="true" />
361 <para>Specify a reason why the member is in paused state.</para>
362 </option>
363 </optionlist>
364 </parameter>
365 <parameter name="membername" />
366 <parameter name="stateinterface" />
367 <parameter name="wrapuptime" />
368 </syntax>
369 <description>
370 <para>Dynamically adds interface to an existing queue. If the interface is
371 already in the queue it will return an error.</para>
372 <para>This application sets the following channel variable upon completion:</para>
373 <variablelist>
374 <variable name="AQMSTATUS">
375 <para>The status of the attempt to add a queue member as a text string.</para>
376 <value name="ADDED" />
377 <value name="MEMBERALREADY" />
378 <value name="NOSUCHQUEUE" />
379 </variable>
380 </variablelist>
381 </description>
382 <see-also>
383 <ref type="application">Queue</ref>
384 <ref type="application">QueueLog</ref>
385 <ref type="application">AddQueueMember</ref>
386 <ref type="application">RemoveQueueMember</ref>
387 <ref type="application">PauseQueueMember</ref>
388 <ref type="application">UnpauseQueueMember</ref>
389 <ref type="function">QUEUE_VARIABLES</ref>
390 <ref type="function">QUEUE_MEMBER</ref>
391 <ref type="function">QUEUE_EXISTS</ref>
392 <ref type="function">QUEUE_GET_CHANNEL</ref>
393 <ref type="function">QUEUE_WAITING_COUNT</ref>
394 <ref type="function">QUEUE_MEMBER_LIST</ref>
395 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
396 </see-also>
397 </application>
398 <application name="RemoveQueueMember" language="en_US">
399 <since>
400 <version>0.5.0</version>
401 </since>
402 <synopsis>
403 Dynamically removes queue members.
404 </synopsis>
405 <syntax>
406 <parameter name="queuename" required="true" />
407 <parameter name="interface" />
408 </syntax>
409 <description>
410 <para>If the interface is <emphasis>NOT</emphasis> in the queue it will return an error.</para>
411 <para>This application sets the following channel variable upon completion:</para>
412 <variablelist>
413 <variable name="RQMSTATUS">
414 <value name="REMOVED" />
415 <value name="NOTINQUEUE" />
416 <value name="NOSUCHQUEUE" />
417 <value name="NOTDYNAMIC" />
418 </variable>
419 </variablelist>
420 <example title="Remove queue member">
421 same => n,RemoveQueueMember(techsupport,SIP/3000)
422 </example>
423 </description>
424 <see-also>
425 <ref type="application">Queue</ref>
426 <ref type="application">QueueLog</ref>
427 <ref type="application">AddQueueMember</ref>
428 <ref type="application">RemoveQueueMember</ref>
429 <ref type="application">PauseQueueMember</ref>
430 <ref type="application">UnpauseQueueMember</ref>
431 <ref type="function">QUEUE_VARIABLES</ref>
432 <ref type="function">QUEUE_MEMBER</ref>
433 <ref type="function">QUEUE_EXISTS</ref>
434 <ref type="function">QUEUE_GET_CHANNEL</ref>
435 <ref type="function">QUEUE_WAITING_COUNT</ref>
436 <ref type="function">QUEUE_MEMBER_LIST</ref>
437 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
438 </see-also>
439 </application>
440 <application name="PauseQueueMember" language="en_US">
441 <since>
442 <version>1.2.0</version>
443 </since>
444 <synopsis>
445 Pauses a queue member.
446 </synopsis>
447 <syntax>
448 <parameter name="queuename" />
449 <parameter name="interface" required="true" />
450 <parameter name="options" />
451 <parameter name="reason">
452 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
453 </parameter>
454 </syntax>
455 <description>
456 <para>Pauses (blocks calls for) a queue member. The given interface will be paused in the given queue.
457 This prevents any calls from being sent from the queue to the interface until it is
458 unpaused with UnpauseQueueMember or the manager interface. If no queuename is given,
459 the interface is paused in every queue it is a member of. The application will fail if the
460 interface is not found.</para>
461 <para>This application sets the following channel variable upon completion:</para>
462 <variablelist>
463 <variable name="PQMSTATUS">
464 <para>The status of the attempt to pause a queue member as a text string.</para>
465 <value name="PAUSED" />
466 <value name="NOTFOUND" />
467 </variable>
468 </variablelist>
469 <example title="Pause queue member">
470 same => n,PauseQueueMember(,SIP/3000)
471 </example>
472 </description>
473 <see-also>
474 <ref type="application">Queue</ref>
475 <ref type="application">QueueLog</ref>
476 <ref type="application">AddQueueMember</ref>
477 <ref type="application">RemoveQueueMember</ref>
478 <ref type="application">PauseQueueMember</ref>
479 <ref type="application">UnpauseQueueMember</ref>
480 <ref type="function">QUEUE_VARIABLES</ref>
481 <ref type="function">QUEUE_MEMBER</ref>
482 <ref type="function">QUEUE_EXISTS</ref>
483 <ref type="function">QUEUE_GET_CHANNEL</ref>
484 <ref type="function">QUEUE_WAITING_COUNT</ref>
485 <ref type="function">QUEUE_MEMBER_LIST</ref>
486 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
487 </see-also>
488 </application>
489 <application name="UnpauseQueueMember" language="en_US">
490 <since>
491 <version>1.2.0</version>
492 </since>
493 <synopsis>
494 Unpauses a queue member.
495 </synopsis>
496 <syntax>
497 <parameter name="queuename" />
498 <parameter name="interface" required="true" />
499 <parameter name="options" />
500 <parameter name="reason">
501 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
502 </parameter>
503 </syntax>
504 <description>
505 <para>Unpauses (resumes calls to) a queue member. This is the counterpart to <literal>PauseQueueMember()</literal>
506 and operates exactly the same way, except it unpauses instead of pausing the given interface.</para>
507 <para>This application sets the following channel variable upon completion:</para>
508 <variablelist>
509 <variable name="UPQMSTATUS">
510 <para>The status of the attempt to unpause a queue member as a text string.</para>
511 <value name="UNPAUSED" />
512 <value name="NOTFOUND" />
513 </variable>
514 </variablelist>
515 <example title="Unpause queue member">
516 same => n,UnpauseQueueMember(,SIP/3000)
517 </example>
518 </description>
519 <see-also>
520 <ref type="application">Queue</ref>
521 <ref type="application">QueueLog</ref>
522 <ref type="application">AddQueueMember</ref>
523 <ref type="application">RemoveQueueMember</ref>
524 <ref type="application">PauseQueueMember</ref>
525 <ref type="application">UnpauseQueueMember</ref>
526 <ref type="function">QUEUE_VARIABLES</ref>
527 <ref type="function">QUEUE_MEMBER</ref>
528 <ref type="function">QUEUE_EXISTS</ref>
529 <ref type="function">QUEUE_GET_CHANNEL</ref>
530 <ref type="function">QUEUE_WAITING_COUNT</ref>
531 <ref type="function">QUEUE_MEMBER_LIST</ref>
532 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
533 </see-also>
534 </application>
535 <application name="QueueLog" language="en_US">
536 <since>
537 <version>1.4.0</version>
538 </since>
539 <synopsis>
540 Writes to the queue_log file.
541 </synopsis>
542 <syntax>
543 <parameter name="queuename" required="true" />
544 <parameter name="uniqueid" required="true" />
545 <parameter name="agent" required="true" />
546 <parameter name="event" required="true" />
547 <parameter name="additionalinfo" />
548 </syntax>
549 <description>
550 <para>Allows you to write your own events into the queue log.</para>
551 <example title="Log custom queue event">
552 same => n,QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)
553 </example>
554 </description>
555 <see-also>
556 <ref type="application">Queue</ref>
557 <ref type="application">QueueLog</ref>
558 <ref type="application">AddQueueMember</ref>
559 <ref type="application">RemoveQueueMember</ref>
560 <ref type="application">PauseQueueMember</ref>
561 <ref type="application">UnpauseQueueMember</ref>
562 <ref type="function">QUEUE_VARIABLES</ref>
563 <ref type="function">QUEUE_MEMBER</ref>
564 <ref type="function">QUEUE_EXISTS</ref>
565 <ref type="function">QUEUE_GET_CHANNEL</ref>
566 <ref type="function">QUEUE_WAITING_COUNT</ref>
567 <ref type="function">QUEUE_MEMBER_LIST</ref>
568 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
569 </see-also>
570 </application>
571 <application name="QueueUpdate" language="en_US">
572 <since>
573 <version>15.0.0</version>
574 </since>
575 <synopsis>
576 Writes to the queue_log file for outbound calls and updates Realtime Data.
577 Is used at h extension to be able to have all the parameters.
578 </synopsis>
579 <syntax>
580 <parameter name="queuename" required="true" />
581 <parameter name="uniqueid" required="true" />
582 <parameter name="agent" required="true" />
583 <parameter name="status" required="true" />
584 <parameter name="talktime" required="true" />
585 <parameter name="params" required="false" />
586 </syntax>
587 <description>
588 <para>Allows you to write Outbound events into the queue log.</para>
589 <example title="Write outbound event into queue log">
590 exten => h,1,QueueUpdate(${QUEUE}, ${UNIQUEID}, ${AGENT}, ${DIALSTATUS}, ${ANSWEREDTIME}, ${DIALEDTIME} | ${DIALEDNUMBER})
591 </example>
592 </description>
593 </application>
594 <function name="QUEUE_VARIABLES" language="en_US">
595 <since>
596 <version>1.6.0</version>
597 </since>
598 <synopsis>
599 Return Queue information in variables.
600 </synopsis>
601 <syntax>
602 <parameter name="queuename" required="true">
603 <enumlist>
604 <enum name="QUEUEMAX">
605 <para>Maximum number of calls allowed.</para>
606 </enum>
607 <enum name="QUEUESTRATEGY">
608 <para>The strategy of the queue.</para>
609 </enum>
610 <enum name="QUEUECALLS">
611 <para>Number of calls currently in the queue.</para>
612 </enum>
613 <enum name="QUEUEHOLDTIME">
614 <para>Current average hold time.</para>
615 </enum>
616 <enum name="QUEUECOMPLETED">
617 <para>Number of completed calls for the queue.</para>
618 </enum>
619 <enum name="QUEUEABANDONED">
620 <para>Number of abandoned calls.</para>
621 </enum>
622 <enum name="QUEUESRVLEVEL">
623 <para>Queue service level.</para>
624 </enum>
625 <enum name="QUEUESRVLEVELPERF">
626 <para>Current service level performance.</para>
627 </enum>
628 </enumlist>
629 </parameter>
630 </syntax>
631 <description>
632 <para>Makes the following queue variables available.</para>
633 <para>Returns <literal>0</literal> if queue is found and setqueuevar is defined, <literal>-1</literal> otherwise.</para>
634 </description>
635 <see-also>
636 <ref type="application">Queue</ref>
637 <ref type="application">QueueLog</ref>
638 <ref type="application">AddQueueMember</ref>
639 <ref type="application">RemoveQueueMember</ref>
640 <ref type="application">PauseQueueMember</ref>
641 <ref type="application">UnpauseQueueMember</ref>
642 <ref type="function">QUEUE_VARIABLES</ref>
643 <ref type="function">QUEUE_MEMBER</ref>
644 <ref type="function">QUEUE_EXISTS</ref>
645 <ref type="function">QUEUE_GET_CHANNEL</ref>
646 <ref type="function">QUEUE_WAITING_COUNT</ref>
647 <ref type="function">QUEUE_MEMBER_LIST</ref>
648 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
649 </see-also>
650 </function>
651 <function name="QUEUE_MEMBER" language="en_US">
652 <since>
653 <version>1.6.0</version>
654 </since>
655 <synopsis>
656 Provides a count of queue members based on the provided criteria, or updates a
657 queue member's settings.
658 </synopsis>
659 <syntax>
660 <parameter name="queuename" required="false" />
661 <parameter name="option" required="true">
662 <enumlist>
663 <enum name="logged">
664 <para>Returns the number of logged-in members for the specified queue.</para>
665 </enum>
666 <enum name="free">
667 <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>
668 </enum>
669 <enum name="ready">
670 <para>Returns the number of logged-in members for the specified queue that are immediately available to answer a call.</para>
671 </enum>
672 <enum name="count">
673 <para>Returns the total number of members for the specified queue.</para>
674 </enum>
675 <enum name="penalty">
676 <para>Gets or sets queue member penalty. If
677 <replaceable>queuename</replaceable> is not specified
678 when setting the penalty then the penalty is set in all queues
679 the interface is a member.</para>
680 </enum>
681 <enum name="paused">
682 <para>Gets or sets queue member paused status. If
683 <replaceable>queuename</replaceable> is not specified
684 when setting the paused status then the paused status is set
685 in all queues the interface is a member.</para>
686 </enum>
687 <enum name="ringinuse">
688 <para>Gets or sets queue member ringinuse. If
689 <replaceable>queuename</replaceable> is not specified
690 when setting ringinuse then ringinuse is set
691 in all queues the interface is a member.</para>
692 </enum>
693 </enumlist>
694 </parameter>
695 <parameter name="interface" required="false" />
696 </syntax>
697 <description>
698 <para>Allows access to queue counts [R] and member information [R/W].</para>
699 <para><replaceable>queuename</replaceable> is required for all read operations.</para>
700 <para><replaceable>interface</replaceable> is required for all member operations.</para>
701 </description>
702 <see-also>
703 <ref type="application">Queue</ref>
704 <ref type="application">QueueLog</ref>
705 <ref type="application">AddQueueMember</ref>
706 <ref type="application">RemoveQueueMember</ref>
707 <ref type="application">PauseQueueMember</ref>
708 <ref type="application">UnpauseQueueMember</ref>
709 <ref type="function">QUEUE_VARIABLES</ref>
710 <ref type="function">QUEUE_MEMBER</ref>
711 <ref type="function">QUEUE_EXISTS</ref>
712 <ref type="function">QUEUE_GET_CHANNEL</ref>
713 <ref type="function">QUEUE_WAITING_COUNT</ref>
714 <ref type="function">QUEUE_MEMBER_LIST</ref>
715 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
716 </see-also>
717 </function>
718 <function name="QUEUE_EXISTS" language="en_US">
719 <since>
720 <version>1.8.0</version>
721 </since>
722 <synopsis>
723 Check if a named queue exists on this server
724 </synopsis>
725 <syntax>
726 <parameter name="queuename" />
727 </syntax>
728 <description>
729 <para>Returns 1 if the specified queue exists, 0 if it does not</para>
730 </description>
731 <see-also>
732 <ref type="application">Queue</ref>
733 <ref type="application">QueueLog</ref>
734 <ref type="application">AddQueueMember</ref>
735 <ref type="application">RemoveQueueMember</ref>
736 <ref type="application">PauseQueueMember</ref>
737 <ref type="application">UnpauseQueueMember</ref>
738 <ref type="function">QUEUE_VARIABLES</ref>
739 <ref type="function">QUEUE_MEMBER</ref>
740 <ref type="function">QUEUE_EXISTS</ref>
741 <ref type="function">QUEUE_GET_CHANNEL</ref>
742 <ref type="function">QUEUE_WAITING_COUNT</ref>
743 <ref type="function">QUEUE_MEMBER_LIST</ref>
744 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
745 </see-also>
746 </function>
747 <function name="QUEUE_GET_CHANNEL" language="en_US">
748 <since>
749 <version>14.0.0</version>
750 </since>
751 <synopsis>
752 Return caller at the specified position in a queue.
753 </synopsis>
754 <syntax>
755 <parameter name="queuename" required="true" />
756 <parameter name="position" />
757 </syntax>
758 <description>
759 <para>Returns the caller channel at <replaceable>position</replaceable> in the specified <replaceable>queuename</replaceable>.</para>
760 <para>If <replaceable>position</replaceable> is unspecified the first channel is returned.</para>
761 </description>
762 <see-also>
763 <ref type="application">Queue</ref>
764 <ref type="application">QueueLog</ref>
765 <ref type="application">AddQueueMember</ref>
766 <ref type="application">RemoveQueueMember</ref>
767 <ref type="application">PauseQueueMember</ref>
768 <ref type="application">UnpauseQueueMember</ref>
769 <ref type="function">QUEUE_VARIABLES</ref>
770 <ref type="function">QUEUE_MEMBER</ref>
771 <ref type="function">QUEUE_EXISTS</ref>
772 <ref type="function">QUEUE_WAITING_COUNT</ref>
773 <ref type="function">QUEUE_MEMBER_LIST</ref>
774 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
775 </see-also>
776 </function>
777 <function name="QUEUE_WAITING_COUNT" language="en_US">
778 <since>
779 <version>1.4.0</version>
780 </since>
781 <synopsis>
782 Count number of calls currently waiting in a queue.
783 </synopsis>
784 <syntax>
785 <parameter name="queuename" />
786 </syntax>
787 <description>
788 <para>Returns the number of callers currently waiting in the specified <replaceable>queuename</replaceable>.</para>
789 </description>
790 <see-also>
791 <ref type="application">Queue</ref>
792 <ref type="application">QueueLog</ref>
793 <ref type="application">AddQueueMember</ref>
794 <ref type="application">RemoveQueueMember</ref>
795 <ref type="application">PauseQueueMember</ref>
796 <ref type="application">UnpauseQueueMember</ref>
797 <ref type="function">QUEUE_VARIABLES</ref>
798 <ref type="function">QUEUE_MEMBER</ref>
799 <ref type="function">QUEUE_EXISTS</ref>
800 <ref type="function">QUEUE_GET_CHANNEL</ref>
801 <ref type="function">QUEUE_WAITING_COUNT</ref>
802 <ref type="function">QUEUE_MEMBER_LIST</ref>
803 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
804 </see-also>
805 </function>
806 <function name="QUEUE_MEMBER_LIST" language="en_US">
807 <since>
808 <version>1.4.0</version>
809 </since>
810 <synopsis>
811 Returns a list of interfaces on a queue.
812 </synopsis>
813 <syntax>
814 <parameter name="queuename" required="true" />
815 </syntax>
816 <description>
817 <para>Returns a comma-separated list of members associated with the specified <replaceable>queuename</replaceable>.</para>
818 </description>
819 <see-also>
820 <ref type="application">Queue</ref>
821 <ref type="application">QueueLog</ref>
822 <ref type="application">AddQueueMember</ref>
823 <ref type="application">RemoveQueueMember</ref>
824 <ref type="application">PauseQueueMember</ref>
825 <ref type="application">UnpauseQueueMember</ref>
826 <ref type="function">QUEUE_VARIABLES</ref>
827 <ref type="function">QUEUE_MEMBER</ref>
828 <ref type="function">QUEUE_EXISTS</ref>
829 <ref type="function">QUEUE_GET_CHANNEL</ref>
830 <ref type="function">QUEUE_WAITING_COUNT</ref>
831 <ref type="function">QUEUE_MEMBER_LIST</ref>
832 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
833 </see-also>
834 </function>
835 <function name="QUEUE_MEMBER_PENALTY" language="en_US">
836 <since>
837 <version>1.6.0</version>
838 </since>
839 <synopsis>
840 Gets or sets queue members penalty.
841 </synopsis>
842 <syntax>
843 <parameter name="queuename" required="true" />
844 <parameter name="interface" required="true" />
845 </syntax>
846 <description>
847 <para>Gets or sets queue members penalty.</para>
848 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
849 </description>
850 <see-also>
851 <ref type="application">Queue</ref>
852 <ref type="application">QueueLog</ref>
853 <ref type="application">AddQueueMember</ref>
854 <ref type="application">RemoveQueueMember</ref>
855 <ref type="application">PauseQueueMember</ref>
856 <ref type="application">UnpauseQueueMember</ref>
857 <ref type="function">QUEUE_VARIABLES</ref>
858 <ref type="function">QUEUE_MEMBER</ref>
859 <ref type="function">QUEUE_EXISTS</ref>
860 <ref type="function">QUEUE_GET_CHANNEL</ref>
861 <ref type="function">QUEUE_WAITING_COUNT</ref>
862 <ref type="function">QUEUE_MEMBER_LIST</ref>
863 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
864 </see-also>
865 </function>
866 <manager name="QueueStatus" language="en_US">
867 <since>
868 <version>0.5.0</version>
869 </since>
870 <synopsis>
871 Show queue status.
872 </synopsis>
873 <syntax>
874 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
875 <parameter name="Queue">
876 <para>Limit the response to the status of the specified queue.</para>
877 </parameter>
878 <parameter name="Member">
879 <para>Limit the response to the status of the specified member.</para>
880 </parameter>
881 </syntax>
882 <description>
883 <para>Check the status of one or more queues.</para>
884 </description>
885 </manager>
886 <manager name="QueueSummary" language="en_US">
887 <since>
888 <version>1.6.0</version>
889 </since>
890 <synopsis>
891 Show queue summary.
892 </synopsis>
893 <syntax>
894 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
895 <parameter name="Queue">
896 <para>Queue for which the summary is requested.</para>
897 </parameter>
898 </syntax>
899 <description>
900 <para>Request the manager to send a QueueSummary event.</para>
901 </description>
902 </manager>
903 <manager name="QueueAdd" language="en_US">
904 <since>
905 <version>1.0.0</version>
906 </since>
907 <synopsis>
908 Add interface to queue.
909 </synopsis>
910 <syntax>
911 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
912 <parameter name="Queue" required="true">
913 <para>Queue's name.</para>
914 </parameter>
915 <parameter name="Interface" required="true">
916 <para>The name of the interface (tech/name) to add to the queue.</para>
917 </parameter>
918 <parameter name="Penalty">
919 <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>
920 </parameter>
921 <parameter name="Paused">
922 <para>To pause or not the member initially (true/false or 1/0).</para>
923 </parameter>
924 <parameter name="Reason" required="false">
925 <para>Text description why the member is paused.</para>
926 </parameter>
927 <parameter name="MemberName">
928 <para>Text alias for the interface.</para>
929 </parameter>
930 <parameter name="StateInterface" />
931 </syntax>
932 <description>
933 </description>
934 </manager>
935 <manager name="QueueRemove" language="en_US">
936 <since>
937 <version>1.0.0</version>
938 </since>
939 <synopsis>
940 Remove interface from queue.
941 </synopsis>
942 <syntax>
943 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
944 <parameter name="Queue" required="true">
945 <para>The name of the queue to take action on.</para>
946 </parameter>
947 <parameter name="Interface" required="true">
948 <para>The interface (tech/name) to remove from queue.</para>
949 </parameter>
950 </syntax>
951 <description>
952 </description>
953 </manager>
954 <manager name="QueuePause" language="en_US">
955 <since>
956 <version>1.2.0</version>
957 </since>
958 <synopsis>
959 Makes a queue member temporarily unavailable.
960 </synopsis>
961 <syntax>
962 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
963 <parameter name="Interface" required="true">
964 <para>The name of the interface (tech/name) to pause or unpause.</para>
965 </parameter>
966 <parameter name="Paused" required="true">
967 <para>Pause or unpause the interface. Set to 'true' to pause the member or 'false' to unpause.</para>
968 </parameter>
969 <parameter name="Queue" required="false">
970 <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>
971 </parameter>
972 <parameter name="Reason" required="false">
973 <para>Text description, returned in the event QueueMemberPause.</para>
974 </parameter>
975 </syntax>
976 <description>
977 <para>Pause or unpause a member in a queue.</para>
978 </description>
979 </manager>
980 <manager name="QueueLog" language="en_US">
981 <since>
982 <version>1.6.0</version>
983 </since>
984 <synopsis>
985 Adds custom entry in queue_log.
986 </synopsis>
987 <syntax>
988 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
989 <parameter name="Queue" required="true" />
990 <parameter name="Event" required="true" />
991 <parameter name="Uniqueid" />
992 <parameter name="Interface" />
993 <parameter name="Message" />
994 </syntax>
995 <description>
996 </description>
997 </manager>
998 <manager name="QueuePenalty" language="en_US">
999 <since>
1000 <version>1.6.0</version>
1001 </since>
1002 <synopsis>
1003 Set the penalty for a queue member.
1004 </synopsis>
1005 <syntax>
1006 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1007 <parameter name="Interface" required="true">
1008 <para>The interface (tech/name) of the member whose penalty to change.</para>
1009 </parameter>
1010 <parameter name="Penalty" required="true">
1011 <para>The new penalty (number) for the member. Must be nonnegative.</para>
1012 </parameter>
1013 <parameter name="Queue">
1014 <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>
1015 </parameter>
1016 </syntax>
1017 <description>
1018 <para>Change the penalty of a queue member</para>
1019 </description>
1020 </manager>
1021 <manager name="QueueMemberRingInUse" language="en_US">
1022 <since>
1023 <version>11.0.0</version>
1024 </since>
1025 <synopsis>
1026 Set the ringinuse value for a queue member.
1027 </synopsis>
1028 <syntax>
1029 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1030 <parameter name="Interface" required="true" />
1031 <parameter name="RingInUse" required="true" />
1032 <parameter name="Queue" />
1033 </syntax>
1034 <description>
1035 </description>
1036 </manager>
1037 <manager name="QueueRule" language="en_US">
1038 <since>
1039 <version>1.6.0</version>
1040 </since>
1041 <synopsis>
1042 Queue Rules.
1043 </synopsis>
1044 <syntax>
1045 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1046 <parameter name="Rule">
1047 <para>The name of the rule in queuerules.conf whose contents to list.</para>
1048 </parameter>
1049 </syntax>
1050 <description>
1051 <para>List queue rules defined in queuerules.conf</para>
1052 </description>
1053 </manager>
1054 <manager name="QueueReload" language="en_US">
1055 <since>
1056 <version>1.6.2.0</version>
1057 </since>
1058 <synopsis>
1059 Reload a queue, queues, or any sub-section of a queue or queues.
1060 </synopsis>
1061 <syntax>
1062 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1063 <parameter name="Queue">
1064 <para>The name of the queue to take action on. If no queue name is specified, then all queues are affected.</para>
1065 </parameter>
1066 <parameter name="Members">
1067 <para>Whether to reload the queue's members.</para>
1068 <enumlist>
1069 <enum name="yes" />
1070 <enum name="no" />
1071 </enumlist>
1072 </parameter>
1073 <parameter name="Rules">
1074 <para>Whether to reload queuerules.conf</para>
1075 <enumlist>
1076 <enum name="yes" />
1077 <enum name="no" />
1078 </enumlist>
1079 </parameter>
1080 <parameter name="Parameters">
1081 <para>Whether to reload the other queue options.</para>
1082 <enumlist>
1083 <enum name="yes" />
1084 <enum name="no" />
1085 </enumlist>
1086 </parameter>
1087 </syntax>
1088 <description>
1089 </description>
1090 </manager>
1091 <manager name="QueueReset" language="en_US">
1092 <since>
1093 <version>1.6.2.0</version>
1094 </since>
1095 <synopsis>
1096 Reset queue statistics.
1097 </synopsis>
1098 <syntax>
1099 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1100 <parameter name="Queue">
1101 <para>The name of the queue on which to reset statistics.</para>
1102 </parameter>
1103 </syntax>
1104 <description>
1105 <para>Reset the statistics for a queue.</para>
1106 </description>
1107 </manager>
1108 <manager name="QueueChangePriorityCaller" language="en_US">
1109 <since>
1110 <version>15.0.0</version>
1111 </since>
1112 <synopsis>
1113 Change priority of a caller on queue.
1114 </synopsis>
1115 <syntax>
1116 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1117 <parameter name="Queue" required="true">
1118 <para>The name of the queue to take action on.</para>
1119 </parameter>
1120 <parameter name="Caller" required="true">
1121 <para>The caller (channel) to change priority on queue.</para>
1122 </parameter>
1123
1124 <parameter name="Priority" required="true">
1125 <para>Priority value for change for caller on queue.</para>
1126 </parameter>
1127 <parameter name="Immediate">
1128 <para>When set to yes will cause the priority change to be reflected immediately, causing the channel to change position within the queue.</para>
1129 </parameter>
1130 </syntax>
1131 <description>
1132 </description>
1133 </manager>
1134 <manager name="QueueWithdrawCaller" language="en_US">
1135 <since>
1136 <version>19.3.0</version>
1137 <version>18.11.0</version>
1138 <version>16.25.0</version>
1139 </since>
1140 <synopsis>
1141 Request to withdraw a caller from the queue back to the dialplan.
1142 </synopsis>
1143 <syntax>
1144 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1145 <parameter name="Queue" required="true">
1146 <para>The name of the queue to take action on.</para>
1147 </parameter>
1148 <parameter name="Caller" required="true">
1149 <para>The caller (channel) to withdraw from the queue.</para>
1150 </parameter>
1151 <parameter name="WithdrawInfo" required="false">
1152 <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>
1153 </parameter>
1154 </syntax>
1155 <description>
1156 </description>
1157 </manager>
1158
1159 <managerEvent language="en_US" name="QueueParams">
1160 <managerEventInstance class="EVENT_FLAG_AGENT">
1161 <since>
1162 <version>16.24.0</version>
1163 <version>18.10.0</version>
1164 <version>19.2.0</version>
1165 </since>
1166 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1167 <syntax>
1168 <parameter name="Max">
1169 <para>The name of the queue.</para>
1170 </parameter>
1171 <parameter name="Strategy">
1172 <para>The strategy of the queue.</para>
1173 </parameter>
1174 <parameter name="Calls">
1175 <para>The queue member's channel technology or location.</para>
1176 </parameter>
1177 <parameter name="Holdtime">
1178 <para>The queue's hold time.</para>
1179 </parameter>
1180 <parameter name="TalkTime">
1181 <para>The queue's talk time.</para>
1182 </parameter>
1183 <parameter name="Completed">
1184 <para>The queue's completion time.</para>
1185 </parameter>
1186 <parameter name="Abandoned">
1187 <para>The queue's call abandonment metric.</para>
1188 </parameter>
1189 <parameter name="ServiceLevelPerf">
1190 <para>Primary service level performance metric.</para>
1191 </parameter>
1192 <parameter name="ServiceLevelPerf2">
1193 <para>Secondary service level performance metric.</para>
1194 </parameter>
1195 </syntax>
1196 <see-also>
1197 <ref type="managerEvent">QueueMember</ref>
1198 <ref type="managerEvent">QueueEntry</ref>
1199 </see-also>
1200 </managerEventInstance>
1201 </managerEvent>
1202 <managerEvent language="en_US" name="QueueEntry">
1203 <managerEventInstance class="EVENT_FLAG_AGENT">
1204 <since>
1205 <version>16.24.0</version>
1206 <version>18.10.0</version>
1207 <version>19.2.0</version>
1208 </since>
1209 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1210 <syntax>
1211 <parameter name="Queue">
1212 <para>The name of the queue.</para>
1213 </parameter>
1214 <parameter name="Position">
1215 <para>The caller's position within the queue.</para>
1216 </parameter>
1217 <parameter name="Channel">
1218 <para>The name of the caller's channel.</para>
1219 </parameter>
1220 <parameter name="Uniqueid">
1221 <para>The unique ID of the channel.</para>
1222 </parameter>
1223 <parameter name="CallerIDNum">
1224 <para>The Caller ID number.</para>
1225 </parameter>
1226 <parameter name="CallerIDName">
1227 <para>The Caller ID name.</para>
1228 </parameter>
1229 <parameter name="ConnectedLineNum">
1230 <para>The bridged party's number.</para>
1231 </parameter>
1232 <parameter name="ConnectedLineName">
1233 <para>The bridged party's name.</para>
1234 </parameter>
1235 <parameter name="Wait">
1236 <para>The caller's wait time.</para>
1237 </parameter>
1238 <parameter name="Priority">
1239 <para>The caller's priority within the queue.</para>
1240 </parameter>
1241 </syntax>
1242 <see-also>
1243 <ref type="managerEvent">QueueParams</ref>
1244 <ref type="managerEvent">QueueMember</ref>
1245 </see-also>
1246 </managerEventInstance>
1247 </managerEvent>
1248 <managerEvent language="en_US" name="QueueMemberStatus">
1249 <managerEventInstance class="EVENT_FLAG_AGENT">
1250 <since>
1251 <version>12.0.0</version>
1252 </since>
1253 <synopsis>Raised when a Queue member's status has changed.</synopsis>
1254 <syntax>
1255 <parameter name="Queue">
1256 <para>The name of the queue.</para>
1257 </parameter>
1258 <parameter name="MemberName">
1259 <para>The name of the queue member.</para>
1260 </parameter>
1261 <parameter name="Interface">
1262 <para>The queue member's channel technology or location.</para>
1263 </parameter>
1264 <parameter name="StateInterface">
1265 <para>Channel technology or location from which to read device state changes.</para>
1266 </parameter>
1267 <parameter name="Membership">
1268 <enumlist>
1269 <enum name="dynamic"/>
1270 <enum name="realtime"/>
1271 <enum name="static"/>
1272 </enumlist>
1273 </parameter>
1274 <parameter name="Penalty">
1275 <para>The penalty associated with the queue member.</para>
1276 </parameter>
1277 <parameter name="CallsTaken">
1278 <para>The number of calls this queue member has serviced.</para>
1279 </parameter>
1280 <parameter name="LastCall">
1281 <para>The time this member last took a call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1282 </parameter>
1283 <parameter name="LastPause">
1284 <para>The time when started last paused the queue member.</para>
1285 </parameter>
1286 <parameter name="LoginTime">
1287 <para>The time this member logged in to the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1288 </parameter>
1289 <parameter name="InCall">
1290 <para>Set to 1 if member is in call. Set to 0 after LastCall time is updated.</para>
1291 <enumlist>
1292 <enum name="0"/>
1293 <enum name="1"/>
1294 </enumlist>
1295 </parameter>
1296 <parameter name="Status">
1297 <para>The numeric device state status of the queue member.</para>
1298 <enumlist>
1299 <enum name="0"><para>AST_DEVICE_UNKNOWN</para></enum>
1300 <enum name="1"><para>AST_DEVICE_NOT_INUSE</para></enum>
1301 <enum name="2"><para>AST_DEVICE_INUSE</para></enum>
1302 <enum name="3"><para>AST_DEVICE_BUSY</para></enum>
1303 <enum name="4"><para>AST_DEVICE_INVALID</para></enum>
1304 <enum name="5"><para>AST_DEVICE_UNAVAILABLE</para></enum>
1305 <enum name="6"><para>AST_DEVICE_RINGING</para></enum>
1306 <enum name="7"><para>AST_DEVICE_RINGINUSE</para></enum>
1307 <enum name="8"><para>AST_DEVICE_ONHOLD</para></enum>
1308 </enumlist>
1309 </parameter>
1310 <parameter name="Paused">
1311 <enumlist>
1312 <enum name="0"/>
1313 <enum name="1"/>
1314 </enumlist>
1315 </parameter>
1316 <parameter name="PausedReason">
1317 <para>If set when paused, the reason the queue member was paused.</para>
1318 </parameter>
1319 <parameter name="Ringinuse">
1320 <enumlist>
1321 <enum name="0"/>
1322 <enum name="1"/>
1323 </enumlist>
1324 </parameter>
1325 <parameter name="Wrapuptime">
1326 <para>The Wrapup Time of the queue member. If this value is set will override the wrapup time of queue.</para>
1327 </parameter>
1328 </syntax>
1329 </managerEventInstance>
1330 </managerEvent>
1331 <managerEvent language="en_US" name="QueueMemberAdded">
1332 <managerEventInstance class="EVENT_FLAG_AGENT">
1333 <since>
1334 <version>12.0.0</version>
1335 </since>
1336 <synopsis>Raised when a member is added to the queue.</synopsis>
1337 <syntax>
1338 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1339 </syntax>
1340 <see-also>
1341 <ref type="managerEvent">QueueMemberRemoved</ref>
1342 <ref type="application">AddQueueMember</ref>
1343 </see-also>
1344 </managerEventInstance>
1345 </managerEvent>
1346 <managerEvent language="en_US" name="QueueMemberRemoved">
1347 <managerEventInstance class="EVENT_FLAG_AGENT">
1348 <since>
1349 <version>12.0.0</version>
1350 </since>
1351 <synopsis>Raised when a member is removed from the queue.</synopsis>
1352 <syntax>
1353 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1354 </syntax>
1355 <see-also>
1356 <ref type="managerEvent">QueueMemberAdded</ref>
1357 <ref type="application">RemoveQueueMember</ref>
1358 </see-also>
1359 </managerEventInstance>
1360 </managerEvent>
1361 <managerEvent language="en_US" name="QueueMemberPause">
1362 <managerEventInstance class="EVENT_FLAG_AGENT">
1363 <since>
1364 <version>12.2.0</version>
1365 </since>
1366 <synopsis>Raised when a member is paused/unpaused in the queue.</synopsis>
1367 <syntax>
1368 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1369 </syntax>
1370 <see-also>
1371 <ref type="application">PauseQueueMember</ref>
1372 <ref type="application">UnpauseQueueMember</ref>
1373 </see-also>
1374 </managerEventInstance>
1375 </managerEvent>
1376 <managerEvent language="en_US" name="QueueMemberPenalty">
1377 <managerEventInstance class="EVENT_FLAG_AGENT">
1378 <since>
1379 <version>12.0.0</version>
1380 </since>
1381 <synopsis>Raised when a member's penalty is changed.</synopsis>
1382 <syntax>
1383 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1384 </syntax>
1385 <see-also>
1386 <ref type="function">QUEUE_MEMBER</ref>
1387 </see-also>
1388 </managerEventInstance>
1389 </managerEvent>
1390 <managerEvent language="en_US" name="QueueMemberRinginuse">
1391 <managerEventInstance class="EVENT_FLAG_AGENT">
1392 <since>
1393 <version>12.0.0</version>
1394 </since>
1395 <synopsis>Raised when a member's ringinuse setting is changed.</synopsis>
1396 <syntax>
1397 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1398 </syntax>
1399 <see-also>
1400 <ref type="function">QUEUE_MEMBER</ref>
1401 </see-also>
1402 </managerEventInstance>
1403 </managerEvent>
1404 <managerEvent language="en_US" name="QueueCallerJoin">
1405 <managerEventInstance class="EVENT_FLAG_AGENT">
1406 <since>
1407 <version>12.0.0</version>
1408 </since>
1409 <synopsis>Raised when a caller joins a Queue.</synopsis>
1410 <syntax>
1411 <channel_snapshot/>
1412 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1413 <parameter name="Position">
1414 <para>This channel's current position in the queue.</para>
1415 </parameter>
1416 <parameter name="Count">
1417 <para>The total number of channels in the queue.</para>
1418 </parameter>
1419 </syntax>
1420 <see-also>
1421 <ref type="managerEvent">QueueCallerLeave</ref>
1422 <ref type="application">Queue</ref>
1423 </see-also>
1424 </managerEventInstance>
1425 </managerEvent>
1426 <managerEvent language="en_US" name="QueueCallerLeave">
1427 <managerEventInstance class="EVENT_FLAG_AGENT">
1428 <since>
1429 <version>12.0.0</version>
1430 </since>
1431 <synopsis>Raised when a caller leaves a Queue.</synopsis>
1432 <syntax>
1433 <channel_snapshot/>
1434 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1435 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Count'])" />
1436 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1437 </syntax>
1438 <see-also>
1439 <ref type="managerEvent">QueueCallerJoin</ref>
1440 </see-also>
1441 </managerEventInstance>
1442 </managerEvent>
1443 <managerEvent language="en_US" name="QueueCallerAbandon">
1444 <managerEventInstance class="EVENT_FLAG_AGENT">
1445 <since>
1446 <version>12.0.0</version>
1447 </since>
1448 <synopsis>Raised when a caller abandons the queue.</synopsis>
1449 <syntax>
1450 <channel_snapshot/>
1451 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1452 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1453 <parameter name="OriginalPosition">
1454 <para>The channel's original position in the queue.</para>
1455 </parameter>
1456 <parameter name="HoldTime">
1457 <para>The time the channel was in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1458 </parameter>
1459 </syntax>
1460 </managerEventInstance>
1461 </managerEvent>
1462 <managerEvent language="en_US" name="AgentCalled">
1463 <managerEventInstance class="EVENT_FLAG_AGENT">
1464 <since>
1465 <version>12.0.0</version>
1466 </since>
1467 <synopsis>Raised when an queue member is notified of a caller in the queue.</synopsis>
1468 <syntax>
1469 <channel_snapshot/>
1470 <channel_snapshot prefix="Dest"/>
1471 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1472 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1473 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1474 </syntax>
1475 <see-also>
1476 <ref type="managerEvent">AgentRingNoAnswer</ref>
1477 <ref type="managerEvent">AgentComplete</ref>
1478 <ref type="managerEvent">AgentConnect</ref>
1479 </see-also>
1480 </managerEventInstance>
1481 </managerEvent>
1482 <managerEvent language="en_US" name="AgentRingNoAnswer">
1483 <managerEventInstance class="EVENT_FLAG_AGENT">
1484 <since>
1485 <version>12.0.0</version>
1486 </since>
1487 <synopsis>Raised when a queue member is notified of a caller in the queue and fails to answer.</synopsis>
1488 <syntax>
1489 <channel_snapshot/>
1490 <channel_snapshot prefix="Dest"/>
1491 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1492 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1493 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1494 <parameter name="RingTime">
1495 <para>The time the queue member was rung, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1496 </parameter>
1497 </syntax>
1498 <see-also>
1499 <ref type="managerEvent">AgentCalled</ref>
1500 </see-also>
1501 </managerEventInstance>
1502 </managerEvent>
1503 <managerEvent language="en_US" name="AgentComplete">
1504 <managerEventInstance class="EVENT_FLAG_AGENT">
1505 <since>
1506 <version>12.0.0</version>
1507 </since>
1508 <synopsis>Raised when a queue member has finished servicing a caller in the queue.</synopsis>
1509 <syntax>
1510 <channel_snapshot/>
1511 <channel_snapshot prefix="Dest"/>
1512 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1513 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1514 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1515 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1516 <parameter name="TalkTime">
1517 <para>The time the queue member talked with the caller in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1518 </parameter>
1519 <parameter name="Reason">
1520 <enumlist>
1521 <enum name="caller"/>
1522 <enum name="agent"/>
1523 <enum name="transfer"/>
1524 </enumlist>
1525 </parameter>
1526 </syntax>
1527 <see-also>
1528 <ref type="managerEvent">AgentCalled</ref>
1529 <ref type="managerEvent">AgentConnect</ref>
1530 </see-also>
1531 </managerEventInstance>
1532 </managerEvent>
1533 <managerEvent language="en_US" name="AgentDump">
1534 <managerEventInstance class="EVENT_FLAG_AGENT">
1535 <since>
1536 <version>12.0.0</version>
1537 </since>
1538 <synopsis>Raised when a queue member hangs up on a caller in the queue.</synopsis>
1539 <syntax>
1540 <channel_snapshot/>
1541 <channel_snapshot prefix="Dest"/>
1542 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1543 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1544 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1545 </syntax>
1546 <see-also>
1547 <ref type="managerEvent">AgentCalled</ref>
1548 <ref type="managerEvent">AgentConnect</ref>
1549 </see-also>
1550 </managerEventInstance>
1551 </managerEvent>
1552 <managerEvent language="en_US" name="AgentConnect">
1553 <managerEventInstance class="EVENT_FLAG_AGENT">
1554 <since>
1555 <version>12.0.0</version>
1556 </since>
1557 <synopsis>Raised when a queue member answers and is bridged to a caller in the queue.</synopsis>
1558 <syntax>
1559 <channel_snapshot/>
1560 <channel_snapshot prefix="Dest"/>
1561 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1562 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1563 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1564 <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='RingTime'])" />
1565 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1566 </syntax>
1567 <see-also>
1568 <ref type="managerEvent">AgentCalled</ref>
1569 <ref type="managerEvent">AgentComplete</ref>
1570 <ref type="managerEvent">AgentDump</ref>
1571 </see-also>
1572 </managerEventInstance>
1573 </managerEvent>
1574 ***/
1575
1576enum {
1578 OPT_GO_ON = (1 << 1),
1587 OPT_NO_RETRY = (1 << 10),
1588 OPT_RINGING = (1 << 11),
1599};
1600
1601enum {
1606 /* note: this entry _MUST_ be the last one in the enum */
1609
1634
1635
1637 AQMFLAG_PAUSED = (1 << 1),
1638 AQMFLAG_REASON = (1 << 2),
1639};
1640
1643 AQM_OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
1644};
1645
1650
1651enum {
1660};
1661
1662enum {
1667
1674
1675static const struct strategy {
1677 const char *name;
1678} strategies[] = {
1679 { QUEUE_STRATEGY_RINGALL, "ringall" },
1680 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
1681 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
1682 { QUEUE_STRATEGY_RANDOM, "random" },
1683 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
1684 { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
1685 { QUEUE_STRATEGY_LINEAR, "linear" },
1686 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
1687 { QUEUE_STRATEGY_RRORDERED, "rrordered"},
1689
1690static const struct autopause {
1692 const char *name;
1693} autopausesmodes [] = {
1694 { QUEUE_AUTOPAUSE_OFF,"no" },
1695 { QUEUE_AUTOPAUSE_ON, "yes" },
1696 { QUEUE_AUTOPAUSE_ALL,"all" },
1698
1699#define DEFAULT_RETRY 5
1700#define DEFAULT_TIMEOUT 15
1701#define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
1702#define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
1703/*!
1704 * \brief The minimum number of seconds between position announcements.
1705 * \note The default value of 15 provides backwards compatibility.
1706 */
1707#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
1708
1709#define MAX_QUEUE_BUCKETS 53
1710
1711#define RES_OKAY 0 /*!< Action completed */
1712#define RES_EXISTS (-1) /*!< Entry already exists */
1713#define RES_OUTOFMEMORY (-2) /*!< Out of memory */
1714#define RES_NOSUCHQUEUE (-3) /*!< No such queue */
1715#define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
1716#define RES_NOT_CALLER (-5) /*!< Caller not found */
1717
1718static char *app = "Queue";
1719
1720static char *app_aqm = "AddQueueMember" ;
1721
1722static char *app_rqm = "RemoveQueueMember" ;
1723
1724static char *app_pqm = "PauseQueueMember" ;
1725
1726static char *app_upqm = "UnpauseQueueMember" ;
1727
1728static char *app_ql = "QueueLog" ;
1729
1730static char *app_qupd = "QueueUpdate";
1731
1732/*! \brief Persistent Members astdb family */
1733static const char * const pm_family = "Queue/PersistentMembers";
1734
1735/*! \brief queues.conf [general] option */
1737
1738/*! \brief Records that one or more queues use weight */
1739static int use_weight;
1740
1741/*! \brief queues.conf [general] option */
1743
1744/*! \brief queues.conf [general] option */
1746
1747/*! \brief queues.conf [general] option */
1749
1750/*! \brief queuerules.conf [general] option */
1752
1753/*! \brief Subscription to device state change messages */
1755
1756/*! \brief queues.conf [general] option */
1758
1759/*! \brief queues.conf [general] option */
1761
1762/*! \brief queues.conf [general] option */
1764
1765/*! \brief queues.conf [general] option */
1767
1768/*! \brief queues.conf [general] option */
1770
1771/*! \brief name of the ringinuse field in the realtime database */
1773
1774/*! \brief does realtime backend support reason_paused */
1776
1788
1789static const struct {
1791 char *text;
1792} queue_results[] = {
1793 { QUEUE_UNKNOWN, "UNKNOWN" },
1794 { QUEUE_TIMEOUT, "TIMEOUT" },
1795 { QUEUE_JOINEMPTY,"JOINEMPTY" },
1796 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
1797 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
1798 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
1799 { QUEUE_FULL, "FULL" },
1800 { QUEUE_CONTINUE, "CONTINUE" },
1801 { QUEUE_WITHDRAW, "WITHDRAW" },
1803
1808
1809/*! \brief We define a custom "local user" structure because we
1810 * use it not only for keeping track of what is in use but
1811 * also for keeping track of who we're dialing.
1812 *
1813 * There are two "links" defined in this structure, q_next and call_next.
1814 * q_next links ALL defined callattempt structures into a linked list. call_next is
1815 * a link which allows for a subset of the callattempts to be traversed. This subset
1816 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
1817 * also is helpful so that queue logs are always accurate in the case where a call to
1818 * a member times out, especially if using the ringall strategy.
1819*/
1820
1825 char interface[256]; /*!< An Asterisk dial string (not a channel name) */
1828 /*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
1830 /*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
1832 /*! TRUE if the connected line update is blocked. */
1834 /*! TRUE if caller id is not available for connected line */
1835 unsigned int dial_callerid_absent:1;
1836 /*! TRUE if the call is still active */
1837 unsigned int stillgoing:1;
1839 /*! Original channel name. Must be freed. Could be NULL if allocation failed. */
1841};
1842
1843
1845 struct call_queue *parent; /*!< What queue is our parent */
1846 char moh[MAX_MUSICCLASS]; /*!< Name of musiconhold to be used */
1847 char announce[PATH_MAX]; /*!< Announcement to play for member when call is answered */
1848 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
1849 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
1850 const char *predial_callee; /*!< Gosub app arguments for outgoing calls. NULL if not supplied. */
1851 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
1852 int pos; /*!< Where we are in the queue */
1853 int prio; /*!< Our priority */
1854 int last_pos_said; /*!< Last position we told the user */
1855 int ring_when_ringing; /*!< Should we only use ring indication when a channel is ringing? */
1856 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
1857 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
1858 time_t last_pos; /*!< Last time we told the user their position */
1859 int opos; /*!< Where we started in the queue */
1860 int handled; /*!< Whether our call was handled */
1861 int pending; /*!< Non-zero if we are attempting to call a member */
1862 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
1863 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
1864 int raise_penalty; /*!< Float lower penalty members to a minimum penalty */
1865 int raise_respect_min; /*!< A switch raise_penalty should respect min_penalty not just max_penalty */
1866 int linpos; /*!< If using linear strategy, what position are we at? */
1867 int linwrapped; /*!< Is the linpos wrapped? */
1868 time_t start; /*!< When we started holding */
1869 time_t expire; /*!< When this entry should expire (time out of queue) */
1870 int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
1871 unsigned int withdraw:1; /*!< Should this call exit the queue at its next iteration? Used for QueueWithdrawCaller */
1872 char *withdraw_info; /*!< Optional info passed by the caller of QueueWithdrawCaller */
1873 struct ast_channel *chan; /*!< Our channel */
1874 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
1875 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
1876 struct queue_ent *next; /*!< The next queue entry */
1877};
1878
1879struct member {
1880 char interface[AST_CHANNEL_NAME]; /*!< Technology/Location to dial to reach this member*/
1881 char state_exten[AST_MAX_EXTENSION]; /*!< Extension to get state from (if using hint) */
1882 char state_context[AST_MAX_CONTEXT]; /*!< Context to use when getting state (if using hint) */
1883 char state_interface[AST_CHANNEL_NAME]; /*!< Technology/Location from which to read devicestate changes */
1884 int state_id; /*!< Extension state callback id (if using hint) */
1885 char membername[80]; /*!< Member name to use in queue logs */
1886 int penalty; /*!< Are we a last resort? */
1887 int calls; /*!< Number of calls serviced by this member */
1888 int dynamic; /*!< Are we dynamically added? */
1889 int realtime; /*!< Is this member realtime? */
1890 int status; /*!< Status of queue member */
1891 int paused; /*!< Are we paused (not accepting calls)? */
1892 char reason_paused[80]; /*!< Reason of paused if member is paused */
1893 int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
1894 int callcompletedinsl; /*!< Whether the current call was completed within service level */
1895 int wrapuptime; /*!< Wrapup Time */
1896 time_t starttime; /*!< The time at which the member answered the current caller. */
1897 time_t lastcall; /*!< When last successful call was hungup */
1898 time_t lastpause; /*!< When started the last pause */
1899 time_t logintime; /*!< The time when started the login */
1900 struct call_queue *lastqueue; /*!< Last queue we received a call */
1901 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
1902 unsigned int delme:1; /*!< Flag to delete entry on reload */
1903 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
1904 unsigned int ringinuse:1; /*!< Flag to ring queue members even if their status is 'inuse' */
1905};
1906
1917
1922
1923/* values used in multi-bit flags in call_queue */
1924#define ANNOUNCEHOLDTIME_ALWAYS 1
1925#define ANNOUNCEHOLDTIME_ONCE 2
1926#define QUEUE_EVENT_VARIABLES 3
1927
1929 int time; /*!< Number of seconds that need to pass before applying this rule */
1930 int max_value; /*!< The amount specified in the penalty rule for max penalty */
1931 int min_value; /*!< The amount specified in the penalty rule for min penalty */
1932 int raise_value; /*!< The amount specified in the penalty rule for min penalty */
1933 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
1934 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1935 int raise_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1936 int raise_respect_min; /*!< A switch raise_penalty should respect min_penalty not just max_penalty */
1937 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
1938};
1939
1940#define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
1941#define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
1942#define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
1943#define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
1944
1945#define FORCELONGESTWAITINGCALLER_NO 0
1946#define FORCELONGESTWAITINGCALLER_YES 1
1947#define FORCELONGESTWAITINGCALLER_PRIO 2 /*!< Account for call priorities when forcing longest waiting caller */
1948
1951 /*! Queue name */
1953 /*! Music on Hold class */
1955 /*! Announcement to play when call is answered */
1957 /*! Exit context */
1959 /*! Gosub to run upon member connection */
1961 /*! Default rule to use if none specified in call to Queue() */
1963 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
1965 /*! Sound file: "There are currently" (def. queue-thereare) */
1967 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
1969 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
1971 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
1973 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
1975 /*! Sound file: "minutes." (def. queue-minutes) */
1977 /*! Sound file: "minute." (def. queue-minute) */
1979 /*! Sound file: "seconds." (def. queue-seconds) */
1981 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
1983 /*! Sound file: Custom announce for caller, no default */
1985 /*! Sound file: "Hold time" (def. queue-reporthold) */
1988 /*! Sound files: Custom announce, no default */
1990 unsigned int dead:1;
1991 unsigned int ringinuse:1;
1992 unsigned int announce_to_first_user:1; /*!< Whether or not we announce to the first user in a queue */
1993 unsigned int setinterfacevar:1;
1994 unsigned int setqueuevar:1;
1995 unsigned int setqueueentryvar:1;
1996 unsigned int reportholdtime:1;
1997 unsigned int wrapped:1;
1998 unsigned int timeoutrestart:1;
1999 unsigned int announceholdtime:2;
2000 unsigned int announceposition:3;
2001 unsigned int announceposition_only_up:1; /*!< Only announce position if it has improved */
2003 unsigned int realtime:1;
2004 unsigned int found:1;
2006 unsigned int autopausebusy:1;
2007 unsigned int autopauseunavail:1;
2010 int announcepositionlimit; /*!< How many positions we announce? */
2011 int announcefrequency; /*!< How often to announce their position */
2012 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
2013 int periodicannouncestartdelay; /*!< How long into the queue should the periodic accouncement start */
2014 int periodicannouncefrequency; /*!< How often to play periodic announcement */
2015 int numperiodicannounce; /*!< The number of periodic announcements configured */
2016 int randomperiodicannounce; /*!< Are periodic announcements randomly chosen */
2017 int roundingseconds; /*!< How many seconds do we round to? */
2018 int holdtime; /*!< Current avg holdtime, based on an exponential average */
2019 int talktime; /*!< Current avg talktime, based on the same exponential average */
2020 int callscompleted; /*!< Number of queue calls completed */
2021 int callsabandoned; /*!< Number of queue calls abandoned */
2022 int callsabandonedinsl; /*!< Number of queue calls abandoned in servicelevel */
2023 int servicelevel; /*!< seconds setting for servicelevel*/
2024 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
2025 char monfmt[8]; /*!< Format to use when recording calls */
2026 int count; /*!< How many entries */
2027 int maxlen; /*!< Max number of entries */
2028 int wrapuptime; /*!< Wrapup Time */
2029 int penaltymemberslimit; /*!< Disregard penalty when queue has fewer than this many members */
2030
2031 int retry; /*!< Retry calling everyone after this amount of time */
2032 int timeout; /*!< How long to wait for an answer */
2033 int weight; /*!< Respective weight */
2034 int autopause; /*!< Auto pause queue members if they fail to answer */
2035 int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
2036 int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
2037
2038 /* Queue strategy things */
2039 int rrpos; /*!< Round Robin - position */
2040 int memberdelay; /*!< Seconds to delay connecting member to caller */
2041 int autofill; /*!< Ignore the head call status and ring an available agent */
2042
2043 int log_restricted_caller_id:1; /*!< Whether log Restricted Caller ID */
2044
2045 struct ao2_container *members; /*!< Head of the list of members */
2046 struct queue_ent *head; /*!< Head of the list of callers */
2047 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
2048 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
2049};
2050
2056
2058
2059static struct ao2_container *queues;
2060
2061static void update_realtime_members(struct call_queue *q);
2062static struct member *interface_exists(struct call_queue *q, const char *interface);
2063static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
2064static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime);
2065
2066static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface);
2067/*! \brief sets the QUEUESTATUS channel variable */
2068static void set_queue_result(struct ast_channel *chan, enum queue_result res)
2069{
2070 int i;
2071
2072 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
2073 if (queue_results[i].id == res) {
2074 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
2075 return;
2076 }
2077 }
2078}
2079
2080static const char *int2strat(int strategy)
2081{
2082 int x;
2083
2084 for (x = 0; x < ARRAY_LEN(strategies); x++) {
2085 if (strategy == strategies[x].strategy) {
2086 return strategies[x].name;
2087 }
2088 }
2089
2090 return "<unknown>";
2091}
2092
2093static int strat2int(const char *strategy)
2094{
2095 int x;
2096
2097 for (x = 0; x < ARRAY_LEN(strategies); x++) {
2098 if (!strcasecmp(strategy, strategies[x].name)) {
2099 return strategies[x].strategy;
2100 }
2101 }
2102
2103 return -1;
2104}
2105
2106static int autopause2int(const char *autopause)
2107{
2108 int x;
2109 /*This 'double check' that default value is OFF */
2111 return QUEUE_AUTOPAUSE_OFF;
2112 }
2113
2114 /*This 'double check' is to ensure old values works */
2115 if(ast_true(autopause)) {
2116 return QUEUE_AUTOPAUSE_ON;
2117 }
2118
2119 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
2120 if (!strcasecmp(autopause, autopausesmodes[x].name)) {
2121 return autopausesmodes[x].autopause;
2122 }
2123 }
2124
2125 /*This 'double check' that default value is OFF */
2126 return QUEUE_AUTOPAUSE_OFF;
2127}
2128
2129static int queue_hash_cb(const void *obj, const int flags)
2130{
2131 const struct call_queue *q = obj;
2132
2133 return ast_str_case_hash(q->name);
2134}
2135
2136static int queue_cmp_cb(void *obj, void *arg, int flags)
2137{
2138 struct call_queue *q = obj, *q2 = arg;
2139 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
2140}
2141
2142/*!
2143 * \brief Return wrapuptime
2144 *
2145 * This function checks if wrapuptime in member is set and return this value.
2146 * Otherwise return value the wrapuptime in the queue configuration
2147 * \return integer value
2148 */
2149static int get_wrapuptime(struct call_queue *q, struct member *member)
2150{
2151 if (member->wrapuptime) {
2152 return member->wrapuptime;
2153 }
2154 return q->wrapuptime;
2155}
2156
2157/*! \internal
2158 * \brief ao2_callback, Decreases queuepos of all followers with a queuepos greater than arg.
2159 * \param obj the member being acted on
2160 * \param arg pointer to an integer containing the position value that was removed and requires reduction for anything above
2161 * \param flag unused
2162 */
2163static int queue_member_decrement_followers(void *obj, void *arg, int flag)
2164{
2165 struct member *mem = obj;
2166 int *decrement_followers_after = arg;
2167
2168 if (mem->queuepos > *decrement_followers_after) {
2169 mem->queuepos--;
2170 }
2171
2172 return 0;
2173}
2174
2175/*! \internal
2176 * \brief ao2_callback, finds members in a queue marked for deletion and in a cascading fashion runs queue_member_decrement_followers
2177 * on them. This callback should always be ran before performing mass unlinking of delmarked members from queues.
2178 * \param obj member being acted on
2179 * \param arg pointer to the queue members are being removed from
2180 * \param flag unused
2181 */
2182static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
2183{
2184 struct member *mem = obj;
2185 struct call_queue *queue = arg;
2186 int rrpos = mem->queuepos;
2187
2188 if (mem->delme) {
2190 }
2191
2192 return 0;
2193}
2194
2195/*! \internal
2196 * \brief Use this to decrement followers during removal of a member
2197 * \param queue which queue the member is being removed from
2198 * \param mem which member is being removed from the queue
2199 */
2200static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
2201{
2202 int pos = mem->queuepos;
2203
2204 /* 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
2205 * who would have been next otherwise. */
2206 if (pos < queue->rrpos) {
2207 queue->rrpos--;
2208 }
2209
2211}
2212
2213#define queue_ref(q) ao2_bump(q)
2214#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2215#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2216#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2217#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2218#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2219
2220/*! \brief Set variables of queue */
2221static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
2222{
2223 char interfacevar[256]="";
2224 float sl = 0;
2225
2226 ao2_lock(q);
2227
2228 if (q->setqueuevar) {
2229 sl = 0;
2230 if (q->callscompleted > 0) {
2231 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
2232 }
2233
2234 snprintf(interfacevar, sizeof(interfacevar),
2235 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2237
2238 ao2_unlock(q);
2239
2240 pbx_builtin_setvar_multiple(chan, interfacevar);
2241 } else {
2242 ao2_unlock(q);
2243 }
2244}
2245
2246/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
2247static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
2248{
2249 struct queue_ent *cur;
2250
2251 if (!q || !new)
2252 return;
2253 if (prev) {
2254 cur = prev->next;
2255 prev->next = new;
2256 } else {
2257 cur = q->head;
2258 q->head = new;
2259 }
2260 new->next = cur;
2261
2262 /* every queue_ent must have a reference to it's parent call_queue, this
2263 * reference does not go away until the end of the queue_ent's life, meaning
2264 * that even when the queue_ent leaves the call_queue this ref must remain. */
2265 if (!new->parent) {
2266 queue_ref(q);
2267 new->parent = q;
2268 }
2269 new->pos = ++(*pos);
2270 new->opos = *pos;
2271}
2272
2274{
2276 RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
2277 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2278
2279 channel_string = ast_manager_build_channel_state_string(obj->snapshot);
2280 event_string = ast_manager_str_from_json_object(obj->blob, NULL);
2281 if (!channel_string || !event_string) {
2282 return NULL;
2283 }
2284
2286 "%s"
2287 "%s",
2288 ast_str_buffer(channel_string),
2289 ast_str_buffer(event_string));
2290}
2291
2293{
2294 return queue_channel_to_ami("QueueCallerJoin", message);
2295}
2296
2298{
2299 return queue_channel_to_ami("QueueCallerLeave", message);
2300}
2301
2303{
2304 return queue_channel_to_ami("QueueCallerAbandon", message);
2305}
2306
2307STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_join_type,
2309 );
2310STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_leave_type,
2312 );
2313STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_abandon_type,
2315 );
2316
2318{
2319 struct ast_json_payload *payload = stasis_message_data(message);
2320 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2321
2322 event_string = ast_manager_str_from_json_object(payload->json, NULL);
2323 if (!event_string) {
2324 return NULL;
2325 }
2326
2328 "%s",
2329 ast_str_buffer(event_string));
2330}
2331
2333{
2334 return queue_member_to_ami("QueueMemberStatus", message);
2335}
2336
2338{
2339 return queue_member_to_ami("QueueMemberAdded", message);
2340}
2341
2343{
2344 return queue_member_to_ami("QueueMemberRemoved", message);
2345}
2346
2348{
2349 return queue_member_to_ami("QueueMemberPause", message);
2350}
2351
2353{
2354 return queue_member_to_ami("QueueMemberPenalty", message);
2355}
2356
2358{
2359 return queue_member_to_ami("QueueMemberRinginuse", message);
2360}
2361
2362STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_status_type,
2364 );
2365STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_added_type,
2367 );
2368STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_removed_type,
2370 );
2371STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_pause_type,
2373 );
2374STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_penalty_type,
2376 );
2377STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_ringinuse_type,
2379 );
2380
2382{
2385 struct ast_channel_snapshot *agent;
2386 RAII_VAR(struct ast_str *, caller_event_string, NULL, ast_free);
2387 RAII_VAR(struct ast_str *, agent_event_string, NULL, ast_free);
2388 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2389
2391 if (caller) {
2392 caller_event_string = ast_manager_build_channel_state_string(caller);
2393 if (!caller_event_string) {
2394 ast_log(LOG_NOTICE, "No caller event string, bailing\n");
2395 return NULL;
2396 }
2397 }
2398
2399 agent = ast_multi_channel_blob_get_channel(obj, "agent");
2400 if (agent) {
2401 agent_event_string = ast_manager_build_channel_state_string_prefix(agent, "Dest");
2402 if (!agent_event_string) {
2403 ast_log(LOG_NOTICE, "No agent event string, bailing\n");
2404 return NULL;
2405 }
2406 }
2407
2409 if (!event_string) {
2410 return NULL;
2411 }
2412
2414 "%s"
2415 "%s"
2416 "%s",
2417 caller_event_string ? ast_str_buffer(caller_event_string) : "",
2418 agent_event_string ? ast_str_buffer(agent_event_string) : "",
2419 ast_str_buffer(event_string));
2420}
2421
2423{
2424 return queue_multi_channel_to_ami("AgentCalled", message);
2425}
2426
2428{
2429 return queue_multi_channel_to_ami("AgentConnect", message);
2430}
2431
2433{
2434 return queue_multi_channel_to_ami("AgentComplete", message);
2435}
2436
2441
2443{
2444 return queue_multi_channel_to_ami("AgentRingNoAnswer", message);
2445}
2446
2447STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_called_type,
2449 );
2450STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_connect_type,
2452 );
2453STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_complete_type,
2455 );
2458 );
2459STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_ringnoanswer_type,
2461 );
2462
2464 struct ast_channel_snapshot *caller_snapshot,
2465 struct ast_channel_snapshot *agent_snapshot,
2466 struct stasis_message_type *type, struct ast_json *blob)
2467{
2468 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
2469 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2470
2471 if (!type) {
2472 return;
2473 }
2474
2475 payload = ast_multi_channel_blob_create(blob);
2476 if (!payload) {
2477 return;
2478 }
2479
2480 if (caller_snapshot) {
2481 ast_multi_channel_blob_add_channel(payload, "caller", caller_snapshot);
2482 } else {
2483 ast_debug(1, "Empty caller_snapshot; sending incomplete event\n");
2484 }
2485
2486 if (agent_snapshot) {
2487 ast_multi_channel_blob_add_channel(payload, "agent", agent_snapshot);
2488 }
2489
2490 msg = stasis_message_create(type, payload);
2491 if (!msg) {
2492 return;
2493 }
2494
2495 stasis_publish(topic, msg);
2496}
2497
2498static void queue_publish_multi_channel_blob(struct ast_channel *caller, struct ast_channel *agent,
2499 struct stasis_message_type *type, struct ast_json *blob)
2500{
2501 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
2502 RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
2503
2504 ast_channel_lock(caller);
2505 caller_snapshot = ast_channel_snapshot_create(caller);
2506 ast_channel_unlock(caller);
2507 ast_channel_lock(agent);
2508 agent_snapshot = ast_channel_snapshot_create(agent);
2509 ast_channel_unlock(agent);
2510
2511 if (!caller_snapshot || !agent_snapshot) {
2512 return;
2513 }
2514
2516 agent_snapshot, type, blob);
2517}
2518
2519/*!
2520 * \internal
2521 * \brief Publish the member blob.
2522 * \since 12.0.0
2523 *
2524 * \param type Stasis message type to publish.
2525 * \param blob The information being published.
2526 *
2527 * \note The json blob reference is passed to this function.
2528 */
2530{
2531 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
2532 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2533
2534 if (!blob || !type) {
2535 ast_json_unref(blob);
2536 return;
2537 }
2538
2539 payload = ast_json_payload_create(blob);
2540 ast_json_unref(blob);
2541 if (!payload) {
2542 return;
2543 }
2544
2545 msg = stasis_message_create(type, payload);
2546 if (!msg) {
2547 return;
2548 }
2549
2551}
2552
2553static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
2554{
2555 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}",
2556 "Queue", q->name,
2557 "MemberName", mem->membername,
2558 "Interface", mem->interface,
2559 "StateInterface", mem->state_interface,
2560 "Membership", (mem->dynamic ? "dynamic" : (mem->realtime ? "realtime" : "static")),
2561 "Penalty", mem->penalty,
2562 "CallsTaken", mem->calls,
2563 "LastCall", (int)mem->lastcall,
2564 "LastPause", (int)mem->lastpause,
2565 "LoginTime", (int)mem->logintime,
2566 "InCall", mem->starttime ? 1 : 0,
2567 "Status", mem->status,
2568 "Paused", mem->paused,
2569 "PausedReason", mem->reason_paused,
2570 "Ringinuse", mem->ringinuse,
2571 "Wrapuptime", mem->wrapuptime);
2572}
2573
2574/*! \brief Check if members are available
2575 *
2576 * This function checks to see if members are available to be called. If any member
2577 * is available, the function immediately returns 0. If no members are available,
2578 * then -1 is returned.
2579 */
2580static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate, int raise_respect_min)
2581{
2582 struct member *member;
2583 struct ao2_iterator mem_iter;
2584
2585 ao2_lock(q);
2586 mem_iter = ao2_iterator_init(q->members, 0);
2587 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
2588 int penalty = member->penalty;
2589 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2590 /* Check if we should respect minimum penalty threshold */
2591 if (raise_respect_min && penalty < min_penalty) {
2592 ast_debug(4, "%s penalty %d not raised (below min %d)\n", member->membername, penalty, min_penalty);
2593 } else {
2594 ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
2595 penalty = raise_penalty;
2596 }
2597 }
2598 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2599 if (conditions & QUEUE_EMPTY_PENALTY) {
2600 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
2601 continue;
2602 }
2603 }
2604
2605 switch (devstate ? ast_device_state(member->state_interface) : member->status) {
2606 case AST_DEVICE_INVALID:
2607 if (conditions & QUEUE_EMPTY_INVALID) {
2608 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
2609 break;
2610 }
2611 goto default_case;
2613 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2614 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
2615 break;
2616 }
2617 goto default_case;
2618 case AST_DEVICE_INUSE:
2619 if (conditions & QUEUE_EMPTY_INUSE) {
2620 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
2621 break;
2622 }
2623 goto default_case;
2624 case AST_DEVICE_RINGING:
2625 if (conditions & QUEUE_EMPTY_RINGING) {
2626 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
2627 break;
2628 }
2629 goto default_case;
2630 case AST_DEVICE_UNKNOWN:
2631 if (conditions & QUEUE_EMPTY_UNKNOWN) {
2632 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
2633 break;
2634 }
2635 /* Fall-through */
2636 default:
2637 default_case:
2638 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2639 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
2640 break;
2641 } else if ((conditions & QUEUE_EMPTY_WRAPUP)
2642 && member->lastcall
2643 && get_wrapuptime(q, member)
2644 && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
2645 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2646 member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
2647 break;
2648 } else {
2649 ao2_ref(member, -1);
2650 ao2_iterator_destroy(&mem_iter);
2651 ao2_unlock(q);
2652 ast_debug(4, "%s is available.\n", member->membername);
2653 return 0;
2654 }
2655 break;
2656 }
2657 }
2658 ao2_iterator_destroy(&mem_iter);
2659 ao2_unlock(q);
2660
2661 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2662 /* member state still may be RINGING due to lag in event message - check again with device state */
2663 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1, raise_respect_min);
2664 }
2665 return -1;
2666}
2667
2668/*
2669 * A "pool" of member objects that calls are currently pending on. If an
2670 * agent is a member of multiple queues it's possible for that agent to be
2671 * called by each of the queues at the same time. This happens because device
2672 * state is slow to notify the queue app of one of it's member's being rung.
2673 * This "pool" allows us to track which members are currently being rung while
2674 * we wait on the device state change.
2675 */
2677#define MAX_CALL_ATTEMPT_BUCKETS 353
2678
2679static int pending_members_hash(const void *obj, const int flags)
2680{
2681 const struct member *object;
2682 const char *key;
2683
2684 switch (flags & OBJ_SEARCH_MASK) {
2685 case OBJ_SEARCH_KEY:
2686 key = obj;
2687 break;
2688 case OBJ_SEARCH_OBJECT:
2689 object = obj;
2690 key = object->interface;
2691 break;
2692 default:
2693 ast_assert(0);
2694 return 0;
2695 }
2696 return ast_str_case_hash(key);
2697}
2698
2699static int pending_members_cmp(void *obj, void *arg, int flags)
2700{
2701 const struct member *object_left = obj;
2702 const struct member *object_right = arg;
2703 const char *right_key = arg;
2704 int cmp;
2705
2706 switch (flags & OBJ_SEARCH_MASK) {
2707 case OBJ_SEARCH_OBJECT:
2708 right_key = object_right->interface;
2709 /* Fall through */
2710 case OBJ_SEARCH_KEY:
2711 cmp = strcasecmp(object_left->interface, right_key);
2712 break;
2714 /* Not supported by container. */
2715 ast_assert(0);
2716 return 0;
2717 default:
2718 cmp = 0;
2719 break;
2720 }
2721 if (cmp) {
2722 return 0;
2723 }
2724 return CMP_MATCH;
2725}
2726
2727static void pending_members_remove(struct member *mem)
2728{
2729 ast_debug(3, "Removed %s from pending_members\n", mem->membername);
2731}
2732
2733/*! \brief set a member's status based on device state of that member's state_interface.
2734 *
2735 * Lock interface list find sc, iterate through each queues queue_member list for member to
2736 * update state inside queues
2737*/
2738static void update_status(struct call_queue *q, struct member *m, const int status)
2739{
2740 if (m->status != status) {
2741 /* If this member has transitioned to being available then update their queue
2742 * information. If they are currently in a call then the leg to the agent will be
2743 * considered done and the call finished.
2744 */
2747 }
2748
2749 m->status = status;
2750
2751 /* Remove the member from the pending members pool only when the status changes.
2752 * This is not done unconditionally because we can occasionally see multiple
2753 * device state notifications of not in use after a previous call has ended,
2754 * including after we have initiated a new call. This is more likely to
2755 * happen when there is latency in the connection to the member.
2756 */
2758
2759 queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2760 }
2761}
2762
2763/*!
2764 * \internal
2765 * \brief Determine if a queue member is available
2766 * \retval 1 if the member is available
2767 * \retval 0 if the member is not available
2768 */
2769static int is_member_available(struct call_queue *q, struct member *mem)
2770{
2771 int available = 0;
2772 int wrapuptime;
2773
2774 switch (mem->status) {
2775 case AST_DEVICE_INVALID:
2777 break;
2778 case AST_DEVICE_INUSE:
2779 case AST_DEVICE_BUSY:
2780 case AST_DEVICE_RINGING:
2782 case AST_DEVICE_ONHOLD:
2783 if (!mem->ringinuse) {
2784 break;
2785 }
2786 /* else fall through */
2788 case AST_DEVICE_UNKNOWN:
2789 if (!mem->paused) {
2790 available = 1;
2791 }
2792 break;
2793 }
2794
2795 /* Let wrapuptimes override device state availability */
2796 wrapuptime = get_wrapuptime(q, mem);
2797 if (mem->lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->lastcall)) {
2798 available = 0;
2799 }
2800 return available;
2801}
2802
2803/*! \brief set a member's status based on device state of that member's interface*/
2804static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
2805{
2806 struct ao2_iterator miter, qiter;
2807 struct ast_device_state_message *dev_state;
2808 struct member *m;
2809 struct call_queue *q;
2810 char interface[80], *slash_pos;
2811 int found = 0; /* Found this member in any queue */
2812 int found_member; /* Found this member in this queue */
2813 int avail = 0; /* Found an available member in this queue */
2814
2816 return;
2817 }
2818
2819 dev_state = stasis_message_data(msg);
2820 if (dev_state->eid) {
2821 /* ignore non-aggregate states */
2822 return;
2823 }
2824
2825 qiter = ao2_iterator_init(queues, 0);
2826 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
2827 ao2_lock(q);
2828
2829 avail = 0;
2830 found_member = 0;
2831 miter = ao2_iterator_init(q->members, 0);
2832 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2833 if (!found_member) {
2834 ast_copy_string(interface, m->state_interface, sizeof(interface));
2835
2836 if ((slash_pos = strchr(interface, '/'))) {
2837 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) {
2838 *slash_pos = '\0';
2839 }
2840 }
2841
2842 if (!strcasecmp(interface, dev_state->device)) {
2843 found_member = 1;
2844 update_status(q, m, dev_state->state);
2845 }
2846 }
2847
2848 /* check every member until we find one NOT_INUSE */
2849 if (!avail) {
2850 avail = is_member_available(q, m);
2851 }
2852 if (avail && found_member) {
2853 /* early exit as we've found an available member and the member of interest */
2854 ao2_ref(m, -1);
2855 break;
2856 }
2857 }
2858
2859 if (found_member) {
2860 found = 1;
2861 if (avail) {
2863 } else {
2865 }
2866 }
2867
2868 ao2_iterator_destroy(&miter);
2869
2870 ao2_unlock(q);
2871 queue_t_unref(q, "Done with iterator");
2872 }
2873 ao2_iterator_destroy(&qiter);
2874
2875 if (found) {
2876 ast_debug(1, "Device '%s' changed to state '%u' (%s)\n",
2877 dev_state->device,
2878 dev_state->state,
2879 ast_devstate2str(dev_state->state));
2880 } else {
2881 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",
2882 dev_state->device,
2883 dev_state->state,
2884 ast_devstate2str(dev_state->state));
2885 }
2886
2887 return;
2888}
2889
2890/*! \brief Helper function which converts from extension state to device state values */
2892{
2893 switch (state) {
2896 break;
2899 break;
2900 case AST_EXTENSION_BUSY:
2902 break;
2905 break;
2908 break;
2911 break;
2914 break;
2917 break;
2920 default:
2922 break;
2923 }
2924
2925 return state;
2926}
2927
2928/*!
2929 * \brief Returns if one context includes another context
2930 *
2931 * \param parent Parent context to search for child
2932 * \param child Context to check for inclusion in parent
2933 *
2934 * This function recursively checks if the context child is included in the context parent.
2935 *
2936 * \retval 1 if child is included in parent
2937 * \retval 0 if not
2938 */
2939static int context_included(const char *parent, const char *child);
2940static int context_included(const char *parent, const char *child)
2941{
2942 struct ast_context *c = NULL;
2943
2944 c = ast_context_find(parent);
2945 if (!c) {
2946 /* well, if parent doesn't exist, how can the child be included in it? */
2947 return 0;
2948 }
2949 if (!strcmp(ast_get_context_name(c), parent)) {
2950 /* found the context of the hint app_queue is using. Now, see
2951 if that context includes the one that just changed state */
2952 struct ast_include *inc = NULL;
2953
2954 while ((inc = (struct ast_include*) ast_walk_context_includes(c, inc))) {
2955 const char *includename = ast_get_include_name(inc);
2956 if (!strcasecmp(child, includename)) {
2957 return 1;
2958 }
2959 /* recurse on this context, for nested includes. The
2960 PBX extension parser will prevent infinite recursion. */
2961 if (context_included(includename, child)) {
2962 return 1;
2963 }
2964 }
2965 }
2966 return 0;
2967}
2968
2969static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
2970{
2971 struct ao2_iterator miter, qiter;
2972 struct member *m;
2973 struct call_queue *q;
2974 int state = info->exten_state;
2975 int found = 0, device_state = extensionstate2devicestate(state);
2976
2977 /* only interested in extension state updates involving device states */
2978 if (info->reason != AST_HINT_UPDATE_DEVICE) {
2979 return 0;
2980 }
2981
2982 qiter = ao2_iterator_init(queues, 0);
2983 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
2984 ao2_lock(q);
2985
2986 miter = ao2_iterator_init(q->members, 0);
2987 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2988 if (!strcmp(m->state_exten, exten) &&
2990 /* context could be included in m->state_context. We need to check. */
2991 found = 1;
2992 update_status(q, m, device_state);
2993 }
2994 }
2995 ao2_iterator_destroy(&miter);
2996
2997 ao2_unlock(q);
2998 queue_t_unref(q, "Done with iterator");
2999 }
3000 ao2_iterator_destroy(&qiter);
3001
3002 if (found) {
3003 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
3004 } else {
3005 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",
3006 exten, context, device_state, ast_devstate2str(device_state));
3007 }
3008
3009 return 0;
3010}
3011
3012/*! \brief Return the current state of a member */
3017
3018static void destroy_queue_member_cb(void *obj)
3019{
3020 struct member *mem = obj;
3021
3022 if (mem->state_id != -1) {
3024 }
3025}
3026
3027/*! \brief allocate space for new queue member and set fields based on parameters passed */
3028static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
3029{
3030 struct member *cur;
3031
3032 if ((cur = ao2_alloc(sizeof(*cur), destroy_queue_member_cb))) {
3033 cur->ringinuse = ringinuse;
3034 cur->penalty = penalty;
3035 cur->paused = paused;
3036 cur->wrapuptime = wrapuptime;
3037 if (paused) {
3038 time(&cur->lastpause); /* Update time of last pause */
3039 }
3040 time(&cur->logintime);
3041 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
3044 } else {
3046 }
3048 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
3049 } else {
3050 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
3051 }
3052 if (!strchr(cur->interface, '/')) {
3053 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
3054 }
3055 if (!strncmp(cur->state_interface, "hint:", 5)) {
3056 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
3057 char *exten = strsep(&context, "@") + 5;
3058
3059 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
3060 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
3061
3063 } else {
3064 cur->state_id = -1;
3065 }
3066 cur->status = get_queue_member_status(cur);
3067 }
3068
3069 return cur;
3070}
3071
3072
3073static int compress_char(const char c)
3074{
3075 if (c < 32) {
3076 return 0;
3077 } else if (c > 96) {
3078 return c - 64;
3079 }
3080 return c - 32;
3081}
3082
3083static int member_hash_fn(const void *obj, const int flags)
3084{
3085 const struct member *mem = obj;
3086 const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
3087 const char *chname = strchr(interface, '/');
3088 int ret = 0, i;
3089
3090 if (!chname) {
3091 chname = interface;
3092 }
3093 for (i = 0; i < 5 && chname[i]; i++) {
3094 ret += compress_char(chname[i]) << (i * 6);
3095 }
3096 return ret;
3097}
3098
3099static int member_cmp_fn(void *obj1, void *obj2, int flags)
3100{
3101 struct member *mem1 = obj1;
3102 struct member *mem2 = obj2;
3103 const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;
3104
3105 return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
3106}
3107
3108/*!
3109 * \brief Initialize Queue default values.
3110 * \note the queue's lock must be held before executing this function
3111*/
3112static void init_queue(struct call_queue *q)
3113{
3114 int i;
3115 struct penalty_rule *pr_iter;
3116
3117 q->dead = 0;
3118 q->retry = DEFAULT_RETRY;
3120 q->maxlen = 0;
3121
3122 ast_string_field_set(q, announce, "");
3123 ast_string_field_set(q, context, "");
3124 ast_string_field_set(q, membergosub, "");
3125 ast_string_field_set(q, defaultrule, "");
3126
3127 q->announcefrequency = 0;
3129 q->announceholdtime = 1;
3131 q->announcepositionlimit = 10; /* Default 10 positions */
3132 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
3133 q->roundingseconds = 0; /* Default - don't announce seconds */
3134 q->servicelevel = 0;
3135 q->ringinuse = 1;
3137 q->setinterfacevar = 0;
3138 q->setqueuevar = 0;
3139 q->setqueueentryvar = 0;
3141 q->monfmt[0] = '\0';
3142 q->reportholdtime = 0;
3143 q->wrapuptime = 0;
3144 q->penaltymemberslimit = 0;
3145 q->joinempty = 0;
3146 q->leavewhenempty = 0;
3147 q->memberdelay = 0;
3148 q->weight = 0;
3149 q->timeoutrestart = 0;
3153 q->numperiodicannounce = 0;
3156 q->autopausebusy = 0;
3157 q->autopauseunavail = 0;
3159 q->autopausedelay = 0;
3161 if (!q->members) {
3163 /* linear strategy depends on order, so we have to place all members in a list */
3165 } else {
3168 }
3169 }
3170 q->found = 1;
3171
3172 ast_string_field_set(q, moh, "");
3173 ast_string_field_set(q, sound_next, "queue-youarenext");
3174 ast_string_field_set(q, sound_thereare, "queue-thereare");
3175 ast_string_field_set(q, sound_calls, "queue-callswaiting");
3176 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
3177 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
3178 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
3179 ast_string_field_set(q, sound_minutes, "queue-minutes");
3180 ast_string_field_set(q, sound_minute, "queue-minute");
3181 ast_string_field_set(q, sound_seconds, "queue-seconds");
3182 ast_string_field_set(q, sound_thanks, "queue-thankyou");
3183 ast_string_field_set(q, sound_callerannounce, "");
3184 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
3185
3186 if (!q->sound_periodicannounce[0]) {
3188 }
3189
3190 if (q->sound_periodicannounce[0]) {
3191 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
3192 }
3193
3194 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3195 if (q->sound_periodicannounce[i]) {
3196 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
3197 }
3198 }
3199
3200 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
3201 ast_free(pr_iter);
3202 }
3203
3204 /* On restart assume no members are available.
3205 * The queue_avail hint is a boolean state to indicate whether a member is available or not.
3206 *
3207 * This seems counter intuitive, but is required to light a BLF
3208 * AST_DEVICE_INUSE indicates no members are available.
3209 * AST_DEVICE_NOT_INUSE indicates a member is available.
3210 */
3212}
3213
3214static void clear_queue(struct call_queue *q)
3215{
3216 q->holdtime = 0;
3217 q->callscompleted = 0;
3218 q->callsabandoned = 0;
3219 q->callscompletedinsl = 0;
3220 q->callsabandonedinsl = 0;
3221 q->talktime = 0;
3222
3223 if (q->members) {
3224 struct member *mem;
3225 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3226 while ((mem = ao2_iterator_next(&mem_iter))) {
3227 mem->calls = 0;
3228 mem->callcompletedinsl = 0;
3229 mem->lastcall = 0;
3230 mem->starttime = 0;
3231 ao2_ref(mem, -1);
3232 }
3233 ao2_iterator_destroy(&mem_iter);
3234 }
3235}
3236
3237/*!
3238 * \brief Change queue penalty by adding rule.
3239 *
3240 * Check rule for errors with time or formatting, see if rule is relative to rest
3241 * of queue, iterate list of rules to find correct insertion point, insert and return.
3242 * \retval -1 on failure
3243 * \retval 0 on success
3244 * \note Call this with the rule_lists locked
3245*/
3246static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
3247{
3248 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3249 struct penalty_rule *rule = NULL, *rule_iter;
3250 struct rule_list *rl_iter;
3251 int penaltychangetime, inserted = 0;
3252
3253 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
3254 return -1;
3255 }
3256
3257 contentdup = ast_strdupa(content);
3258
3259 if (!(maxstr = strchr(contentdup, ','))) {
3260 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3261 ast_free(rule);
3262 return -1;
3263 }
3264
3265 *maxstr++ = '\0';
3266 if ((minstr = strchr(maxstr,','))) {
3267 *minstr++ = '\0';
3268 if ((raisestr = strchr(minstr,','))) {
3269 *raisestr++ = '\0';
3270 }
3271 } else {
3272 raisestr = NULL;
3273 }
3274
3275 timestr = contentdup;
3276 if ((penaltychangetime = atoi(timestr)) < 0) {
3277 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3278 ast_free(rule);
3279 return -1;
3280 }
3281
3282 rule->time = penaltychangetime;
3283
3284 /* The last check will evaluate true if either no penalty change is indicated for a given rule
3285 * OR if a min penalty change is indicated but no max penalty change is */
3286 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
3287 rule->max_relative = 1;
3288 }
3289
3290 rule->max_value = atoi(maxstr);
3291
3292 if (!ast_strlen_zero(minstr)) {
3293 if (*minstr == '+' || *minstr == '-') {
3294 rule->min_relative = 1;
3295 }
3296 rule->min_value = atoi(minstr);
3297 } else { /*there was no minimum specified, so assume this means no change*/
3298 rule->min_relative = 1;
3299 }
3300
3301 if (!ast_strlen_zero(raisestr)) {
3302 rule->raise_respect_min = 0; /* Initialize to 0 */
3303 if (*raisestr == 'r') {
3304 rule->raise_respect_min = 1; /* Set the flag */
3305 raisestr++;
3306 }
3307 if (*raisestr == '+' || *raisestr == '-') {
3308 rule->raise_relative = 1;
3309 }
3310 rule->raise_value = atoi(raisestr);
3311 } else { /*there was no raise specified, so assume this means no change*/
3312 rule->raise_relative = 1;
3313 }
3314
3315 /*We have the rule made, now we need to insert it where it belongs*/
3316 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
3317 if (strcasecmp(rl_iter->name, list_name)) {
3318 continue;
3319 }
3320
3321 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
3322 if (rule->time < rule_iter->time) {
3324 inserted = 1;
3325 break;
3326 }
3327 }
3329
3330 if (!inserted) {
3331 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
3332 inserted = 1;
3333 }
3334
3335 break;
3336 }
3337
3338 if (!inserted) {
3339 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
3340 ast_free(rule);
3341 return -1;
3342 }
3343 return 0;
3344}
3345
3346/*!
3347 * \brief Load queue rules from realtime.
3348 *
3349 * Check rule for errors with time or formatting, see if rule is relative to rest
3350 * of queue, iterate list of rules to find correct insertion point, insert and return.
3351 * \retval -1 on failure
3352 * \retval 0 on success
3353 * \note Call this with the rule_lists locked
3354*/
3355static int load_realtime_rules(void)
3356{
3357 struct ast_config *cfg;
3358 struct rule_list *rl_iter, *new_rl;
3359 struct penalty_rule *pr_iter;
3360 char *rulecat = NULL;
3361
3362 if (!ast_check_realtime("queue_rules")) {
3363 ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
3364 return 0;
3365 }
3366 if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
3367 ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
3368 return 0;
3369 }
3370 while ((rulecat = ast_category_browse(cfg, rulecat))) {
3371 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3372 int penaltychangetime, rule_exists = 0, inserted = 0;
3373 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3374 int min_relative = 0, max_relative = 0, raise_relative = 0;
3375 struct penalty_rule *new_penalty_rule = NULL;
3376
3377 rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
3378 if (ast_strlen_zero(rule_name)) {
3379 continue;
3380 }
3381
3382 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
3383 if (!(strcasecmp(rl_iter->name, rule_name))) {
3384 rule_exists = 1;
3385 new_rl = rl_iter;
3386 break;
3387 }
3388 }
3389 if (!rule_exists) {
3390 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
3391 ast_config_destroy(cfg);
3392 return -1;
3393 }
3394 ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
3396 }
3397 timestr = ast_variable_retrieve(cfg, rulecat, "time");
3398 if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
3399 ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
3400 (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
3401 continue;
3402 }
3403 if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
3404 ast_config_destroy(cfg);
3405 return -1;
3406 }
3407 if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
3408 ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
3409 max_penalty = 0;
3410 max_relative = 1;
3411 } else {
3412 if (*maxstr == '+' || *maxstr == '-') {
3413 max_relative = 1;
3414 }
3415 }
3416 if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
3417 ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
3418 min_penalty = 0;
3419 min_relative = 1;
3420 } else {
3421 if (*minstr == '+' || *minstr == '-') {
3422 min_relative = 1;
3423 }
3424 }
3425 if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
3426 ast_strlen_zero(raisestr) ) {
3427 raise_penalty = 0;
3428 raise_relative = 1;
3429 } else {
3430 if (*raisestr == 'r') {
3431 new_penalty_rule->raise_respect_min = 1;
3432 raisestr++;
3433 } else {
3434 new_penalty_rule->raise_respect_min = 0;
3435 }
3436 if (*raisestr == '+' || *raisestr == '-') {
3437 raise_relative = 1;
3438 }
3439 if (sscanf(raisestr, "%30d", &raise_penalty) != 1) {
3440 raise_penalty = 0;
3441 raise_relative = 1;
3442 }
3443 }
3444 new_penalty_rule->time = penaltychangetime;
3445 new_penalty_rule->max_relative = max_relative;
3446 new_penalty_rule->max_value = max_penalty;
3447 new_penalty_rule->min_relative = min_relative;
3448 new_penalty_rule->min_value = min_penalty;
3449 new_penalty_rule->raise_relative = raise_relative;
3450 new_penalty_rule->raise_value = raise_penalty;
3451 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
3452 if (new_penalty_rule->time < pr_iter->time) {
3453 AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
3454 inserted = 1;
3455 }
3456 }
3458 if (!inserted) {
3459 AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
3460 }
3461 }
3462
3463 ast_config_destroy(cfg);
3464 return 0;
3465}
3466
3467static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
3468{
3469 char *value_copy = ast_strdupa(value);
3470 char *option = NULL;
3471 while ((option = strsep(&value_copy, ","))) {
3472 if (!strcasecmp(option, "paused")) {
3473 *empty |= QUEUE_EMPTY_PAUSED;
3474 } else if (!strcasecmp(option, "penalty")) {
3475 *empty |= QUEUE_EMPTY_PENALTY;
3476 } else if (!strcasecmp(option, "inuse")) {
3477 *empty |= QUEUE_EMPTY_INUSE;
3478 } else if (!strcasecmp(option, "ringing")) {
3479 *empty |= QUEUE_EMPTY_RINGING;
3480 } else if (!strcasecmp(option, "invalid")) {
3481 *empty |= QUEUE_EMPTY_INVALID;
3482 } else if (!strcasecmp(option, "wrapup")) {
3483 *empty |= QUEUE_EMPTY_WRAPUP;
3484 } else if (!strcasecmp(option, "unavailable")) {
3485 *empty |= QUEUE_EMPTY_UNAVAILABLE;
3486 } else if (!strcasecmp(option, "unknown")) {
3487 *empty |= QUEUE_EMPTY_UNKNOWN;
3488 } else if (!strcasecmp(option, "loose")) {
3490 } else if (!strcasecmp(option, "strict")) {
3492 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
3494 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
3495 *empty = 0;
3496 } else {
3497 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
3498 }
3499 }
3500}
3501
3502/*! \brief Configure a queue parameter.
3503 *
3504 * The failunknown flag is set for config files (and static realtime) to show
3505 * errors for unknown parameters. It is cleared for dynamic realtime to allow
3506 * extra fields in the tables.
3507 * \note For error reporting, line number is passed for .conf static configuration,
3508 * for Realtime queues, linenum is -1.
3509*/
3510static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
3511{
3512 if (!strcasecmp(param, "musicclass") ||
3513 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
3514 ast_string_field_set(q, moh, val);
3515 } else if (!strcasecmp(param, "announce")) {
3516 ast_string_field_set(q, announce, val);
3517 } else if (!strcasecmp(param, "context")) {
3518 ast_string_field_set(q, context, val);
3519 } else if (!strcasecmp(param, "timeout")) {
3520 q->timeout = atoi(val);
3521 if (q->timeout < 0) {
3523 }
3524 } else if (!strcasecmp(param, "ringinuse")) {
3525 q->ringinuse = ast_true(val);
3526 } else if (!strcasecmp(param, "setinterfacevar")) {
3528 } else if (!strcasecmp(param, "setqueuevar")) {
3529 q->setqueuevar = ast_true(val);
3530 } else if (!strcasecmp(param, "setqueueentryvar")) {
3532 } else if (!strcasecmp(param, "monitor-format")) {
3533 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
3534 } else if (!strcasecmp(param, "membergosub")) {
3535 ast_string_field_set(q, membergosub, val);
3536 } else if (!strcasecmp(param, "queue-youarenext")) {
3537 ast_string_field_set(q, sound_next, val);
3538 } else if (!strcasecmp(param, "queue-thereare")) {
3539 ast_string_field_set(q, sound_thereare, val);
3540 } else if (!strcasecmp(param, "queue-callswaiting")) {
3541 ast_string_field_set(q, sound_calls, val);
3542 } else if (!strcasecmp(param, "queue-quantity1")) {
3543 ast_string_field_set(q, queue_quantity1, val);
3544 } else if (!strcasecmp(param, "queue-quantity2")) {
3545 ast_string_field_set(q, queue_quantity2, val);
3546 } else if (!strcasecmp(param, "queue-holdtime")) {
3547 ast_string_field_set(q, sound_holdtime, val);
3548 } else if (!strcasecmp(param, "queue-minutes")) {
3549 ast_string_field_set(q, sound_minutes, val);
3550 } else if (!strcasecmp(param, "queue-minute")) {
3551 ast_string_field_set(q, sound_minute, val);
3552 } else if (!strcasecmp(param, "queue-seconds")) {
3553 ast_string_field_set(q, sound_seconds, val);
3554 } else if (!strcasecmp(param, "queue-thankyou")) {
3555 ast_string_field_set(q, sound_thanks, val);
3556 } else if (!strcasecmp(param, "queue-callerannounce")) {
3557 ast_string_field_set(q, sound_callerannounce, val);
3558 } else if (!strcasecmp(param, "queue-reporthold")) {
3559 ast_string_field_set(q, sound_reporthold, val);
3560 } else if (!strcasecmp(param, "announce-frequency")) {
3561 q->announcefrequency = atoi(val);
3562 } else if (!strcasecmp(param, "announce-to-first-user")) {
3564 } else if (!strcasecmp(param, "min-announce-frequency")) {
3565 q->minannouncefrequency = atoi(val);
3566 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
3567 } else if (!strcasecmp(param, "announce-round-seconds")) {
3568 q->roundingseconds = atoi(val);
3569 /* Rounding to any other values just doesn't make sense... */
3570 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
3571 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
3572 if (linenum >= 0) {
3573 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3574 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3575 val, param, q->name, linenum);
3576 } else {
3577 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3578 "using 0 instead for queue '%s'\n", val, param, q->name);
3579 }
3580 q->roundingseconds=0;
3581 }
3582 } else if (!strcasecmp(param, "announce-holdtime")) {
3583 if (!strcasecmp(val, "once")) {
3585 } else if (ast_true(val)) {
3587 } else {
3588 q->announceholdtime = 0;
3589 }
3590 } else if (!strcasecmp(param, "announce-position")) {
3591 if (!strcasecmp(val, "limit")) {
3593 } else if (!strcasecmp(val, "more")) {
3595 } else if (ast_true(val)) {
3597 } else {
3599 }
3600 } else if (!strcasecmp(param, "announce-position-only-up")) {
3602 } else if (!strcasecmp(param, "announce-position-limit")) {
3603 q->announcepositionlimit = atoi(val);
3604 } else if (!strcasecmp(param, "periodic-announce")) {
3605 if (strchr(val, ',')) {
3606 char *s, *buf = ast_strdupa(val);
3607 unsigned int i = 0;
3608
3609 while ((s = strsep(&buf, ",|"))) {
3610 if (!q->sound_periodicannounce[i]) {
3612 }
3613 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
3614 i++;
3615 if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
3616 break;
3617 }
3618 }
3619 q->numperiodicannounce = i;
3620 } else {
3621 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
3622 q->numperiodicannounce = 1;
3623 }
3624 } else if (!strcasecmp(param, "periodic-announce-startdelay")) {
3626 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
3627 q->periodicannouncefrequency = atoi(val);
3628 } else if (!strcasecmp(param, "relative-periodic-announce")) {
3630 } else if (!strcasecmp(param, "random-periodic-announce")) {
3632 } else if (!strcasecmp(param, "retry")) {
3633 q->retry = atoi(val);
3634 if (q->retry <= 0) {
3635 q->retry = DEFAULT_RETRY;
3636 }
3637 } else if (!strcasecmp(param, "wrapuptime")) {
3638 q->wrapuptime = atoi(val);
3639 } else if (!strcasecmp(param, "penaltymemberslimit")) {
3640 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
3641 q->penaltymemberslimit = 0;
3642 }
3643 } else if (!strcasecmp(param, "autofill")) {
3644 q->autofill = ast_true(val);
3645 } else if (!strcasecmp(param, "autopause")) {
3647 } else if (!strcasecmp(param, "autopausedelay")) {
3648 q->autopausedelay = atoi(val);
3649 } else if (!strcasecmp(param, "autopausebusy")) {
3651 } else if (!strcasecmp(param, "autopauseunavail")) {
3653 } else if (!strcasecmp(param, "maxlen")) {
3654 q->maxlen = atoi(val);
3655 if (q->maxlen < 0) {
3656 q->maxlen = 0;
3657 }
3658 } else if (!strcasecmp(param, "servicelevel")) {
3659 q->servicelevel= atoi(val);
3660 } else if (!strcasecmp(param, "strategy")) {
3661 int strategy;
3662
3663 /* We are a static queue and already have set this, no need to do it again */
3664 if (failunknown) {
3665 return;
3666 }
3668 if (strategy < 0) {
3669 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3670 val, q->name);
3672 }
3673 if (strategy == q->strategy) {
3674 return;
3675 }
3677 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
3678 return;
3679 }
3680 q->strategy = strategy;
3681 } else if (!strcasecmp(param, "joinempty")) {
3683 } else if (!strcasecmp(param, "leavewhenempty")) {
3685 } else if (!strcasecmp(param, "reportholdtime")) {
3687 } else if (!strcasecmp(param, "memberdelay")) {
3688 q->memberdelay = atoi(val);
3689 } else if (!strcasecmp(param, "weight")) {
3690 q->weight = atoi(val);
3691 } else if (!strcasecmp(param, "timeoutrestart")) {
3693 } else if (!strcasecmp(param, "defaultrule")) {
3694 ast_string_field_set(q, defaultrule, val);
3695 } else if (!strcasecmp(param, "timeoutpriority")) {
3696 if (!strcasecmp(val, "conf")) {
3698 } else {
3700 }
3701 } else if (!strcasecmp(param, "log-restricted-caller-id")) {
3703 } else if (failunknown) {
3704 if (linenum >= 0) {
3705 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3706 q->name, param, linenum);
3707 } else {
3708 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
3709 }
3710 }
3711}
3712
3713
3714#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3715#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3716#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3717
3718/*! \internal
3719 * \brief If adding a single new member to a queue, use this function instead of ao2_linking.
3720 * This adds round robin queue position data for a fresh member as well as links it.
3721 * \param queue Which queue the member is being added to
3722 * \param mem Which member is being added to the queue
3723 */
3724static void member_add_to_queue(struct call_queue *queue, struct member *mem)
3725{
3726 ao2_lock(queue->members);
3727 mem->queuepos = ao2_container_count(queue->members);
3728 ao2_link(queue->members, mem);
3730 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", queue->name, mem->interface);
3731 ao2_unlock(queue->members);
3732}
3733
3734/*! \internal
3735 * \brief If removing a single member from a queue, use this function instead of ao2_unlinking.
3736 * This will perform round robin queue position reordering for the remaining members.
3737 * \param queue Which queue the member is being removed from
3738 * \param mem Which member is being removed from the queue
3739 */
3740static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
3741{
3743 ao2_lock(queue->members);
3746 ao2_unlink(queue->members, mem);
3747 ao2_unlock(queue->members);
3748}
3749
3750/*!
3751 * \brief Find rt member record to update otherwise create one.
3752 *
3753 * Search for member in queue, if found update penalty/paused state,
3754 * if no member exists create one flag it as a RT member and add to queue member list.
3755*/
3756static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
3757{
3758 struct member *m;
3759 struct ao2_iterator mem_iter;
3760 int penalty = 0;
3761 int paused = 0;
3762 int found = 0;
3763 int wrapuptime = 0;
3764 int ringinuse = q->ringinuse;
3765
3766 const char *config_val;
3767 const char *interface = ast_variable_retrieve(member_config, category, "interface");
3768 const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
3769 const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
3770 const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
3771 const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
3772 const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
3773 const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
3774 const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
3775
3776 if (ast_strlen_zero(rt_uniqueid)) {
3777 ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
3778 S_OR(membername, "NULL"));
3779 return;
3780 }
3781
3782 if (ast_strlen_zero(interface)) {
3783 ast_log(LOG_WARNING, "Realtime field 'interface' is empty for member %s\n",
3784 S_OR(membername, "NULL"));
3785 return;
3786 }
3787
3788 if (penalty_str) {
3789 penalty = atoi(penalty_str);
3790 if ((penalty < 0) && negative_penalty_invalid) {
3791 return;
3792 } else if (penalty < 0) {
3793 penalty = 0;
3794 }
3795 }
3796
3797 if (paused_str) {
3798 paused = atoi(paused_str);
3799 if (paused < 0) {
3800 paused = 0;
3801 }
3802 }
3803
3804 if (wrapuptime_str) {
3805 wrapuptime = atoi(wrapuptime_str);
3806 if (wrapuptime < 0) {
3807 wrapuptime = 0;
3808 }
3809 }
3810
3811 if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3812 if (ast_true(config_val)) {
3813 ringinuse = 1;
3814 } else if (ast_false(config_val)) {
3815 ringinuse = 0;
3816 } else {
3817 ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
3818 }
3819 }
3820
3821 /* Find member by realtime uniqueid and update */
3822 mem_iter = ao2_iterator_init(q->members, 0);
3823 while ((m = ao2_iterator_next(&mem_iter))) {
3824 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
3825 m->dead = 0; /* Do not delete this one. */
3826 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3827 if (paused_str) {
3828 m->paused = paused;
3829 if (paused && m->lastpause == 0) {
3830 time(&m->lastpause); /* XXX: Should this come from realtime? */
3831 }
3833 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, m->interface);
3834 }
3835 if (strcasecmp(state_interface, m->state_interface)) {
3836 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
3837 }
3838 m->penalty = penalty;
3839 m->ringinuse = ringinuse;
3840 m->wrapuptime = wrapuptime;
3842 ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
3843 }
3844 found = 1;
3845 ao2_ref(m, -1);
3846 break;
3847 }
3848 ao2_ref(m, -1);
3849 }
3850 ao2_iterator_destroy(&mem_iter);
3851
3852 /* Create a new member */
3853 if (!found) {
3854 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3855 m->dead = 0;
3856 m->realtime = 1;
3857 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3858 if (!ast_strlen_zero(reason_paused)) {
3859 ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
3860 }
3862 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3863 } else {
3864 ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3865 }
3866 member_add_to_queue(q, m);
3867 ao2_ref(m, -1);
3868 m = NULL;
3869 }
3870 }
3871}
3872
3873/*! \brief Iterate through queue's member list and delete them */
3874static void free_members(struct call_queue *q, int all)
3875{
3876 /* Free non-dynamic members */
3877 struct member *cur;
3878 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3879
3880 while ((cur = ao2_iterator_next(&mem_iter))) {
3881 if (all || !cur->dynamic) {
3883 }
3884 ao2_ref(cur, -1);
3885 }
3886 ao2_iterator_destroy(&mem_iter);
3887}
3888
3889/*! \brief Free queue's member list then its string fields */
3890static void destroy_queue(void *obj)
3891{
3892 struct call_queue *q = obj;
3893 int i;
3894
3895 free_members(q, 1);
3897 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3898 if (q->sound_periodicannounce[i]) {
3900 }
3901 }
3902 ao2_ref(q->members, -1);
3903}
3904
3905static struct call_queue *alloc_queue(const char *queuename)
3906{
3907 struct call_queue *q;
3908
3909 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
3910 if (ast_string_field_init(q, 64)) {
3911 queue_t_unref(q, "String field allocation failed");
3912 return NULL;
3913 }
3914 ast_string_field_set(q, name, queuename);
3915 }
3916 return q;
3917}
3918
3919/*!
3920 * \brief Reload a single queue via realtime.
3921 *
3922 * Check for statically defined queue first, check if deleted RT queue,
3923 * check for new RT queue, if queue vars are not defined init them with defaults.
3924 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
3925 * \retval the queue,
3926 * \retval NULL if it doesn't exist.
3927 * \note Should be called with the "queues" container locked.
3928*/
3929static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
3930{
3931 struct ast_variable *v;
3932 struct call_queue *q, tmpq = {
3933 .name = queuename,
3934 };
3935 struct member *m;
3936 struct ao2_iterator mem_iter;
3937 char *category = NULL;
3938 const char *tmp_name;
3939 char *tmp;
3940 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
3941
3942 /* Static queues override realtime. */
3943 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
3944 ao2_lock(q);
3945 if (!q->realtime) {
3946 if (q->dead) {
3947 ao2_unlock(q);
3948 queue_t_unref(q, "Queue is dead; can't return it");
3949 return NULL;
3950 }
3951 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
3952 ao2_unlock(q);
3953 return q;
3954 }
3955 } else if (!member_config) {
3956 /* Not found in the list, and it's not realtime ... */
3957 return NULL;
3958 }
3959 /* Check if queue is defined in realtime. */
3960 if (!queue_vars) {
3961 /* Delete queue from in-core list if it has been deleted in realtime. */
3962 if (q) {
3963 /*! \note Hmm, can't seem to distinguish a DB failure from a not
3964 found condition... So we might delete an in-core queue
3965 in case of DB failure. */
3966 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
3967
3968 q->dead = 1;
3969 /* Delete if unused (else will be deleted when last caller leaves). */
3970 queues_t_unlink(queues, q, "Unused; removing from container");
3971 ao2_unlock(q);
3972 queue_t_unref(q, "Queue is dead; can't return it");
3973 }
3974 return NULL;
3975 }
3976
3977 /* Create a new queue if an in-core entry does not exist yet. */
3978 if (!q) {
3979 struct ast_variable *tmpvar = NULL;
3980 if (!(q = alloc_queue(queuename))) {
3981 return NULL;
3982 }
3983 ao2_lock(q);
3984 clear_queue(q);
3985 q->realtime = 1;
3986 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
3987 * will allocate the members properly
3988 */
3989 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
3990 if (!strcasecmp(tmpvar->name, "strategy")) {
3991 q->strategy = strat2int(tmpvar->value);
3992 if (q->strategy < 0) {
3993 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3994 tmpvar->value, q->name);
3996 }
3997 break;
3998 }
3999 }
4000 /* We traversed all variables and didn't find a strategy */
4001 if (!tmpvar) {
4003 }
4004 queues_t_link(queues, q, "Add queue to container");
4005 }
4006 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
4007
4008 memset(tmpbuf, 0, sizeof(tmpbuf));
4009 for (v = queue_vars; v; v = v->next) {
4010 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
4011 if (strchr(v->name, '_')) {
4012 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
4013 tmp_name = tmpbuf;
4014 tmp = tmpbuf;
4015 while ((tmp = strchr(tmp, '_'))) {
4016 *tmp++ = '-';
4017 }
4018 } else {
4019 tmp_name = v->name;
4020 }
4021
4022 /* NULL values don't get returned from realtime; blank values should
4023 * still get set. If someone doesn't want a value to be set, they
4024 * should set the realtime column to NULL, not blank. */
4025 queue_set_param(q, tmp_name, v->value, -1, 0);
4026 }
4027
4028 /* Temporarily set realtime members dead so we can detect deleted ones. */
4029 mem_iter = ao2_iterator_init(q->members, 0);
4030 while ((m = ao2_iterator_next(&mem_iter))) {
4031 if (m->realtime) {
4032 m->dead = 1;
4033 }
4034 ao2_ref(m, -1);
4035 }
4036 ao2_iterator_destroy(&mem_iter);
4037
4038 while ((category = ast_category_browse(member_config, category))) {
4039 rt_handle_member_record(q, category, member_config);
4040 }
4041
4042 /* Delete all realtime members that have been deleted in DB. */
4043 mem_iter = ao2_iterator_init(q->members, 0);
4044 while ((m = ao2_iterator_next(&mem_iter))) {
4045 if (m->dead) {
4047 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4048 } else {
4049 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4050 }
4052 }
4053 ao2_ref(m, -1);
4054 }
4055 ao2_iterator_destroy(&mem_iter);
4056
4057 ao2_unlock(q);
4058
4059 return q;
4060}
4061
4062/*!
4063 * note */
4064
4065/*!
4066 * \internal
4067 * \brief Returns reference to the named queue. If the queue is realtime, it will load the queue as well.
4068 * \param queuename - name of the desired queue
4069 *
4070 * \retval the queue
4071 * \retval NULL if it doesn't exist
4072 */
4073static struct call_queue *find_load_queue_rt_friendly(const char *queuename)
4074{
4075 struct ast_variable *queue_vars;
4076 struct ast_config *member_config = NULL;
4077 struct call_queue *q = NULL, tmpq = {
4078 .name = queuename,
4079 };
4080 int prev_weight = 0;
4081
4082 /* Find the queue in the in-core list first. */
4083 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
4084
4085 if (!q || q->realtime) {
4086 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
4087 queue operations while waiting for the DB.
4088
4089 This will be two separate database transactions, so we might
4090 see queue parameters as they were before another process
4091 changed the queue and member list as it was after the change.
4092 Thus we might see an empty member list when a queue is
4093 deleted. In practise, this is unlikely to cause a problem. */
4094
4095 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
4096 if (queue_vars) {
4097 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
4098 if (!member_config) {
4099 ast_debug(1, "No queue_members defined in config extconfig.conf\n");
4100 member_config = ast_config_new();
4101 }
4102 }
4103 if (q) {
4104 prev_weight = q->weight ? 1 : 0;
4105 queue_t_unref(q, "Need to find realtime queue");
4106 }
4107
4108 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
4109 ast_config_destroy(member_config);
4110 ast_variables_destroy(queue_vars);
4111
4112 /* update the use_weight value if the queue's has gained or lost a weight */
4113 if (q) {
4114 if (!q->weight && prev_weight) {
4116 }
4117 if (q->weight && !prev_weight) {
4119 }
4120 }
4121 /* Other cases will end up with the proper value for use_weight */
4122 } else {
4124 }
4125 return q;
4126}
4127
4128/*!
4129 * \internal
4130 * \brief Load queues and members from realtime.
4131 *
4132 * \param queuename - name of the desired queue to load or empty if need to load all queues
4133*/
4134static void load_realtime_queues(const char *queuename)
4135{
4136 struct ast_config *cfg = NULL;
4137 char *category = NULL;
4138 const char *name = NULL;
4139 struct call_queue *q = NULL;
4140
4141 if (!ast_check_realtime("queues")) {
4142 return;
4143 }
4144
4145 if (ast_strlen_zero(queuename)) {
4146 if ((cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
4147 while ((category = ast_category_browse(cfg, category))) {
4148 name = ast_variable_retrieve(cfg, category, "name");
4150 queue_unref(q);
4151 }
4152 }
4153 ast_config_destroy(cfg);
4154 }
4155 } else {
4156 if ((q = find_load_queue_rt_friendly(queuename))) {
4157 queue_unref(q);
4158 }
4159 }
4160}
4161
4162static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
4163{
4164 int ret = -1;
4165
4166 if (ast_strlen_zero(mem->rt_uniqueid)) {
4167 return ret;
4168 }
4169
4170 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) >= 0) {
4171 ret = 0;
4172 }
4173
4174 return ret;
4175}
4176
4177
4179{
4180 struct ast_config *member_config = NULL;
4181 struct member *m;
4182 char *category = NULL;
4183 struct ao2_iterator mem_iter;
4184
4185 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
4186 /* This queue doesn't have realtime members. If the queue still has any realtime
4187 * members in memory, they need to be removed.
4188 */
4189 ao2_lock(q);
4190 mem_iter = ao2_iterator_init(q->members, 0);
4191 while ((m = ao2_iterator_next(&mem_iter))) {
4192 if (m->realtime) {
4194 }
4195 ao2_ref(m, -1);
4196 }
4197 ao2_iterator_destroy(&mem_iter);
4198 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
4199 ao2_unlock(q);
4200 return;
4201 }
4202
4203 ao2_lock(q);
4204
4205 /* Temporarily set realtime members dead so we can detect deleted ones.*/
4206 mem_iter = ao2_iterator_init(q->members, 0);
4207 while ((m = ao2_iterator_next(&mem_iter))) {
4208 if (m->realtime) {
4209 m->dead = 1;
4210 }
4211 ao2_ref(m, -1);
4212 }
4213 ao2_iterator_destroy(&mem_iter);
4214
4215 while ((category = ast_category_browse(member_config, category))) {
4216 rt_handle_member_record(q, category, member_config);
4217 }
4218
4219 /* Delete all realtime members that have been deleted in DB. */
4220 mem_iter = ao2_iterator_init(q->members, 0);
4221 while ((m = ao2_iterator_next(&mem_iter))) {
4222 if (m->dead) {
4224 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4225 } else {
4226 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4227 }
4229 }
4230 ao2_ref(m, -1);
4231 }
4232 ao2_iterator_destroy(&mem_iter);
4233 ao2_unlock(q);
4234 ast_config_destroy(member_config);
4235}
4236
4237static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
4238{
4239 struct call_queue *q;
4240 struct queue_ent *cur, *prev = NULL;
4241 int res = -1;
4242 int pos = 0;
4243 int inserted = 0;
4244
4246 return res;
4247 }
4248 ao2_lock(q);
4249
4250 /* This is our one */
4251 if (q->joinempty) {
4252 int status = 0;
4253 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, qe->raise_penalty, q->joinempty, 0, qe->raise_respect_min))) {
4254 *reason = QUEUE_JOINEMPTY;
4255 ao2_unlock(q);
4256 queue_t_unref(q, "Done with realtime queue");
4257 return res;
4258 }
4259 }
4260 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) {
4261 *reason = QUEUE_FULL;
4262 } else if (*reason == QUEUE_UNKNOWN) {
4263 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4264
4265 /* There's space for us, put us at the right position inside
4266 * the queue.
4267 * Take into account the priority of the calling user */
4268 inserted = 0;
4269 prev = NULL;
4270 cur = q->head;
4271 while (cur) {
4272 /* We have higher priority than the current user, enter
4273 * before him, after all the other users with priority
4274 * higher or equal to our priority. */
4275 if ((!inserted) && (qe->prio > cur->prio)) {
4276 insert_entry(q, prev, qe, &pos);
4277 inserted = 1;
4278 }
4279 /* <= is necessary for the position comparison because it may not be possible to enter
4280 * at our desired position since higher-priority callers may have taken the position we want
4281 */
4282 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
4283 insert_entry(q, prev, qe, &pos);
4284 inserted = 1;
4285 /*pos is incremented inside insert_entry, so don't need to add 1 here*/
4286 if (position < pos) {
4287 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
4288 }
4289 }
4290 cur->pos = ++pos;
4291 prev = cur;
4292 cur = cur->next;
4293 }
4294 /* No luck, join at the end of the queue */
4295 if (!inserted) {
4296 insert_entry(q, prev, qe, &pos);
4297 }
4298 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
4299 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
4300 ast_copy_string(qe->context, q->context, sizeof(qe->context));
4301 q->count++;
4302 if (q->count == 1) {
4304 }
4305
4306 res = 0;
4307
4308 blob = ast_json_pack("{s: s, s: i, s: i}",
4309 "Queue", q->name,
4310 "Position", qe->pos,
4311 "Count", q->count);
4313 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, ast_channel_name(qe->chan), qe->pos );
4314 }
4315 ao2_unlock(q);
4316 queue_t_unref(q, "Done with realtime queue");
4317
4318 return res;
4319}
4320
4321static int play_file(struct ast_channel *chan, const char *filename)
4322{
4323 int res;
4324
4325 if (ast_strlen_zero(filename)) {
4326 return 0;
4327 }
4328
4329 if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
4330 return 0;
4331 }
4332
4334
4336 if (!res) {
4338 }
4339
4341
4342 return res;
4343}
4344
4345/*!
4346 * \brief Check for valid exit from queue via goto
4347 * \retval 0 if failure
4348 * \retval 1 if successful
4349*/
4350static int valid_exit(struct queue_ent *qe, char digit)
4351{
4352 int digitlen = strlen(qe->digits);
4353
4354 /* Prevent possible buffer overflow */
4355 if (digitlen < sizeof(qe->digits) - 2) {
4356 qe->digits[digitlen] = digit;
4357 qe->digits[digitlen + 1] = '\0';
4358 } else {
4359 qe->digits[0] = '\0';
4360 return 0;
4361 }
4362
4363 /* If there's no context to goto, short-circuit */
4364 if (ast_strlen_zero(qe->context)) {
4365 return 0;
4366 }
4367
4368 /* If the extension is bad, then reset the digits to blank */
4369 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
4371 qe->digits[0] = '\0';
4372 return 0;
4373 }
4374
4375 /* We have an exact match */
4376 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
4377 qe->valid_digits = 1;
4378 /* Return 1 on a successful goto */
4379 return 1;
4380 }
4381
4382 return 0;
4383}
4384
4385static int say_position(struct queue_ent *qe, int ringing)
4386{
4387 int res = 0, say_thanks = 0;
4389 time_t now;
4390
4391 /* Let minannouncefrequency seconds pass between the start of each position announcement */
4392 time(&now);
4393 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) {
4394 return 0;
4395 }
4396
4397 /* If either our position has changed, or we are over the freq timer, say position */
4398 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) {
4399 return 0;
4400 }
4401
4402 /* Only announce if the caller's queue position has improved since last time */
4403 if (qe->parent->announceposition_only_up && qe->last_pos_said > 0 && qe->last_pos_said <= qe->pos) {
4404 return 0;
4405 }
4406
4407 if (ringing) {
4408 ast_indicate(qe->chan,-1);
4409 } else {
4410 ast_moh_stop(qe->chan);
4411 }
4412
4413 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
4414 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
4415 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
4416 qe->pos <= qe->parent->announcepositionlimit)) {
4417 say_thanks = 1;
4418 /* Say we're next, if we are */
4419 if (qe->pos == 1) {
4420 res = play_file(qe->chan, qe->parent->sound_next);
4421 if (!res) {
4422 goto posout;
4423 }
4424 /* Say there are more than N callers */
4425 } else if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit) {
4426 res = (
4427 play_file(qe->chan, qe->parent->queue_quantity1) ||
4428 ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY,
4429 ast_channel_language(qe->chan), NULL) || /* Needs gender */
4430 play_file(qe->chan, qe->parent->queue_quantity2));
4431 /* Say there are currently N callers waiting */
4432 } else {
4433 res = (
4434 play_file(qe->chan, qe->parent->sound_thereare) ||
4435 ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY,
4436 ast_channel_language(qe->chan), "n") || /* Needs gender */
4437 play_file(qe->chan, qe->parent->sound_calls));
4438 }
4439 if (res) {
4440 goto playout;
4441 }
4442 }
4443 /* Round hold time to nearest minute */
4444 avgholdmins = labs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
4445
4446 /* If they have specified a rounding then round the seconds as well */
4447 if (qe->parent->roundingseconds) {
4448 avgholdsecs = (labs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
4449 avgholdsecs *= qe->parent->roundingseconds;
4450 } else {
4451 avgholdsecs = 0;
4452 }
4453
4454 ast_verb(3, "Hold time for %s is %ld minute(s) %ld seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
4455
4456 /* If the hold time is >1 min, if it's enabled, and if it's not
4457 supposed to be only once and we have already said it, say it */
4458 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
4459 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
4460 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
4461 say_thanks = 1;
4462 res = play_file(qe->chan, qe->parent->sound_holdtime);
4463 if (res) {
4464 goto playout;
4465 }
4466
4467 if (avgholdmins >= 1) {
4469 if (res) {
4470 goto playout;
4471 }
4472
4473 if (avgholdmins == 1) {
4474 res = play_file(qe->chan, qe->parent->sound_minute);
4475 if (res) {
4476 goto playout;
4477 }
4478 } else {
4479 res = play_file(qe->chan, qe->parent->sound_minutes);
4480 if (res) {
4481 goto playout;
4482 }
4483 }
4484 }
4485 if (avgholdsecs >= 1) {
4487 if (res) {
4488 goto playout;
4489 }
4490
4491 res = play_file(qe->chan, qe->parent->sound_seconds);
4492 if (res) {
4493 goto playout;
4494 }
4495 }
4496 }
4497
4498posout:
4499 if (qe->parent->announceposition) {
4500 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
4501 ast_channel_name(qe->chan), qe->parent->name, qe->pos);
4502 }
4503 if (say_thanks) {
4504 res = play_file(qe->chan, qe->parent->sound_thanks);
4505 }
4506playout:
4507
4508 if ((res > 0 && !valid_exit(qe, res))) {
4509 res = 0;
4510 }
4511
4512 /* Set our last_pos indicators */
4513 qe->last_pos = now;
4514 qe->last_pos_said = qe->pos;
4515
4516 /* Don't restart music on hold if we're about to exit the caller from the queue */
4517 if (!res) {
4518 if (ringing) {
4520 } else {
4521 ast_moh_start(qe->chan, qe->moh, NULL);
4522 }
4523 }
4524 return res;
4525}
4526
4527static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
4528{
4529 int oldvalue;
4530
4531 /* Calculate holdtime using an exponential average */
4532 /* Thanks to SRT for this contribution */
4533 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
4534
4535 ao2_lock(qe->parent);
4536 if ((qe->parent->callscompleted + qe->parent->callsabandoned) == 0) {
4537 qe->parent->holdtime = newholdtime;
4538 } else {
4539 oldvalue = qe->parent->holdtime;
4540 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4541 }
4542 ao2_unlock(qe->parent);
4543}
4544
4545/*! \brief Caller leaving queue.
4546 *
4547 * Search the queue to find the leaving client, if found remove from queue
4548 * create manager event, move others up the queue.
4549*/
4550static void leave_queue(struct queue_ent *qe)
4551{
4552 struct call_queue *q;
4553 struct queue_ent *current, *prev = NULL;
4554 struct penalty_rule *pr_iter;
4555 int pos = 0;
4556
4557 if (!(q = qe->parent)) {
4558 return;
4559 }
4560 queue_t_ref(q, "Copy queue pointer from queue entry");
4561 ao2_lock(q);
4562
4563 prev = NULL;
4564 for (current = q->head; current; current = current->next) {
4565 if (current == qe) {
4566 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4567 char posstr[20];
4568 q->count--;
4569 if (!q->count) {
4571 }
4572
4573 blob = ast_json_pack("{s: s, s: i, s: i}",
4574 "Queue", q->name,
4575 "Position", qe->pos,
4576 "Count", q->count);
4577 ast_channel_publish_cached_blob(qe->chan, queue_caller_leave_type(), blob);
4578 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
4579 /* Take us out of the queue */
4580 if (prev) {
4581 prev->next = current->next;
4582 } else {
4583 q->head = current->next;
4584 }
4585 /* Free penalty rules */
4586 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
4587 ast_free(pr_iter);
4588 }
4589 qe->pr = NULL;
4590 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
4591 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
4592 } else {
4593 /* Renumber the people after us in the queue based on a new count */
4594 current->pos = ++pos;
4595 prev = current;
4596 }
4597 }
4598 ao2_unlock(q);
4599
4600 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
4601 if (q->realtime) {
4602 struct ast_variable *var;
4603 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
4604 q->dead = 1;
4605 } else {
4607 }
4608 }
4609
4610 if (q->dead) {
4611 /* It's dead and nobody is in it, so kill it */
4612 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
4613 }
4614 /* unref the explicit ref earlier in the function */
4615 queue_t_unref(q, "Expire copied reference");
4616}
4617
4618/*!
4619 * \internal
4620 * \brief Destroy the given callattempt structure and free it.
4621 * \since 1.8
4622 *
4623 * \param doomed callattempt structure to destroy.
4624 */
4625static void callattempt_free(struct callattempt *doomed)
4626{
4627 if (doomed->member) {
4628 ao2_ref(doomed->member, -1);
4629 }
4631 ast_free(doomed->orig_chan_name);
4632 ast_free(doomed);
4633}
4634
4635static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
4636{
4637 struct callattempt *cur;
4638
4639 for (cur = outgoing; cur; cur = cur->q_next) {
4640 if (cur->chan && cur->chan != exception) {
4642 }
4643 }
4644}
4645
4646/*! \brief Hang up a list of outgoing calls */
4647static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
4648{
4649 struct callattempt *oo;
4650
4651 while (outgoing) {
4652 /* If someone else answered the call we should indicate this in the CANCEL */
4653 /* Hangup any existing lines we have open */
4654 if (outgoing->chan && (outgoing->chan != exception)) {
4655 if (exception || cancel_answered_elsewhere) {
4657 }
4658 ast_channel_publish_dial(qe->chan, outgoing->chan, outgoing->interface, "CANCEL");
4659
4660 /* When dialing channels it is possible that they may not ever
4661 * leave the not in use state (Local channels in particular) by
4662 * the time we cancel them. If this occurs but we know they were
4663 * dialed we explicitly remove them from the pending members
4664 * container so that subsequent call attempts occur.
4665 */
4666 if (outgoing->member->status == AST_DEVICE_NOT_INUSE) {
4668 }
4669
4670 ast_hangup(outgoing->chan);
4671 }
4672 oo = outgoing;
4673 outgoing = outgoing->q_next;
4675 callattempt_free(oo);
4676 }
4677}
4678
4679/*!
4680 * \brief Get the number of members available to accept a call.
4681 *
4682 * \note The queue passed in should be locked prior to this function call
4683 *
4684 * \param[in] q The queue for which we are counting the number of available members
4685 * \return Return the number of available members in queue q
4686 */
4688{
4689 struct member *mem;
4690 int avl = 0;
4691 struct ao2_iterator mem_iter;
4692
4693 mem_iter = ao2_iterator_init(q->members, 0);
4694 while ((mem = ao2_iterator_next(&mem_iter))) {
4695
4696 avl += is_member_available(q, mem);
4697 ao2_ref(mem, -1);
4698
4699 /* If autofill is not enabled or if the queue's strategy is ringall, then
4700 * we really don't care about the number of available members so much as we
4701 * do that there is at least one available.
4702 *
4703 * In fact, we purposely will return from this function stating that only
4704 * one member is available if either of those conditions hold. That way,
4705 * functions which determine what action to take based on the number of available
4706 * members will operate properly. The reasoning is that even if multiple
4707 * members are available, only the head caller can actually be serviced.
4708 */
4709 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4710 break;
4711 }
4712 }
4713 ao2_iterator_destroy(&mem_iter);
4714
4715 return avl;
4716}
4717
4718/* traverse all defined queues which have calls waiting and contain this member
4719 return 0 if no other queue has precedence (higher weight) or 1 if found */
4720static int compare_weight(struct call_queue *rq, struct member *member)
4721{
4722 struct call_queue *q;
4723 struct member *mem;
4724 int found = 0;
4725 struct ao2_iterator queue_iter;
4726
4727 queue_iter = ao2_iterator_init(queues, 0);
4728 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4729 if (q == rq) { /* don't check myself, could deadlock */
4730 queue_t_unref(q, "Done with iterator");
4731 continue;
4732 }
4733 ao2_lock(q);
4734 if (q->count && q->members) {
4735 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4736 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4737 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
4738 ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
4739 found = 1;
4740 }
4741 ao2_ref(mem, -1);
4742 }
4743 }
4744 ao2_unlock(q);
4745 queue_t_unref(q, "Done with iterator");
4746 if (found) {
4747 break;
4748 }
4749 }
4750 ao2_iterator_destroy(&queue_iter);
4751 return found;
4752}
4753
4754static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
4755{
4756 struct call_queue *q;
4757 struct member *mem;
4758 int is_longest_waiting = 1;
4759 struct ao2_iterator queue_iter;
4760 struct queue_ent *ch;
4761
4763 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4764 if (q == caller->parent) { /* don't check myself, could deadlock */
4765 queue_t_unref(q, "Done with iterator");
4766 continue;
4767 }
4768 ao2_lock(q);
4769 /*
4770 * If the other queue has equal weight, see if we should let that handle
4771 * their call first. If weights are not equal, compare_weights will step in.
4772 */
4773 if (q->weight == caller->parent->weight && q->count && q->members) {
4774 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4775 ast_debug(2, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4776
4777 /* Does this queue have a caller that's been waiting longer? */
4778 ch = q->head;
4779 while (ch) {
4780 /* If ch->pending, the other call (which may be waiting for a longer period of time),
4781 * is already ringing at another agent. Ignore such callers; otherwise, all agents
4782 * will be unused until the first caller is picked up.
4783 */
4784 if (!ch->pending) {
4786 if (ch->prio > caller->prio) { /* This queue has a caller with higher priority. */
4787 ast_debug(1, "Queue %s has a call at position %i that's higher priority (%d vs %d)\n",
4788 q->name, ch->pos, ch->prio, caller->prio);
4790 }
4791 } else if (ch->start < caller->start) {
4792 ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",
4793 q->name, ch->pos, ch->start, caller->start);
4795 }
4796 break;
4797 }
4798 ch = ch->next;
4799 }
4800
4801 ao2_ref(mem, -1);
4802 }
4803 }
4804 ao2_unlock(q);
4805 queue_t_unref(q, "Done with iterator");
4806 if (!is_longest_waiting) {
4807 break;
4808 }
4809 }
4811 return is_longest_waiting;
4812}
4813
4814/*! \brief common hangup actions */
4815static void do_hang(struct callattempt *o)
4816{
4817 o->stillgoing = 0;
4818 ast_hangup(o->chan);
4819 pending_members_remove(o->member);
4820 o->chan = NULL;
4821}
4822
4823/*!
4824 * \internal
4825 * \brief Check if the member status is available.
4826 *
4827 * \param status Member status to check if available.
4828 *
4829 * \retval non-zero if the member status is available.
4830 */
4832{
4834}
4835
4836/*!
4837 * \internal
4838 * \brief Determine if can ring a queue entry.
4839 *
4840 * \param qe Queue entry to check.
4841 * \param call Member call attempt.
4842 *
4843 * \retval non-zero if an entry can be called.
4844 */
4845static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
4846{
4847 struct member *memberp = call->member;
4848 int wrapuptime;
4849
4850 if (memberp->paused) {
4851 ast_debug(1, "%s paused, can't receive call\n", call->interface);
4852 return 0;
4853 }
4854
4855 if (!memberp->ringinuse && !member_status_available(memberp->status)) {
4856 ast_debug(1, "%s not available, can't receive call\n", call->interface);
4857 return 0;
4858 }
4859
4860 if (memberp->lastqueue) {
4861 wrapuptime = get_wrapuptime(memberp->lastqueue, memberp);
4862 } else {
4863 wrapuptime = get_wrapuptime(qe->parent, memberp);
4864 }
4865 if (wrapuptime && (time(NULL) - memberp->lastcall) < wrapuptime) {
4866 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
4867 (memberp->lastqueue ? memberp->lastqueue->name : qe->parent->name),
4868 call->interface);
4869 return 0;
4870 }
4871
4872 if (use_weight && compare_weight(qe->parent, memberp)) {
4873 ast_debug(1, "Priority queue delaying call to %s:%s\n",
4874 qe->parent->name, call->interface);
4875 return 0;
4876 }
4877
4879 ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n",
4880 qe->parent->name, call->interface);
4881 return 0;
4882 }
4883
4884 if (!memberp->ringinuse) {
4885 struct member *mem;
4886
4888
4889 mem = ao2_find(pending_members, memberp,
4891 if (mem) {
4892 /*
4893 * If found that means this member is currently being attempted
4894 * from another calling thread, so stop trying from this thread
4895 */
4896 ast_debug(1, "%s has another call trying, can't receive call\n",
4897 call->interface);
4898 ao2_ref(mem, -1);
4900 return 0;
4901 }
4902
4903 /*
4904 * If not found add it to the container so another queue
4905 * won't attempt to call this member at the same time.
4906 */
4907 ast_debug(3, "Add %s to pending_members\n", memberp->membername);
4908 ao2_link(pending_members, memberp);
4910
4911 /*
4912 * The queue member is available. Get current status to be sure
4913 * because the device state and extension state callbacks may
4914 * not have updated the status yet.
4915 */
4917 ast_debug(1, "%s actually not available, can't receive call\n",
4918 call->interface);
4919 pending_members_remove(memberp);
4920 return 0;
4921 }
4922 }
4923
4924 return 1;
4925}
4926
4927/*!
4928 * \brief Part 2 of ring_one
4929 *
4930 * Does error checking before attempting to request a channel and call a member.
4931 * This function is only called from ring_one().
4932 * Failure can occur if:
4933 * - Agent on call
4934 * - Agent is paused
4935 * - Wrapup time not expired
4936 * - Priority by another queue
4937 *
4938 * \retval 1 on success to reach a free agent
4939 * \retval 0 on failure to get agent.
4940 */
4941static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
4942{
4943 int res;
4944 int status;
4945 char tech[256];
4946 char *location;
4947 struct ast_format_cap *nativeformats;
4948 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4949
4950 /* on entry here, we know that tmp->chan == NULL */
4951 if (!can_ring_entry(qe, tmp)) {
4952 tmp->stillgoing = 0;
4953 ++*busies;
4954 return 0;
4955 }
4956
4957 ast_copy_string(tech, tmp->interface, sizeof(tech));
4958 if ((location = strchr(tech, '/'))) {
4959 *location++ = '\0';
4960 } else {
4961 location = "";
4962 }
4963
4965 nativeformats = ao2_bump(ast_channel_nativeformats(qe->chan));
4967
4968 /* Request the peer */
4969 tmp->chan = ast_request(tech, nativeformats, NULL, qe->chan, location, &status);
4970 ao2_cleanup(nativeformats);
4971 if (!tmp->chan) { /* If we can't, just go on to the next call */
4972 ao2_lock(qe->parent);
4973 qe->parent->rrpos++;
4974 qe->linpos++;
4975 ao2_unlock(qe->parent);
4976
4978
4979 publish_dial_end_event(qe->chan, tmp, NULL, "BUSY");
4980 tmp->stillgoing = 0;
4981 ++*busies;
4982 return 0;
4983 }
4984
4985 ast_channel_lock_both(tmp->chan, qe->chan);
4986
4989 if (qe->cancel_answered_elsewhere) {
4991 }
4992 ast_channel_appl_set(tmp->chan, "AppQueue");
4993 ast_channel_data_set(tmp->chan, "(Outgoing Line)");
4994 memset(ast_channel_whentohangup(tmp->chan), 0, sizeof(*ast_channel_whentohangup(tmp->chan)));
4995
4996 /* If the new channel has no callerid, try to guess what it should be */
4997 if (!ast_channel_caller(tmp->chan)->id.number.valid) {
4999 struct ast_party_caller caller;
5000
5002 caller.id = ast_channel_connected(qe->chan)->id;
5003 caller.ani = ast_channel_connected(qe->chan)->ani;
5004 ast_channel_set_caller_event(tmp->chan, &caller, NULL);
5005 } else if (!ast_strlen_zero(ast_channel_dialed(qe->chan)->number.str)) {
5007 } else if (!ast_strlen_zero(ast_channel_exten(qe->chan))) {
5009 }
5010 tmp->dial_callerid_absent = 1;
5011 }
5012
5014
5016
5018
5019 /* Inherit specially named variables from parent channel */
5023
5024 /* Presense of ADSI CPE on outgoing channel follows ours */
5026
5027 /* Inherit context and extension */
5028 ast_channel_dialcontext_set(tmp->chan, ast_channel_context(qe->chan));
5030
5031 /* Save the original channel name to detect call pickup masquerading in. */
5033
5036
5037 /* location is tmp->interface where tech/ has been stripped, so it follow the same syntax as DIALEDPEERNUMBER in app_dial.c */
5038 pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", strlen(location) ? location : tmp->interface);
5039
5040 /* PREDIAL: Run gosub on the callee's channel */
5041 if (qe->predial_callee) {
5042 ast_pre_call(tmp->chan, qe->predial_callee);
5043 }
5044
5045 /* Place the call, but don't wait on the answer */
5046 if ((res = ast_call(tmp->chan, location, 0))) {
5047 /* Again, keep going even if there's an error */
5048 ast_verb(3, "Couldn't call %s\n", tmp->interface);
5049 do_hang(tmp);
5050 ++*busies;
5051 return 0;
5052 }
5053
5054 ast_channel_lock_both(tmp->chan, qe->chan);
5055
5056 blob = ast_json_pack("{s: s, s: s, s: s}",
5057 "Queue", qe->parent->name,
5058 "Interface", tmp->interface,
5059 "MemberName", tmp->member->membername);
5060 queue_publish_multi_channel_blob(qe->chan, tmp->chan, queue_agent_called_type(), blob);
5061
5063
5066
5067 ast_verb(3, "Called %s\n", tmp->interface);
5068
5069 return 1;
5070}
5071
5072/*! \brief find the entry with the best metric, or NULL */
5074{
5075 struct callattempt *best = NULL, *cur;
5076
5077 for (cur = outgoing; cur; cur = cur->q_next) {
5078 if (cur->stillgoing && /* Not already done */
5079 !cur->chan && /* Isn't already going */
5080 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
5081 best = cur;
5082 }
5083 }
5084
5085 return best;
5086}
5087
5088/*!
5089 * \brief Place a call to a queue member.
5090 *
5091 * Once metrics have been calculated for each member, this function is used
5092 * to place a call to the appropriate member (or members). The low-level
5093 * channel-handling and error detection is handled in ring_entry
5094 *
5095 * \retval 1 if a member was called successfully
5096 * \retval 0 otherwise
5097 */
5098static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
5099{
5100 int ret = 0;
5101 struct callattempt *cur;
5102
5103 if (qe->predial_callee) {
5105 for (cur = outgoing; cur; cur = cur->q_next) {
5106 if (cur->stillgoing && cur->chan) {
5108 }
5109 }
5110 }
5111
5112 while (ret == 0) {
5113 struct callattempt *best = find_best(outgoing);
5114 if (!best) {
5115 ast_debug(1, "Nobody left to try ringing in queue\n");
5116 break;
5117 }
5119 /* Ring everyone who shares this best metric (for ringall) */
5120 for (cur = outgoing; cur; cur = cur->q_next) {
5121 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
5122 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
5123 ret |= ring_entry(qe, cur, busies);
5124 if (qe->predial_callee && cur->chan) {
5126 }
5127 }
5128 }
5129 } else {
5130 /* Ring just the best channel */
5131 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
5132 ret = ring_entry(qe, best, busies);
5133 if (qe->predial_callee && best->chan) {
5135 }
5136 }
5137
5138 /* If we have timed out, break out */
5139 if (qe->expire && (time(NULL) >= qe->expire)) {
5140 ast_debug(1, "Queue timed out while ringing members.\n");
5141 ret = 0;
5142 break;
5143 }
5144 }
5145 if (qe->predial_callee) {
5146 for (cur = outgoing; cur; cur = cur->q_next) {
5147 if (cur->stillgoing && cur->chan) {
5149 }
5150 }
5152 }
5153
5154 return ret;
5155}
5156
5157/*! \brief Search for best metric and add to Round Robbin queue */
5158static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
5159{
5160 struct callattempt *best = find_best(outgoing);
5161
5162 if (best) {
5163 /* Ring just the best channel */
5164 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
5165 qe->parent->rrpos = best->metric % 1000;
5166 } else {
5167 /* Just increment rrpos */
5168 if (qe->parent->wrapped) {
5169 /* No more channels, start over */
5170 qe->parent->rrpos = 0;
5171 } else {
5172 /* Prioritize next entry */
5173 qe->parent->rrpos++;
5174 }
5175 }
5176 qe->parent->wrapped = 0;
5177
5178 return 0;
5179}
5180
5181/*! \brief Search for best metric and add to Linear queue */
5182static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
5183{
5184 struct callattempt *best = find_best(outgoing);
5185
5186 if (best) {
5187 /* Ring just the best channel */
5188 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
5189 qe->linpos = best->metric % 1000;
5190 } else {
5191 /* Just increment rrpos */
5192 if (qe->linwrapped) {
5193 /* No more channels, start over */
5194 qe->linpos = 0;
5195 } else {
5196 /* Prioritize next entry */
5197 qe->linpos++;
5198 }
5199 }
5200 qe->linwrapped = 0;
5201
5202 return 0;
5203}
5204
5205/*! \brief Playback announcement to queued members if period has elapsed */
5207{
5208 int res = 0;
5209 time_t now;
5210
5211 /* Get the current time */
5212 time(&now);
5213
5214 /* Check to see if it is time to announce */
5216 return 0;
5217 }
5218
5219 /* Stop the music on hold so we can play our own file */
5220 if (ringing) {
5221 ast_indicate(qe->chan,-1);
5222 } else {
5223 ast_moh_stop(qe->chan);
5224 }
5225
5226 ast_verb(3, "Playing periodic announcement\n");
5227
5229 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
5233 }
5234
5235 /* play the announcement */
5237
5238 if (res > 0 && !valid_exit(qe, res)) {
5239 res = 0;
5240 }
5241
5242 /* Resume Music on Hold if the caller is going to stay in the queue */
5243 if (!res) {
5244 if (ringing) {
5246 } else {
5247 ast_moh_start(qe->chan, qe->moh, NULL);
5248 }
5249 }
5250
5251 /* update last_periodic_announce_time */
5253 time(&qe->last_periodic_announce_time);
5254 } else {
5256 }
5257
5258 /* Update the current periodic announcement to the next announcement */
5259 if (!qe->parent->randomperiodicannounce) {
5261 }
5262
5263 return res;
5264}
5265
5266/*! \brief Record that a caller gave up on waiting in queue */
5267static void record_abandoned(struct queue_ent *qe)
5268{
5269 int callabandonedinsl = 0;
5270 time_t now;
5271
5272 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5273
5274 pbx_builtin_setvar_helper(qe->chan, "ABANDONED", "TRUE");
5275
5277 ao2_lock(qe->parent);
5278 blob = ast_json_pack("{s: s, s: i, s: i, s: i}",
5279 "Queue", qe->parent->name,
5280 "Position", qe->pos,
5281 "OriginalPosition", qe->opos,
5282 "HoldTime", (int)(time(NULL) - qe->start));
5283
5284
5285 time(&now);
5286 callabandonedinsl = ((now - qe->start) <= qe->parent->servicelevel);
5287 if (callabandonedinsl) {
5289 }
5290
5291 qe->parent->callsabandoned++;
5292 ao2_unlock(qe->parent);
5293
5294 ast_channel_publish_cached_blob(qe->chan, queue_caller_abandon_type(), blob);
5295}
5296
5297/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
5298static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, char *interface, char *membername, int autopause)
5299{
5300 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5301
5302 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
5303
5304 /* Stop ringing, and resume MOH if specified */
5305 if (qe->ring_when_ringing) {
5306 ast_indicate(qe->chan, -1);
5307 ast_moh_start(qe->chan, qe->moh, NULL);
5308 }
5309
5310 blob = ast_json_pack("{s: s, s: s, s: s, s: i}",
5311 "Queue", qe->parent->name,
5312 "Interface", interface,
5313 "MemberName", membername,
5314 "RingTime", rnatime);
5315 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_ringnoanswer_type(), blob);
5316
5317 ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), membername, "RINGNOANSWER", "%d", rnatime);
5319 if (qe->parent->autopausedelay > 0) {
5320 struct member *mem;
5321 ao2_lock(qe->parent);
5322 if ((mem = interface_exists(qe->parent, interface))) {
5323 time_t idletime = time(&idletime)-mem->lastcall;
5324 if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
5325 ao2_unlock(qe->parent);
5326 ao2_ref(mem, -1);
5327 return;
5328 }
5329 ao2_ref(mem, -1);
5330 }
5331 ao2_unlock(qe->parent);
5332 }
5333 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
5334 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
5335 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
5336 interface, qe->parent->name);
5337 } else {
5338 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
5339 }
5340 } else {
5341 /* If queue autopause is mode all, just don't send any queue to stop.
5342 * the function will stop in all queues */
5343 if (!set_member_paused("", interface, "Auto-Pause", 1)) {
5344 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
5345 interface, qe->parent->name);
5346 } else {
5347 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
5348 }
5349 }
5350 }
5351 return;
5352}
5353
5354/*!
5355 * \internal
5356 * \brief Update connected line on chan from peer.
5357 * \since 13.6.0
5358 *
5359 * \param chan Channel to get connected line updated.
5360 * \param peer Channel providing connected line information.
5361 * \param is_caller Non-zero if chan is the calling channel.
5362 */
5363static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
5364{
5365 struct ast_party_connected_line connected_caller;
5366
5367 ast_party_connected_line_init(&connected_caller);
5368
5369 ast_channel_lock(peer);
5371 ast_channel_unlock(peer);
5373 if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)) {
5374 ast_channel_update_connected_line(chan, &connected_caller, NULL);
5375 }
5376 ast_party_connected_line_free(&connected_caller);
5377}
5378
5379#define AST_MAX_WATCHERS 256
5380/*!
5381 * \brief Wait for a member to answer the call
5382 *
5383 * \param[in] qe the queue_ent corresponding to the caller in the queue
5384 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
5385 * \param[in] to the amount of time (in milliseconds) to wait for a response
5386 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
5387 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
5388 * \param[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
5389 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
5390 *
5391 * \todo eventually all call forward logic should be integrated into and replaced by ast_call_forward()
5392 */
5393static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
5394{
5395 const char *queue = qe->parent->name;
5396 struct callattempt *o, *start = NULL, *prev = NULL;
5397 int status;
5398 int numbusies = prebusies;
5399 int numnochan = 0;
5400 int stillgoing = 0;
5401 int orig = *to;
5402 struct ast_frame *f;
5403 struct callattempt *peer = NULL;
5404 struct ast_channel *winner;
5405 struct ast_channel *in = qe->chan;
5406 char on[80] = "";
5407 char membername[80] = "";
5408 long starttime = 0;
5409 long endtime = 0;
5410 char *inchan_name;
5411 struct timeval start_time_tv = ast_tvnow();
5412 int canceled_by_caller = 0; /* 1 when caller hangs up or press digit or press * */
5413
5415 inchan_name = ast_strdupa(ast_channel_name(qe->chan));
5417
5418 starttime = (long) time(NULL);
5419
5420 while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
5421 int numlines, retry, pos = 1;
5422 struct ast_channel *watchers[AST_MAX_WATCHERS];
5423 watchers[0] = in;
5424 start = NULL;
5425
5426 for (retry = 0; retry < 2; retry++) {
5427 numlines = 0;
5428 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
5429 if (o->stillgoing) { /* Keep track of important channels */
5430 stillgoing = 1;
5431 if (o->chan) {
5432 if (pos < AST_MAX_WATCHERS) {
5433 watchers[pos++] = o->chan;
5434 }
5435 if (!start) {
5436 start = o;
5437 } else {
5438 prev->call_next = o;
5439 }
5440 prev = o;
5441 }
5442 } else if (prev) {
5443 prev->call_next = NULL;
5444 }
5445 numlines++;
5446 }
5447 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
5448 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) {
5449 break;
5450 }
5451 /* On "ringall" strategy we only move to the next penalty level
5452 when *all* ringing phones are done in the current penalty level */
5453 ring_one(qe, outgoing, &numbusies);
5454 /* and retry... */
5455 }
5456 if (pos == 1 /* not found */) {
5457 if (numlines == (numbusies + numnochan)) {
5458 ast_debug(1, "Everyone is busy at this time\n");
5459 } else {
5460 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
5461 }
5462 *to = 0;
5463 return NULL;
5464 }
5465
5466 /* Poll for events from both the incoming channel as well as any outgoing channels */
5467 winner = ast_waitfor_n(watchers, pos, to);
5468
5469 /* Service all of the outgoing channels */
5470 for (o = start; o; o = o->call_next) {
5471 /* We go with a fixed buffer here instead of using ast_strdupa. Using
5472 * ast_strdupa in a loop like this one can cause a stack overflow
5473 */
5474 char ochan_name[AST_CHANNEL_NAME];
5475
5476 if (o->chan) {
5478 ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
5480 }
5481 if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
5482 if (!peer) {
5483 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5484 if (o->orig_chan_name
5485 && strcmp(o->orig_chan_name, ochan_name)) {
5486 /*
5487 * The channel name changed so we must generate COLP update.
5488 * Likely because a call pickup channel masqueraded in.
5489 */
5491 } else if (!o->block_connected_update) {
5492 if (o->pending_connected_update) {
5495 }
5496 } else if (!o->dial_callerid_absent) {
5498 }
5499 }
5500 if (o->aoc_s_rate_list) {
5501 size_t encoded_size;
5502 struct ast_aoc_encoded *encoded;
5503 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5504 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5505 ast_aoc_destroy_encoded(encoded);
5506 }
5507 }
5508 peer = o;
5509 }
5510 } else if (o->chan && (o->chan == winner)) {
5511
5512 ast_copy_string(on, o->member->interface, sizeof(on));
5513 ast_copy_string(membername, o->member->membername, sizeof(membername));
5514
5515 /* Before processing channel, go ahead and check for forwarding */
5516 if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
5517 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
5519 "CANCEL", ast_channel_call_forward(o->chan));
5520 numnochan++;
5521 do_hang(o);
5522 winner = NULL;
5523 continue;
5525 struct ast_channel *original = o->chan;
5526 char forwarder[AST_CHANNEL_NAME];
5527 char tmpchan[256];
5528 char *stuff;
5529 char *tech;
5530 int failed = 0;
5531
5532 ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
5533 ast_copy_string(forwarder, ast_channel_name(o->chan), sizeof(forwarder));
5534 if ((stuff = strchr(tmpchan, '/'))) {
5535 *stuff++ = '\0';
5536 tech = tmpchan;
5537 } else {
5538 const char *forward_context;
5540 forward_context = pbx_builtin_getvar_helper(o->chan, "FORWARD_CONTEXT");
5541 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), forward_context ? forward_context : ast_channel_context(o->chan));
5543 stuff = tmpchan;
5544 tech = "Local";
5545 }
5546 if (!strcasecmp(tech, "Local")) {
5547 /*
5548 * Drop the connected line update block for local channels since
5549 * this is going to run dialplan and the user can change his
5550 * mind about what connected line information he wants to send.
5551 */
5553 }
5554
5555 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
5556 /* Setup parameters */
5558 if (!o->chan) {
5560 "Forwarding failed to create channel to dial '%s/%s'\n",
5561 tech, stuff);
5562 o->stillgoing = 0;
5563 numnochan++;
5564 } else {
5565 ast_channel_lock_both(o->chan, original);
5567 ast_channel_redirecting(original));
5569 ast_channel_unlock(original);
5570
5574 pbx_builtin_setvar_helper(o->chan, "FORWARDERNAME", forwarder);
5576
5577 if (o->pending_connected_update) {
5578 /*
5579 * Re-seed the callattempt's connected line information with
5580 * previously acquired connected line info from the queued
5581 * channel. The previously acquired connected line info could
5582 * have been set through the CONNECTED_LINE dialplan function.
5583 */
5586 }
5587
5590
5592
5595 /*
5596 * The call was not previously redirected so it is
5597 * now redirected from this number.
5598 */
5604 }
5605
5607
5612
5615 && !o->block_connected_update) {
5616 struct ast_party_redirecting redirecting;
5617
5618 /*
5619 * Redirecting updates to the caller make sense only on single
5620 * call at a time strategies.
5621 *
5622 * Need to re-evaluate if calling unlock is still required as we no longer
5623 * use macro.
5624 */
5625 ast_party_redirecting_init(&redirecting);
5628 if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0)) {
5629 ast_channel_update_redirecting(in, &redirecting, NULL);
5630 }
5631 ast_party_redirecting_free(&redirecting);
5632 } else {
5634 }
5635
5636 if (ast_call(o->chan, stuff, 0)) {
5637 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
5638 tech, stuff);
5639 failed = 1;
5640 }
5641 }
5642
5644 "CANCEL", ast_channel_call_forward(original));
5645 if (o->chan) {
5646 ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
5647 }
5648
5649 if (failed) {
5650 do_hang(o);
5651 numnochan++;
5652 }
5653
5654 /* Hangup the original channel now, in case we needed it */
5655 ast_hangup(winner);
5656 continue;
5657 }
5658 f = ast_read(winner);
5659 if (f) {
5660 if (f->frametype == AST_FRAME_CONTROL) {
5661 switch (f->subclass.integer) {
5662 case AST_CONTROL_ANSWER:
5663 /* This is our guy if someone answered. */
5664 if (!peer) {
5665 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5666 ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
5667 publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
5668 if (o->orig_chan_name
5669 && strcmp(o->orig_chan_name, ochan_name)) {
5670 /*
5671 * The channel name changed so we must generate COLP update.
5672 * Likely because a call pickup channel masqueraded in.
5673 */
5675 } else if (!o->block_connected_update) {
5676 if (o->pending_connected_update) {
5679 }
5680 } else if (!o->dial_callerid_absent) {
5682 }
5683 }
5684 if (o->aoc_s_rate_list) {
5685 size_t encoded_size;
5686 struct ast_aoc_encoded *encoded;
5687 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5688 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5689 ast_aoc_destroy_encoded(encoded);
5690 }
5691 }
5692 peer = o;
5693 }
5694 break;
5695 case AST_CONTROL_BUSY:
5696 ast_verb(3, "%s is busy\n", ochan_name);
5697 ast_channel_publish_dial(qe->chan, o->chan, on, "BUSY");
5698 endtime = (long) time(NULL);
5699 endtime -= starttime;
5700 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopausebusy);
5701 do_hang(o);
5703 if (qe->parent->timeoutrestart) {
5704 start_time_tv = ast_tvnow();
5705 }
5706 /* Have enough time for a queue member to answer? */
5707 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5708 ring_one(qe, outgoing, &numbusies);
5709 starttime = (long) time(NULL);
5710 }
5711 }
5712 numbusies++;
5713 break;
5715 ast_verb(3, "%s is circuit-busy\n", ochan_name);
5716 ast_channel_publish_dial(qe->chan, o->chan, on, "CONGESTION");
5717 endtime = (long) time(NULL);
5718 endtime -= starttime;
5719 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopauseunavail);
5720 do_hang(o);
5722 if (qe->parent->timeoutrestart) {
5723 start_time_tv = ast_tvnow();
5724 }
5725 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5726 ring_one(qe, outgoing, &numbusies);
5727 starttime = (long) time(NULL);
5728 }
5729 }
5730 numbusies++;
5731 break;
5733 ast_verb(3, "%s is ringing\n", ochan_name);
5734
5735 ast_channel_publish_dial(qe->chan, o->chan, on, "RINGING");
5736
5737 /* Start ring indication when the channel is ringing, if specified */
5738 if (qe->ring_when_ringing) {
5739 ast_moh_stop(qe->chan);
5741 }
5742 break;
5744 /* Ignore going off hook */
5745 break;
5747 if (o->block_connected_update) {
5748 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
5749 break;
5750 }
5753
5754 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
5760 break;
5761 }
5762
5763 /*
5764 * Prevent using the CallerID from the outgoing channel since we
5765 * got a connected line update from it.
5766 */
5767 o->dial_callerid_absent = 1;
5768
5769 if (ast_channel_connected_line_sub(o->chan, in, f, 1)) {
5771 }
5772 break;
5773 case AST_CONTROL_AOC:
5774 {
5775 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
5776 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
5778 o->aoc_s_rate_list = decoded;
5779 } else {
5780 ast_aoc_destroy_decoded(decoded);
5781 }
5782 }
5783 break;
5786 /*
5787 * Redirecting updates to the caller make sense only on single
5788 * call at a time strategies.
5789 */
5790 break;
5791 }
5792 if (o->block_connected_update) {
5793 ast_verb(3, "Redirecting update to %s prevented\n",
5794 inchan_name);
5795 break;
5796 }
5797 ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
5798 ochan_name, inchan_name);
5799 if (ast_channel_redirecting_sub(o->chan, in, f, 1)) {
5801 }
5802 break;
5805 break;
5806 default:
5807 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
5808 break;
5809 }
5810 }
5811 ast_frfree(f);
5812 } else { /* ast_read() returned NULL */
5813 endtime = (long) time(NULL) - starttime;
5814 ast_channel_publish_dial(qe->chan, o->chan, on, "NOANSWER");
5815 rna(endtime * 1000, qe, o->chan, on, membername, 1);
5816 do_hang(o);
5818 if (qe->parent->timeoutrestart) {
5819 start_time_tv = ast_tvnow();
5820 }
5821 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5822 ring_one(qe, outgoing, &numbusies);
5823 starttime = (long) time(NULL);
5824 }
5825 }
5826 }
5827 }
5828 }
5829
5830 /* If we received an event from the caller, deal with it. */
5831 if (winner == in) {
5832 f = ast_read(in);
5833 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
5834 /* Got hung up */
5835 *to = -1;
5836 if (f) {
5837 if (f->data.uint32) {
5839 }
5840 ast_frfree(f);
5841 }
5842 canceled_by_caller = 1;
5843 } else if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
5844 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
5845 *to = 0;
5846 ast_frfree(f);
5847 canceled_by_caller = 1;
5848 } else if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
5849 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
5850 *to = 0;
5851 *digit = f->subclass.integer;
5852 ast_frfree(f);
5853 canceled_by_caller = 1;
5854 }
5855 /* When caller hung up or pressed * or digit. */
5856 if (canceled_by_caller) {
5858 for (o = start; o; o = o->call_next) {
5859 if (o->chan) {
5860 ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), o->member->membername, "RINGCANCELED", "%d", (int) ast_tvdiff_ms(ast_tvnow(), start_time_tv));
5861 }
5862 }
5863 return NULL;
5864 }
5865
5866 /* Send the frame from the in channel to all outgoing channels. */
5867 for (o = start; o; o = o->call_next) {
5868 if (!o->stillgoing || !o->chan) {
5869 /* This outgoing channel has died so don't send the frame to it. */
5870 continue;
5871 }
5872 switch (f->frametype) {
5873 case AST_FRAME_CONTROL:
5874 switch (f->subclass.integer) {
5876 if (o->block_connected_update) {
5877 ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
5878 break;
5879 }
5880 if (ast_channel_connected_line_sub(in, o->chan, f, 1)) {
5882 }
5883 break;
5885 if (o->block_connected_update) {
5886 ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
5887 break;
5888 }
5889 if (ast_channel_redirecting_sub(in, o->chan, f, 1)) {
5891 }
5892 break;
5893 default:
5894 /* We are not going to do anything with this frame. */
5895 goto skip_frame;
5896 }
5897 break;
5898 default:
5899 /* We are not going to do anything with this frame. */
5900 goto skip_frame;
5901 }
5902 }
5903skip_frame:;
5904
5905 ast_frfree(f);
5906 }
5907 }
5908
5909 if (!*to) {
5910 for (o = start; o; o = o->call_next) {
5911 if (o->chan) {
5912 rna(orig, qe, o->chan, o->interface, o->member->membername, 1);
5913 }
5914 }
5915
5916 publish_dial_end_event(qe->chan, outgoing, NULL, "NOANSWER");
5917 }
5918
5919 return peer;
5920}
5921
5922/*!
5923 * \brief Check if we should start attempting to call queue members.
5924 *
5925 * A simple process, really. Count the number of members who are available
5926 * to take our call and then see if we are in a position in the queue at
5927 * which a member could accept our call.
5928 *
5929 * \param[in] qe The caller who wants to know if it is his turn
5930 * \retval 0 It is not our turn
5931 * \retval 1 It is our turn
5932 */
5933static int is_our_turn(struct queue_ent *qe)
5934{
5935 struct queue_ent *ch;
5936 int res;
5937 int avl;
5938 int idx = 0;
5939 /* This needs a lock. How many members are available to be served? */
5940 ao2_lock(qe->parent);
5941
5942 avl = num_available_members(qe->parent);
5943
5944 ch = qe->parent->head;
5945
5946 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
5947
5948 while ((idx < avl) && (ch) && (ch != qe)) {
5949 if (!ch->pending) {
5950 idx++;
5951 }
5952 ch = ch->next;
5953 }
5954
5955 ao2_unlock(qe->parent);
5956 /* If the queue entry is within avl [the number of available members] calls from the top ...
5957 * Autofill and position check added to support autofill=no (as only calls
5958 * from the front of the queue are valid when autofill is disabled)
5959 */
5960 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
5961 ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
5962 res = 1;
5963 } else {
5964 ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
5965 res = 0;
5966 }
5967
5968 /* Update realtime members if this is the first call and number of avalable members is 0 */
5969 if (avl == 0 && qe->pos == 1) {
5970 update_realtime_members(qe->parent);
5971 }
5972
5973 return res;
5974}
5975
5976/*!
5977 * \brief update rules for queues
5978 *
5979 * Calculate min/max penalties making sure if relative they stay within bounds.
5980 * Update queues penalty and set dialplan vars, goto next list entry.
5981*/
5982static void update_qe_rule(struct queue_ent *qe)
5983{
5984 int max_penalty = INT_MAX;
5985
5986 if (qe->max_penalty != INT_MAX) {
5987 char max_penalty_str[20];
5988
5989 if (qe->pr->max_relative) {
5990 max_penalty = qe->max_penalty + qe->pr->max_value;
5991 } else {
5992 max_penalty = qe->pr->max_value;
5993 }
5994
5995 /* a relative change to the penalty could put it below 0 */
5996 if (max_penalty < 0) {
5997 max_penalty = 0;
5998 }
5999
6001 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
6002 qe->max_penalty = max_penalty;
6003 ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
6004 qe->max_penalty, ast_channel_name(qe->chan), qe->pr->time);
6005 }
6006
6007 if (qe->min_penalty != INT_MAX) {
6008 char min_penalty_str[20];
6009 int min_penalty;
6010
6011 if (qe->pr->min_relative) {
6012 min_penalty = qe->min_penalty + qe->pr->min_value;
6013 } else {
6014 min_penalty = qe->pr->min_value;
6015 }
6016
6017 /* a relative change to the penalty could put it below 0 */
6018 if (min_penalty < 0) {
6019 min_penalty = 0;
6020 }
6021
6024 }
6025
6027 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
6028 qe->min_penalty = min_penalty;
6029 ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
6030 qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
6031 }
6032
6033 if (qe->raise_penalty != INT_MAX) {
6034 char raise_penalty_str[20];
6035 int raise_penalty;
6036
6037 if (qe->pr->raise_relative) {
6038 raise_penalty = qe->raise_penalty + qe->pr->raise_value;
6039 } else {
6040 raise_penalty = qe->pr->raise_value;
6041 }
6042
6043 /* a relative change to the penalty could put it below 0 */
6044 if (raise_penalty < 0) {
6045 raise_penalty = 0;
6046 }
6047
6050 }
6051
6052 qe->raise_respect_min = qe->pr->raise_respect_min;
6053 if (qe->raise_respect_min) {
6055 } else {
6057 }
6058 pbx_builtin_setvar_helper(qe->chan, "QUEUE_RAISE_PENALTY", raise_penalty_str);
6059 qe->raise_penalty = raise_penalty;
6060 ast_debug(3, "Setting raised penalty to %d for caller %s since %d seconds have elapsed\n",
6061 qe->raise_penalty, ast_channel_name(qe->chan), qe->pr->time);
6062 }
6063
6064 qe->pr = AST_LIST_NEXT(qe->pr, list);
6065}
6066
6067/*! \brief The waiting areas for callers who are not actively calling members
6068 *
6069 * This function is one large loop. This function will return if a caller
6070 * either exits the queue or it becomes that caller's turn to attempt calling
6071 * queue members. Inside the loop, we service the caller with periodic announcements,
6072 * holdtime announcements, etc. as configured in queues.conf
6073 *
6074 * \retval 0 if the caller's turn has arrived
6075 * \retval -1 if the caller should exit the queue.
6076 */
6077static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
6078{
6079 int res = 0;
6080
6081 /* This is the holding pen for callers 2 through maxlen */
6082 for (;;) {
6083
6084 /* A request to withdraw this call from the queue arrived */
6085 if (qe->withdraw) {
6086 *reason = QUEUE_WITHDRAW;
6087 res = 1;
6088 break;
6089 }
6090
6091 if (is_our_turn(qe)) {
6092 break;
6093 }
6094
6095 /* If we have timed out, break out */
6096 if (qe->expire && (time(NULL) >= qe->expire)) {
6097 *reason = QUEUE_TIMEOUT;
6098 break;
6099 }
6100
6101 if (qe->parent->leavewhenempty) {
6102 int status = 0;
6103
6104 if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->raise_penalty, qe->parent->leavewhenempty, 0, qe->raise_respect_min))) {
6106 *reason = QUEUE_LEAVEEMPTY;
6107 ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
6108 res = -1;
6109 qe->handled = -1;
6110 break;
6111 }
6112 }
6113
6114 /* Make a position announcement, if enabled */
6115 if (qe->parent->announcefrequency &&
6116 (res = say_position(qe,ringing))) {
6117 break;
6118 }
6119
6120 /* If we have timed out, break out */
6121 if (qe->expire && (time(NULL) >= qe->expire)) {
6122 *reason = QUEUE_TIMEOUT;
6123 break;
6124 }
6125
6126 /* Make a periodic announcement, if enabled */
6127 if (qe->parent->periodicannouncefrequency &&
6129 break;
6130
6131 /* see if we need to move to the next penalty level for this queue */
6132 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
6134 }
6135
6136 /* If we have timed out, break out */
6137 if (qe->expire && (time(NULL) >= qe->expire)) {
6138 *reason = QUEUE_TIMEOUT;
6139 break;
6140 }
6141
6142 /* Wait a second before checking again */
6143 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
6144 if (res > 0 && !valid_exit(qe, res)) {
6145 res = 0;
6146 } else {
6147 break;
6148 }
6149 }
6150
6151 /* If we have timed out, break out */
6152 if (qe->expire && (time(NULL) >= qe->expire)) {
6153 *reason = QUEUE_TIMEOUT;
6154 break;
6155 }
6156 }
6157
6158 return res;
6159}
6160
6161/*!
6162 * \brief update the queue status
6163 * \retval 0 always
6164*/
6165static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
6166{
6167 int oldtalktime;
6168 int newtalktime = time(NULL) - starttime;
6169 struct member *mem;
6170 struct call_queue *qtmp;
6171 struct ao2_iterator queue_iter;
6172
6173 /* It is possible for us to be called when a call has already been considered terminated
6174 * and data updated, so to ensure we only act on the call that the agent is currently in
6175 * we check when the call was bridged.
6176 */
6177 if (!starttime || (member->starttime != starttime)) {
6178 return 0;
6179 }
6180
6181 if (shared_lastcall) {
6182 queue_iter = ao2_iterator_init(queues, 0);
6183 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
6184 ao2_lock(qtmp);
6185 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
6186 time(&mem->lastcall);
6187 mem->calls++;
6188 mem->callcompletedinsl = 0;
6189 mem->starttime = 0;
6190 mem->lastqueue = q;
6191 ao2_ref(mem, -1);
6192 }
6193 ao2_unlock(qtmp);
6194 queue_t_unref(qtmp, "Done with iterator");
6195 }
6196 ao2_iterator_destroy(&queue_iter);
6197 } else {
6198 ao2_lock(q);
6199 time(&member->lastcall);
6201 member->calls++;
6202 member->starttime = 0;
6203 member->lastqueue = q;
6204 ao2_unlock(q);
6205 }
6206 /* Member might never experience any direct status change (local
6207 * channel with forwarding in particular). If that's the case,
6208 * this is the last chance to remove it from pending or subsequent
6209 * calls will not occur.
6210 */
6212
6213 ao2_lock(q);
6214 q->callscompleted++;
6215 if (callcompletedinsl) {
6216 q->callscompletedinsl++;
6217 }
6218 if (q->callscompleted == 1) {
6219 q->talktime = newtalktime;
6220 } else {
6221 /* Calculate talktime using the same exponential average as holdtime code */
6222 oldtalktime = q->talktime;
6223 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
6224 }
6225 ao2_unlock(q);
6226 return 0;
6227}
6228
6229/*! \brief Calculate the metric of each member in the outgoing callattempts
6230 *
6231 * A numeric metric is given to each member depending on the ring strategy used
6232 * by the queue. Members with lower metrics will be called before members with
6233 * higher metrics
6234 * \retval -1 if penalties are exceeded
6235 * \retval 0 otherwise
6236 */
6237static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
6238{
6239 /* disregarding penalty on too few members? */
6240 int membercount = ao2_container_count(q->members);
6241 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
6242 int penalty = mem->penalty;
6243
6244 if (usepenalty) {
6245 if (qe->raise_penalty != INT_MAX && penalty < qe->raise_penalty && !(qe->raise_respect_min && qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) {
6246 /* Low penalty is raised up to the current minimum */
6247 penalty = qe->raise_penalty;
6248 }
6249 if ((qe->max_penalty != INT_MAX && penalty > qe->max_penalty) ||
6250 (qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) {
6251 return -1;
6252 }
6253 } else {
6254 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
6255 membercount, q->penaltymemberslimit);
6256 }
6257
6258 switch (q->strategy) {
6260 /* Everyone equal, except for penalty */
6261 tmp->metric = penalty * 1000000 * usepenalty;
6262 break;
6264 if (pos < qe->linpos) {
6265 tmp->metric = 1000 + pos;
6266 } else {
6267 if (pos > qe->linpos) {
6268 /* Indicate there is another priority */
6269 qe->linwrapped = 1;
6270 }
6271 tmp->metric = pos;
6272 }
6273 tmp->metric += penalty * 1000000 * usepenalty;
6274 break;
6277 pos = mem->queuepos;
6278 if (pos < q->rrpos) {
6279 tmp->metric = 1000 + pos;
6280 } else {
6281 if (pos > q->rrpos) {
6282 /* Indicate there is another priority */
6283 q->wrapped = 1;
6284 }
6285 tmp->metric = pos;
6286 }
6287 tmp->metric += penalty * 1000000 * usepenalty;
6288 break;
6290 tmp->metric = ast_random() % 1000;
6291 tmp->metric += penalty * 1000000 * usepenalty;
6292 break;
6294 tmp->metric = ast_random() % ((1 + penalty) * 1000);
6295 break;
6297 tmp->metric = mem->calls;
6298 tmp->metric += penalty * 1000000 * usepenalty;
6299 break;
6301 if (!mem->lastcall) {
6302 tmp->metric = 0;
6303 } else {
6304 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
6305 }
6306 tmp->metric += penalty * 1000000 * usepenalty;
6307 break;
6308 default:
6309 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
6310 break;
6311 }
6312 return 0;
6313}
6314
6320
6321/*! \brief Send out AMI message with member call completion status information */
6322static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller,
6323 struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart,
6324 time_t callstart, enum agent_complete_reason rsn)
6325{
6326 const char *reason = NULL; /* silence dumb compilers */
6327 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
6328
6329 switch (rsn) {
6330 case CALLER:
6331 reason = "caller";
6332 break;
6333 case AGENT:
6334 reason = "agent";
6335 break;
6336 case TRANSFER:
6337 reason = "transfer";
6338 break;
6339 }
6340
6341 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I, s: s}",
6342 "Queue", queuename,
6343 "Interface", member->interface,
6344 "MemberName", member->membername,
6345 "HoldTime", (ast_json_int_t)(callstart - holdstart),
6346 "TalkTime", (ast_json_int_t)(time(NULL) - callstart),
6347 "Reason", reason ?: "");
6348
6350 queue_agent_complete_type(), blob);
6351}
6352
6353static void queue_agent_cb(void *userdata, struct stasis_subscription *sub,
6354 struct stasis_message *msg)
6355{
6356 struct ast_channel_blob *agent_blob;
6357
6358 agent_blob = stasis_message_data(msg);
6359
6361 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6362 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6363 "AGENTLOGIN", "%s", agent_blob->snapshot->base->name);
6365 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6366 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6367 "AGENTLOGOFF", "%s|%ld", agent_blob->snapshot->base->name,
6368 (long) ast_json_integer_get(ast_json_object_get(agent_blob->blob, "logintime")));
6369 }
6370}
6371
6372/*!
6373 * \brief Structure representing relevant data during a local channel optimization
6374 *
6375 * The reason we care about local channel optimizations is that we want to be able
6376 * to accurately report when the caller and queue member have stopped talking to
6377 * each other. A local channel optimization can cause it to appear that the conversation
6378 * has stopped immediately after it has begun. By tracking that the relevant channels
6379 * to monitor have changed due to a local channel optimization, we can give accurate
6380 * reports.
6381 *
6382 * Local channel optimizations for queues are restricted from their normal operation.
6383 * Bridges created by queues can only be the destination of local channel optimizations,
6384 * not the source. In addition, move-swap local channel optimizations are the only
6385 * permitted types of local channel optimization.
6386 *
6387 * This data is populated when we are told that a local channel optimization begin
6388 * is occurring. When we get told the optimization has ended successfully, we then
6389 * apply the data here into the queue_stasis_data.
6390 */
6392 /*! The uniqueid of the channel that will be taking the place of the caller or member */
6394 /*! Indication of whether we think there is a local channel optimization in progress */
6396 /*! The identifier for this local channel optimization */
6397 unsigned int id;
6398};
6399
6400/*!
6401 * \brief User data for stasis subscriptions used for queue calls.
6402 *
6403 * app_queue subscribes to channel and bridge events for all bridged calls.
6404 * app_queue cares about the following events:
6405 *
6406 * \li bridge enter: To determine the unique ID of the bridge created for the call.
6407 * \li blind transfer: To send an appropriate agent complete event.
6408 * \li attended transfer: To send an appropriate agent complete event.
6409 * \li local optimization: To update caller and member unique IDs for the call.
6410 * \li hangup: To send an appropriate agent complete event.
6411 *
6412 * The stasis subscriptions last until we determine that the caller and the member
6413 * are no longer bridged with each other.
6414 */
6417 /*! The unique ID of the caller's channel. */
6419 /*! The unique ID of the queue member's channel */
6421 /*! The unique ID of the bridge created by the queue */
6424 /*! The relevant queue */
6426 /*! The queue member that has answered the call */
6428 /*! The time at which the caller entered the queue. Start of the caller's hold time */
6430 /*! The time at which the member answered the call. */
6432 /*! The original position of the caller when he entered the queue */
6434 /*! Indication if the call was answered within the configured service level of the queue */
6436 /*! Indicates if the stasis subscriptions are shutting down */
6438 /*! The stasis message router for bridge events */
6440 /*! The stasis message router for channel events */
6442 /*! Local channel optimization details for the caller */
6444 /*! Local channel optimization details for the member */
6446};
6447
6448/*!
6449 * \internal
6450 * \brief Free memory for a queue_stasis_data
6451 */
6452static void queue_stasis_data_destructor(void *obj)
6453{
6454 struct queue_stasis_data *queue_data = obj;
6455
6456 /* This can only happen if refcounts for this object have got severely messed up */
6457 ast_assert(queue_data->bridge_router == NULL);
6458 ast_assert(queue_data->channel_router == NULL);
6459
6460 ao2_cleanup(queue_data->member);
6461 queue_unref(queue_data->queue);
6462 ast_string_field_free_memory(queue_data);
6463}
6464
6465/*!
6466 * \internal
6467 * \brief End all stasis subscriptions on a queue_stasis_data
6468 */
6469static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
6470{
6471 SCOPED_AO2LOCK(lock, queue_data);
6472
6473 queue_data->dying = 1;
6475 queue_data->bridge_router = NULL;
6477 queue_data->channel_router = NULL;
6478}
6479
6480/*!
6481 * \internal
6482 * \brief Allocate a queue_stasis_data and initialize its data.
6483 */
6485 struct ast_channel *peer, struct member *mem, time_t holdstart,
6486 time_t starttime, int callcompletedinsl)
6487{
6488 struct queue_stasis_data *queue_data;
6489
6490 queue_data = ao2_alloc(sizeof(*queue_data), queue_stasis_data_destructor);
6491 if (!queue_data) {
6492 return NULL;
6493 }
6494
6495 if (ast_string_field_init(queue_data, 64)) {
6496 ao2_cleanup(queue_data);
6497 return NULL;
6498 }
6499
6502 queue_data->queue = queue_ref(qe->parent);
6503 queue_data->starttime = starttime;
6504 queue_data->holdstart = holdstart;
6506 queue_data->caller_pos = qe->opos;
6507 ao2_ref(mem, +1);
6508 queue_data->member = mem;
6509
6510 return queue_data;
6511}
6512
6513/*!
6514 * \internal
6515 * \brief Log an attended transfer in the queue log.
6516 *
6517 * Attended transfer queue log messages vary based on the method by which the
6518 * attended transfer was completed.
6519 *
6520 * \param queue_data Data pertaining to the particular call in the queue.
6521 * \param atxfer_msg The stasis attended transfer message data.
6522 */
6523static void log_attended_transfer(struct queue_stasis_data *queue_data,
6524 struct ast_attended_transfer_message *atxfer_msg)
6525{
6526 RAII_VAR(struct ast_str *, transfer_str, ast_str_create(32), ast_free);
6527
6528 if (!transfer_str) {
6529 ast_log(LOG_WARNING, "Unable to log attended transfer to queue log\n");
6530 return;
6531 }
6532
6533 switch (atxfer_msg->dest_type) {
6535 ast_str_set(&transfer_str, 0, "BRIDGE|%s", atxfer_msg->dest.bridge);
6536 break;
6539 ast_str_set(&transfer_str, 0, "APP|%s", atxfer_msg->dest.app);
6540 break;
6542 ast_str_set(&transfer_str, 0, "LINK|%s|%s", atxfer_msg->dest.links[0]->base->name,
6543 atxfer_msg->dest.links[1]->base->name);
6544 break;
6547 /* Threeways are headed off and should not be logged here */
6548 ast_assert(0);
6549 return;
6550 }
6551
6552 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername, "ATTENDEDTRANSFER", "%s|%ld|%ld|%d",
6553 ast_str_buffer(transfer_str),
6554 (long) (queue_data->starttime - queue_data->holdstart),
6555 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6556}
6557
6558/*!
6559 * \internal
6560 * \brief Handle a stasis bridge enter event.
6561 *
6562 * We track this particular event in order to learn what bridge
6563 * was created for the queue call.
6564 *
6565 * \param userdata Data pertaining to the particular call in the queue.
6566 * \param sub The stasis subscription on which the message occurred.
6567 * \param msg The stasis message for the bridge enter event
6568 */
6569static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub,
6570 struct stasis_message *msg)
6571{
6572 struct queue_stasis_data *queue_data = userdata;
6573 struct ast_bridge_blob *enter_blob = stasis_message_data(msg);
6574 SCOPED_AO2LOCK(lock, queue_data);
6575
6576 if (queue_data->dying) {
6577 return;
6578 }
6579
6580 if (!ast_strlen_zero(queue_data->bridge_uniqueid)) {
6581 return;
6582 }
6583
6584 if (!strcmp(enter_blob->channel->base->uniqueid, queue_data->caller_uniqueid)) {
6585 ast_string_field_set(queue_data, bridge_uniqueid,
6586 enter_blob->bridge->uniqueid);
6587 ast_debug(3, "Detected entry of caller channel %s into bridge %s\n",
6588 enter_blob->channel->base->name, queue_data->bridge_uniqueid);
6589 }
6590}
6591
6592/*!
6593 * \brief Handle a blind transfer event
6594 *
6595 * This event is important in order to be able to log the end of the
6596 * call to the queue log and to stasis.
6597 *
6598 * \param userdata Data pertaining to the particular call in the queue.
6599 * \param sub The stasis subscription on which the message occurred.
6600 * \param msg The stasis message for the blind transfer event
6601 */
6602static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub,
6603 struct stasis_message *msg)
6604{
6605 struct queue_stasis_data *queue_data = userdata;
6606 struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
6607 const char *exten;
6608 const char *context;
6609 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6610 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6611
6612 if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
6613 return;
6614 }
6615
6616 ao2_lock(queue_data);
6617
6618 if (queue_data->dying) {
6619 ao2_unlock(queue_data);
6620 return;
6621 }
6622
6623 if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
6624 strcmp(queue_data->bridge_uniqueid, transfer_msg->bridge->uniqueid)) {
6625 ao2_unlock(queue_data);
6626 return;
6627 }
6628
6629 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6630 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6631
6632 ao2_unlock(queue_data);
6633
6634 exten = transfer_msg->exten;
6635 context = transfer_msg->context;
6636
6637 ast_debug(3, "Detected blind transfer in queue %s\n", queue_data->queue->name);
6638 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6639 "BLINDTRANSFER", "%s|%s|%ld|%ld|%d",
6640 exten, context,
6641 (long) (queue_data->starttime - queue_data->holdstart),
6642 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6643
6644 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6645 queue_data->holdstart, queue_data->starttime, TRANSFER);
6646 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6647 queue_data->starttime);
6648 remove_stasis_subscriptions(queue_data);
6649}
6650
6651/*!
6652 * \brief Handle an attended transfer event
6653 *
6654 * This event is important in order to be able to log the end of the
6655 * call to the queue log and to stasis.
6656 *
6657 * \param userdata Data pertaining to the particular call in the queue.
6658 * \param sub The stasis subscription on which the message occurred.
6659 * \param msg The stasis message for the attended transfer event.
6660 */
6661static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub,
6662 struct stasis_message *msg)
6663{
6664 struct queue_stasis_data *queue_data = userdata;
6665 struct ast_attended_transfer_message *atxfer_msg = stasis_message_data(msg);
6666 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6667 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6668
6669 if (atxfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS ||
6671 return;
6672 }
6673
6674 ao2_lock(queue_data);
6675
6676 if (queue_data->dying) {
6677 ao2_unlock(queue_data);
6678 return;
6679 }
6680
6681 if (ast_strlen_zero(queue_data->bridge_uniqueid)) {
6682 ao2_unlock(queue_data);
6683 return;
6684 }
6685
6686 if ((!atxfer_msg->to_transferee.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6687 atxfer_msg->to_transferee.bridge_snapshot->uniqueid)) &&
6688 (!atxfer_msg->to_transfer_target.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6690 ao2_unlock(queue_data);
6691 return;
6692 }
6693
6694 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6695 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6696
6697 ao2_unlock(queue_data);
6698
6699 ast_debug(3, "Detected attended transfer in queue %s\n", queue_data->queue->name);
6700 log_attended_transfer(queue_data, atxfer_msg);
6701
6702 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6703 queue_data->holdstart, queue_data->starttime, TRANSFER);
6704 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6705 queue_data->starttime);
6706 remove_stasis_subscriptions(queue_data);
6707}
6708
6709/*!
6710 * \internal
6711 * \brief Callback for all stasis bridge events
6712 *
6713 * Based on the event and what bridge it is on, the task is farmed out to relevant
6714 * subroutines for further processing.
6715 */
6716static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub,
6717 struct stasis_message *msg)
6718{
6720 ao2_cleanup(userdata);
6721 }
6722}
6723
6724/*!
6725 * \internal
6726 * \brief Handler for the beginning of a local channel optimization
6727 *
6728 * This method gathers data relevant to the local channel optimization and stores
6729 * it to be used once the local optimization completes.
6730 *
6731 * \param userdata Data pertaining to the particular call in the queue.
6732 * \param sub The stasis subscription on which the message occurred.
6733 * \param msg The stasis message for the local optimization begin event
6734 */
6735static void handle_local_optimization_begin(void *userdata, struct stasis_subscription *sub,
6736 struct stasis_message *msg)
6737{
6738 struct queue_stasis_data *queue_data = userdata;
6739 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6740 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6741 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6742 struct ast_channel_snapshot *source = ast_multi_channel_blob_get_channel(optimization_blob, "source");
6743 struct local_optimization *optimization;
6744 unsigned int id;
6745 SCOPED_AO2LOCK(lock, queue_data);
6746
6747 if (!local_one || !local_two || !source) {
6748 ast_debug(1, "Local optimization begin missing channel snapshots:%s%s%s\n",
6749 !local_one ? " local_one," : "",
6750 !local_two ? " local_two," : "",
6751 !source ? " source," : "");
6752 return;
6753 }
6754
6755 if (queue_data->dying) {
6756 return;
6757 }
6758
6759 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6760 optimization = &queue_data->member_optimize;
6761 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6762 optimization = &queue_data->caller_optimize;
6763 } else {
6764 return;
6765 }
6766
6767 /* We only allow move-swap optimizations, so there had BETTER be a source */
6768 ast_assert(source != NULL);
6769
6770 optimization->source_chan_uniqueid = ast_strdup(source->base->uniqueid);
6771 if (!optimization->source_chan_uniqueid) {
6772 ast_log(LOG_ERROR, "Unable to track local channel optimization for channel %s. Expect further errors\n", local_one->base->name);
6773 return;
6774 }
6776
6777 optimization->id = id;
6778 optimization->in_progress = 1;
6779}
6780
6781/*!
6782 * \internal
6783 * \brief Handler for the end of a local channel optimization
6784 *
6785 * This method takes the data gathered during the local channel optimization begin
6786 * event and applies it to the queue stasis data appropriately. This generally involves
6787 * updating the caller or member unique ID with the channel that is taking the place of
6788 * the previous caller or member.
6789 *
6790 * \param userdata Data pertaining to the particular call in the queue.
6791 * \param sub The stasis subscription on which the message occurred.
6792 * \param msg The stasis message for the local optimization end event
6793 */
6794static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub,
6795 struct stasis_message *msg)
6796{
6797 struct queue_stasis_data *queue_data = userdata;
6798 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6799 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6800 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6801 struct local_optimization *optimization;
6802 int is_caller;
6803 unsigned int id;
6804 SCOPED_AO2LOCK(lock, queue_data);
6805
6806 if (queue_data->dying) {
6807 return;
6808 }
6809
6810 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6811 optimization = &queue_data->member_optimize;
6812 is_caller = 0;
6813 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6814 optimization = &queue_data->caller_optimize;
6815 is_caller = 1;
6816 } else {
6817 return;
6818 }
6819
6821
6822 if (!optimization->in_progress) {
6823 ast_log(LOG_WARNING, "Told of a local optimization end when we had no previous begin\n");
6824 return;
6825 }
6826
6827 if (id != optimization->id) {
6828 ast_log(LOG_WARNING, "Local optimization end event ID does not match begin (%u != %u)\n",
6829 id, optimization->id);
6830 return;
6831 }
6832
6833 if (is_caller) {
6834 ast_debug(3, "Local optimization: Changing queue caller uniqueid from %s to %s\n",
6835 queue_data->caller_uniqueid, optimization->source_chan_uniqueid);
6836 ast_string_field_set(queue_data, caller_uniqueid, optimization->source_chan_uniqueid);
6837 } else {
6838 ast_debug(3, "Local optimization: Changing queue member uniqueid from %s to %s\n",
6839 queue_data->member_uniqueid, optimization->source_chan_uniqueid);
6840 ast_string_field_set(queue_data, member_uniqueid, optimization->source_chan_uniqueid);
6841 }
6842
6843 optimization->in_progress = 0;
6844}
6845
6846/*!
6847 * \internal
6848 * \brief Handler for hangup stasis event
6849 *
6850 * This is how we determine that the caller or member has hung up and the call
6851 * has ended. An appropriate queue log and stasis message are raised in this
6852 * callback.
6853 *
6854 * \param userdata Data pertaining to the particular call in the queue.
6855 * \param sub The stasis subscription on which the message occurred.
6856 * \param msg The stasis message for the hangup event.
6857 */
6858static void handle_hangup(void *userdata, struct stasis_subscription *sub,
6859 struct stasis_message *msg)
6860{
6861 struct queue_stasis_data *queue_data = userdata;
6862 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6863 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6864 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6865 RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
6866 enum agent_complete_reason reason;
6867
6868 ao2_lock(queue_data);
6869
6870 if (queue_data->dying) {
6871 ao2_unlock(queue_data);
6872 return;
6873 }
6874
6875 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6876 reason = CALLER;
6877 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6878 reason = AGENT;
6879 } else {
6880 ao2_unlock(queue_data);
6881 return;
6882 }
6883
6884 chan = ast_channel_get_by_name(channel_blob->snapshot->base->name);
6885 if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
6886 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
6887 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
6888 /* Channel that is hanging up is doing it as part of a transfer.
6889 * We'll get a transfer event later
6890 */
6891 ao2_unlock(queue_data);
6892 return;
6893 }
6894
6895 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6896 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6897
6898 ao2_unlock(queue_data);
6899
6900 ast_debug(3, "Detected hangup of queue %s channel %s\n", reason == CALLER ? "caller" : "member",
6901 channel_blob->snapshot->base->name);
6902
6903 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6904 reason == CALLER ? "COMPLETECALLER" : "COMPLETEAGENT", "%ld|%ld|%d",
6905 (long) (queue_data->starttime - queue_data->holdstart),
6906 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6907
6908 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6909 queue_data->holdstart, queue_data->starttime, reason);
6910 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6911 queue_data->starttime);
6912 remove_stasis_subscriptions(queue_data);
6913}
6914
6915static void handle_masquerade(void *userdata, struct stasis_subscription *sub,
6916 struct stasis_message *msg)
6917{
6918 struct queue_stasis_data *queue_data = userdata;
6919 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6920 const char *new_channel_id;
6921
6922 new_channel_id = ast_json_string_get(ast_json_object_get(channel_blob->blob, "newchanneluniqueid"));
6923
6924 ao2_lock(queue_data);
6925
6926 if (queue_data->dying) {
6927 ao2_unlock(queue_data);
6928 return;
6929 }
6930
6931 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6932 ast_debug(1, "Replacing caller channel %s with %s due to masquerade\n", queue_data->caller_uniqueid, new_channel_id);
6933 ast_string_field_set(queue_data, caller_uniqueid, new_channel_id);
6934 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6935 ast_debug(1, "Replacing member channel %s with %s due to masquerade\n", queue_data->member_uniqueid, new_channel_id);
6936 ast_string_field_set(queue_data, member_uniqueid, new_channel_id);
6937 }
6938
6939 ao2_unlock(queue_data);
6940}
6941
6942/*!
6943 * \internal
6944 * \brief Callback for all stasis channel events
6945 *
6946 * Based on the event and the channels involved, the work is farmed out into
6947 * subroutines for further processing.
6948 */
6949static void queue_channel_cb(void *userdata, struct stasis_subscription *sub,
6950 struct stasis_message *msg)
6951{
6953 ao2_cleanup(userdata);
6954 }
6955}
6956
6957/*!
6958 * \internal
6959 * \brief Create stasis subscriptions for a particular call in the queue.
6960 *
6961 * These subscriptions are created once the call has been answered. The subscriptions
6962 * are put in place so that call progress may be tracked. Once the call can be determined
6963 * to have ended, then messages are logged to the queue log and stasis events are emitted.
6964 *
6965 * \param qe The queue entry representing the caller
6966 * \param peer The channel that has answered the call
6967 * \param mem The queue member that answered the call
6968 * \param holdstart The time at which the caller entered the queue
6969 * \param starttime The time at which the call was answered
6970 * \param callcompletedinsl Indicates if the call was answered within the configured service level of the queue.
6971 * \retval 0 Success
6972 * \retval non-zero Failure
6973 */
6974static int setup_stasis_subs(struct queue_ent *qe, struct ast_channel *peer, struct member *mem,
6975 time_t holdstart, time_t starttime, int callcompletedinsl)
6976{
6977 struct queue_stasis_data *queue_data = queue_stasis_data_alloc(qe, peer, mem, holdstart, starttime, callcompletedinsl);
6978
6979 if (!queue_data) {
6980 return -1;
6981 }
6982
6984 if (!queue_data->bridge_router) {
6985 ao2_ref(queue_data, -1);
6986 return -1;
6987 }
6988
6990 handle_bridge_enter, queue_data);
6992 handle_blind_transfer, queue_data);
6994 handle_attended_transfer, queue_data);
6996 queue_bridge_cb, queue_data);
6997
6999 if (!queue_data->channel_router) {
7000 /* Unsubscribing from the bridge router will remove the only ref of queue_data,
7001 * thus beginning the destruction process
7002 */
7004 queue_data->bridge_router = NULL;
7005 return -1;
7006 }
7007
7008 ao2_ref(queue_data, +1);
7012 handle_local_optimization_end, queue_data);
7014 handle_hangup, queue_data);
7016 handle_masquerade, queue_data);
7018 queue_channel_cb, queue_data);
7019
7020 return 0;
7021}
7022
7024 struct call_queue *q;
7026 struct timeval start_time;
7027};
7028
7029/*!
7030 * \internal
7031 * \brief Helper to set the standard Dial duration variables
7032 */
7033static void set_duration_var(struct ast_channel *chan, const char *var_base, int64_t duration)
7034{
7035 char buf[32];
7036 char full_var_name[128];
7037
7038 snprintf(buf, sizeof(buf), "%" PRId64, duration / 1000);
7039 pbx_builtin_setvar_helper(chan, var_base, buf);
7040
7041 snprintf(full_var_name, sizeof(full_var_name), "%s_MS", var_base);
7042 snprintf(buf, sizeof(buf), "%" PRId64, duration);
7043 pbx_builtin_setvar_helper(chan, full_var_name, buf);
7044}
7045
7046static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
7047{
7048 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
7049 ao2_ref(qeb, +1);
7050 qeb->chan = originator;
7051}
7052
7053static void end_bridge_callback(void *data)
7054{
7055 struct queue_end_bridge *qeb = data;
7056 struct call_queue *q = qeb->q;
7057 struct ast_channel *chan = qeb->chan;
7058 int64_t answered_time_ms;
7059
7060 if (ao2_ref(qeb, -1) == 1) {
7061 set_queue_variables(q, chan);
7062
7063 /* Match Dial() timing variables */
7064 ast_channel_lock(chan);
7066 answered_time_ms = ast_tvdiff_ms(ast_tvnow(), qeb->start_time);
7067 set_duration_var(chan, "ANSWEREDTIME", answered_time_ms);
7068 set_duration_var(chan, "DIALEDTIME", ast_channel_get_duration_ms(chan));
7070 ast_channel_unlock(chan);
7071
7072 /* This unrefs the reference we made in try_calling when we allocated qeb */
7073 queue_t_unref(q, "Expire bridge_config reference");
7074 }
7075}
7076
7077/*!
7078 * \internal
7079 * \brief Setup the after bridge goto location on the peer.
7080 * \since 12.0.0
7081 *
7082 * \param chan Calling channel for bridge.
7083 * \param peer Peer channel for bridge.
7084 * \param opts Dialing option flags.
7085 * \param opt_args Dialing option argument strings.
7086 */
7087static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
7088{
7089 const char *context;
7090 const char *extension;
7091 int priority;
7092
7093 if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
7094 ast_channel_lock(chan);
7098 ast_channel_unlock(chan);
7100 opt_args[OPT_ARG_CALLEE_GO_ON]);
7101 }
7102}
7103
7104static void escape_and_substitute(struct ast_channel *chan, const char *input,
7105 char *output, size_t size)
7106{
7107 const char *m = input;
7108 char escaped[size];
7109 char *p;
7110
7111 for (p = escaped; p < escaped + size - 1; p++, m++) {
7112 switch (*m) {
7113 case '^':
7114 if (*(m + 1) == '{') {
7115 *p = '$';
7116 }
7117 break;
7118 case ',':
7119 *p++ = '\\';
7120 /* Fall through */
7121 default:
7122 *p = *m;
7123 }
7124 if (*m == '\0')
7125 break;
7126 }
7127
7128 if (p == escaped + size) {
7129 escaped[size - 1] = '\0';
7130 }
7131
7132 pbx_substitute_variables_helper(chan, escaped, output, size - 1);
7133}
7134
7135static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
7136{
7137 char escaped_filename[256];
7138 char file_with_ext[sizeof(escaped_filename) + sizeof(qe->parent->monfmt)];
7139 char mixmonargs[1512];
7140 char escaped_monitor_exec[1024];
7141 const char *monitor_options;
7142 const char *monitor_exec;
7143
7144 escaped_monitor_exec[0] = '\0';
7145
7146 if (filename) {
7147 escape_and_substitute(qe->chan, filename, escaped_filename, sizeof(escaped_filename));
7148 } else {
7149 ast_copy_string(escaped_filename, ast_channel_uniqueid(qe->chan), sizeof(escaped_filename));
7150 }
7151
7153 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
7154 monitor_exec = ast_strdupa(monitor_exec);
7155 }
7156 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
7157 monitor_options = ast_strdupa(monitor_options);
7158 } else {
7159 monitor_options = "";
7160 }
7162
7163 if (monitor_exec) {
7164 escape_and_substitute(qe->chan, monitor_exec, escaped_monitor_exec, sizeof(escaped_monitor_exec));
7165 }
7166
7167 snprintf(file_with_ext, sizeof(file_with_ext), "%s.%s", escaped_filename, qe->parent->monfmt);
7168
7169 if (!ast_strlen_zero(escaped_monitor_exec)) {
7170 snprintf(mixmonargs, sizeof(mixmonargs), "b%s,%s", monitor_options, escaped_monitor_exec);
7171 } else {
7172 snprintf(mixmonargs, sizeof(mixmonargs), "b%s", monitor_options);
7173 }
7174
7175 ast_debug(1, "Arguments being passed to MixMonitor: %s,%s\n", file_with_ext, mixmonargs);
7176
7177 if (ast_start_mixmonitor(qe->chan, file_with_ext, mixmonargs)) {
7178 ast_log(LOG_WARNING, "Unable to start mixmonitor. Is the MixMonitor app loaded?\n");
7179 }
7180}
7181
7182/*!
7183 * \internal
7184 * \brief A large function which calls members, updates statistics, and bridges the caller and a member
7185 *
7186 * Here is the process of this function
7187 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
7188 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member.
7189 * 3. Call ring_one to place a call to the appropriate member(s)
7190 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
7191 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
7192 * 6. Start the monitor or mixmonitor if the option is set
7193 * 7. Remove the caller from the queue to allow other callers to advance
7194 * 8. Bridge the call.
7195 * 9. Do any post processing after the call has disconnected.
7196 *
7197 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
7198 * \param[in] opts the options passed as the third parameter to the Queue() application
7199 * \param[in] opt_args the options passed as the third parameter to the Queue() application
7200 * \param[in] announceoverride filename to play to user when waiting
7201 * \param[in] url the url passed as the fourth parameter to the Queue() application
7202 * \param[in,out] tries the number of times we have tried calling queue members
7203 * \param[out] noption set if the call to Queue() has the 'n' option set.
7204 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
7205 * \param[in] gosub the gosub passed as the sixth parameter to the Queue() application
7206 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
7207 */
7208static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *gosub, int ringing)
7209{
7210 struct member *cur;
7211 struct callattempt *outgoing = NULL; /* the list of calls we are building */
7212 int to, orig;
7213 char oldexten[AST_MAX_EXTENSION]="";
7214 char oldcontext[AST_MAX_CONTEXT]="";
7215 char queuename[256]="";
7216 struct ast_channel *peer;
7217 struct callattempt *lpeer;
7218 struct member *member;
7219 struct ast_app *application;
7220 int res = 0, bridge = 0;
7221 int numbusies = 0;
7222 int x=0;
7223 char *announce = NULL;
7224 char digit = 0;
7225 time_t now = time(NULL);
7226 struct ast_bridge_config bridge_config;
7227 char nondataquality = 1;
7228 char *agiexec = NULL;
7229 char *gosubexec = NULL;
7230 const char *monitorfilename;
7231 int forwardsallowed = 1;
7232 int block_connected_line = 0;
7233 struct ao2_iterator memi;
7235 int callcompletedinsl;
7236 time_t starttime;
7237
7238 memset(&bridge_config, 0, sizeof(bridge_config));
7239 time(&now);
7240
7241 /* If we've already exceeded our timeout, then just stop
7242 * This should be extremely rare. queue_exec will take care
7243 * of removing the caller and reporting the timeout as the reason.
7244 */
7245 if (qe->expire && now >= qe->expire) {
7246 res = 0;
7247 goto out;
7248 }
7249
7250 if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) {
7252 }
7253 if (ast_test_flag(&opts, OPT_CALLER_TRANSFER)) {
7255 }
7256 if (ast_test_flag(&opts, OPT_CALLEE_AUTOMON)) {
7258 }
7259 if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
7261 }
7262 if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
7263 nondataquality = 0;
7264 }
7265 if (ast_test_flag(&opts, OPT_CALLEE_HANGUP)) {
7267 }
7268 if (ast_test_flag(&opts, OPT_CALLER_HANGUP)) {
7270 }
7271 if (ast_test_flag(&opts, OPT_CALLEE_PARK)) {
7273 }
7274 if (ast_test_flag(&opts, OPT_CALLER_PARK)) {
7276 }
7277 if (ast_test_flag(&opts, OPT_NO_RETRY)) {
7280 (*tries)++;
7281 } else {
7282 *tries = ao2_container_count(qe->parent->members);
7283 }
7284 *noption = 1;
7285 }
7286 if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
7287 forwardsallowed = 0;
7288 }
7290 block_connected_line = 1;
7291 }
7294 }
7297 }
7298 if (ast_test_flag(&opts, OPT_MARK_AS_ANSWERED)) {
7300 }
7301
7302 /* if the calling channel has AST_CAUSE_ANSWERED_ELSEWHERE set, make sure this is inherited.
7303 (this is mainly to support unreal/local channels)
7304 */
7307 }
7308
7309 ao2_lock(qe->parent);
7310 ast_debug(1, "%s is trying to call a queue member.\n",
7311 ast_channel_name(qe->chan));
7312 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
7313 if (!ast_strlen_zero(qe->announce)) {
7314 announce = qe->announce;
7315 }
7316 if (!ast_strlen_zero(announceoverride)) {
7317 announce = announceoverride;
7318 }
7319
7320 memi = ao2_iterator_init(qe->parent->members, 0);
7321 while ((cur = ao2_iterator_next(&memi))) {
7322 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
7323 if (!tmp) {
7324 ao2_ref(cur, -1);
7325 ao2_iterator_destroy(&memi);
7326 ao2_unlock(qe->parent);
7327 goto out;
7328 }
7329
7330 /*
7331 * Seed the callattempt's connected line information with previously
7332 * acquired connected line info from the queued channel. The
7333 * previously acquired connected line info could have been set
7334 * through the CONNECTED_LINE dialplan function.
7335 */
7339
7340 tmp->block_connected_update = block_connected_line;
7341 tmp->stillgoing = 1;
7342 tmp->member = cur; /* Place the reference for cur into callattempt. */
7343 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
7344 /* Calculate the metric for the appropriate strategy. */
7345 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
7346 /* Put them in the list of outgoing thingies... We're ready now.
7347 XXX If we're forcibly removed, these outgoing calls won't get
7348 hung up XXX */
7349 tmp->q_next = outgoing;
7350 outgoing = tmp;
7351 } else {
7352 callattempt_free(tmp);
7353 }
7354 }
7355 ao2_iterator_destroy(&memi);
7356
7358 /* Application arguments have higher timeout priority (behaviour for <=1.6) */
7359 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) {
7360 to = (qe->expire - now) * 1000;
7361 } else {
7362 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
7363 }
7364 } else {
7365 /* Config timeout is higher priority thatn application timeout */
7366 if (qe->expire && qe->expire<=now) {
7367 to = 0;
7368 } else if (qe->parent->timeout) {
7369 to = qe->parent->timeout * 1000;
7370 } else {
7371 to = -1;
7372 }
7373 }
7374 orig = to;
7375 ++qe->pending;
7376 ao2_unlock(qe->parent);
7377 /* Call the queue members with the best metric now. */
7378 ring_one(qe, outgoing, &numbusies);
7379 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
7381 forwardsallowed);
7382
7383 ao2_lock(qe->parent);
7386
7387 }
7390 }
7391 ao2_unlock(qe->parent);
7392 peer = lpeer ? lpeer->chan : NULL;
7393 if (!peer) {
7394 qe->pending = 0;
7395 if (to) {
7396 /* Must gotten hung up */
7397 res = -1;
7398 } else {
7399 /* User exited by pressing a digit */
7400 res = digit;
7401 }
7402 if (res == -1) {
7403 ast_debug(1, "%s: Nobody answered.\n", ast_channel_name(qe->chan));
7404 }
7405 } else { /* peer is valid */
7406 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
7407 RAII_VAR(struct ast_str *, interfacevar, ast_str_create(325), ast_free);
7408 /* Ah ha! Someone answered within the desired timeframe. Of course after this
7409 we will always return with -1 so that it is hung up properly after the
7410 conversation. */
7411 if (!strcmp(ast_channel_tech(qe->chan)->type, "DAHDI")) {
7412 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7413 }
7414 if (!strcmp(ast_channel_tech(peer)->type, "DAHDI")) {
7415 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7416 }
7417 /* Update parameters for the queue */
7418 time(&now);
7419 recalc_holdtime(qe, (now - qe->start));
7420 member = lpeer->member;
7421 ao2_lock(qe->parent);
7422 callcompletedinsl = member->callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
7423 ao2_unlock(qe->parent);
7424 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
7425 ao2_ref(member, 1);
7427 outgoing = NULL;
7428 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
7429 int res2;
7430
7431 res2 = ast_autoservice_start(qe->chan);
7432 if (!res2) {
7433 if (qe->parent->memberdelay) {
7434 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
7435 res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
7436 }
7437 if (!res2 && announce) {
7438 char *front;
7439 char *announcefiles = ast_strdupa(announce);
7440 while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
7441 if (play_file(peer, front) < 0) {
7442 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
7443 }
7444 }
7445 }
7446 if (!res2 && qe->parent->reportholdtime) {
7447 if (!play_file(peer, qe->parent->sound_reporthold)) {
7448 long holdtime, holdtimesecs;
7449
7450 time(&now);
7451 holdtime = labs((now - qe->start) / 60);
7452 holdtimesecs = labs((now - qe->start) % 60);
7453 if (holdtime > 0) {
7454 ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7455 if (play_file(peer, qe->parent->sound_minutes) < 0) {
7456 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, ast_channel_name(peer));
7457 }
7458 }
7459 if (holdtimesecs > 1) {
7460 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7461 if (play_file(peer, qe->parent->sound_seconds) < 0) {
7462 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, ast_channel_name(peer));
7463 }
7464 }
7465 }
7466 }
7468 }
7469 if (ast_check_hangup(peer)) {
7470 /* Agent must have hung up */
7471 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", ast_channel_name(peer));
7472 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "AGENTDUMP", "%s", "");
7473
7474 blob = ast_json_pack("{s: s, s: s, s: s}",
7475 "Queue", queuename,
7476 "Interface", member->interface,
7477 "MemberName", member->membername);
7478 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_dump_type(), blob);
7479
7483 ao2_ref(member, -1);
7484 goto out;
7485 } else if (ast_check_hangup(qe->chan)) {
7486 /* Caller must have hung up just before being connected */
7487 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
7488 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
7489 record_abandoned(qe);
7490 qe->handled = -1;
7494 ao2_ref(member, -1);
7495 return -1;
7496 }
7497 }
7498 /* Stop music on hold */
7499 if (ringing) {
7500 ast_indicate(qe->chan,-1);
7501 } else {
7502 ast_moh_stop(qe->chan);
7503 }
7504
7505 /* Make sure channels are compatible */
7506 res = ast_channel_make_compatible(qe->chan, peer);
7507 if (res < 0) {
7508 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "SYSCOMPAT", "%s", "");
7509 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(qe->chan), ast_channel_name(peer));
7510 record_abandoned(qe);
7514 ao2_ref(member, -1);
7515 return -1;
7516 }
7517
7518 /* Play announcement to the caller telling it's his turn if defined */
7520 if (play_file(qe->chan, qe->parent->sound_callerannounce)) {
7521 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
7522 }
7523 }
7524
7525 ao2_lock(qe->parent);
7526 /* if setinterfacevar is defined, make member variables available to the channel */
7527 /* use pbx_builtin_setvar to set a load of variables with one call */
7528 if (qe->parent->setinterfacevar && interfacevar) {
7529 ast_str_set(&interfacevar, 0, "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
7532 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7533 }
7534
7535 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
7536 /* use pbx_builtin_setvar to set a load of variables with one call */
7537 if (qe->parent->setqueueentryvar && interfacevar) {
7538 ast_str_set(&interfacevar, 0, "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
7539 (long) (time(NULL) - qe->start), qe->opos);
7541 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7542 }
7543
7544 ao2_unlock(qe->parent);
7545
7546 /* try to set queue variables if configured to do so*/
7548 set_queue_variables(qe->parent, peer);
7549
7550 setup_peer_after_bridge_goto(qe->chan, peer, &opts, opt_args);
7552 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
7553 monitorfilename = ast_strdupa(monitorfilename);
7554 }
7556
7557 /* Begin Monitoring */
7558 if (*qe->parent->monfmt) {
7559 setup_mixmonitor(qe, monitorfilename);
7560 }
7561 /* Drop out of the queue at this point, to prepare for next caller */
7562 leave_queue(qe);
7564 ast_debug(1, "app_queue: sendurl=%s.\n", url);
7565 ast_channel_sendurl(peer, url);
7566 }
7567
7568 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
7569 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
7570 if (!ast_strlen_zero(gosub)) {
7571 gosubexec = ast_strdupa(gosub);
7572 } else {
7573 if (qe->parent->membergosub) {
7574 gosubexec = ast_strdupa(qe->parent->membergosub);
7575 }
7576 }
7577
7578 if (!ast_strlen_zero(gosubexec)) {
7579 char *gosub_args = NULL;
7580 char *gosub_argstart;
7581
7582 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
7583
7584 gosub_argstart = strchr(gosubexec, ',');
7585 if (gosub_argstart) {
7586 const char *what_is_s = "s";
7587 *gosub_argstart = 0;
7588 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
7589 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7590 what_is_s = "~~s~~";
7591 }
7592 if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
7593 gosub_args = NULL;
7594 }
7595 *gosub_argstart = ',';
7596 } else {
7597 const char *what_is_s = "s";
7598 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
7599 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7600 what_is_s = "~~s~~";
7601 }
7602 if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
7603 gosub_args = NULL;
7604 }
7605 }
7606 if (gosub_args) {
7607 ast_app_exec_sub(qe->chan, peer, gosub_args, 0);
7608 ast_free(gosub_args);
7609 } else {
7610 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
7611 }
7612 }
7613
7614 if (!ast_strlen_zero(agi)) {
7615 ast_debug(1, "app_queue: agi=%s.\n", agi);
7616 application = pbx_findapp("agi");
7617 if (application) {
7618 agiexec = ast_strdupa(agi);
7619 pbx_exec(qe->chan, application, agiexec);
7620 } else {
7621 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
7622 }
7623 }
7624 qe->handled++;
7625
7626 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer),
7627 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
7628 /* Queue hold time until agent answered */
7629 set_duration_var(qe->chan, "QUEUEWAIT", (int64_t)(time(NULL) - qe->start) * 1000);
7630
7631 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I}",
7632 "Queue", queuename,
7633 "Interface", member->interface,
7634 "MemberName", member->membername,
7635 "HoldTime", (ast_json_int_t)(time(NULL) - qe->start),
7636 "RingTime", (ast_json_int_t)(orig - to > 0 ? (orig - to) / 1000 : 0));
7637 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_connect_type(), blob);
7638
7639 ast_copy_string(oldcontext, ast_channel_context(qe->chan), sizeof(oldcontext));
7640 ast_copy_string(oldexten, ast_channel_exten(qe->chan), sizeof(oldexten));
7641
7642 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
7643 queue_end_bridge->q = qe->parent;
7644 queue_end_bridge->chan = qe->chan;
7649 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
7650 * to make sure to increase the refcount of this queue so it cannot be freed until we
7651 * are done with it. We remove this reference in end_bridge_callback.
7652 */
7653 queue_t_ref(qe->parent, "For bridge_config reference");
7654 }
7655
7656 ao2_lock(qe->parent);
7657 time(&member->starttime);
7658 starttime = member->starttime;
7659 ao2_unlock(qe->parent);
7660 /* As a queue member may end up in multiple calls at once if a transfer occurs with
7661 * a Local channel in the mix we pass the current call information (starttime) to the
7662 * Stasis subscriptions so when they update the queue member data it becomes a noop
7663 * if this call is no longer between the caller and the queue member.
7664 */
7665 setup_stasis_subs(qe, peer, member, qe->start, starttime, callcompletedinsl);
7666 bridge = ast_bridge_call_with_flags(qe->chan, peer, &bridge_config,
7668
7669 res = bridge ? bridge : 1;
7670 ao2_ref(member, -1);
7671 }
7672out:
7674
7675 return res;
7676}
7677
7678static int wait_a_bit(struct queue_ent *qe)
7679{
7680 /* Don't need to hold the lock while we setup the outgoing calls */
7681 int retrywait = qe->parent->retry * 1000;
7682
7683 int res = ast_waitfordigit(qe->chan, retrywait);
7684 if (res > 0 && !valid_exit(qe, res)) {
7685 res = 0;
7686 }
7687
7688 return res;
7689}
7690
7691static struct member *interface_exists(struct call_queue *q, const char *interface)
7692{
7693 struct member *mem;
7694 struct ao2_iterator mem_iter;
7695
7696 if (!q) {
7697 return NULL;
7698 }
7699 mem_iter = ao2_iterator_init(q->members, 0);
7700 while ((mem = ao2_iterator_next(&mem_iter))) {
7701 if (!strcasecmp(interface, mem->interface)) {
7702 ao2_iterator_destroy(&mem_iter);
7703 return mem;
7704 }
7705 ao2_ref(mem, -1);
7706 }
7707 ao2_iterator_destroy(&mem_iter);
7708
7709 return NULL;
7710}
7711
7712
7713/*! \brief Dump all members in a specific queue to the database
7714 * \code
7715 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
7716 * \endcode
7717 */
7718static void dump_queue_members(struct call_queue *pm_queue)
7719{
7720 struct member *cur_member;
7721 struct ast_str *value;
7722 struct ao2_iterator mem_iter;
7723
7724 if (!pm_queue) {
7725 return;
7726 }
7727
7728 /* 4K is a reasonable default for most applications, but we grow to
7729 * accommodate more if necessary. */
7730 if (!(value = ast_str_create(4096))) {
7731 return;
7732 }
7733
7734 mem_iter = ao2_iterator_init(pm_queue->members, 0);
7735 while ((cur_member = ao2_iterator_next(&mem_iter))) {
7736 if (!cur_member->dynamic) {
7737 ao2_ref(cur_member, -1);
7738 continue;
7739 }
7740
7741 ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s;%s;%d",
7742 ast_str_strlen(value) ? "|" : "",
7743 cur_member->interface,
7744 cur_member->penalty,
7745 cur_member->paused,
7746 cur_member->membername,
7747 cur_member->state_interface,
7748 cur_member->reason_paused,
7749 cur_member->wrapuptime);
7750
7751 ao2_ref(cur_member, -1);
7752 }
7753 ao2_iterator_destroy(&mem_iter);
7754
7755 if (ast_str_strlen(value) && !cur_member) {
7756 if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) {
7757 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
7758 }
7759 } else {
7760 /* Delete the entry if the queue is empty or there is an error */
7761 ast_db_del(pm_family, pm_queue->name);
7762 }
7763
7764 ast_free(value);
7765}
7766
7767/*! \brief Remove member from queue
7768 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7769 * \retval RES_NOSUCHQUEUE queue does not exist
7770 * \retval RES_OKAY removed member from queue
7771 * \retval RES_EXISTS queue exists but no members
7772*/
7773static int remove_from_queue(const char *queuename, const char *interface)
7774{
7775 struct call_queue *q, tmpq = {
7776 .name = queuename,
7777 };
7778 struct member *mem, tmpmem;
7779 int res = RES_NOSUCHQUEUE;
7780
7781 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
7782 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
7783 ao2_lock(q);
7784 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
7785 /* XXX future changes should beware of this assumption!! */
7786 /*Change Penalty on realtime users*/
7788 update_realtime_member_field(mem, q->name, "penalty", "-1");
7789 } else if (!mem->dynamic) {
7790 ao2_ref(mem, -1);
7791 ao2_unlock(q);
7792 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
7793 return RES_NOT_DYNAMIC;
7794 }
7795 queue_publish_member_blob(queue_member_removed_type(), queue_member_blob_create(q, mem));
7796
7798 ao2_ref(mem, -1);
7799
7802 }
7803
7804 if (!num_available_members(q)) {
7806 }
7807
7808 res = RES_OKAY;
7809 } else {
7810 res = RES_EXISTS;
7811 }
7812 ao2_unlock(q);
7813 queue_t_unref(q, "Expiring temporary reference");
7814 }
7815
7816 return res;
7817}
7818
7819/*! \brief Add member to queue
7820 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7821 * \retval RES_NOSUCHQUEUE queue does not exist
7822 * \retval RES_OKAY added member from queue
7823 * \retval RES_EXISTS queue exists but no members
7824 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
7825*/
7826static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface, const char *reason_paused, int wrapuptime)
7827{
7828 struct call_queue *q;
7829 struct member *new_member, *old_member;
7830 int res = RES_NOSUCHQUEUE;
7831
7832 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7833 * short-circuits if the queue is already in memory. */
7834 if (!(q = find_load_queue_rt_friendly(queuename))) {
7835 return res;
7836 }
7837
7838 ao2_lock(q);
7839 if ((old_member = interface_exists(q, interface)) == NULL) {
7841 new_member->dynamic = 1;
7842 if (reason_paused) {
7843 ast_copy_string(new_member->reason_paused, reason_paused, sizeof(new_member->reason_paused));
7844 }
7845 member_add_to_queue(q, new_member);
7846 queue_publish_member_blob(queue_member_added_type(), queue_member_blob_create(q, new_member));
7847
7848 if (is_member_available(q, new_member)) {
7850 }
7851
7852 ao2_ref(new_member, -1);
7853 new_member = NULL;
7854
7855 if (dump) {
7857 }
7858
7859 res = RES_OKAY;
7860 } else {
7861 res = RES_OUTOFMEMORY;
7862 }
7863 } else {
7864 ao2_ref(old_member, -1);
7865 res = RES_EXISTS;
7866 }
7867 ao2_unlock(q);
7868 queue_t_unref(q, "Expiring temporary reference");
7869
7870 return res;
7871}
7872
7873
7874/*! \brief Change priority caller into a queue
7875 * \retval RES_NOSUCHQUEUE queue does not exist
7876 * \retval RES_OKAY change priority
7877 * \retval RES_NOT_CALLER queue exists but no caller
7878*/
7879static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
7880{
7881 struct call_queue *q;
7882 struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
7883 int res = RES_NOSUCHQUEUE;
7884
7885 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7886 * short-circuits if the queue is already in memory. */
7888 return res;
7889 }
7890
7891 ao2_lock(q);
7893 for (current = q->head; current; current = current->next) {
7894 if (strcmp(ast_channel_name(current->chan), caller) == 0) {
7895 ast_debug(1, "%s Caller new priority %d in queue %s\n",
7896 caller, priority, queuename);
7897 current->prio = priority;
7898 if (immediate) {
7899 /* This caller is being immediately moved in the queue so remove them */
7900 if (prev) {
7901 prev->next = current->next;
7902 } else {
7903 q->head = current->next;
7904 }
7906 /* The position for all callers is not recalculated in here as it will
7907 * be updated when the moved caller is inserted back into the queue
7908 */
7909 }
7910 res = RES_OKAY;
7911 break;
7912 } else if (immediate) {
7913 prev = current;
7914 }
7915 }
7916
7917 if (caller_qe) {
7918 int inserted = 0, pos = 0;
7919
7920 /* If a caller queue entry exists, we are applying their priority immediately
7921 * and have to reinsert them at the correct position.
7922 */
7923 prev = NULL;
7924 current = q->head;
7925 while (current) {
7926 if (!inserted && (caller_qe->prio > current->prio)) {
7927 insert_entry(q, prev, caller_qe, &pos);
7928 inserted = 1;
7929 }
7930
7931 /* We always update the position as it may have changed */
7932 current->pos = ++pos;
7933
7934 /* Move to the next caller in the queue */
7935 prev = current;
7936 current = current->next;
7937 }
7938
7939 if (!inserted) {
7940 insert_entry(q, prev, caller_qe, &pos);
7941 }
7942 }
7943
7944 ao2_unlock(q);
7945 return res;
7946}
7947
7948
7949/*! \brief Request to withdraw a caller from a queue
7950 * \retval RES_NOSUCHQUEUE queue does not exist
7951 * \retval RES_OKAY withdraw request sent
7952 * \retval RES_NOT_CALLER queue exists but no caller
7953 * \retval RES_EXISTS a withdraw request was already sent for this caller (channel) and queue
7954*/
7955static int request_withdraw_caller_from_queue(const char *queuename, const char *caller, const char *withdraw_info)
7956{
7957 struct call_queue *q;
7958 struct queue_ent *qe;
7959 int res = RES_NOSUCHQUEUE;
7960
7961 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7962 * short-circuits if the queue is already in memory. */
7964 return res;
7965 }
7966
7967 ao2_lock(q);
7969 for (qe = q->head; qe; qe = qe->next) {
7970 if (!strcmp(ast_channel_name(qe->chan), caller)) {
7971 if (qe->withdraw) {
7972 ast_debug(1, "Ignoring duplicate withdraw request of caller %s from queue %s\n", caller, queuename);
7973 res = RES_EXISTS;
7974 } else {
7975 ast_debug(1, "Requested withdraw of caller %s from queue %s\n", caller, queuename);
7976 /* It is not possible to change the withdraw info by further withdraw requests for this caller (channel)
7977 in this queue, so we do not need to worry about a memory leak here. */
7978 if (withdraw_info) {
7979 qe->withdraw_info = ast_strdup(withdraw_info);
7980 }
7981 qe->withdraw = 1;
7982 res = RES_OKAY;
7983 }
7984 break;
7985 }
7986 }
7987 ao2_unlock(q);
7988 queue_unref(q);
7989
7990 return res;
7991}
7992
7993
7995{
7996 struct ast_json *json_blob = queue_member_blob_create(q, member);
7997
7998 if (!json_blob) {
7999 return -1;
8000 }
8001
8002 queue_publish_member_blob(queue_member_pause_type(), json_blob);
8003
8004 return 0;
8005}
8006
8007/*!
8008 * \internal
8009 * \brief Set the pause status of the specific queue member.
8010 *
8011 * \param q Which queue the member belongs.
8012 * \param mem Queue member being paused/unpaused.
8013 * \param reason Why is this happening (Can be NULL/empty for no reason given.)
8014 * \param paused Set to 1 if the member is being paused or 0 to unpause.
8015 *
8016 * \pre The q is locked on entry.
8017 */
8018static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
8019{
8020 if (mem->paused == paused) {
8021 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n",
8022 (paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
8023 if (log_unpause_on_reason_change && paused) {
8024 if (!ast_strings_equal(mem->reason_paused, reason)) {
8025 ast_queue_log(q->name, "NONE", mem->membername, "UNPAUSE", "%s", "Auto-Unpause");
8026 }
8027 }
8028 }
8029
8030 if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid)) {
8032 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "reason_paused", S_OR(reason, ""), "paused", paused ? "1" : "0", SENTINEL) < 0) {
8033 ast_log(LOG_WARNING, "Failed update of realtime queue member %s:%s %spause and reason '%s'\n",
8034 q->name, mem->interface, (paused ? "" : "un"), S_OR(reason, ""));
8035 }
8036 } else {
8037 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "paused", paused ? "1" : "0", SENTINEL) < 0) {
8038 ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
8039 (paused ? "" : "un"), q->name, mem->interface);
8040 }
8041 }
8042 }
8043
8044 mem->paused = paused;
8045 if (paused) {
8046 time(&mem->lastpause); /* update last pause field */
8047 }
8048 if (paused && !ast_strlen_zero(reason)) {
8049 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
8050 } else {
8051 /* We end up filling this in again later (temporarily) but we need it
8052 * empty for now so that the intervening code - specifically
8053 * dump_queue_members() - has the correct view of things. */
8054 mem->reason_paused[0] = '\0';
8055 }
8056
8058 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
8059
8062 }
8063
8064 if (is_member_available(q, mem)) {
8066 "Queue:%s_avail", q->name);
8067 } else if (!num_available_members(q)) {
8069 "Queue:%s_avail", q->name);
8070 }
8071
8072 if (!paused && !ast_strlen_zero(reason)) {
8073 /* Because we've been unpaused with a 'reason' we need to ensure that
8074 * that reason is emitted when the subsequent PauseQueueMember event
8075 * is raised. So temporarily set it on the member and clear it out
8076 * again right after. */
8077 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
8078 }
8079
8080 ast_queue_log(q->name, "NONE", mem->membername, paused ? "PAUSE" : "UNPAUSE",
8081 "%s", mem->reason_paused);
8082
8084
8085 if (!paused) {
8086 mem->reason_paused[0] = '\0';
8087 }
8088}
8089
8090static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
8091{
8092 int found = 0;
8093 struct call_queue *q;
8094 struct ao2_iterator queue_iter;
8095
8096 if (ast_check_realtime("queues")) {
8097 load_realtime_queues(queuename);
8098 }
8099
8100 queue_iter = ao2_iterator_init(queues, 0);
8101 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
8102 ao2_lock(q);
8103 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
8104 struct member *mem;
8105
8106 if ((mem = interface_exists(q, interface))) {
8107 /*
8108 * Before we do the PAUSE/UNPAUSE, log if this was a
8109 * PAUSEALL/UNPAUSEALL but only on the first found entry.
8110 */
8111 ++found;
8112 if (found == 1
8113 && ast_strlen_zero(queuename)) {
8114 /*
8115 * XXX In all other cases, we use the queue name,
8116 * but since this affects all queues, we cannot.
8117 */
8118 ast_queue_log("NONE", "NONE", mem->membername,
8119 (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", S_OR(reason, ""));
8120 }
8121
8122 set_queue_member_pause(q, mem, reason, paused);
8123 ao2_ref(mem, -1);
8124 }
8125
8126 if (!ast_strlen_zero(queuename)) {
8127 ao2_unlock(q);
8128 queue_t_unref(q, "Done with iterator");
8129 break;
8130 }
8131 }
8132
8133 ao2_unlock(q);
8134 queue_t_unref(q, "Done with iterator");
8135 }
8136 ao2_iterator_destroy(&queue_iter);
8137
8138 return found ? RESULT_SUCCESS : RESULT_FAILURE;
8139}
8140
8141/*!
8142 * \internal
8143 * \brief helper function for set_member_penalty - given a queue, sets all member penalties with the interface
8144 * \param[in] q queue which is having its member's penalty changed - must be unlocked prior to calling
8145 * \param[in] interface String of interface used to search for queue members being changed
8146 * \param[in] penalty Value penalty is being changed to for the member.
8147 * \retval 0 if the there is no member with interface belonging to q and no change is made
8148 * \retval 1 if the there is a member with interface belonging to q and changes are made
8149 */
8150static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
8151{
8152 struct member *mem;
8153 int foundinterface = 0;
8154
8155 ao2_lock(q);
8156 if ((mem = interface_exists(q, interface))) {
8157 foundinterface++;
8158 if (mem->realtime) {
8159 char rtpenalty[80];
8160
8161 sprintf(rtpenalty, "%i", penalty);
8162 update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
8163 }
8164
8165 mem->penalty = penalty;
8166
8167 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
8168 queue_publish_member_blob(queue_member_penalty_type(), queue_member_blob_create(q, mem));
8169 ao2_ref(mem, -1);
8170 }
8171 ao2_unlock(q);
8172
8173 return foundinterface;
8174}
8175
8176/*!
8177 * \internal
8178 * \brief Set the ringinuse value of the specific queue member.
8179 *
8180 * \param q Which queue the member belongs.
8181 * \param mem Queue member being set.
8182 * \param ringinuse Set to 1 if the member is called when inuse.
8183 *
8184 * \pre The q is locked on entry.
8185 */
8186static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
8187{
8188 if (mem->realtime) {
8190 ringinuse ? "1" : "0");
8191 }
8192
8193 mem->ringinuse = ringinuse;
8194
8195 ast_queue_log(q->name, "NONE", mem->interface, "RINGINUSE", "%d", ringinuse);
8196 queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
8197}
8198
8200{
8201 struct member *mem;
8202 int foundinterface = 0;
8203
8204 ao2_lock(q);
8205 if ((mem = interface_exists(q, interface))) {
8206 foundinterface++;
8208 ao2_ref(mem, -1);
8209 }
8210 ao2_unlock(q);
8211
8212 return foundinterface;
8213}
8214
8215static int set_member_value_help_members(struct call_queue *q, const char *interface, int property, int value)
8216{
8217 switch(property) {
8218 case MEMBER_PENALTY:
8220
8221 case MEMBER_RINGINUSE:
8223
8224 default:
8225 ast_log(LOG_ERROR, "Attempted to set invalid property\n");
8226 return 0;
8227 }
8228}
8229
8230/*!
8231 * \internal
8232 * \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues.
8233 * \param[in] queuename If specified, only act on a member if it belongs to this queue
8234 * \param[in] interface Interface of queue member(s) having priority set.
8235 * \param[in] property Which queue property is being set
8236 * \param[in] value Value penalty is being changed to for each member
8237 */
8238static int set_member_value(const char *queuename, const char *interface, int property, int value)
8239{
8240 int foundinterface = 0, foundqueue = 0;
8241 struct call_queue *q;
8242 struct ast_config *queue_config = NULL;
8243 struct ao2_iterator queue_iter;
8244
8245 /* property dependent restrictions on values should be checked in this switch */
8246 switch (property) {
8247 case MEMBER_PENALTY:
8248 if (value < 0 && !negative_penalty_invalid) {
8249 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", value);
8250 return RESULT_FAILURE;
8251 }
8252 }
8253
8254 if (ast_strlen_zero(queuename)) { /* This means we need to iterate through all the queues. */
8255 if (ast_check_realtime("queues")) {
8256 queue_config = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
8257 if (queue_config) {
8258 char *category = NULL;
8259 while ((category = ast_category_browse(queue_config, category))) {
8260 const char *name = ast_variable_retrieve(queue_config, category, "name");
8261 if (ast_strlen_zero(name)) {
8262 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
8263 continue;
8264 }
8265 if ((q = find_load_queue_rt_friendly(name))) {
8266 foundqueue++;
8267 foundinterface += set_member_value_help_members(q, interface, property, value);
8268 queue_unref(q);
8269 }
8270 }
8271
8272 ast_config_destroy(queue_config);
8273 }
8274 }
8275
8276 /* After hitting realtime queues, go back and get the regular ones. */
8277 queue_iter = ao2_iterator_init(queues, 0);
8278 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
8279 foundqueue++;
8280 foundinterface += set_member_value_help_members(q, interface, property, value);
8281 queue_unref(q);
8282 }
8283 ao2_iterator_destroy(&queue_iter);
8284 } else { /* We actually have a queuename, so we can just act on the single queue. */
8285 if ((q = find_load_queue_rt_friendly(queuename))) {
8286 foundqueue++;
8287 foundinterface += set_member_value_help_members(q, interface, property, value);
8288 queue_unref(q);
8289 }
8290 }
8291
8292 if (foundinterface) {
8293 return RESULT_SUCCESS;
8294 } else if (!foundqueue) {
8295 ast_log (LOG_ERROR, "Invalid queuename\n");
8296 } else {
8297 ast_log (LOG_ERROR, "Invalid interface\n");
8298 }
8299
8300 return RESULT_FAILURE;
8301}
8302
8303/*!
8304 * \brief Gets members penalty.
8305 * \return Return the members penalty or RESULT_FAILURE on error.
8306 */
8307static int get_member_penalty(char *queuename, char *interface)
8308{
8309 int foundqueue = 0, penalty;
8310 struct call_queue *q;
8311 struct member *mem;
8312
8313 if ((q = find_load_queue_rt_friendly(queuename))) {
8314 foundqueue = 1;
8315 ao2_lock(q);
8316 if ((mem = interface_exists(q, interface))) {
8317 penalty = mem->penalty;
8318 ao2_ref(mem, -1);
8319 ao2_unlock(q);
8320 queue_t_unref(q, "Search complete");
8321 return penalty;
8322 }
8323 ao2_unlock(q);
8324 queue_t_unref(q, "Search complete");
8325 }
8326
8327 /* some useful debugging */
8328 if (foundqueue) {
8329 ast_log (LOG_ERROR, "Invalid queuename\n");
8330 } else {
8331 ast_log (LOG_ERROR, "Invalid interface\n");
8332 }
8333
8334 return RESULT_FAILURE;
8335}
8336
8337/*! \brief Reload dynamic queue members persisted into the astdb */
8338static void reload_queue_members(void)
8339{
8340 char *cur_ptr;
8341 const char *queue_name;
8342 char *member;
8343 char *interface;
8344 char *membername = NULL;
8345 char *state_interface;
8346 char *penalty_tok;
8347 int penalty = 0;
8348 char *paused_tok;
8349 int paused = 0;
8350 char *wrapuptime_tok;
8351 int wrapuptime = 0;
8352 char *reason_paused;
8353 struct ast_db_entry *db_tree;
8354 struct ast_db_entry *entry;
8355 struct call_queue *cur_queue;
8356 char *queue_data;
8357
8358 /* Each key in 'pm_family' is the name of a queue */
8359 db_tree = ast_db_gettree(pm_family, NULL);
8360 for (entry = db_tree; entry; entry = entry->next) {
8361
8362 queue_name = entry->key + strlen(pm_family) + 2;
8363
8364 {
8365 struct call_queue tmpq = {
8366 .name = queue_name,
8367 };
8368 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
8369 }
8370
8371 if (!cur_queue) {
8372 cur_queue = find_load_queue_rt_friendly(queue_name);
8373 }
8374
8375 if (!cur_queue) {
8376 /* If the queue no longer exists, remove it from the
8377 * database */
8378 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
8379 ast_db_del(pm_family, queue_name);
8380 continue;
8381 }
8382
8383 if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
8384 queue_t_unref(cur_queue, "Expire reload reference");
8385 continue;
8386 }
8387
8388 cur_ptr = queue_data;
8389 while ((member = strsep(&cur_ptr, ",|"))) {
8390 if (ast_strlen_zero(member)) {
8391 continue;
8392 }
8393
8394 interface = strsep(&member, ";");
8395 penalty_tok = strsep(&member, ";");
8396 paused_tok = strsep(&member, ";");
8397 membername = strsep(&member, ";");
8398 state_interface = strsep(&member, ";");
8399 reason_paused = strsep(&member, ";");
8400 wrapuptime_tok = strsep(&member, ";");
8401
8402 if (!penalty_tok) {
8403 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
8404 break;
8405 }
8406 penalty = strtol(penalty_tok, NULL, 10);
8407 if (errno == ERANGE) {
8408 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
8409 break;
8410 }
8411
8412 if (!paused_tok) {
8413 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
8414 break;
8415 }
8416 paused = strtol(paused_tok, NULL, 10);
8417 if ((errno == ERANGE) || paused < 0 || paused > 1) {
8418 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
8419 break;
8420 }
8421
8422 if (!ast_strlen_zero(wrapuptime_tok)) {
8423 wrapuptime = strtol(wrapuptime_tok, NULL, 10);
8424 if (errno == ERANGE) {
8425 ast_log(LOG_WARNING, "Error converting wrapuptime: %s: Out of range.\n", wrapuptime_tok);
8426 break;
8427 }
8428 }
8429
8430 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d ReasonPause: %s Wrapuptime: %d\n",
8431 queue_name, interface, membername, penalty, paused, reason_paused, wrapuptime);
8432
8433 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface, reason_paused, wrapuptime) == RES_OUTOFMEMORY) {
8434 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
8435 break;
8436 }
8437 }
8438 queue_t_unref(cur_queue, "Expire reload reference");
8439 ast_free(queue_data);
8440 }
8441
8442 if (db_tree) {
8443 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
8444 ast_db_freetree(db_tree);
8445 }
8446}
8447
8448/*! \brief PauseQueueMember application */
8449static int pqm_exec(struct ast_channel *chan, const char *data)
8450{
8451 char *parse;
8453 AST_APP_ARG(queuename);
8454 AST_APP_ARG(interface);
8456 AST_APP_ARG(reason);
8457 );
8458
8459 if (ast_strlen_zero(data)) {
8460 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
8461 return -1;
8462 }
8463
8464 parse = ast_strdupa(data);
8465
8467
8468 if (ast_strlen_zero(args.interface)) {
8469 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
8470 return -1;
8471 }
8472
8473 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
8474 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
8475 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
8476 return 0;
8477 }
8478
8479 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
8480
8481 return 0;
8482}
8483
8484/*! \brief UnpauseQueueMember application */
8485static int upqm_exec(struct ast_channel *chan, const char *data)
8486{
8487 char *parse;
8489 AST_APP_ARG(queuename);
8490 AST_APP_ARG(interface);
8492 AST_APP_ARG(reason);
8493 );
8494
8495 if (ast_strlen_zero(data)) {
8496 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
8497 return -1;
8498 }
8499
8500 parse = ast_strdupa(data);
8501
8503
8504 if (ast_strlen_zero(args.interface)) {
8505 ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
8506 return -1;
8507 }
8508
8509 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
8510 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
8511 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
8512 return 0;
8513 }
8514
8515 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
8516
8517 return 0;
8518}
8519
8520/*! \brief RemoveQueueMember application */
8521static int rqm_exec(struct ast_channel *chan, const char *data)
8522{
8523 int res=-1;
8524 char *parse, *temppos = NULL;
8525 struct member *mem = NULL;
8526
8528 AST_APP_ARG(queuename);
8530 );
8531
8532
8533 if (ast_strlen_zero(data)) {
8534 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
8535 return -1;
8536 }
8537
8538 parse = ast_strdupa(data);
8539
8541
8542 if (ast_strlen_zero(args.interface)) {
8543 args.interface = ast_strdupa(ast_channel_name(chan));
8544 temppos = strrchr(args.interface, '-');
8545 if (temppos) {
8546 *temppos = '\0';
8547 }
8548 }
8549
8550 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
8551
8553 mem = find_member_by_queuename_and_interface(args.queuename, args.interface);
8554 }
8555
8556 switch (remove_from_queue(args.queuename, args.interface)) {
8557 case RES_OKAY:
8558 if (!mem || ast_strlen_zero(mem->membername)) {
8559 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "REMOVEMEMBER", "%s", "");
8560 } else {
8561 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), mem->membername, "REMOVEMEMBER", "%s", "");
8562 }
8563 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
8564 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
8565 res = 0;
8566 break;
8567 case RES_EXISTS:
8568 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
8569 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
8570 res = 0;
8571 break;
8572 case RES_NOSUCHQUEUE:
8573 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
8574 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
8575 res = 0;
8576 break;
8577 case RES_NOT_DYNAMIC:
8578 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
8579 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
8580 res = 0;
8581 break;
8582 }
8583
8584 if (mem) {
8585 ao2_ref(mem, -1);
8586 }
8587
8588 return res;
8589}
8590
8591/*! \brief AddQueueMember application */
8592static int aqm_exec(struct ast_channel *chan, const char *data)
8593{
8594 int res=-1;
8595 char *parse, *tmp, *temppos = NULL, *reason = NULL;
8597 AST_APP_ARG(queuename);
8604 );
8605 int penalty = 0;
8606 int paused = 0;
8607 int wrapuptime;
8608 struct ast_flags flags = { 0 };
8609
8610 if (ast_strlen_zero(data)) {
8611 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface][,wrapuptime]]]]])\n");
8612 return -1;
8613 }
8614
8615 parse = ast_strdupa(data);
8616
8618
8619 if (args.options) {
8620 char *opts[AQM_OPT_ARG_ARRAY_SIZE] = { NULL, };
8621 ast_app_parse_options(aqm_opts, &flags, opts, args.options);
8623 paused = 1;
8625 reason = ast_strdupa(opts[AQM_OPT_ARG_PAUSE_REASON]);
8626 }
8627 }
8628 }
8629
8630 if (ast_strlen_zero(args.interface)) {
8631 args.interface = ast_strdupa(ast_channel_name(chan));
8632 temppos = strrchr(args.interface, '-');
8633 if (temppos) {
8634 *temppos = '\0';
8635 }
8636 }
8637
8638 if (!ast_strlen_zero(args.penalty)) {
8639 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
8640 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
8641 penalty = 0;
8642 }
8643 }
8644
8645 if (!ast_strlen_zero(args.wrapuptime)) {
8646 tmp = args.wrapuptime;
8647 ast_strip(tmp);
8648 wrapuptime = atoi(tmp);
8649 if (wrapuptime < 0) {
8650 wrapuptime = 0;
8651 }
8652 } else {
8653 wrapuptime = 0;
8654 }
8655
8656 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, paused, queue_persistent_members, args.state_interface, reason, wrapuptime)) {
8657 case RES_OKAY:
8658 if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
8659 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
8660 } else {
8661 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
8662 }
8663 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
8664 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
8665 res = 0;
8666 break;
8667 case RES_EXISTS:
8668 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
8669 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
8670 res = 0;
8671 break;
8672 case RES_NOSUCHQUEUE:
8673 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
8674 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
8675 res = 0;
8676 break;
8677 case RES_OUTOFMEMORY:
8678 ast_log(LOG_ERROR, "Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
8679 break;
8680 }
8681
8682 return res;
8683}
8684
8685/*! \brief QueueLog application */
8686static int ql_exec(struct ast_channel *chan, const char *data)
8687{
8688 char *parse;
8689
8691 AST_APP_ARG(queuename);
8692 AST_APP_ARG(uniqueid);
8693 AST_APP_ARG(membername);
8695 AST_APP_ARG(params);
8696 );
8697
8698 if (ast_strlen_zero(data)) {
8699 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
8700 return -1;
8701 }
8702
8703 parse = ast_strdupa(data);
8704
8706
8707 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
8708 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
8709 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
8710 return -1;
8711 }
8712
8713 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
8714 "%s", args.params ? args.params : "");
8715
8716 return 0;
8717}
8718
8719/*! \brief Copy rule from global list into specified queue */
8720static void copy_rules(struct queue_ent *qe, const char *rulename)
8721{
8722 struct penalty_rule *pr_iter;
8723 struct rule_list *rl_iter;
8724 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
8726 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
8727 if (!strcasecmp(rl_iter->name, tmp)) {
8728 break;
8729 }
8730 }
8731 if (rl_iter) {
8732 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
8733 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
8734 if (!new_pr) {
8735 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
8736 break;
8737 }
8738 new_pr->time = pr_iter->time;
8739 new_pr->max_value = pr_iter->max_value;
8740 new_pr->min_value = pr_iter->min_value;
8741 new_pr->raise_value = pr_iter->raise_value;
8742 new_pr->max_relative = pr_iter->max_relative;
8743 new_pr->min_relative = pr_iter->min_relative;
8744 new_pr->raise_relative = pr_iter->raise_relative;
8745 new_pr->raise_respect_min = pr_iter->raise_respect_min;
8746 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
8747 }
8748 }
8750}
8751
8752/*!\brief The starting point for all queue calls
8753 *
8754 * The process involved here is to
8755 * 1. Parse the options specified in the call to Queue()
8756 * 2. Join the queue
8757 * 3. Wait in a loop until it is our turn to try calling a queue member
8758 * 4. Attempt to call a queue member
8759 * 5. If 4. did not result in a bridged call, then check for between
8760 * call options such as periodic announcements etc.
8761 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
8762 * exit the queue.
8763 */
8764static int queue_exec(struct ast_channel *chan, const char *data)
8765{
8766 int res=-1;
8767 int ringing=0;
8768 const char *user_priority;
8769 const char *max_penalty_str;
8770 const char *min_penalty_str;
8771 const char *raise_penalty_str;
8772 int prio;
8773 int qcontinue = 0;
8774 int max_penalty, min_penalty, raise_penalty;
8775 enum queue_result reason = QUEUE_UNKNOWN;
8776 /* whether to exit Queue application after the timeout hits */
8777 int tries = 0;
8778 int noption = 0;
8779 char *parse;
8780 int makeannouncement = 0;
8781 int position = 0;
8783 AST_APP_ARG(queuename);
8786 AST_APP_ARG(announceoverride);
8787 AST_APP_ARG(queuetimeoutstr);
8788 AST_APP_ARG(agi);
8789 AST_APP_ARG(gosub);
8791 AST_APP_ARG(position);
8792 );
8793 /* Our queue entry */
8794 struct queue_ent qe = { 0 };
8795 struct ast_flags opts = { 0, };
8796 char *opt_args[OPT_ARG_ARRAY_SIZE];
8797 int max_forwards;
8798 int cid_allow;
8799 /* Reset variables to avoid stale data */
8800 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", "");
8801 pbx_builtin_setvar_helper(chan, "ANSWEREDTIME_MS", "");
8802 pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
8803 pbx_builtin_setvar_helper(chan, "DIALEDTIME_MS", "");
8804 pbx_builtin_setvar_helper(chan, "QUEUEWAIT", "");
8805 pbx_builtin_setvar_helper(chan, "QUEUEWAIT_MS", "");
8806
8807 if (ast_strlen_zero(data)) {
8808 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,gosub[,rule[,position]]]]]]]]\n");
8809 return -1;
8810 }
8811
8812 ast_channel_lock(chan);
8814 ast_channel_unlock(chan);
8815
8816 if (max_forwards <= 0) {
8817 ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
8818 return -1;
8819 }
8820
8821 parse = ast_strdupa(data);
8823
8824 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, gosub: %s, rule: %s, position: %s\n",
8825 args.queuename,
8826 S_OR(args.options, ""),
8827 S_OR(args.url, ""),
8828 S_OR(args.announceoverride, ""),
8829 S_OR(args.queuetimeoutstr, ""),
8830 S_OR(args.agi, ""),
8831 S_OR(args.gosub, ""),
8832 S_OR(args.rule, ""),
8833 S_OR(args.position, ""));
8834
8835 if (!ast_strlen_zero(args.options)) {
8836 ast_app_parse_options(queue_exec_options, &opts, opt_args, args.options);
8837 }
8838
8839 /* Setup our queue entry */
8840 qe.start = time(NULL);
8841
8842 pbx_builtin_setvar_helper(chan, "ABANDONED", NULL);
8843
8844 /* set the expire time based on the supplied timeout; */
8845 if (!ast_strlen_zero(args.queuetimeoutstr)) {
8846 qe.expire = qe.start + atoi(args.queuetimeoutstr);
8847 } else {
8848 qe.expire = 0;
8849 }
8850
8851 /* Get the priority from the variable ${QUEUE_PRIO} */
8852 ast_channel_lock(chan);
8853 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
8854 if (user_priority) {
8855 if (sscanf(user_priority, "%30d", &prio) == 1) {
8856 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
8857 } else {
8858 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
8859 user_priority, ast_channel_name(chan));
8860 prio = 0;
8861 }
8862 } else {
8863 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
8864 prio = 0;
8865 }
8866
8867 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
8868
8869 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
8870 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
8871 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
8872 } else {
8873 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
8874 max_penalty_str, ast_channel_name(chan));
8875 max_penalty = INT_MAX;
8876 }
8877 } else {
8878 max_penalty = INT_MAX;
8879 }
8880
8881 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
8882 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
8883 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
8884 } else {
8885 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
8886 min_penalty_str, ast_channel_name(chan));
8887 min_penalty = INT_MAX;
8888 }
8889 } else {
8890 min_penalty = INT_MAX;
8891 }
8892
8893 if ((raise_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_RAISE_PENALTY"))) {
8894 if (*raise_penalty_str == 'r') {
8895 qe.raise_respect_min = 1;
8896 raise_penalty_str++;
8897 } else {
8898 qe.raise_respect_min = 0;
8899 }
8900 if (sscanf(raise_penalty_str, "%30d", &raise_penalty) == 1) {
8901 ast_debug(1, "%s: Got raise penalty %s%d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), qe.raise_respect_min ? "r" : "", raise_penalty);
8902 } else {
8903 ast_log(LOG_WARNING, "${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n",
8904 raise_penalty_str, ast_channel_name(chan));
8905 raise_penalty = INT_MAX;
8906 }
8907 } else {
8908 raise_penalty = INT_MAX;
8909 }
8910 ast_channel_unlock(chan);
8911
8912 if (ast_test_flag(&opts, OPT_RINGING)) {
8913 ringing = 1;
8914 }
8915
8916 if (ringing != 1 && ast_test_flag(&opts, OPT_RING_WHEN_RINGING)) {
8917 qe.ring_when_ringing = 1;
8918 }
8919
8920 if (ast_test_flag(&opts, OPT_GO_ON)) {
8921 qcontinue = 1;
8922 }
8923
8924 if (args.position) {
8925 position = atoi(args.position);
8926 if (position < 0) {
8927 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
8928 position = 0;
8929 }
8930 }
8931
8932 ast_debug(1, "queue: %s, expires: %ld, priority: %d\n",
8933 args.queuename, (long)qe.expire, prio);
8934
8935 qe.chan = chan;
8936 qe.prio = prio;
8937 qe.max_penalty = max_penalty;
8938 qe.min_penalty = min_penalty;
8939 qe.raise_penalty = raise_penalty;
8940 qe.last_pos_said = 0;
8941 qe.last_pos = 0;
8944 qe.valid_digits = 0;
8945 if (join_queue(args.queuename, &qe, &reason, position)) {
8946 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
8947 set_queue_result(chan, reason);
8948 return 0;
8949 }
8950 ast_assert(qe.parent != NULL);
8951
8952 if (qe.parent->periodicannouncestartdelay >= 0) {
8955 }
8956
8958
8959 if (log_caller_id_name) {
8960 char *escaped_cidname = NULL;
8961 /* Ensure caller ID name is valid and not NULL before processing */
8962 if (cid_allow && ast_channel_caller(chan)->id.name.valid && ast_channel_caller(chan)->id.name.str) {
8963 escaped_cidname = ast_strdupa(ast_channel_caller(chan)->id.name.str);
8964 /* Only iterate if '|' is found */
8965 if (strchr(escaped_cidname, '|')) {
8966 for (char *p = escaped_cidname; *p; p++) {
8967 if (*p == '|') {
8968 *p = '_';
8969 }
8970 }
8971 }
8972 }
8973
8974 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d|%s",
8975 S_OR(args.url, ""),
8976 S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8977 qe.opos,
8978 S_OR(escaped_cidname, ""));
8979 } else {
8980 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
8981 S_OR(args.url, ""),
8982 S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8983 qe.opos);
8984 }
8985
8986 /* PREDIAL: Preprocess any callee gosub arguments. */
8988 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
8991 }
8992
8993 /* PREDIAL: Run gosub on the caller's channel */
8995 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
8997 ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
8998 }
8999
9000 /* Music on hold class override */
9003 ast_copy_string(qe.moh, opt_args[OPT_ARG_MUSICONHOLD_CLASS], sizeof(qe.moh));
9004 }
9005
9006 copy_rules(&qe, args.rule);
9007 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
9008check_turns:
9009 if (ringing) {
9011 } else {
9012 ast_moh_start(chan, qe.moh, NULL);
9013 }
9014
9015 /* This is the wait loop for callers 2 through maxlen */
9016 res = wait_our_turn(&qe, ringing, &reason);
9017 if (res) {
9018 goto stop;
9019 }
9020
9021 makeannouncement = qe.parent->announce_to_first_user;
9022
9023 for (;;) {
9024 /* This is the wait loop for the head caller*/
9025 /* To exit, they may get their call answered; */
9026 /* they may dial a digit from the queue context; */
9027 /* or, they may timeout. */
9028
9029 /* A request to withdraw this call from the queue arrived */
9030 if (qe.withdraw) {
9031 reason = QUEUE_WITHDRAW;
9032 res = 1;
9033 break;
9034 }
9035
9036 /* Leave if we have exceeded our queuetimeout */
9037 if (qe.expire && (time(NULL) >= qe.expire)) {
9038 record_abandoned(&qe);
9039 reason = QUEUE_TIMEOUT;
9040 res = 0;
9041 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
9042 qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9043 break;
9044 }
9045
9046 if (makeannouncement) {
9047 /* Make a position announcement, if enabled */
9048 if (qe.parent->announcefrequency) {
9049 if ((res = say_position(&qe, ringing))) {
9050 goto stop;
9051 }
9052 }
9053
9054 /* Make a periodic announcement, if enabled */
9056 if ((res = say_periodic_announcement(&qe, ringing))) {
9057 goto stop;
9058 }
9059 }
9060 }
9061
9062 /* A request to withdraw this call from the queue arrived */
9063 if (qe.withdraw) {
9064 reason = QUEUE_WITHDRAW;
9065 res = 1;
9066 break;
9067 }
9068
9069 /* Leave if we have exceeded our queuetimeout */
9070 if (qe.expire && (time(NULL) >= qe.expire)) {
9071 record_abandoned(&qe);
9072 reason = QUEUE_TIMEOUT;
9073 res = 0;
9074 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
9075 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9076 break;
9077 }
9078
9079 /* see if we need to move to the next penalty level for this queue */
9080 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
9081 update_qe_rule(&qe);
9082 }
9083
9084 /* Try calling all queue members for 'timeout' seconds */
9085 res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.gosub, ringing);
9086 if (res) {
9087 goto stop;
9088 }
9089
9090 if (qe.parent->leavewhenempty) {
9091 int status = 0;
9093 record_abandoned(&qe);
9094 reason = QUEUE_LEAVEEMPTY;
9095 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
9096 res = 0;
9097 break;
9098 }
9099 }
9100
9101 /* exit after 'timeout' cycle if 'n' option enabled */
9102 if (noption && tries >= ao2_container_count(qe.parent->members)) {
9103 ast_verb(3, "Exiting on time-out cycle\n");
9104 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
9105 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9106 record_abandoned(&qe);
9107 reason = QUEUE_TIMEOUT;
9108 res = 0;
9109 break;
9110 }
9111
9112
9113 /* Leave if we have exceeded our queuetimeout */
9114 if (qe.expire && (time(NULL) >= qe.expire)) {
9115 record_abandoned(&qe);
9116 reason = QUEUE_TIMEOUT;
9117 res = 0;
9118 ast_queue_log(qe.parent->name, ast_channel_uniqueid(qe.chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9119 break;
9120 }
9121
9122 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
9123 res = wait_a_bit(&qe);
9124 if (res) {
9125 goto stop;
9126 }
9127
9128 /* If using dynamic realtime members, we should regenerate the member list for this queue */
9130
9131 /* Since this is a priority queue and
9132 * it is not sure that we are still at the head
9133 * of the queue, go and check for our turn again.
9134 */
9135 if (!is_our_turn(&qe)) {
9136 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.chan));
9137 goto check_turns;
9138 }
9139 }
9140
9141stop:
9142 if (qe.chan) {
9145 /* 1. Handle QUEUEWAIT (Total time spent waiting in queue) */
9146 if (ast_strlen_zero(pbx_builtin_getvar_helper(qe.chan, "QUEUEWAIT"))) {
9147 set_duration_var(qe.chan, "QUEUEWAIT", (int64_t)(time(NULL) - qe.start) * 1000);
9148 }
9149
9150 /* 2. Handle DIALEDTIME (Total time spent from beginning of the call) */
9151 if (ast_strlen_zero(pbx_builtin_getvar_helper(qe.chan, "DIALEDTIME"))) {
9153 }
9154
9155 /* 3. Handle ANSWEREDTIME (Time spent talking to an agent) */
9156 if (ast_strlen_zero(pbx_builtin_getvar_helper(qe.chan, "ANSWEREDTIME"))) {
9157 /* If we are here and it's still empty, the call was never answered */
9158 set_duration_var(qe.chan, "ANSWEREDTIME", 0);
9159 }
9162 }
9163 if (res) {
9164 if (reason == QUEUE_WITHDRAW) {
9165 record_abandoned(&qe);
9166 ast_queue_log(qe.parent->name, ast_channel_uniqueid(qe.chan), "NONE", "WITHDRAW", "%d|%d|%ld|%.40s", qe.pos, qe.opos, (long) (time(NULL) - qe.start), qe.withdraw_info ? qe.withdraw_info : "");
9167 if (qe.withdraw_info) {
9168 pbx_builtin_setvar_helper(qe.chan, "QUEUE_WITHDRAW_INFO", qe.withdraw_info);
9169 }
9170 res = 0;
9171 } else if (res < 0) {
9172 if (!qe.handled) {
9173 record_abandoned(&qe);
9174 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
9175 "%d|%d|%ld", qe.pos, qe.opos,
9176 (long) (time(NULL) - qe.start));
9177 res = -1;
9178 } else if (reason == QUEUE_LEAVEEMPTY) {
9179 /* Return back to dialplan, don't hang up */
9180 res = 0;
9181 } else if (qcontinue) {
9182 reason = QUEUE_CONTINUE;
9183 res = 0;
9184 }
9185 } else if (qe.valid_digits) {
9186 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
9187 "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9188 }
9189 }
9190
9191 /* Free the optional withdraw info if present */
9192 /* This is done here to catch all cases. e.g. if the call eventually wasn't withdrawn, e.g. answered */
9193 if (qe.withdraw_info) {
9195 qe.withdraw_info = NULL;
9196 }
9197
9198 /* Don't allow return code > 0 */
9199 if (res >= 0) {
9200 res = 0;
9201 if (ringing) {
9202 ast_indicate(chan, -1);
9203 } else {
9204 ast_moh_stop(chan);
9205 }
9206 ast_stopstream(chan);
9207 }
9208
9210
9211 leave_queue(&qe);
9212 if (reason != QUEUE_UNKNOWN)
9213 set_queue_result(chan, reason);
9214
9215 /*
9216 * every queue_ent is given a reference to it's parent
9217 * call_queue when it joins the queue. This ref must be taken
9218 * away right before the queue_ent is destroyed. In this case
9219 * the queue_ent is about to be returned on the stack
9220 */
9221 qe.parent = queue_unref(qe.parent);
9222
9223 return res;
9224}
9225
9226/*!
9227 * \brief create interface var with all queue details.
9228 * \retval 0 on success
9229 * \retval -1 on error
9230*/
9231static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9232{
9233 int res = -1;
9234 struct call_queue *q;
9235 char interfacevar[256] = "";
9236 float sl = 0;
9237
9238 if (ast_strlen_zero(data)) {
9239 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9240 return -1;
9241 }
9242
9243 if ((q = find_load_queue_rt_friendly(data))) {
9244 ao2_lock(q);
9245 if (q->setqueuevar) {
9246 sl = 0;
9247 res = 0;
9248
9249 if (q->callscompleted > 0) {
9250 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
9251 }
9252
9253 snprintf(interfacevar, sizeof(interfacevar),
9254 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
9256
9257 pbx_builtin_setvar_multiple(chan, interfacevar);
9258 }
9259
9260 ao2_unlock(q);
9261 queue_t_unref(q, "Done with QUEUE() function");
9262 } else {
9263 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9264 }
9265
9266 snprintf(buf, len, "%d", res);
9267
9268 return 0;
9269}
9270
9271/*!
9272 * \brief Check if a given queue exists
9273 *
9274 */
9275static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9276{
9277 struct call_queue *q;
9278
9279 buf[0] = '\0';
9280
9281 if (ast_strlen_zero(data)) {
9282 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9283 return -1;
9284 }
9286 snprintf(buf, len, "%d", q != NULL? 1 : 0);
9287 if (q) {
9288 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
9289 }
9290
9291 return 0;
9292}
9293
9294static struct member *get_interface_helper(struct call_queue *q, const char *interface)
9295{
9296 struct member *m;
9297
9299 ast_log(LOG_ERROR, "QUEUE_MEMBER: Missing required interface argument.\n");
9300 return NULL;
9301 }
9302
9304 if (!m) {
9305 ast_log(LOG_ERROR, "Queue member interface '%s' not in queue '%s'.\n",
9306 interface, q->name);
9307 }
9308 return m;
9309}
9310
9311/*!
9312 * \brief Get number either busy / free / ready or total members of a specific queue
9313 * \brief Get or set member properties penalty / paused / ringinuse
9314 * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ringinuse)
9315 * \retval -1 on error
9316 */
9317static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9318{
9319 int count = 0;
9320 struct member *m;
9321 struct ao2_iterator mem_iter;
9322 struct call_queue *q;
9323
9325 AST_APP_ARG(queuename);
9326 AST_APP_ARG(option);
9327 AST_APP_ARG(interface);
9328 );
9329 /* Make sure the returned value on error is zero length string. */
9330 buf[0] = '\0';
9331
9332 if (ast_strlen_zero(data)) {
9334 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9335 cmd);
9336 return -1;
9337 }
9338
9340
9341 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
9343 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9344 cmd);
9345 return -1;
9346 }
9347
9348 if ((q = find_load_queue_rt_friendly(args.queuename))) {
9349 ao2_lock(q);
9350 if (!strcasecmp(args.option, "logged")) {
9351 mem_iter = ao2_iterator_init(q->members, 0);
9352 while ((m = ao2_iterator_next(&mem_iter))) {
9353 /* Count the agents who are logged in and presently answering calls */
9354 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9355 count++;
9356 }
9357 ao2_ref(m, -1);
9358 }
9359 ao2_iterator_destroy(&mem_iter);
9360 } else if (!strcasecmp(args.option, "free")) {
9361 mem_iter = ao2_iterator_init(q->members, 0);
9362 while ((m = ao2_iterator_next(&mem_iter))) {
9363 /* Count the agents who are logged in and presently answering calls */
9364 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
9365 count++;
9366 }
9367 ao2_ref(m, -1);
9368 }
9369 ao2_iterator_destroy(&mem_iter);
9370 } else if (!strcasecmp(args.option, "ready")) {
9371 time_t now;
9372 time(&now);
9373 mem_iter = ao2_iterator_init(q->members, 0);
9374 while ((m = ao2_iterator_next(&mem_iter))) {
9375 /* Count the agents who are logged in, not paused and not wrapping up */
9376 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
9377 !(m->lastcall && get_wrapuptime(q, m) && ((now - get_wrapuptime(q, m)) < m->lastcall))) {
9378 count++;
9379 }
9380 ao2_ref(m, -1);
9381 }
9382 ao2_iterator_destroy(&mem_iter);
9383 } else if (!strcasecmp(args.option, "count")) {
9385 } else if (!strcasecmp(args.option, "penalty")) {
9386 m = get_interface_helper(q, args.interface);
9387 if (m) {
9388 count = m->penalty;
9389 ao2_ref(m, -1);
9390 }
9391 } else if (!strcasecmp(args.option, "paused")) {
9392 m = get_interface_helper(q, args.interface);
9393 if (m) {
9394 count = m->paused;
9395 ao2_ref(m, -1);
9396 }
9397 } else if ((!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9398 || !strcasecmp(args.option, "ringinuse"))) {
9399 m = get_interface_helper(q, args.interface);
9400 if (m) {
9401 count = m->ringinuse;
9402 ao2_ref(m, -1);
9403 }
9404 } else {
9405 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9406 }
9407 ao2_unlock(q);
9408 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
9409 } else {
9410 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9411 }
9412
9413 snprintf(buf, len, "%d", count);
9414
9415 return 0;
9416}
9417
9418/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse. */
9419static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9420{
9421 int memvalue;
9422
9424 AST_APP_ARG(queuename);
9425 AST_APP_ARG(option);
9426 AST_APP_ARG(interface);
9427 );
9428
9429 if (ast_strlen_zero(data)) {
9431 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9432 cmd);
9433 return -1;
9434 }
9435
9437
9438 if (ast_strlen_zero(args.option)
9439 || ast_strlen_zero(args.interface)) {
9441 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9442 cmd);
9443 return -1;
9444 }
9445
9446 /*
9447 * If queuename is empty then the option will be
9448 * set for the interface in all queues.
9449 */
9450
9451 memvalue = atoi(value);
9452 if (!strcasecmp(args.option, "penalty")) {
9453 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
9454 ast_log(LOG_ERROR, "Invalid interface, queue, or penalty\n");
9455 return -1;
9456 }
9457 } else if (!strcasecmp(args.option, "paused")) {
9458 memvalue = (memvalue <= 0) ? 0 : 1;
9459 if (set_member_paused(args.queuename, args.interface, NULL, memvalue)) {
9460 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9461 return -1;
9462 }
9463 } else if (!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9464 || !strcasecmp(args.option, "ringinuse")) {
9465 memvalue = (memvalue <= 0) ? 0 : 1;
9466 if (set_member_value(args.queuename, args.interface, MEMBER_RINGINUSE, memvalue)) {
9467 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9468 return -1;
9469 }
9470 } else {
9471 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9472 return -1;
9473 }
9474 return 0;
9475}
9476
9477/*! \brief Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue */
9478static int queue_function_queuegetchannel(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9479{
9480 int position;
9481 char *parse;
9482 struct call_queue *q;
9483 struct ast_variable *var;
9484
9486 AST_APP_ARG(queuename);
9487 AST_APP_ARG(position);
9488 );
9489
9490 buf[0] = '\0';
9491
9492 if (ast_strlen_zero(data)) {
9493 ast_log(LOG_ERROR, "Missing argument. QUEUE_GET_CHANNEL(<queuename>,<position>)\n");
9494 return -1;
9495 }
9496
9497 parse = ast_strdupa(data);
9499
9500 if (ast_strlen_zero(args.queuename)) {
9501 ast_log (LOG_ERROR, "The <queuename> parameter is required.\n");
9502 return -1;
9503 }
9504
9505 if (ast_strlen_zero(args.position)) {
9506 position = 1;
9507 } else {
9508 if (sscanf(args.position, "%30d", &position) != 1) {
9509 ast_log (LOG_ERROR, "<position> parameter must be an integer.\n");
9510 return -1;
9511 }
9512 if (position < 1) {
9513 ast_log (LOG_ERROR, "<position> parameter must be an integer greater than zero.\n");
9514 return -1;
9515 }
9516 }
9517
9518 {
9519 struct call_queue tmpq = {
9520 .name = args.queuename,
9521 };
9522
9523 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_GET_CHANNEL()");
9524 }
9525 if (q) {
9526 ao2_lock(q);
9527 if (q->count >= position) {
9528 struct queue_ent *qe;
9529
9530 for (qe = q->head; qe; qe = qe->next) {
9531 if (qe->pos == position) {
9533 break;
9534 }
9535 }
9536 }
9537 ao2_unlock(q);
9538 queue_t_unref(q, "Done with reference in QUEUE_GET_CHANNEL()");
9539 return 0;
9540 }
9541
9542 var = ast_load_realtime("queues", "name", args.queuename, SENTINEL);
9543 if (var) {
9544 /* if the queue is realtime but was not found in memory, this
9545 * means that the queue had been deleted from memory since it was
9546 * "dead."
9547 */
9549 return 0;
9550 }
9551
9552 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9553 return 0;
9554}
9555
9556/*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
9557static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9558{
9559 int count = 0;
9560 struct call_queue *q, tmpq = {
9561 .name = data,
9562 };
9563 struct ast_variable *var = NULL;
9564
9565 buf[0] = '\0';
9566
9567 if (ast_strlen_zero(data)) {
9568 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
9569 return -1;
9570 }
9571
9572 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
9573 ao2_lock(q);
9574 count = q->count;
9575 ao2_unlock(q);
9576 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
9577 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
9578 /* if the queue is realtime but was not found in memory, this
9579 * means that the queue had been deleted from memory since it was
9580 * "dead." This means it has a 0 waiting count
9581 */
9582 count = 0;
9584 } else {
9585 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9586 }
9587
9588 snprintf(buf, len, "%d", count);
9589
9590 return 0;
9591}
9592
9593/*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
9594static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9595{
9596 struct call_queue *q;
9597 struct member *m;
9598
9599 /* Ensure an otherwise empty list doesn't return garbage */
9600 buf[0] = '\0';
9601
9602 if (ast_strlen_zero(data)) {
9603 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
9604 return -1;
9605 }
9606
9607 if ((q = find_load_queue_rt_friendly(data))) {
9608 int buflen = 0, count = 0;
9609 struct ao2_iterator mem_iter;
9610
9611 ao2_lock(q);
9612 mem_iter = ao2_iterator_init(q->members, 0);
9613 while ((m = ao2_iterator_next(&mem_iter))) {
9614 /* strcat() is always faster than printf() */
9615 if (count++) {
9616 strncat(buf + buflen, ",", len - buflen - 1);
9617 buflen++;
9618 }
9619 strncat(buf + buflen, m->interface, len - buflen - 1);
9620 buflen += strlen(m->interface);
9621 /* Safeguard against overflow (negative length) */
9622 if (buflen >= len - 2) {
9623 ao2_ref(m, -1);
9624 ast_log(LOG_WARNING, "Truncating list\n");
9625 break;
9626 }
9627 ao2_ref(m, -1);
9628 }
9629 ao2_iterator_destroy(&mem_iter);
9630 ao2_unlock(q);
9631 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
9632 } else
9633 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9634
9635 /* We should already be terminated, but let's make sure. */
9636 buf[len - 1] = '\0';
9637
9638 return 0;
9639}
9640
9641/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
9642static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9643{
9644 int penalty;
9646 AST_APP_ARG(queuename);
9647 AST_APP_ARG(interface);
9648 );
9649 /* Make sure the returned value on error is NULL. */
9650 buf[0] = '\0';
9651
9652 if (ast_strlen_zero(data)) {
9653 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9654 return -1;
9655 }
9656
9658
9659 if (args.argc < 2) {
9660 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9661 return -1;
9662 }
9663
9664 penalty = get_member_penalty (args.queuename, args.interface);
9665
9666 if (penalty >= 0) { /* remember that buf is already '\0' */
9667 snprintf (buf, len, "%d", penalty);
9668 }
9669
9670 return 0;
9671}
9672
9673/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
9674static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9675{
9676 int penalty;
9678 AST_APP_ARG(queuename);
9679 AST_APP_ARG(interface);
9680 );
9681
9682 if (ast_strlen_zero(data)) {
9683 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9684 return -1;
9685 }
9686
9688
9689 if (args.argc < 2) {
9690 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9691 return -1;
9692 }
9693
9694 penalty = atoi(value);
9695
9696 if (ast_strlen_zero(args.interface)) {
9697 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
9698 return -1;
9699 }
9700
9701 /* if queuename = NULL then penalty will be set for interface in all the queues. */
9702 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, penalty)) {
9703 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
9704 return -1;
9705 }
9706
9707 return 0;
9708}
9709
9711 .name = "QUEUE_EXISTS",
9712 .read = queue_function_exists,
9713};
9714
9716 .name = "QUEUE_VARIABLES",
9717 .read = queue_function_var,
9718};
9719
9721 .name = "QUEUE_MEMBER",
9723 .write = queue_function_mem_write,
9724};
9725
9727 .name = "QUEUE_GET_CHANNEL",
9729};
9730
9732 .name = "QUEUE_WAITING_COUNT",
9734};
9735
9737 .name = "QUEUE_MEMBER_LIST",
9739};
9740
9742 .name = "QUEUE_MEMBER_PENALTY",
9745};
9746
9747/*! Reset the global queue rules parameters even if there is no "general" section of queuerules.conf */
9749{
9750 realtime_rules = 0;
9751}
9752
9753/*! Set the global queue rules parameters as defined in the "general" section of queuerules.conf */
9755{
9756 const char *general_val = NULL;
9757 if ((general_val = ast_variable_retrieve(cfg, "general", "realtime_rules"))) {
9758 realtime_rules = ast_true(general_val);
9759 }
9760}
9761
9762/*! \brief Reload the rules defined in queuerules.conf
9763 *
9764 * \param reload If 1, then only process queuerules.conf if the file
9765 * has changed since the last time we inspected it.
9766 * \return Always returns AST_MODULE_LOAD_SUCCESS
9767 */
9769{
9770 struct ast_config *cfg;
9771 struct rule_list *rl_iter, *new_rl;
9772 struct penalty_rule *pr_iter;
9773 char *rulecat = NULL;
9774 struct ast_variable *rulevar = NULL;
9775 struct ast_flags config_flags = { (reload && !realtime_rules) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9776
9777 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
9778 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
9780 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9781 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
9783 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
9784 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n");
9786 }
9787
9789 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
9790 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
9791 ast_free(pr_iter);
9792 ast_free(rl_iter);
9793 }
9795 while ((rulecat = ast_category_browse(cfg, rulecat))) {
9796 if (!strcasecmp(rulecat, "general")) {
9798 continue;
9799 }
9800 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
9802 ast_config_destroy(cfg);
9804 } else {
9805 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
9806 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
9807 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
9808 if(!strcasecmp(rulevar->name, "penaltychange"))
9809 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
9810 else
9811 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
9812 }
9813 }
9814
9815 ast_config_destroy(cfg);
9816
9820 }
9821
9824}
9825
9826/*! Always set the global queue defaults, even if there is no "general" section in queues.conf */
9838
9839/*! Set the global queue parameters as defined in the "general" section of queues.conf */
9840static void queue_set_global_params(struct ast_config *cfg)
9841{
9842 const char *general_val = NULL;
9843 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
9844 queue_persistent_members = ast_true(general_val);
9845 }
9846 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) {
9847 autofill_default = ast_true(general_val);
9848 }
9849 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
9850 if (!strcasecmp(general_val, "mixmonitor"))
9851 montype_default = 1;
9852 }
9853 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) {
9854 shared_lastcall = ast_true(general_val);
9855 }
9856 if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid"))) {
9857 negative_penalty_invalid = ast_true(general_val);
9858 }
9859 if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
9860 log_membername_as_agent = ast_true(general_val);
9861 }
9862 if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) {
9863 if (!strcasecmp(general_val, "prio")) {
9865 } else if (ast_true(general_val)) {
9867 } else {
9869 }
9870 }
9871 if ((general_val = ast_variable_retrieve(cfg, "general", "log_unpause_on_reason_change"))) {
9873 }
9874 /* Apply log-caller-id-name in the same place as other global settings */
9875 if ((general_val = ast_variable_retrieve(cfg, "general", "log-caller-id-name"))) {
9876 log_caller_id_name = ast_true(general_val);
9877 }
9878}
9879
9880/*! \brief reload information pertaining to a single member
9881 *
9882 * This function is called when a member = line is encountered in
9883 * queues.conf.
9884 *
9885 * \param memberdata The part after member = in the config file
9886 * \param q The queue to which this member belongs
9887 */
9888static void reload_single_member(const char *memberdata, struct call_queue *q)
9889{
9890 char *membername, *interface, *state_interface, *tmp;
9891 char *parse;
9892 struct member *cur, *newm;
9893 struct member tmpmem;
9894 int penalty;
9895 int ringinuse;
9896 int wrapuptime;
9897 int paused;
9906 );
9907
9908 if (ast_strlen_zero(memberdata)) {
9909 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
9910 return;
9911 }
9912
9913 /* Add a new member */
9914 parse = ast_strdupa(memberdata);
9915
9917
9918 interface = args.interface;
9919 if (!ast_strlen_zero(args.penalty)) {
9920 tmp = args.penalty;
9921 ast_strip(tmp);
9922 penalty = atoi(tmp);
9923 if (penalty < 0) {
9924 penalty = 0;
9925 }
9926 } else {
9927 penalty = 0;
9928 }
9929
9930 if (!ast_strlen_zero(args.membername)) {
9931 membername = args.membername;
9932 ast_strip(membername);
9933 } else {
9934 membername = interface;
9935 }
9936
9937 if (!ast_strlen_zero(args.state_interface)) {
9938 state_interface = args.state_interface;
9939 ast_strip(state_interface);
9940 } else {
9941 state_interface = interface;
9942 }
9943
9944 if (!ast_strlen_zero(args.ringinuse)) {
9945 tmp = args.ringinuse;
9946 ast_strip(tmp);
9947 if (ast_true(tmp)) {
9948 ringinuse = 1;
9949 } else if (ast_false(tmp)) {
9950 ringinuse = 0;
9951 } else {
9952 ast_log(LOG_ERROR, "Member %s has an invalid ringinuse value. Using %s ringinuse value.\n",
9953 membername, q->name);
9954 ringinuse = q->ringinuse;
9955 }
9956 } else {
9957 ringinuse = q->ringinuse;
9958 }
9959
9960 if (!ast_strlen_zero(args.wrapuptime)) {
9961 tmp = args.wrapuptime;
9962 ast_strip(tmp);
9963 wrapuptime = atoi(tmp);
9964 if (wrapuptime < 0) {
9965 wrapuptime = 0;
9966 }
9967 } else {
9968 wrapuptime = 0;
9969 }
9970
9971 if (!ast_strlen_zero(args.paused)) {
9972 tmp = args.paused;
9973 ast_strip(tmp);
9974 if (ast_true(tmp)) {
9975 paused = 1;
9976 } else if (ast_false(tmp)) {
9977 paused = 0;
9978 } else {
9979 ast_log(LOG_ERROR, "Member %s has an invalid paused value.\n", membername);
9980 paused = 0;
9981 }
9982 } else {
9983 paused = 0;
9984 }
9985
9986 /* Find the old position in the list */
9987 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
9988 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
9989
9990 if (cur) {
9991 paused = cur->paused;
9992 }
9993
9994 if ((newm = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
9995 newm->wrapuptime = wrapuptime;
9996 if (cur) {
9997 ao2_lock(q->members);
9998 /* Round Robin Queue Position must be copied if this is replacing an existing member */
9999 newm->queuepos = cur->queuepos;
10000 /* Don't reset agent stats either */
10001 newm->calls = cur->calls;
10002 newm->lastcall = cur->lastcall;
10003
10004 ao2_link(q->members, newm);
10005 ao2_unlink(q->members, cur);
10006 ao2_unlock(q->members);
10007 } else {
10008 /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
10009 member_add_to_queue(q, newm);
10010 }
10011 ao2_ref(newm, -1);
10012 }
10013 newm = NULL;
10014
10015 if (cur) {
10016 ao2_ref(cur, -1);
10017 }
10018}
10019
10020static int mark_member_dead(void *obj, void *arg, int flags)
10021{
10022 struct member *member = obj;
10023 if (!member->dynamic && !member->realtime) {
10024 member->delme = 1;
10025 }
10026 return 0;
10027}
10028
10029static int kill_dead_members(void *obj, void *arg, int flags)
10030{
10031 struct member *member = obj;
10032
10033 if (!member->delme) {
10035 return 0;
10036 } else {
10037 return CMP_MATCH;
10038 }
10039}
10040
10041/*! \brief Reload information pertaining to a particular queue
10042 *
10043 * Once we have isolated a queue within reload_queues, we call this. This will either
10044 * reload information for the queue or if we're just reloading member information, we'll just
10045 * reload that without touching other settings within the queue
10046 *
10047 * \param cfg The configuration which we are reading
10048 * \param mask Tells us what information we need to reload
10049 * \param queuename The name of the queue we are reloading information from
10050 */
10051static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
10052{
10053 int new;
10054 struct call_queue *q = NULL;
10055 struct member *member;
10056 /*We're defining a queue*/
10057 struct call_queue tmpq = {
10058 .name = queuename,
10059 };
10060 const char *tmpvar;
10061 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
10062 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
10063 int prev_weight = 0;
10064 struct ast_variable *var;
10065 struct ao2_iterator mem_iter;
10066
10067 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
10068 if (queue_reload) {
10069 /* Make one then */
10070 if (!(q = alloc_queue(queuename))) {
10071 return;
10072 }
10073 } else {
10074 /* Since we're not reloading queues, this means that we found a queue
10075 * in the configuration file which we don't know about yet. Just return.
10076 */
10077 return;
10078 }
10079 new = 1;
10080 } else {
10081 new = 0;
10082 }
10083
10084 if (!new) {
10085 ao2_lock(q);
10086 prev_weight = q->weight ? 1 : 0;
10087 }
10088 /* Check if we already found a queue with this name in the config file */
10089 if (q->found) {
10090 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
10091 if (!new) {
10092 /* It should be impossible to *not* hit this case*/
10093 ao2_unlock(q);
10094 }
10095 queue_t_unref(q, "We exist! Expiring temporary pointer");
10096 return;
10097 }
10098 /* Due to the fact that the "linear" strategy will have a different allocation
10099 * scheme for queue members, we must devise the queue's strategy before other initializations.
10100 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
10101 * container used will have only a single bucket instead of the typical number.
10102 */
10103 if (queue_reload) {
10104 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
10105 q->strategy = strat2int(tmpvar);
10106 if (q->strategy < 0) {
10107 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
10108 tmpvar, q->name);
10110 }
10111 } else {
10113 }
10114 init_queue(q);
10115 }
10116 if (member_reload) {
10118 q->found = 1;
10119 }
10120
10121 /* On the first pass we just read the parameters of the queue */
10122 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
10123 if (queue_reload && strcasecmp(var->name, "member")) {
10124 queue_set_param(q, var->name, var->value, var->lineno, 1);
10125 }
10126 }
10127
10128 /* On the second pass, we read members */
10129 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
10130 if (member_reload && !strcasecmp(var->name, "member")) {
10131 reload_single_member(var->value, q);
10132 }
10133 }
10134
10135 /* Update ringinuse for dynamic members */
10136 if (member_reload) {
10137 ao2_lock(q->members);
10139 while ((member = ao2_iterator_next(&mem_iter))) {
10140 if (member->dynamic) {
10142 }
10143 ao2_ref(member, -1);
10144 }
10145 ao2_iterator_destroy(&mem_iter);
10146 ao2_unlock(q->members);
10147 }
10148
10149 /* At this point, we've determined if the queue has a weight, so update use_weight
10150 * as appropriate
10151 */
10152 if (!q->weight && prev_weight) {
10154 } else if (q->weight && !prev_weight) {
10156 }
10157
10158 /* Free remaining members marked as delme */
10159 if (member_reload) {
10160 ao2_lock(q->members);
10163 ao2_unlock(q->members);
10164 }
10165
10166 if (new) {
10167 queues_t_link(queues, q, "Add queue to container");
10168 } else {
10169 ao2_unlock(q);
10170 }
10171 queue_t_unref(q, "Expiring creation reference");
10172}
10173
10174static int mark_unfound(void *obj, void *arg, int flags)
10175{
10176 struct call_queue *q = obj;
10177 char *queuename = arg;
10178 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
10179 q->found = 0;
10180 }
10181 return 0;
10182}
10183
10184static int kill_if_unfound(void *obj, void *arg, int flags)
10185{
10186 struct call_queue *q = obj;
10187 char *queuename = arg;
10188 if (!q->realtime && !q->found && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
10189 q->dead = 1;
10190 return CMP_MATCH;
10191 } else {
10192 return 0;
10193 }
10194}
10195
10196/*! \brief reload the queues.conf file
10197 *
10198 * This function reloads the information in the general section of the queues.conf
10199 * file and potentially more, depending on the value of mask.
10200 *
10201 * \param reload 0 if we are calling this the first time, 1 every other time
10202 * \param mask Gives flags telling us what information to actually reload
10203 * \param queuename If set to a non-zero string, then only reload information from
10204 * that particular queue. Otherwise inspect all queues
10205 * \retval -1 Failure occurred
10206 * \retval 0 All clear!
10207 */
10208static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
10209{
10210 struct ast_config *cfg;
10211 char *cat;
10212 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10213 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
10214
10215 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
10216 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
10217 return -1;
10218 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
10219 return 0;
10220 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10221 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n");
10222 return -1;
10223 }
10224
10225 /* We've made it here, so it looks like we're doing operations on all queues. */
10227
10228 /* Mark non-realtime queues not found at the beginning. */
10229 ao2_callback(queues, OBJ_NODATA, mark_unfound, (char *) queuename);
10230
10231 /* Chug through config file. */
10232 cat = NULL;
10234 while ((cat = ast_category_browse(cfg, cat)) ) {
10235 if (!strcasecmp(cat, "general") && queue_reload) {
10237 continue;
10238 }
10239 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
10240 reload_single_queue(cfg, mask, cat);
10241 }
10242
10243 ast_config_destroy(cfg);
10244 if (queue_reload) {
10245 /* Unlink and mark dead all non-realtime queues that were not found in the configuration file. */
10247 }
10249 return 0;
10250}
10251
10252/*! \brief Facilitates resetting statistics for a queue
10253 *
10254 * This function actually does not reset any statistics, but
10255 * rather finds a call_queue struct which corresponds to the
10256 * passed-in queue name and passes that structure to the
10257 * clear_queue function. If no queuename is passed in, then
10258 * all queues will have their statistics reset.
10259 *
10260 * \param queuename The name of the queue to reset the statistics
10261 * for. If this is NULL or zero-length, then this means to reset
10262 * the statistics for all queues
10263 * \retval 0 always
10264 */
10265static int clear_stats(const char *queuename)
10266{
10267 struct call_queue *q;
10268 struct ao2_iterator queue_iter;
10269
10270 queue_iter = ao2_iterator_init(queues, 0);
10271 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10272 ao2_lock(q);
10273 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
10274 clear_queue(q);
10275 ao2_unlock(q);
10276 queue_t_unref(q, "Done with iterator");
10277 }
10278 ao2_iterator_destroy(&queue_iter);
10279 return 0;
10280}
10281
10282/*! \brief The command center for all reload operations
10283 *
10284 * Whenever any piece of queue information is to be reloaded, this function
10285 * is called. It interprets the flags set in the mask parameter and acts
10286 * based on how they are set.
10287 *
10288 * \param reload True if we are reloading information, false if we are loading
10289 * information for the first time.
10290 * \param mask A bitmask which tells the handler what actions to take
10291 * \param queuename The name of the queue on which we wish to take action
10292 * \retval 0 All reloads were successful
10293 * \retval non-zero There was a failure
10294 */
10295static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
10296{
10297 int res = 0;
10298
10299 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
10300 res |= reload_queue_rules(reload);
10301 }
10302 if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
10303 res |= clear_stats(queuename);
10304 }
10306 res |= reload_queues(reload, mask, queuename);
10307 }
10308 return res;
10309}
10310
10311/*! \brief direct output to manager or cli with proper terminator */
10312static void do_print(struct mansession *s, int fd, const char *str)
10313{
10314 if (s) {
10315 astman_append(s, "%s\r\n", str);
10316 } else {
10317 ast_cli(fd, "%s\n", str);
10318 }
10319}
10320
10321/*! \brief Print a single queue to AMI or the CLI */
10322static void print_queue(struct mansession *s, int fd, struct call_queue *q)
10323{
10324 float sl;
10325 float sl2;
10326 struct ao2_iterator mem_iter;
10327 struct ast_str *out = ast_str_alloca(512);
10328 time_t now = time(NULL);
10329
10330 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
10331 if (q->maxlen) {
10332 ast_str_append(&out, 0, "%d", q->maxlen);
10333 } else {
10334 ast_str_append(&out, 0, "unlimited");
10335 }
10336 sl = 0;
10337 sl2 = 0;
10338 if (q->callscompleted > 0) {
10339 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
10340 }
10341 if (q->callscompleted + q->callsabandoned > 0) {
10342 sl2 =100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted));
10343 }
10344
10345 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%%, SL2:%2.1f%% within %ds",
10347 do_print(s, fd, ast_str_buffer(out));
10348 if (!ao2_container_count(q->members)) {
10349 do_print(s, fd, " No Members");
10350 } else {
10351 struct member *mem;
10352
10353 do_print(s, fd, " Members: ");
10354 mem_iter = ao2_iterator_init(q->members, 0);
10355 while ((mem = ao2_iterator_next(&mem_iter))) {
10356 ast_str_set(&out, 0, " %s", mem->membername);
10357 if (strcasecmp(mem->membername, mem->interface)) {
10358 ast_str_append(&out, 0, " (%s", mem->interface);
10360 && strcmp(mem->state_interface, mem->interface)) {
10361 ast_str_append(&out, 0, " from %s", mem->state_interface);
10362 }
10363 ast_str_append(&out, 0, ")");
10364 }
10365 if (mem->penalty) {
10366 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
10367 }
10368
10369 ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
10370
10371 ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
10372 mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
10373 mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
10374 mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset());
10375
10376 if (mem->paused) {
10377 ast_str_append(&out, 0, " %s(paused%s%s was %ld secs ago)%s",
10379 ast_strlen_zero(mem->reason_paused) ? "" : ":",
10381 (long) (now - mem->lastpause),
10382 ast_term_reset());
10383 }
10384
10385 ast_str_append(&out, 0, " (%s%s%s)",
10390 if (mem->calls) {
10391 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
10392 mem->calls, (long) (now - mem->lastcall));
10393 } else {
10394 ast_str_append(&out, 0, " has taken no calls yet");
10395 }
10396 ast_str_append(&out, 0, " %s(login was %ld secs ago)%s",
10398 (long) (now - mem->logintime),
10399 ast_term_reset());
10400 do_print(s, fd, ast_str_buffer(out));
10401 ao2_ref(mem, -1);
10402 }
10403 ao2_iterator_destroy(&mem_iter);
10404 }
10405 if (!q->head) {
10406 do_print(s, fd, " No Callers");
10407 } else {
10408 struct queue_ent *qe;
10409 int pos = 1;
10410
10411 do_print(s, fd, " Callers: ");
10412 for (qe = q->head; qe; qe = qe->next) {
10413 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
10414 pos++, ast_channel_name(qe->chan), (long) (now - qe->start) / 60,
10415 (long) (now - qe->start) % 60, qe->prio);
10416 do_print(s, fd, ast_str_buffer(out));
10417 }
10418 }
10419 do_print(s, fd, ""); /* blank line between entries */
10420}
10421
10423
10424/*!
10425 * \brief Show queue(s) status and statistics
10426 *
10427 * List the queues strategy, calls processed, members logged in,
10428 * other queue statistics such as avg hold time.
10429*/
10430static char *__queues_show(struct mansession *s, int fd, int argc, const char * const *argv)
10431{
10432 struct call_queue *q;
10433 struct ast_str *out = ast_str_alloca(512);
10434 struct ao2_container *sorted_queues;
10435
10436 struct ao2_iterator queue_iter;
10437 int found = 0;
10438
10439 if (argc != 2 && argc != 3) {
10440 return CLI_SHOWUSAGE;
10441 }
10442
10443 if (argc == 3) { /* specific queue */
10444 if ((q = find_load_queue_rt_friendly(argv[2]))) {
10445 ao2_lock(q);
10446 print_queue(s, fd, q);
10447 ao2_unlock(q);
10448 queue_unref(q);
10449 } else {
10450 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
10451 do_print(s, fd, ast_str_buffer(out));
10452 }
10453 return CLI_SUCCESS;
10454 }
10455
10456 if (ast_check_realtime("queues")) {
10457 /* This block is to find any queues which are defined in realtime but
10458 * which have not yet been added to the in-core container
10459 */
10460 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
10461 if (cfg) {
10462 char *category = NULL;
10463 while ((category = ast_category_browse(cfg, category))) {
10464 const char *queuename = ast_variable_retrieve(cfg, category, "name");
10465 if (ast_strlen_zero(queuename)) {
10466 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
10467 continue;
10468 }
10469 if ((q = find_load_queue_rt_friendly(queuename))) {
10470 queue_t_unref(q, "Done with temporary pointer");
10471 }
10472 }
10473 ast_config_destroy(cfg);
10474 }
10475 }
10476
10477 /*
10478 * Snapping a copy of the container prevents having to lock both the queues container
10479 * and the queue itself at the same time. It also allows us to sort the entries.
10480 */
10481 sorted_queues = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, call_queue_sort_fn, NULL);
10482 if (!sorted_queues) {
10483 return CLI_SUCCESS;
10484 }
10485 if (ao2_container_dup(sorted_queues, queues, 0)) {
10486 ao2_ref(sorted_queues, -1);
10487 return CLI_SUCCESS;
10488 }
10489
10490 /*
10491 * No need to lock the container since it's temporary and static.
10492 * We also unlink the entries as we use them so the container is
10493 * empty when the iterator finishes. We can then just unref the container.
10494 */
10495 queue_iter = ao2_iterator_init(sorted_queues, AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK);
10496 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10497 struct call_queue *realtime_queue = NULL;
10498 ao2_lock(q);
10499 /* This check is to make sure we don't print information for realtime
10500 * queues which have been deleted from realtime but which have not yet
10501 * been deleted from the in-core container. Only do this if we're not
10502 * looking for a specific queue.
10503 */
10504 if (q->realtime) {
10505 realtime_queue = find_load_queue_rt_friendly(q->name);
10506 if (!realtime_queue) {
10507 ao2_unlock(q);
10508 queue_t_unref(q, "Done with iterator");
10509 continue;
10510 }
10511 queue_t_unref(realtime_queue, "Queue is already in memory");
10512 }
10513
10514 found = 1;
10515 print_queue(s, fd, q);
10516
10517 ao2_unlock(q);
10518 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
10519 }
10520 ao2_iterator_destroy(&queue_iter);
10521 ao2_ref(sorted_queues, -1);
10522 if (!found) {
10523 ast_str_set(&out, 0, "No queues.");
10524 do_print(s, fd, ast_str_buffer(out));
10525 }
10526 return CLI_SUCCESS;
10527}
10528
10529/*!
10530 * \brief Check if a given word is in a space-delimited list
10531 *
10532 * \param list Space delimited list of words
10533 * \param word The word used to search the list
10534 *
10535 * \note This function will not return 1 if the word is at the very end of the
10536 * list (followed immediately by a \0, not a space) since it is used for
10537 * checking tab-completion and a word at the end is still being tab-completed.
10538 *
10539 * \retval 1 if the word is found
10540 * \retval 0 if the word is not found
10541*/
10542static int word_in_list(const char *list, const char *word) {
10543 int list_len, word_len = strlen(word);
10544 const char *find, *end_find, *end_list;
10545
10546 /* strip whitespace from front */
10547 while(isspace(*list)) {
10548 list++;
10549 }
10550
10551 while((find = strstr(list, word))) {
10552 /* beginning of find starts inside another word? */
10553 if (find != list && *(find - 1) != ' ') {
10554 list = find;
10555 /* strip word from front */
10556 while(!isspace(*list) && *list != '\0') {
10557 list++;
10558 }
10559 /* strip whitespace from front */
10560 while(isspace(*list)) {
10561 list++;
10562 }
10563 continue;
10564 }
10565
10566 /* end of find ends inside another word or at very end of list? */
10567 list_len = strlen(list);
10568 end_find = find + word_len;
10569 end_list = list + list_len;
10570 if (end_find == end_list || *end_find != ' ') {
10571 list = find;
10572 /* strip word from front */
10573 while(!isspace(*list) && *list != '\0') {
10574 list++;
10575 }
10576 /* strip whitespace from front */
10577 while(isspace(*list)) {
10578 list++;
10579 }
10580 continue;
10581 }
10582
10583 /* terminating conditions satisfied, word at beginning or separated by ' ' */
10584 return 1;
10585 }
10586
10587 return 0;
10588}
10589
10590/*!
10591 * \brief Check if a given word is in a space-delimited list
10592 *
10593 * \param line The line as typed not including the current word being completed
10594 * \param word The word currently being completed
10595 * \param pos The number of completed words in line
10596 * \param state The nth desired completion option
10597 * \param word_list_offset Offset into the line where the list of queues begins. If non-zero, queues in the list will not be offered for further completion.
10598 *
10599 * \return Returns the queue tab-completion for the given word and state
10600*/
10601static char *complete_queue(const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset)
10602{
10603 struct call_queue *q;
10604 char *ret = NULL;
10605 int which = 0;
10606 int wordlen = strlen(word);
10607 struct ao2_iterator queue_iter;
10608 const char *word_list = NULL;
10609
10610 /* for certain commands, already completed items should be left out of
10611 * the list */
10612 if (word_list_offset && strlen(line) >= word_list_offset) {
10613 word_list = line + word_list_offset;
10614 }
10615
10616 queue_iter = ao2_iterator_init(queues, 0);
10617 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10618 if (!strncasecmp(word, q->name, wordlen) && ++which > state
10619 && (!word_list_offset || !word_list || !word_in_list(word_list, q->name))) {
10620 ret = ast_strdup(q->name);
10621 queue_t_unref(q, "Done with iterator");
10622 break;
10623 }
10624 queue_t_unref(q, "Done with iterator");
10625 }
10626 ao2_iterator_destroy(&queue_iter);
10627
10628 /* Pretend "rules" is at the end of the queues list in certain
10629 * circumstances since it is an alternate command that should be
10630 * tab-completable for "queue show" */
10631 if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
10632 ret = ast_strdup("rules");
10633 }
10634
10635 return ret;
10636}
10637
10638static char *complete_queue_show(const char *line, const char *word, int pos, int state)
10639{
10640 if (pos == 2) {
10641 return complete_queue(line, word, pos, state, 0);
10642 }
10643 return NULL;
10644}
10645
10646static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10647{
10648 switch ( cmd ) {
10649 case CLI_INIT:
10650 e->command = "queue show";
10651 e->usage =
10652 "Usage: queue show\n"
10653 " Provides summary information on a specified queue.\n";
10654 return NULL;
10655 case CLI_GENERATE:
10656 return complete_queue_show(a->line, a->word, a->pos, a->n);
10657 }
10658
10659 return __queues_show(NULL, a->fd, a->argc, a->argv);
10660}
10661
10662static int manager_queue_rule_show(struct mansession *s, const struct message *m)
10663{
10664 const char *rule = astman_get_header(m, "Rule");
10665 const char *id = astman_get_header(m, "ActionID");
10666 struct rule_list *rl_iter;
10667 struct penalty_rule *pr_iter;
10668
10669 astman_append(s, "Response: Success\r\n");
10670 if (!ast_strlen_zero(id)) {
10671 astman_append(s, "ActionID: %s\r\n", id);
10672 }
10673
10675 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
10676 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
10677 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
10678 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
10679 astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
10680 }
10681 if (!ast_strlen_zero(rule)) {
10682 break;
10683 }
10684 }
10685 }
10687
10688 /*
10689 * Two blank lines instead of one because the Response and
10690 * ActionID headers used to not be present.
10691 */
10692 astman_append(s, "\r\n\r\n");
10693
10694 return RESULT_SUCCESS;
10695}
10696
10697/*! \brief Summary of queue info via the AMI */
10698static int manager_queues_summary(struct mansession *s, const struct message *m)
10699{
10700 time_t now;
10701 int qmemcount = 0;
10702 int qmemavail = 0;
10703 int qchancount = 0;
10704 int qlongestholdtime = 0;
10705 int qsummaries = 0;
10706 const char *id = astman_get_header(m, "ActionID");
10707 const char *queuefilter = astman_get_header(m, "Queue");
10708 char idText[256];
10709 struct call_queue *q;
10710 struct queue_ent *qe;
10711 struct member *mem;
10712 struct ao2_iterator queue_iter;
10713 struct ao2_iterator mem_iter;
10714
10715 if (ast_check_realtime("queues")) {
10716 load_realtime_queues(queuefilter);
10717 }
10718
10719 astman_send_listack(s, m, "Queue summary will follow", "start");
10720 time(&now);
10721 idText[0] = '\0';
10722 if (!ast_strlen_zero(id)) {
10723 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10724 }
10725 queue_iter = ao2_iterator_init(queues, 0);
10726 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10727 ao2_lock(q);
10728
10729 /* List queue properties */
10730 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10731 /* Reset the necessary local variables if no queuefilter is set*/
10732 qmemcount = 0;
10733 qmemavail = 0;
10734 qchancount = 0;
10735 qlongestholdtime = 0;
10736
10737 /* List Queue Members */
10738 mem_iter = ao2_iterator_init(q->members, 0);
10739 while ((mem = ao2_iterator_next(&mem_iter))) {
10740 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
10741 ++qmemcount;
10742 if (member_status_available(mem->status) && !mem->paused) {
10743 ++qmemavail;
10744 }
10745 }
10746 ao2_ref(mem, -1);
10747 }
10748 ao2_iterator_destroy(&mem_iter);
10749 for (qe = q->head; qe; qe = qe->next) {
10750 if ((now - qe->start) > qlongestholdtime) {
10751 qlongestholdtime = now - qe->start;
10752 }
10753 ++qchancount;
10754 }
10755 astman_append(s, "Event: QueueSummary\r\n"
10756 "Queue: %s\r\n"
10757 "LoggedIn: %d\r\n"
10758 "Available: %d\r\n"
10759 "Callers: %d\r\n"
10760 "HoldTime: %d\r\n"
10761 "TalkTime: %d\r\n"
10762 "LongestHoldTime: %d\r\n"
10763 "%s"
10764 "\r\n",
10765 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
10766 ++qsummaries;
10767 }
10768 ao2_unlock(q);
10769 queue_t_unref(q, "Done with iterator");
10770 }
10771 ao2_iterator_destroy(&queue_iter);
10772
10773 astman_send_list_complete_start(s, m, "QueueSummaryComplete", qsummaries);
10775
10776 return RESULT_SUCCESS;
10777}
10778
10779/*! \brief Queue status info via AMI */
10780static int manager_queues_status(struct mansession *s, const struct message *m)
10781{
10782 time_t now;
10783 int pos;
10784 int q_items = 0;
10785 const char *id = astman_get_header(m,"ActionID");
10786 const char *queuefilter = astman_get_header(m,"Queue");
10787 const char *memberfilter = astman_get_header(m,"Member");
10788 char idText[256];
10789 struct call_queue *q;
10790 struct queue_ent *qe;
10791 float sl = 0;
10792 float sl2 = 0;
10793 struct member *mem;
10794 struct ao2_iterator queue_iter;
10795 struct ao2_iterator mem_iter;
10796
10797 if (ast_check_realtime("queues")) {
10798 load_realtime_queues(queuefilter);
10799 }
10800
10801 astman_send_listack(s, m, "Queue status will follow", "start");
10802 time(&now);
10803 idText[0] = '\0';
10804 if (!ast_strlen_zero(id)) {
10805 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10806 }
10807
10808 queue_iter = ao2_iterator_init(queues, 0);
10809 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10810 ao2_lock(q);
10811
10812 /* List queue properties */
10813 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10814 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
10815 sl2 = (((q->callscompleted + q->callsabandoned) > 0) ? 100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted)) : 0);
10816
10817 astman_append(s, "Event: QueueParams\r\n"
10818 "Queue: %s\r\n"
10819 "Max: %d\r\n"
10820 "Strategy: %s\r\n"
10821 "Calls: %d\r\n"
10822 "Holdtime: %d\r\n"
10823 "TalkTime: %d\r\n"
10824 "Completed: %d\r\n"
10825 "Abandoned: %d\r\n"
10826 "ServiceLevel: %d\r\n"
10827 "ServicelevelPerf: %2.1f\r\n"
10828 "ServicelevelPerf2: %2.1f\r\n"
10829 "Weight: %d\r\n"
10830 "%s"
10831 "\r\n",
10832 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
10833 q->callsabandoned, q->servicelevel, sl, sl2, q->weight, idText);
10834 ++q_items;
10835
10836 /* List Queue Members */
10837 mem_iter = ao2_iterator_init(q->members, 0);
10838 while ((mem = ao2_iterator_next(&mem_iter))) {
10839 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
10840 astman_append(s, "Event: QueueMember\r\n"
10841 "Queue: %s\r\n"
10842 "Name: %s\r\n"
10843 "Location: %s\r\n"
10844 "StateInterface: %s\r\n"
10845 "Membership: %s\r\n"
10846 "Penalty: %d\r\n"
10847 "CallsTaken: %d\r\n"
10848 "LastCall: %d\r\n"
10849 "LastPause: %d\r\n"
10850 "LoginTime: %d\r\n"
10851 "InCall: %d\r\n"
10852 "Status: %d\r\n"
10853 "Paused: %d\r\n"
10854 "PausedReason: %s\r\n"
10855 "Wrapuptime: %d\r\n"
10856 "%s"
10857 "\r\n",
10858 q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
10859 mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, (int)mem->logintime, mem->starttime ? 1 : 0, mem->status,
10860 mem->paused, mem->reason_paused, mem->wrapuptime, idText);
10861 ++q_items;
10862 }
10863 ao2_ref(mem, -1);
10864 }
10865 ao2_iterator_destroy(&mem_iter);
10866
10867 /* List Queue Entries */
10868 pos = 1;
10869 for (qe = q->head; qe; qe = qe->next) {
10870 astman_append(s, "Event: QueueEntry\r\n"
10871 "Queue: %s\r\n"
10872 "Position: %d\r\n"
10873 "Channel: %s\r\n"
10874 "Uniqueid: %s\r\n"
10875 "CallerIDNum: %s\r\n"
10876 "CallerIDName: %s\r\n"
10877 "ConnectedLineNum: %s\r\n"
10878 "ConnectedLineName: %s\r\n"
10879 "Wait: %ld\r\n"
10880 "Priority: %d\r\n"
10881 "%s"
10882 "\r\n",
10883 q->name, pos++, ast_channel_name(qe->chan), ast_channel_uniqueid(qe->chan),
10888 (long) (now - qe->start), qe->prio, idText);
10889 ++q_items;
10890 }
10891 }
10892 ao2_unlock(q);
10893 queue_t_unref(q, "Done with iterator");
10894 }
10895 ao2_iterator_destroy(&queue_iter);
10896
10897 astman_send_list_complete_start(s, m, "QueueStatusComplete", q_items);
10899
10900 return RESULT_SUCCESS;
10901}
10902
10903static int manager_add_queue_member(struct mansession *s, const struct message *m)
10904{
10905 const char *queuename, *interface, *penalty_s, *paused_s, *reason, *membername, *state_interface, *wrapuptime_s;
10906 int paused, penalty, wrapuptime = 0;
10907
10908 queuename = astman_get_header(m, "Queue");
10909 interface = astman_get_header(m, "Interface");
10910 penalty_s = astman_get_header(m, "Penalty");
10911 paused_s = astman_get_header(m, "Paused");
10912 reason = astman_get_header(m, "Reason"); /* Optional */
10913 membername = astman_get_header(m, "MemberName");
10914 state_interface = astman_get_header(m, "StateInterface");
10915 wrapuptime_s = astman_get_header(m, "Wrapuptime");
10916
10917 if (ast_strlen_zero(queuename)) {
10918 astman_send_error(s, m, "'Queue' not specified.");
10919 return 0;
10920 }
10921
10922 if (ast_strlen_zero(interface)) {
10923 astman_send_error(s, m, "'Interface' not specified.");
10924 return 0;
10925 }
10926
10927 if (ast_strlen_zero(penalty_s)) {
10928 penalty = 0;
10929 } else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) {
10930 penalty = 0;
10931 }
10932
10933 if (ast_strlen_zero(wrapuptime_s)) {
10934 wrapuptime = 0;
10935 } else if (sscanf(wrapuptime_s, "%30d", &wrapuptime) != 1 || wrapuptime < 0) {
10936 wrapuptime = 0;
10937 }
10938
10939 if (ast_strlen_zero(paused_s)) {
10940 paused = 0;
10941 } else {
10942 paused = abs(ast_true(paused_s));
10943 }
10944
10945 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, reason, wrapuptime)) {
10946 case RES_OKAY:
10947 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
10948 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10949 } else {
10950 ast_queue_log(queuename, "MANAGER", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10951 }
10952 astman_send_ack(s, m, "Added interface to queue");
10953 break;
10954 case RES_EXISTS:
10955 astman_send_error(s, m, "Unable to add interface: Already there");
10956 break;
10957 case RES_NOSUCHQUEUE:
10958 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
10959 break;
10960 case RES_OUTOFMEMORY:
10961 astman_send_error(s, m, "Out of memory");
10962 break;
10963 }
10964
10965 return 0;
10966}
10967
10968static int manager_remove_queue_member(struct mansession *s, const struct message *m)
10969{
10970 const char *queuename, *interface;
10971 struct member *mem = NULL;
10972
10973 queuename = astman_get_header(m, "Queue");
10974 interface = astman_get_header(m, "Interface");
10975
10976 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
10977 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
10978 return 0;
10979 }
10980
10982 mem = find_member_by_queuename_and_interface(queuename, interface);
10983 }
10984
10985 switch (remove_from_queue(queuename, interface)) {
10986 case RES_OKAY:
10987 if (!mem || ast_strlen_zero(mem->membername)) {
10988 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
10989 } else {
10990 ast_queue_log(queuename, "MANAGER", mem->membername, "REMOVEMEMBER", "%s", "");
10991 }
10992 astman_send_ack(s, m, "Removed interface from queue");
10993 break;
10994 case RES_EXISTS:
10995 astman_send_error(s, m, "Unable to remove interface: Not there");
10996 break;
10997 case RES_NOSUCHQUEUE:
10998 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
10999 break;
11000 case RES_OUTOFMEMORY:
11001 astman_send_error(s, m, "Out of memory");
11002 break;
11003 case RES_NOT_DYNAMIC:
11004 astman_send_error(s, m, "Member not dynamic");
11005 break;
11006 }
11007
11008 if (mem) {
11009 ao2_ref(mem, -1);
11010 }
11011
11012 return 0;
11013}
11014
11015static int manager_pause_queue_member(struct mansession *s, const struct message *m)
11016{
11017 const char *queuename, *interface, *paused_s, *reason;
11018 int paused;
11019
11020 interface = astman_get_header(m, "Interface");
11021 paused_s = astman_get_header(m, "Paused");
11022 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
11023 reason = astman_get_header(m, "Reason"); /* Optional */
11024
11025 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
11026 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
11027 return 0;
11028 }
11029
11030 paused = abs(ast_true(paused_s));
11031
11032 if (set_member_paused(queuename, interface, reason, paused)) {
11033 astman_send_error(s, m, "Interface not found");
11034 } else {
11035 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
11036 }
11037 return 0;
11038}
11039
11040static int manager_queue_log_custom(struct mansession *s, const struct message *m)
11041{
11042 const char *queuename, *event, *message, *interface, *uniqueid;
11043
11044 queuename = astman_get_header(m, "Queue");
11045 uniqueid = astman_get_header(m, "UniqueId");
11046 interface = astman_get_header(m, "Interface");
11047 event = astman_get_header(m, "Event");
11048 message = astman_get_header(m, "Message");
11049
11050 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
11051 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
11052 return 0;
11053 }
11054
11055 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
11056 astman_send_ack(s, m, "Event added successfully");
11057
11058 return 0;
11059}
11060
11061static int manager_queue_reload(struct mansession *s, const struct message *m)
11062{
11063 struct ast_flags mask = {0,};
11064 const char *queuename = NULL;
11065 int header_found = 0;
11066
11067 queuename = astman_get_header(m, "Queue");
11068 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
11070 header_found = 1;
11071 }
11072 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
11074 header_found = 1;
11075 }
11076 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
11078 header_found = 1;
11079 }
11080
11081 if (!header_found) {
11083 }
11084
11085 if (!reload_handler(1, &mask, queuename)) {
11086 astman_send_ack(s, m, "Queue reloaded successfully");
11087 } else {
11088 astman_send_error(s, m, "Error encountered while reloading queue");
11089 }
11090 return 0;
11091}
11092
11093static int manager_queue_reset(struct mansession *s, const struct message *m)
11094{
11095 const char *queuename = NULL;
11096 struct ast_flags mask = {QUEUE_RESET_STATS,};
11097
11098 queuename = astman_get_header(m, "Queue");
11099
11100 if (!reload_handler(1, &mask, queuename)) {
11101 astman_send_ack(s, m, "Queue stats reset successfully");
11102 } else {
11103 astman_send_error(s, m, "Error encountered while resetting queue stats");
11104 }
11105 return 0;
11106}
11107
11108static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
11109{
11110 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
11111 switch (pos) {
11112 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
11113 return NULL;
11114 case 4: /* only one possible match, "to" */
11115 return state == 0 ? ast_strdup("to") : NULL;
11116 case 5: /* <queue> */
11117 return complete_queue(line, word, pos, state, 0);
11118 case 6: /* only one possible match, "penalty" */
11119 return state == 0 ? ast_strdup("penalty") : NULL;
11120 case 7:
11121 if (0 <= state && state < 100) { /* 0-99 */
11122 char *num;
11123 if ((num = ast_malloc(3))) {
11124 sprintf(num, "%d", state);
11125 }
11126 return num;
11127 } else {
11128 return NULL;
11129 }
11130 case 8: /* only one possible match, "as" */
11131 return state == 0 ? ast_strdup("as") : NULL;
11132 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
11133 return NULL;
11134 default:
11135 return NULL;
11136 }
11137}
11138
11139static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
11140{
11141 const char *queuename, *interface, *ringinuse_s;
11142 int ringinuse;
11143
11144 interface = astman_get_header(m, "Interface");
11145 ringinuse_s = astman_get_header(m, "RingInUse");
11146
11147 /* Optional - if not supplied, set the ringinuse value for the given Interface in all queues */
11148 queuename = astman_get_header(m, "Queue");
11149
11150 if (ast_strlen_zero(interface) || ast_strlen_zero(ringinuse_s)) {
11151 astman_send_error(s, m, "Need 'Interface' and 'RingInUse' parameters.");
11152 return 0;
11153 }
11154
11155 if (ast_true(ringinuse_s)) {
11156 ringinuse = 1;
11157 } else if (ast_false(ringinuse_s)) {
11158 ringinuse = 0;
11159 } else {
11160 astman_send_error(s, m, "'RingInUse' parameter must be a truth value (yes/no, on/off, 0/1, etc)");
11161 return 0;
11162 }
11163
11164 if (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11165 astman_send_error(s, m, "Invalid interface, queuename, or ringinuse value\n");
11166 } else {
11167 astman_send_ack(s, m, "Interface ringinuse set successfully");
11168 }
11169
11170 return 0;
11171}
11172
11173static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
11174{
11175 const char *queuename, *interface, *penalty_s;
11176 int penalty;
11177
11178 interface = astman_get_header(m, "Interface");
11179 penalty_s = astman_get_header(m, "Penalty");
11180 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
11181 queuename = astman_get_header(m, "Queue");
11182
11183 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
11184 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
11185 return 0;
11186 }
11187
11188 penalty = atoi(penalty_s);
11189
11190 if (set_member_value((char *)queuename, (char *)interface, MEMBER_PENALTY, penalty)) {
11191 astman_send_error(s, m, "Invalid interface, queuename or penalty");
11192 } else {
11193 astman_send_ack(s, m, "Interface penalty set successfully");
11194 }
11195
11196 return 0;
11197}
11198
11199static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
11200{
11201 const char *queuename, *caller, *priority_s, *immediate_s;
11202 int priority = 0, immediate = 0;
11203
11204 queuename = astman_get_header(m, "Queue");
11205 caller = astman_get_header(m, "Caller");
11206 priority_s = astman_get_header(m, "Priority");
11207 immediate_s = astman_get_header(m, "Immediate");
11208
11209 if (ast_strlen_zero(queuename)) {
11210 astman_send_error(s, m, "'Queue' not specified.");
11211 return 0;
11212 }
11213
11214 if (ast_strlen_zero(caller)) {
11215 astman_send_error(s, m, "'Caller' not specified.");
11216 return 0;
11217 }
11218
11219 if (ast_strlen_zero(priority_s)) {
11220 astman_send_error(s, m, "'Priority' not specified.");
11221 return 0;
11222 } else if (sscanf(priority_s, "%30d", &priority) != 1) {
11223 astman_send_error(s, m, "'Priority' need integer.");
11224 return 0;
11225 }
11226
11227 if (!ast_strlen_zero(immediate_s)) {
11228 immediate = ast_true(immediate_s);
11229 }
11230
11231 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
11232 case RES_OKAY:
11233 astman_send_ack(s, m, "Priority change for caller on queue");
11234 break;
11235 case RES_NOSUCHQUEUE:
11236 astman_send_error(s, m, "Unable to change priority caller on queue: No such queue");
11237 break;
11238 case RES_NOT_CALLER:
11239 astman_send_error(s, m, "Unable to change priority caller on queue: No such caller");
11240 break;
11241 }
11242
11243 return 0;
11244}
11245
11247{
11248 const char *queuename, *caller, *withdraw_info;
11249
11250 queuename = astman_get_header(m, "Queue");
11251 caller = astman_get_header(m, "Caller");
11252 withdraw_info = astman_get_header(m, "WithdrawInfo");
11253
11254 if (ast_strlen_zero(queuename)) {
11255 astman_send_error(s, m, "'Queue' not specified.");
11256 return 0;
11257 }
11258
11259 if (ast_strlen_zero(caller)) {
11260 astman_send_error(s, m, "'Caller' not specified.");
11261 return 0;
11262 }
11263
11264 switch (request_withdraw_caller_from_queue(queuename, caller, withdraw_info)) {
11265 case RES_OKAY:
11266 astman_send_ack(s, m, "Withdraw requested successfully");
11267 break;
11268 case RES_NOSUCHQUEUE:
11269 astman_send_error(s, m, "Unable to request withdraw from queue: No such queue");
11270 break;
11271 case RES_NOT_CALLER:
11272 astman_send_error(s, m, "Unable to request withdraw from queue: No such caller");
11273 break;
11274 case RES_EXISTS:
11275 astman_send_error(s, m, "Unable to request withdraw from queue: Already requested");
11276 break;
11277 }
11278
11279 return 0;
11280}
11281
11282
11283static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11284{
11285 const char *queuename, *interface, *membername = NULL, *state_interface = NULL, *reason = NULL;
11286 int penalty, paused = 0;
11287
11288 switch ( cmd ) {
11289 case CLI_INIT:
11290 e->command = "queue add member";
11291 e->usage =
11292 "Usage: queue add member <dial string> to <queue> [penalty <penalty> [as <membername> [state_interface <interface> [paused <reason>]]]]\n"
11293 " Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally: a penalty, membername and a state_interface\n";
11294 return NULL;
11295 case CLI_GENERATE:
11296 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
11297 }
11298
11299 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12) && (a->argc != 14)) {
11300 return CLI_SHOWUSAGE;
11301 } else if (strcmp(a->argv[4], "to")) {
11302 return CLI_SHOWUSAGE;
11303 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
11304 return CLI_SHOWUSAGE;
11305 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
11306 return CLI_SHOWUSAGE;
11307 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
11308 return CLI_SHOWUSAGE;
11309 } else if ((a->argc == 14) && strcmp(a->argv[12], "paused")) {
11310 return CLI_SHOWUSAGE;
11311 }
11312
11313 queuename = a->argv[5];
11314 interface = a->argv[3];
11315 if (a->argc >= 8) {
11316 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
11317 if (penalty < 0) {
11318 ast_cli(a->fd, "Penalty must be >= 0\n");
11319 penalty = 0;
11320 }
11321 } else {
11322 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
11323 penalty = 0;
11324 }
11325 } else {
11326 penalty = 0;
11327 }
11328
11329 if (a->argc >= 10) {
11330 membername = a->argv[9];
11331 }
11332
11333 if (a->argc >= 12) {
11334 state_interface = a->argv[11];
11335 }
11336
11337 if (a->argc >= 14) {
11338 paused = 1;
11339 reason = a->argv[13];
11340 }
11341
11342 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, reason, 0)) {
11343 case RES_OKAY:
11344 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
11345 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
11346 } else {
11347 ast_queue_log(queuename, "CLI", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
11348 }
11349 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
11350 return CLI_SUCCESS;
11351 case RES_EXISTS:
11352 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
11353 return CLI_FAILURE;
11354 case RES_NOSUCHQUEUE:
11355 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
11356 return CLI_FAILURE;
11357 case RES_OUTOFMEMORY:
11358 ast_cli(a->fd, "Out of memory\n");
11359 return CLI_FAILURE;
11360 case RES_NOT_DYNAMIC:
11361 ast_cli(a->fd, "Member not dynamic\n");
11362 return CLI_FAILURE;
11363 default:
11364 return CLI_FAILURE;
11365 }
11366}
11367
11368static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
11369{
11370 int which = 0;
11371 struct call_queue *q;
11372 struct member *m;
11373 struct ao2_iterator queue_iter;
11374 struct ao2_iterator mem_iter;
11375 int wordlen = strlen(word);
11376
11377 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
11378 if (pos > 5 || pos < 3) {
11379 return NULL;
11380 }
11381 if (pos == 4) { /* only one possible match, 'from' */
11382 return (state == 0 ? ast_strdup("from") : NULL);
11383 }
11384
11385 if (pos == 5) { /* No need to duplicate code */
11386 return complete_queue(line, word, pos, state, 0);
11387 }
11388
11389 /* here is the case for 3, <member> */
11390 queue_iter = ao2_iterator_init(queues, 0);
11391 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
11392 ao2_lock(q);
11393 mem_iter = ao2_iterator_init(q->members, 0);
11394 while ((m = ao2_iterator_next(&mem_iter))) {
11395 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
11396 char *tmp;
11397 tmp = ast_strdup(m->interface);
11398 ao2_ref(m, -1);
11399 ao2_iterator_destroy(&mem_iter);
11400 ao2_unlock(q);
11401 queue_t_unref(q, "Done with iterator, returning interface name");
11402 ao2_iterator_destroy(&queue_iter);
11403 return tmp;
11404 }
11405 ao2_ref(m, -1);
11406 }
11407 ao2_iterator_destroy(&mem_iter);
11408 ao2_unlock(q);
11409 queue_t_unref(q, "Done with iterator");
11410 }
11411 ao2_iterator_destroy(&queue_iter);
11412
11413 return NULL;
11414}
11415
11416static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11417{
11418 const char *queuename, *interface;
11419 struct member *mem = NULL;
11420 char *res = CLI_FAILURE;
11421
11422 switch (cmd) {
11423 case CLI_INIT:
11424 e->command = "queue remove member";
11425 e->usage =
11426 "Usage: queue remove member <channel> from <queue>\n"
11427 " Remove a specific channel from a queue.\n";
11428 return NULL;
11429 case CLI_GENERATE:
11430 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
11431 }
11432
11433 if (a->argc != 6) {
11434 return CLI_SHOWUSAGE;
11435 } else if (strcmp(a->argv[4], "from")) {
11436 return CLI_SHOWUSAGE;
11437 }
11438
11439 queuename = a->argv[5];
11440 interface = a->argv[3];
11441
11443 mem = find_member_by_queuename_and_interface(queuename, interface);
11444 }
11445
11446 switch (remove_from_queue(queuename, interface)) {
11447 case RES_OKAY:
11448 if (!mem || ast_strlen_zero(mem->membername)) {
11449 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
11450 } else {
11451 ast_queue_log(queuename, "CLI", mem->membername, "REMOVEMEMBER", "%s", "");
11452 }
11453 ast_cli(a->fd, "Removed interface %s from queue '%s'\n", interface, queuename);
11454 res = CLI_SUCCESS;
11455 break;
11456 case RES_EXISTS:
11457 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
11458 break;
11459 case RES_NOSUCHQUEUE:
11460 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
11461 break;
11462 case RES_OUTOFMEMORY:
11463 ast_cli(a->fd, "Out of memory\n");
11464 break;
11465 case RES_NOT_DYNAMIC:
11466 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
11467 break;
11468 }
11469
11470 if (mem) {
11471 ao2_ref(mem, -1);
11472 }
11473
11474 return res;
11475}
11476
11477
11478
11479static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11480{
11481 const char *queuename, *caller;
11482 int priority, immediate = 0;
11483 char *res = CLI_FAILURE;
11484
11485 switch (cmd) {
11486 case CLI_INIT:
11487 e->command = "queue priority caller";
11488 e->usage =
11489 "Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
11490 " Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
11491 return NULL;
11492 case CLI_GENERATE:
11493 return NULL;
11494 }
11495
11496 if (a->argc < 8) {
11497 return CLI_SHOWUSAGE;
11498 } else if (strcmp(a->argv[4], "on")) {
11499 return CLI_SHOWUSAGE;
11500 } else if (strcmp(a->argv[6], "to")) {
11501 return CLI_SHOWUSAGE;
11502 } else if (sscanf(a->argv[7], "%30d", &priority) != 1) {
11503 ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
11504 return CLI_SHOWUSAGE;
11505 } else if (a->argc == 9) {
11506 if (strcmp(a->argv[8], "immediate")) {
11507 return CLI_SHOWUSAGE;
11508 }
11509 immediate = 1;
11510 }
11511
11512 caller = a->argv[3];
11513 queuename = a->argv[5];
11514
11515 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
11516 case RES_OKAY:
11517 res = CLI_SUCCESS;
11518 break;
11519 case RES_NOSUCHQUEUE:
11520 ast_cli(a->fd, "Unable change priority caller %s on queue '%s': No such queue\n", caller, queuename);
11521 break;
11522 case RES_NOT_CALLER:
11523 ast_cli(a->fd, "Unable to change priority caller '%s' on queue '%s': Not there\n", caller, queuename);
11524
11525 break;
11526 }
11527
11528 return res;
11529}
11530
11531
11532
11533static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
11534{
11535 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
11536 switch (pos) {
11537 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
11538 return NULL;
11539 case 4: /* only one possible match, "queue" */
11540 return state == 0 ? ast_strdup("queue") : NULL;
11541 case 5: /* <queue> */
11542 return complete_queue(line, word, pos, state, 0);
11543 case 6: /* "reason" */
11544 return state == 0 ? ast_strdup("reason") : NULL;
11545 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
11546 return NULL;
11547 default:
11548 return NULL;
11549 }
11550}
11551
11552static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11553{
11554 const char *queuename, *interface, *reason;
11555 int paused;
11556
11557 switch (cmd) {
11558 case CLI_INIT:
11559 e->command = "queue {pause|unpause} member";
11560 e->usage =
11561 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
11562 " Pause or unpause a queue member. Not specifying a particular queue\n"
11563 " will pause or unpause a member across all queues to which the member\n"
11564 " belongs.\n";
11565 return NULL;
11566 case CLI_GENERATE:
11567 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
11568 }
11569
11570 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
11571 return CLI_SHOWUSAGE;
11572 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
11573 return CLI_SHOWUSAGE;
11574 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
11575 return CLI_SHOWUSAGE;
11576 }
11577
11578
11579 interface = a->argv[3];
11580 queuename = a->argc >= 6 ? a->argv[5] : NULL;
11581 reason = a->argc == 8 ? a->argv[7] : NULL;
11582 paused = !strcasecmp(a->argv[1], "pause");
11583
11584 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
11585 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
11586 if (!ast_strlen_zero(queuename)) {
11587 ast_cli(a->fd, " in queue '%s'", queuename);
11588 }
11589 if (!ast_strlen_zero(reason)) {
11590 ast_cli(a->fd, " for reason '%s'", reason);
11591 }
11592 ast_cli(a->fd, "\n");
11593 return CLI_SUCCESS;
11594 } else {
11595 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
11596 if (!ast_strlen_zero(queuename)) {
11597 ast_cli(a->fd, " in queue '%s'", queuename);
11598 }
11599 if (!ast_strlen_zero(reason)) {
11600 ast_cli(a->fd, " for reason '%s'", reason);
11601 }
11602 ast_cli(a->fd, "\n");
11603 return CLI_FAILURE;
11604 }
11605}
11606
11607static char *complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
11608{
11609 /* 0 - queue; 1 - set; 2 - penalty/ringinuse; 3 - <value>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
11610 switch (pos) {
11611 case 4:
11612 if (state == 0) {
11613 return ast_strdup("on");
11614 } else {
11615 return NULL;
11616 }
11617 case 6:
11618 if (state == 0) {
11619 return ast_strdup("in");
11620 } else {
11621 return NULL;
11622 }
11623 case 7:
11624 return complete_queue(line, word, pos, state, 0);
11625 default:
11626 return NULL;
11627 }
11628}
11629
11630static char *handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11631{
11632 const char *queuename = NULL, *interface;
11633 int ringinuse;
11634
11635 switch (cmd) {
11636 case CLI_INIT:
11637 e->command = "queue set ringinuse";
11638 e->usage =
11639 "Usage: queue set ringinuse <yes/no> on <interface> [in <queue>]\n"
11640 " Set a member's ringinuse in the queue specified. If no queue is specified\n"
11641 " then that interface's penalty is set in all queues to which that interface is a member.\n";
11642 break;
11643 return NULL;
11644 case CLI_GENERATE:
11645 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11646 }
11647
11648 /* Sensible argument counts */
11649 if (a->argc != 6 && a->argc != 8) {
11650 return CLI_SHOWUSAGE;
11651 }
11652
11653 /* Uses proper indicational words */
11654 if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11655 return CLI_SHOWUSAGE;
11656 }
11657
11658 /* Set the queue name if applicable */
11659 if (a->argc == 8) {
11660 queuename = a->argv[7];
11661 }
11662
11663 /* Interface being set */
11664 interface = a->argv[5];
11665
11666 /* Check and set the ringinuse value */
11667 if (ast_true(a->argv[3])) {
11668 ringinuse = 1;
11669 } else if (ast_false(a->argv[3])) {
11670 ringinuse = 0;
11671 } else {
11672 return CLI_SHOWUSAGE;
11673 }
11674
11675 switch (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11676 case RESULT_SUCCESS:
11677 ast_cli(a->fd, "Set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11678 return CLI_SUCCESS;
11679 case RESULT_FAILURE:
11680 ast_cli(a->fd, "Failed to set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11681 return CLI_FAILURE;
11682 default:
11683 return CLI_FAILURE;
11684 }
11685}
11686
11687static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11688{
11689 const char *queuename = NULL, *interface;
11690 int penalty = 0;
11691
11692 switch (cmd) {
11693 case CLI_INIT:
11694 e->command = "queue set penalty";
11695 e->usage =
11696 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
11697 " Set a member's penalty in the queue specified. If no queue is specified\n"
11698 " then that interface's penalty is set in all queues to which that interface is a member\n";
11699 return NULL;
11700 case CLI_GENERATE:
11701 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11702 }
11703
11704 if (a->argc != 6 && a->argc != 8) {
11705 return CLI_SHOWUSAGE;
11706 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11707 return CLI_SHOWUSAGE;
11708 }
11709
11710 if (a->argc == 8) {
11711 queuename = a->argv[7];
11712 }
11713 interface = a->argv[5];
11714 penalty = atoi(a->argv[3]);
11715
11716 switch (set_member_value(queuename, interface, MEMBER_PENALTY, penalty)) {
11717 case RESULT_SUCCESS:
11718 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11719 return CLI_SUCCESS;
11720 case RESULT_FAILURE:
11721 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11722 return CLI_FAILURE;
11723 default:
11724 return CLI_FAILURE;
11725 }
11726}
11727
11728static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
11729{
11730 int which = 0;
11731 struct rule_list *rl_iter;
11732 int wordlen = strlen(word);
11733 char *ret = NULL;
11734 if (pos != 3) /* Wha? */ {
11735 return NULL;
11736 }
11737
11739 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11740 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
11741 ret = ast_strdup(rl_iter->name);
11742 break;
11743 }
11744 }
11746
11747 return ret;
11748}
11749
11750static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11751{
11752 const char *rule;
11753 struct rule_list *rl_iter;
11754 struct penalty_rule *pr_iter;
11755 switch (cmd) {
11756 case CLI_INIT:
11757 e->command = "queue show rules";
11758 e->usage =
11759 "Usage: queue show rules [rulename]\n"
11760 " Show the list of rules associated with rulename. If no\n"
11761 " rulename is specified, list all rules defined in queuerules.conf\n";
11762 return NULL;
11763 case CLI_GENERATE:
11764 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
11765 }
11766
11767 if (a->argc != 3 && a->argc != 4) {
11768 return CLI_SHOWUSAGE;
11769 }
11770
11771 rule = a->argc == 4 ? a->argv[3] : "";
11773 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11774 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
11775 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
11776 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
11777 ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d, adjust QUEUE_MIN_PENALTY %s %d and adjust QUEUE_RAISE_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value, pr_iter->raise_relative ? "by" : "to", pr_iter->raise_value);
11778 }
11779 }
11780 }
11782 return CLI_SUCCESS;
11783}
11784
11785static char *handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11786{
11787 struct ast_flags mask = {QUEUE_RESET_STATS,};
11788 int i;
11789
11790 switch (cmd) {
11791 case CLI_INIT:
11792 e->command = "queue reset stats";
11793 e->usage =
11794 "Usage: queue reset stats [<queuenames>]\n"
11795 "\n"
11796 "Issuing this command will reset statistics for\n"
11797 "<queuenames>, or for all queues if no queue is\n"
11798 "specified.\n";
11799 return NULL;
11800 case CLI_GENERATE:
11801 if (a->pos >= 3) {
11802 return complete_queue(a->line, a->word, a->pos, a->n, 17);
11803 } else {
11804 return NULL;
11805 }
11806 }
11807
11808 if (a->argc < 3) {
11809 return CLI_SHOWUSAGE;
11810 }
11811
11812 if (a->argc == 3) {
11813 reload_handler(1, &mask, NULL);
11814 return CLI_SUCCESS;
11815 }
11816
11817 for (i = 3; i < a->argc; ++i) {
11818 reload_handler(1, &mask, a->argv[i]);
11819 }
11820
11821 return CLI_SUCCESS;
11822}
11823
11824static char *handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11825{
11826 struct ast_flags mask = {0,};
11827 int i;
11828
11829 switch (cmd) {
11830 case CLI_INIT:
11831 e->command = "queue reload {parameters|members|rules|all}";
11832 e->usage =
11833 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
11834 "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
11835 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
11836 "specified in order to know what information to reload. Below is an explanation\n"
11837 "of each of these qualifiers.\n"
11838 "\n"
11839 "\t'members' - reload queue members from queues.conf\n"
11840 "\t'parameters' - reload all queue options except for queue members\n"
11841 "\t'rules' - reload the queuerules.conf file\n"
11842 "\t'all' - reload queue rules, parameters, and members\n"
11843 "\n"
11844 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
11845 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
11846 "one queue is specified when using this command, reloading queue rules may cause\n"
11847 "other queues to be affected\n";
11848 return NULL;
11849 case CLI_GENERATE:
11850 if (a->pos >= 3) {
11851 /* find the point at which the list of queue names starts */
11852 const char *command_end = a->line + strlen("queue reload ");
11853 command_end = strchr(command_end, ' ');
11854 if (!command_end) {
11855 command_end = a->line + strlen(a->line);
11856 }
11857 return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
11858 } else {
11859 return NULL;
11860 }
11861 }
11862
11863 if (a->argc < 3)
11864 return CLI_SHOWUSAGE;
11865
11866 if (!strcasecmp(a->argv[2], "rules")) {
11868 } else if (!strcasecmp(a->argv[2], "members")) {
11870 } else if (!strcasecmp(a->argv[2], "parameters")) {
11872 } else if (!strcasecmp(a->argv[2], "all")) {
11874 }
11875
11876 if (a->argc == 3) {
11877 reload_handler(1, &mask, NULL);
11878 return CLI_SUCCESS;
11879 }
11880
11881 for (i = 3; i < a->argc; ++i) {
11882 reload_handler(1, &mask, a->argv[i]);
11883 }
11884
11885 return CLI_SUCCESS;
11886}
11887
11888/*!
11889 * \brief Update Queue with data of an outgoing call
11890*/
11891static int qupd_exec(struct ast_channel *chan, const char *data)
11892{
11893 int oldtalktime;
11894 char *parse;
11895 struct call_queue *q;
11896 struct member *mem;
11897 int newtalktime = 0;
11898
11900 AST_APP_ARG(queuename);
11901 AST_APP_ARG(uniqueid);
11902 AST_APP_ARG(agent);
11904 AST_APP_ARG(talktime);
11905 AST_APP_ARG(params););
11906
11907 if (ast_strlen_zero(data)) {
11908 ast_log(LOG_WARNING, "QueueUpdate requires arguments (queuename,uniqueid,agent,status,talktime,params[totaltime,callednumber])\n");
11909 return -1;
11910 }
11911
11912 parse = ast_strdupa(data);
11913
11915
11916 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) || ast_strlen_zero(args.agent) || ast_strlen_zero(args.status)) {
11917 ast_log(LOG_WARNING, "Missing argument to QueueUpdate (queuename,uniqueid,agent,status,talktime,params[totaltime|callednumber])\n");
11918 return -1;
11919 }
11920
11921 if (!ast_strlen_zero(args.talktime)) {
11922 newtalktime = atoi(args.talktime);
11923 }
11924
11925 q = find_load_queue_rt_friendly(args.queuename);
11926 if (!q) {
11927 ast_log(LOG_WARNING, "QueueUpdate could not find requested queue '%s'\n", args.queuename);
11928 return 0;
11929 }
11930
11931 ao2_lock(q);
11932 if (q->members) {
11933 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
11934 while ((mem = ao2_iterator_next(&mem_iter))) {
11935 if (!strcasecmp(mem->membername, args.agent)) {
11936 if (!strcasecmp(args.status, "ANSWER")) {
11937 oldtalktime = q->talktime;
11938 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
11939 time(&mem->lastcall);
11940 mem->calls++;
11941 mem->lastqueue = q;
11942 q->callscompleted++;
11943
11944 if (newtalktime <= q->servicelevel) {
11945 q->callscompletedinsl++;
11946 }
11947 } else {
11948
11949 time(&mem->lastcall);
11950 q->callsabandoned++;
11951 }
11952
11953 ast_queue_log(args.queuename, args.uniqueid, args.agent, "OUTCALL", "%s|%s|%s", args.status, args.talktime, args.params);
11954 }
11955
11956 ao2_ref(mem, -1);
11957 }
11958
11959 ao2_iterator_destroy(&mem_iter);
11960 }
11961
11962 ao2_unlock(q);
11963 queue_t_unref(q, "Done with temporary pointer");
11964
11965 return 0;
11966}
11967
11968static struct ast_cli_entry cli_queue[] = {
11969 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
11970 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
11971 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
11972 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
11973 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
11974 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
11975 AST_CLI_DEFINE(handle_queue_set_member_ringinuse, "Set ringinuse for a channel of a specified queue"),
11976 AST_CLI_DEFINE(handle_queue_reload, "Reload queues, members, queue rules, or parameters"),
11977 AST_CLI_DEFINE(handle_queue_reset, "Reset statistics for a queue"),
11978 AST_CLI_DEFINE(handle_queue_change_priority_caller, "Change priority caller on queue"),
11979};
11980
11983
11984static int unload_module(void)
11985{
11988
11990
11991 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_join_type);
11992 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_leave_type);
11993 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_abandon_type);
11994
11995 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_status_type);
11996 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_added_type);
11997 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_removed_type);
11998 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_pause_type);
11999 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_penalty_type);
12000 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_ringinuse_type);
12001
12002 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_called_type);
12003 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_connect_type);
12004 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_complete_type);
12005 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_dump_type);
12006 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_ringnoanswer_type);
12007
12009 ast_manager_unregister("QueueStatus");
12010 ast_manager_unregister("QueueRule");
12011 ast_manager_unregister("QueueSummary");
12012 ast_manager_unregister("QueueAdd");
12013 ast_manager_unregister("QueueRemove");
12014 ast_manager_unregister("QueuePause");
12015 ast_manager_unregister("QueueLog");
12016 ast_manager_unregister("QueueUpdate");
12017 ast_manager_unregister("QueuePenalty");
12018 ast_manager_unregister("QueueReload");
12019 ast_manager_unregister("QueueReset");
12020 ast_manager_unregister("QueueMemberRingInUse");
12021 ast_manager_unregister("QueueChangePriorityCaller");
12022 ast_manager_unregister("QueueWithdrawCaller");
12037
12039
12040 ast_unload_realtime("queue_members");
12043
12044 queues = NULL;
12045 return 0;
12046}
12047
12048/*!
12049 * \brief Load the module
12050 *
12051 * Module loading including tests for configuration or dependencies.
12052 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
12053 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
12054 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
12055 * configuration file or other non-critical problem return
12056 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
12057 */
12058static int load_module(void)
12059{
12060 int err = 0;
12061 struct ast_flags mask = {AST_FLAGS_ALL, };
12062 struct ast_config *member_config;
12063 struct stasis_topic *queue_topic;
12065
12068 if (!queues) {
12070 }
12071
12074 if (!pending_members) {
12075 unload_module();
12077 }
12078
12079 use_weight = 0;
12080
12081 if (reload_handler(0, &mask, NULL)) {
12082 unload_module();
12084 }
12085
12086 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, "reason_paused", RQ_CHAR, 80, SENTINEL);
12087
12088 /*
12089 * This section is used to determine which name for 'ringinuse' to use in realtime members
12090 * Necessary for supporting older setups.
12091 *
12092 * It also checks if 'reason_paused' exists in the realtime backend
12093 */
12094 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
12095 if (!member_config) {
12096 realtime_ringinuse_field = "ringinuse";
12097 } else {
12098 const char *config_val;
12099
12100 if ((config_val = ast_variable_retrieve(member_config, NULL, "ringinuse"))) {
12101 ast_log(LOG_NOTICE, "ringinuse field entries found in queue_members table. Using 'ringinuse'\n");
12102 realtime_ringinuse_field = "ringinuse";
12103 } else if ((config_val = ast_variable_retrieve(member_config, NULL, "ignorebusy"))) {
12104 ast_log(LOG_NOTICE, "ignorebusy field found in queue_members table with no ringinuse field. Using 'ignorebusy'\n");
12105 realtime_ringinuse_field = "ignorebusy";
12106 } else {
12107 ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
12108 realtime_ringinuse_field = "ringinuse";
12109 }
12110
12111 if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
12113 }
12114 }
12115 ast_config_destroy(member_config);
12116
12119 }
12120
12129 err |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
12130 err |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
12137 err |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
12138 err |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
12139 err |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
12140 err |= ast_manager_register_xml("QueueChangePriorityCaller", 0, manager_change_priority_caller_on_queue);
12149
12150 /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
12152 if (!device_state_sub) {
12153 err = -1;
12154 }
12157
12159 queue_topic = ast_queue_topic_all();
12160 if (!manager_topic || !queue_topic) {
12161 unload_module();
12163 }
12165 if (!topic_forwarder) {
12166 unload_module();
12168 }
12169
12172 unload_module();
12174 }
12176 if (!agent_router) {
12177 unload_module();
12179 }
12183 NULL);
12187 NULL);
12188
12189 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_join_type);
12190 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_leave_type);
12191 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_abandon_type);
12192
12193 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_status_type);
12194 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_added_type);
12195 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_removed_type);
12196 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_pause_type);
12197 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_penalty_type);
12198 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_ringinuse_type);
12199
12200 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_called_type);
12201 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_connect_type);
12202 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_complete_type);
12203 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_dump_type);
12204 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_ringnoanswer_type);
12205
12206 if (err) {
12207 unload_module();
12209 }
12211}
12212
12213static int reload(void)
12214{
12215 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
12216 ast_unload_realtime("queue_members");
12217 reload_handler(1, &mask, NULL);
12218 return 0;
12219}
12220
12221/*!
12222 * \brief Find a member by looking up queuename and interface.
12223 * \return member or NULL if member not found.
12224 */
12225static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface)
12226{
12227 struct member *mem = NULL;
12228 struct call_queue *q;
12229
12230 if ((q = find_load_queue_rt_friendly(queuename))) {
12231 ao2_lock(q);
12232 mem = ao2_find(q->members, interface, OBJ_KEY);
12233 ao2_unlock(q);
12234 queue_t_unref(q, "Expiring temporary reference.");
12235 }
12236 return mem;
12237}
12238
12240 .support_level = AST_MODULE_SUPPORT_CORE,
12241 .load = load_module,
12242 .unload = unload_module,
12243 .reload = reload,
12244 .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
Generic Advice of Charge encode and decode routines.
void * ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
free an ast_aoc_encoded object
Definition aoc.c:322
enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
get the message type, AOC-D, AOC-E, or AOC Request
Definition aoc.c:901
struct ast_aoc_decoded * ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
decodes an encoded aoc payload.
Definition aoc.c:458
void * ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Definition aoc.c:316
struct ast_aoc_encoded * ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
encodes a decoded aoc structure so it can be passed on the wire
Definition aoc.c:659
@ AST_AOC_S
Definition aoc.h:64
char digit
jack_status_t status
Definition app_jack.c:149
const char * str
Definition app_jack.c:150
static struct ast_custom_function queuevar_function
Definition app_queue.c:9715
static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6569
static struct member * create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
allocate space for new queue member and set fields based on parameters passed
Definition app_queue.c:3028
static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
Definition app_queue.c:3740
static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
Definition app_queue.c:4754
static void load_realtime_queues(const char *queuename)
Definition app_queue.c:4134
static struct member * interface_exists(struct call_queue *q, const char *interface)
Definition app_queue.c:7691
static int is_our_turn(struct queue_ent *qe)
Check if we should start attempting to call queue members.
Definition app_queue.c:5933
static void record_abandoned(struct queue_ent *qe)
Record that a caller gave up on waiting in queue.
Definition app_queue.c:5267
static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Get number either busy / free / ready or total members of a specific queue.
Definition app_queue.c:9317
static char * queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int get_wrapuptime(struct call_queue *q, struct member *member)
Return wrapuptime.
Definition app_queue.c:2149
static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
Find rt member record to update otherwise create one.
Definition app_queue.c:3756
static int context_included(const char *parent, const char *child)
Returns if one context includes another context.
Definition app_queue.c:2940
static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
Set variables of queue.
Definition app_queue.c:2221
static int manager_queue_reset(struct mansession *s, const struct message *m)
static struct ast_manager_event_blob * queue_member_ringinuse_to_ami(struct stasis_message *message)
Definition app_queue.c:2357
static struct ast_manager_event_blob * queue_member_penalty_to_ami(struct stasis_message *message)
Definition app_queue.c:2352
member_properties
Definition app_queue.c:1918
@ MEMBER_RINGINUSE
Definition app_queue.c:1920
@ MEMBER_PENALTY
Definition app_queue.c:1919
#define RES_NOT_CALLER
Definition app_queue.c:1716
static int setup_stasis_subs(struct queue_ent *qe, struct ast_channel *peer, struct member *mem, time_t holdstart, time_t starttime, int callcompletedinsl)
Definition app_queue.c:6974
#define ANNOUNCEPOSITION_MORE_THAN
Definition app_queue.c:1942
static int pending_members_cmp(void *obj, void *arg, int flags)
Definition app_queue.c:2699
static void queue_reset_global_params(void)
Definition app_queue.c:9827
static int log_caller_id_name
queues.conf [general] option
Definition app_queue.c:1766
static char * complete_queue_rule_show(const char *line, const char *word, int pos, int state)
static void dump_queue_members(struct call_queue *pm_queue)
Dump all members in a specific queue to the database.
Definition app_queue.c:7718
#define QUEUE_UNPAUSED_DEVSTATE
Definition app_queue.c:3715
static struct ast_custom_function queuemembercount_function
Definition app_queue.c:9720
static struct ast_custom_function queuewaitingcount_function
Definition app_queue.c:9731
static int play_file(struct ast_channel *chan, const char *filename)
Definition app_queue.c:4321
static int queue_persistent_members
queues.conf [general] option
Definition app_queue.c:1736
static int montype_default
queues.conf [general] option
Definition app_queue.c:1745
static int mark_member_dead(void *obj, void *arg, int flags)
static char * app_pqm
Definition app_queue.c:1724
static struct ast_custom_function queuememberlist_function
Definition app_queue.c:9736
static struct ast_manager_event_blob * queue_channel_to_ami(const char *type, struct stasis_message *message)
Definition app_queue.c:2273
static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
Definition app_queue.c:8186
static char * realtime_ringinuse_field
name of the ringinuse field in the realtime database
Definition app_queue.c:1772
static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6716
static int remove_from_queue(const char *queuename, const char *interface)
Remove member from queue.
Definition app_queue.c:7773
static int manager_add_queue_member(struct mansession *s, const struct message *m)
#define MAX_PERIODIC_ANNOUNCEMENTS
Definition app_queue.c:1702
static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
Definition app_queue.c:3467
static int aqm_exec(struct ast_channel *chan, const char *data)
AddQueueMember application.
Definition app_queue.c:8592
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
sets the QUEUESTATUS channel variable
Definition app_queue.c:2068
static struct ast_manager_event_blob * queue_member_pause_to_ami(struct stasis_message *message)
Definition app_queue.c:2347
static void leave_queue(struct queue_ent *qe)
Caller leaving queue.
Definition app_queue.c:4550
#define MAX_QUEUE_BUCKETS
Definition app_queue.c:1709
static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
The command center for all reload operations.
static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Check if a given queue exists.
Definition app_queue.c:9275
static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.
Definition app_queue.c:9642
static char * complete_queue_pause_member(const char *line, const char *word, int pos, int state)
static void do_hang(struct callattempt *o)
common hangup actions
Definition app_queue.c:4815
static int set_member_value(const char *queuename, const char *interface, int property, int value)
Definition app_queue.c:8238
static void reload_single_member(const char *memberdata, struct call_queue *q)
reload information pertaining to a single member
Definition app_queue.c:9888
static struct ast_manager_event_blob * queue_agent_ringnoanswer_to_ami(struct stasis_message *message)
Definition app_queue.c:2442
static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart, time_t callstart, enum agent_complete_reason rsn)
Send out AMI message with member call completion status information.
Definition app_queue.c:6322
static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
reload the queues.conf file
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
Playback announcement to queued members if period has elapsed.
Definition app_queue.c:5206
static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
Reload information pertaining to a particular queue.
static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
Definition app_queue.c:7135
static struct ast_manager_event_blob * queue_member_removed_to_ami(struct stasis_message *message)
Definition app_queue.c:2342
#define DEFAULT_RETRY
Definition app_queue.c:1699
static int upqm_exec(struct ast_channel *chan, const char *data)
UnpauseQueueMember application.
Definition app_queue.c:8485
static void clear_queue(struct call_queue *q)
Definition app_queue.c:3214
static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle an attended transfer event.
Definition app_queue.c:6661
static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
Definition app_queue.c:4527
#define RES_NOT_DYNAMIC
Definition app_queue.c:1715
static int compare_weight(struct call_queue *rq, struct member *member)
Definition app_queue.c:4720
static char * handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int set_member_ringinuse_help_members(struct call_queue *q, const char *interface, int ringinuse)
Definition app_queue.c:8199
static void queue_agent_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6353
queue_timeout_priority
Definition app_queue.c:1804
@ TIMEOUT_PRIORITY_CONF
Definition app_queue.c:1806
@ TIMEOUT_PRIORITY_APP
Definition app_queue.c:1805
static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue.
Definition app_queue.c:9594
static char * app_ql
Definition app_queue.c:1728
static void queue_set_global_params(struct ast_config *cfg)
Definition app_queue.c:9840
static void reload_queue_members(void)
Reload dynamic queue members persisted into the astdb.
Definition app_queue.c:8338
static int rqm_exec(struct ast_channel *chan, const char *data)
RemoveQueueMember application.
Definition app_queue.c:8521
static int valid_exit(struct queue_ent *qe, char digit)
Check for valid exit from queue via goto.
Definition app_queue.c:4350
aqm_flags
Definition app_queue.c:1636
@ AQMFLAG_REASON
Definition app_queue.c:1638
@ AQMFLAG_PAUSED
Definition app_queue.c:1637
static int is_member_available(struct call_queue *q, struct member *mem)
Definition app_queue.c:2769
#define QUEUE_PAUSED_DEVSTATE
Definition app_queue.c:3714
#define ANNOUNCEPOSITION_NO
Definition app_queue.c:1941
static char * handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void handle_masquerade(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6915
static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
The waiting areas for callers who are not actively calling members.
Definition app_queue.c:6077
static void print_queue(struct mansession *s, int fd, struct call_queue *q)
Print a single queue to AMI or the CLI.
char * text
Definition app_queue.c:1791
#define MAX_CALL_ATTEMPT_BUCKETS
Definition app_queue.c:2677
static char * app_rqm
Definition app_queue.c:1722
static void copy_rules(struct queue_ent *qe, const char *rulename)
Copy rule from global list into specified queue.
Definition app_queue.c:8720
static int manager_queue_rule_show(struct mansession *s, const struct message *m)
static const struct strategy strategies[]
static int request_withdraw_caller_from_queue(const char *queuename, const char *caller, const char *withdraw_info)
Request to withdraw a caller from a queue.
Definition app_queue.c:7955
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
Definition app_queue.c:8090
static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
Change priority caller into a queue.
Definition app_queue.c:7879
static struct ao2_container * queues
Definition app_queue.c:2059
static int negative_penalty_invalid
queues.conf [general] option
Definition app_queue.c:1757
#define AST_MAX_WATCHERS
Definition app_queue.c:5379
static int load_realtime_rules(void)
Load queue rules from realtime.
Definition app_queue.c:3355
static void queue_rules_reset_global_params(void)
Definition app_queue.c:9748
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
Definition app_queue.c:6165
#define FORCELONGESTWAITINGCALLER_PRIO
Definition app_queue.c:1947
static void escape_and_substitute(struct ast_channel *chan, const char *input, char *output, size_t size)
Definition app_queue.c:7104
#define RES_OUTOFMEMORY
Definition app_queue.c:1713
static int member_hash_fn(const void *obj, const int flags)
Definition app_queue.c:3083
static int member_cmp_fn(void *obj1, void *obj2, int flags)
Definition app_queue.c:3099
#define queues_t_unlink(c, q, tag)
Definition app_queue.c:2218
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY
The minimum number of seconds between position announcements.
Definition app_queue.c:1707
static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
Hang up a list of outgoing calls.
Definition app_queue.c:4647
static void queue_stasis_data_destructor(void *obj)
Definition app_queue.c:6452
static struct ast_manager_event_blob * queue_member_added_to_ami(struct stasis_message *message)
Definition app_queue.c:2337
static struct ast_manager_event_blob * queue_agent_dump_to_ami(struct stasis_message *message)
Definition app_queue.c:2437
static int extensionstate2devicestate(int state)
Helper function which converts from extension state to device state values.
Definition app_queue.c:2891
#define queues_t_link(c, q, tag)
Definition app_queue.c:2217
static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
Definition app_queue.c:2969
static int manager_queue_reload(struct mansession *s, const struct message *m)
static int get_member_penalty(char *queuename, char *interface)
Gets members penalty.
Definition app_queue.c:8307
static int realtime_rules
queuerules.conf [general] option
Definition app_queue.c:1751
static void handle_local_optimization_begin(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6735
static int pqm_exec(struct ast_channel *chan, const char *data)
PauseQueueMember application.
Definition app_queue.c:8449
static char * complete_queue(const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset)
Check if a given word is in a space-delimited list.
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate, int raise_respect_min)
Check if members are available.
Definition app_queue.c:2580
static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
Calculate the metric of each member in the outgoing callattempts.
Definition app_queue.c:6237
static char * handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int qupd_exec(struct ast_channel *chan, const char *data)
Update Queue with data of an outgoing call.
static char * complete_queue_show(const char *line, const char *word, int pos, int state)
#define FORCELONGESTWAITINGCALLER_YES
Definition app_queue.c:1946
#define FORCELONGESTWAITINGCALLER_NO
Definition app_queue.c:1945
static char * handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
create interface var with all queue details.
Definition app_queue.c:9231
static int reload_queue_rules(int reload)
Reload the rules defined in queuerules.conf.
Definition app_queue.c:9768
static char * complete_queue_add_member(const char *line, const char *word, int pos, int state)
static char * handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
static char * complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
Definition app_queue.c:4845
static const char * int2strat(int strategy)
Definition app_queue.c:2080
#define RES_NOSUCHQUEUE
Definition app_queue.c:1714
static void free_members(struct call_queue *q, int all)
Iterate through queue's member list and delete them.
Definition app_queue.c:3874
static int publish_queue_member_pause(struct call_queue *q, struct member *member)
Definition app_queue.c:7994
static void callattempt_free(struct callattempt *doomed)
Definition app_queue.c:4625
static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
Search for best metric and add to Linear queue.
Definition app_queue.c:5182
#define queue_t_unref(q, tag)
Definition app_queue.c:2216
static struct callattempt * wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
Wait for a member to answer the call.
Definition app_queue.c:5393
static void update_status(struct call_queue *q, struct member *m, const int status)
set a member's status based on device state of that member's state_interface.
Definition app_queue.c:2738
static char * app_qupd
Definition app_queue.c:1730
#define queue_unref(q)
Definition app_queue.c:2214
#define ANNOUNCEHOLDTIME_ALWAYS
Definition app_queue.c:1924
static const struct ast_app_option queue_exec_options[128]
Definition app_queue.c:1633
static void queue_publish_multi_channel_blob(struct ast_channel *caller, struct ast_channel *agent, struct stasis_message_type *type, struct ast_json *blob)
Definition app_queue.c:2498
static int shared_lastcall
queues.conf [general] option
Definition app_queue.c:1748
static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
#define queue_t_ref(q, tag)
Definition app_queue.c:2215
static char * __queues_show(struct mansession *s, int fd, int argc, const char *const *argv)
Show queue(s) status and statistics.
static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
Configure a queue parameter.
Definition app_queue.c:3510
static void set_duration_var(struct ast_channel *chan, const char *var_base, int64_t duration)
Definition app_queue.c:7033
#define ANNOUNCEPOSITION_LIMIT
Definition app_queue.c:1943
static char * app_upqm
Definition app_queue.c:1726
static int clear_stats(const char *queuename)
Facilitates resetting statistics for a queue.
static struct ast_custom_function queuememberpenalty_function
Definition app_queue.c:9741
static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
Definition app_queue.c:8018
static int queue_cmp_cb(void *obj, void *arg, int flags)
Definition app_queue.c:2136
queue_result
Definition app_queue.c:1777
@ QUEUE_FULL
Definition app_queue.c:1784
@ QUEUE_UNKNOWN
Definition app_queue.c:1778
@ QUEUE_WITHDRAW
Definition app_queue.c:1786
@ QUEUE_CONTINUE
Definition app_queue.c:1785
@ QUEUE_LEAVEEMPTY
Definition app_queue.c:1781
@ QUEUE_LEAVEUNAVAIL
Definition app_queue.c:1783
@ QUEUE_JOINUNAVAIL
Definition app_queue.c:1782
@ QUEUE_JOINEMPTY
Definition app_queue.c:1780
@ QUEUE_TIMEOUT
Definition app_queue.c:1779
static int manager_request_withdraw_caller_from_queue(struct mansession *s, const struct message *m)
static char * app
Definition app_queue.c:1718
static int log_unpause_on_reason_change
queues.conf [general] option
Definition app_queue.c:1769
static int set_member_value_help_members(struct call_queue *q, const char *interface, int property, int value)
Definition app_queue.c:8215
static int queue_hash_cb(const void *obj, const int flags)
Definition app_queue.c:2129
static struct call_queue * find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
Reload a single queue via realtime.
Definition app_queue.c:3929
static const char *const pm_family
Persistent Members astdb family.
Definition app_queue.c:1733
#define queue_ref(q)
Definition app_queue.c:2213
static int log_membername_as_agent
queues.conf [general] option
Definition app_queue.c:1760
static void update_qe_rule(struct queue_ent *qe)
update rules for queues
Definition app_queue.c:5982
static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
Definition app_queue.c:5363
static struct ast_manager_event_blob * queue_agent_called_to_ami(struct stasis_message *message)
Definition app_queue.c:2422
static int queue_exec(struct ast_channel *chan, const char *data)
The starting point for all queue calls.
Definition app_queue.c:8764
static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
Definition app_queue.c:4237
static char * handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, char *interface, char *membername, int autopause)
RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
Definition app_queue.c:5298
static const struct ast_app_option aqm_opts[128]
Definition app_queue.c:1649
static void destroy_queue_member_cb(void *obj)
Definition app_queue.c:3018
static void init_queue(struct call_queue *q)
Initialize Queue default values.
Definition app_queue.c:3112
static struct ao2_container * pending_members
Definition app_queue.c:2676
static struct ast_manager_event_blob * queue_multi_channel_to_ami(const char *type, struct stasis_message *message)
Definition app_queue.c:2381
static struct member * find_member_by_queuename_and_interface(const char *queuename, const char *interface)
Find a member by looking up queuename and interface.
static int force_longest_waiting_caller
queues.conf [general] option
Definition app_queue.c:1763
#define RES_OKAY
Definition app_queue.c:1711
static struct ast_manager_event_blob * queue_member_status_to_ami(struct stasis_message *message)
Definition app_queue.c:2332
static int kill_dead_members(void *obj, void *arg, int flags)
#define DEFAULT_TIMEOUT
Definition app_queue.c:1700
static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
Definition app_queue.c:6469
static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue.
Definition app_queue.c:9557
static int pending_members_hash(const void *obj, const int flags)
Definition app_queue.c:2679
static int autofill_default
queues.conf [general] option
Definition app_queue.c:1742
static int manager_pause_queue_member(struct mansession *s, const struct message *m)
static void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
Insert the 'new' entry after the 'prev' entry of queue 'q'.
Definition app_queue.c:2247
#define ANNOUNCEHOLDTIME_ONCE
Definition app_queue.c:1925
static struct ast_manager_event_blob * queue_caller_join_to_ami(struct stasis_message *message)
Definition app_queue.c:2292
static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
Definition app_queue.c:2182
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
Change queue penalty by adding rule.
Definition app_queue.c:3246
static char * handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int num_available_members(struct call_queue *q)
Get the number of members available to accept a call.
Definition app_queue.c:4687
static int ql_exec(struct ast_channel *chan, const char *data)
QueueLog application.
Definition app_queue.c:8686
static int manager_remove_queue_member(struct mansession *s, const struct message *m)
static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
Definition app_queue.c:4162
static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
Search for best metric and add to Round Robbin queue.
Definition app_queue.c:5158
static struct stasis_message_router * agent_router
static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
Definition app_queue.c:2200
static struct member * get_interface_helper(struct call_queue *q, const char *interface)
Definition app_queue.c:9294
static int mark_unfound(void *obj, void *arg, int flags)
queue_reload_mask
Definition app_queue.c:1668
@ QUEUE_RELOAD_RULES
Definition app_queue.c:1671
@ QUEUE_RELOAD_MEMBER
Definition app_queue.c:1670
@ QUEUE_RESET_STATS
Definition app_queue.c:1672
@ QUEUE_RELOAD_PARAMETERS
Definition app_queue.c:1669
static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
set a member's status based on device state of that member's interface
Definition app_queue.c:2804
static int load_module(void)
Load the module.
static void member_add_to_queue(struct call_queue *queue, struct member *mem)
Definition app_queue.c:3724
#define RECHECK
Definition app_queue.c:1701
static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
Part 2 of ring_one.
Definition app_queue.c:4941
static void pending_members_remove(struct member *mem)
Definition app_queue.c:2727
static void end_bridge_callback(void *data)
Definition app_queue.c:7053
enum queue_result id
Definition app_queue.c:1790
static struct stasis_subscription * device_state_sub
Subscription to device state change messages.
Definition app_queue.c:1754
static int manager_queues_summary(struct mansession *s, const struct message *m)
Summary of queue info via the AMI.
static struct call_queue * alloc_queue(const char *queuename)
Definition app_queue.c:3905
static struct callattempt * find_best(struct callattempt *outgoing)
find the entry with the best metric, or NULL
Definition app_queue.c:5073
static int get_queue_member_status(struct member *cur)
Return the current state of a member.
Definition app_queue.c:3013
static int unload_module(void)
static int reload(void)
#define QUEUE_UNKNOWN_PAUSED_DEVSTATE
Definition app_queue.c:3716
static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
Definition app_queue.c:4635
static int manager_queues_status(struct mansession *s, const struct message *m)
Queue status info via AMI.
static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6794
static int say_position(struct queue_ent *qe, int ringing)
Definition app_queue.c:4385
static int realtime_reason_paused
does realtime backend support reason_paused
Definition app_queue.c:1775
static struct queue_stasis_data * queue_stasis_data_alloc(struct queue_ent *qe, struct ast_channel *peer, struct member *mem, time_t holdstart, time_t starttime, int callcompletedinsl)
Definition app_queue.c:6484
static void update_realtime_members(struct call_queue *q)
Definition app_queue.c:4178
static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.
Definition app_queue.c:9674
static void queue_publish_multi_channel_snapshot_blob(struct stasis_topic *topic, struct ast_channel_snapshot *caller_snapshot, struct ast_channel_snapshot *agent_snapshot, struct stasis_message_type *type, struct ast_json *blob)
Definition app_queue.c:2463
static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface, const char *reason_paused, int wrapuptime)
Add member to queue.
Definition app_queue.c:7826
static struct ast_manager_event_blob * queue_agent_connect_to_ami(struct stasis_message *message)
Definition app_queue.c:2427
static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
Definition app_queue.c:7046
empty_conditions
Definition app_queue.c:1907
@ QUEUE_EMPTY_INVALID
Definition app_queue.c:1913
@ QUEUE_EMPTY_UNKNOWN
Definition app_queue.c:1914
@ QUEUE_EMPTY_PENALTY
Definition app_queue.c:1908
@ QUEUE_EMPTY_RINGING
Definition app_queue.c:1911
@ QUEUE_EMPTY_INUSE
Definition app_queue.c:1910
@ QUEUE_EMPTY_UNAVAILABLE
Definition app_queue.c:1912
@ QUEUE_EMPTY_WRAPUP
Definition app_queue.c:1915
@ QUEUE_EMPTY_PAUSED
Definition app_queue.c:1909
static struct ast_json * queue_member_blob_create(struct call_queue *q, struct member *mem)
Definition app_queue.c:2553
static void queue_channel_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6949
static int use_weight
Records that one or more queues use weight.
Definition app_queue.c:1739
#define ANNOUNCEPOSITION_YES
Definition app_queue.c:1940
static int wait_a_bit(struct queue_ent *qe)
Definition app_queue.c:7678
static char * app_aqm
Definition app_queue.c:1720
static void destroy_queue(void *obj)
Free queue's member list then its string fields.
Definition app_queue.c:3890
static int kill_if_unfound(void *obj, void *arg, int flags)
static void handle_hangup(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition app_queue.c:6858
static void do_print(struct mansession *s, int fd, const char *str)
direct output to manager or cli with proper terminator
static char * complete_queue_remove_member(const char *line, const char *word, int pos, int state)
static char * handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define RES_EXISTS
Definition app_queue.c:1712
static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse.
Definition app_queue.c:9419
static void queue_rules_set_global_params(struct ast_config *cfg)
Definition app_queue.c:9754
static int member_status_available(int status)
Definition app_queue.c:4831
static int queue_member_decrement_followers(void *obj, void *arg, int flag)
Definition app_queue.c:2163
static struct ast_cli_entry cli_queue[]
@ OPT_ARG_CALLEE_GO_ON
Definition app_queue.c:1602
@ OPT_ARG_PREDIAL_CALLEE
Definition app_queue.c:1603
@ OPT_ARG_MUSICONHOLD_CLASS
Definition app_queue.c:1605
@ OPT_ARG_PREDIAL_CALLER
Definition app_queue.c:1604
@ OPT_ARG_ARRAY_SIZE
Definition app_queue.c:1607
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition app_queue.c:4073
static struct ast_custom_function queuegetchannel_function
Definition app_queue.c:9726
static struct ast_manager_event_blob * queue_caller_abandon_to_ami(struct stasis_message *message)
Definition app_queue.c:2302
static struct stasis_forward * topic_forwarder
static struct ast_custom_function queueexists_function
Definition app_queue.c:9710
aqm_args
Definition app_queue.c:1641
@ AQM_OPT_ARG_ARRAY_SIZE
Definition app_queue.c:1643
@ AQM_OPT_ARG_PAUSE_REASON
Definition app_queue.c:1642
static int strat2int(const char *strategy)
Definition app_queue.c:2093
static int queue_function_queuegetchannel(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue.
Definition app_queue.c:9478
static struct ast_manager_event_blob * queue_agent_complete_to_ami(struct stasis_message *message)
Definition app_queue.c:2432
static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
Definition app_queue.c:7087
static int word_in_list(const char *list, const char *word)
Check if a given word is in a space-delimited list.
static void log_attended_transfer(struct queue_stasis_data *queue_data, struct ast_attended_transfer_message *atxfer_msg)
Definition app_queue.c:6523
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
Place a call to a queue member.
Definition app_queue.c:5098
@ OPT_CALLER_AUTOMON
Definition app_queue.c:1595
@ OPT_CALLEE_PARK
Definition app_queue.c:1585
@ OPT_PREDIAL_CALLER
Definition app_queue.c:1597
@ OPT_GO_ON
Definition app_queue.c:1578
@ OPT_IGNORE_CONNECTEDLINE
Definition app_queue.c:1584
@ OPT_CALLEE_AUTOMON
Definition app_queue.c:1594
@ OPT_CALLEE_TRANSFER
Definition app_queue.c:1590
@ OPT_CALLEE_GO_ON
Definition app_queue.c:1580
@ OPT_MARK_AS_ANSWERED
Definition app_queue.c:1577
@ OPT_IGNORE_CALL_FW
Definition app_queue.c:1583
@ OPT_CALLER_PARK
Definition app_queue.c:1586
@ OPT_NO_RETRY
Definition app_queue.c:1587
@ OPT_DATA_QUALITY
Definition app_queue.c:1579
@ OPT_CALLER_HANGUP
Definition app_queue.c:1582
@ OPT_MUSICONHOLD_CLASS
Definition app_queue.c:1598
@ OPT_CALLEE_AUTOMIXMON
Definition app_queue.c:1592
@ OPT_CALLEE_HANGUP
Definition app_queue.c:1581
@ OPT_CALLER_AUTOMIXMON
Definition app_queue.c:1593
@ OPT_RINGING
Definition app_queue.c:1588
@ OPT_CALLER_TRANSFER
Definition app_queue.c:1591
@ OPT_PREDIAL_CALLEE
Definition app_queue.c:1596
@ OPT_RING_WHEN_RINGING
Definition app_queue.c:1589
static const struct @51 queue_results[]
static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle a blind transfer event.
Definition app_queue.c:6602
static const struct autopause autopausesmodes[]
agent_complete_reason
Definition app_queue.c:6315
@ AGENT
Definition app_queue.c:6317
@ CALLER
Definition app_queue.c:6316
@ TRANSFER
Definition app_queue.c:6318
static struct ast_manager_event_blob * queue_member_to_ami(const char *type, struct stasis_message *message)
Definition app_queue.c:2317
@ QUEUE_STRATEGY_RINGALL
Definition app_queue.c:1652
@ QUEUE_STRATEGY_RRMEMORY
Definition app_queue.c:1656
@ QUEUE_STRATEGY_LINEAR
Definition app_queue.c:1657
@ QUEUE_STRATEGY_LEASTRECENT
Definition app_queue.c:1653
@ QUEUE_STRATEGY_RANDOM
Definition app_queue.c:1655
@ QUEUE_STRATEGY_FEWESTCALLS
Definition app_queue.c:1654
@ QUEUE_STRATEGY_RRORDERED
Definition app_queue.c:1659
@ QUEUE_STRATEGY_WRANDOM
Definition app_queue.c:1658
static int autopause2int(const char *autopause)
Definition app_queue.c:2106
static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
static int compress_char(const char c)
Definition app_queue.c:3073
static void queue_publish_member_blob(struct stasis_message_type *type, struct ast_json *blob)
Definition app_queue.c:2529
@ QUEUE_AUTOPAUSE_ON
Definition app_queue.c:1664
@ QUEUE_AUTOPAUSE_OFF
Definition app_queue.c:1663
@ QUEUE_AUTOPAUSE_ALL
Definition app_queue.c:1665
static int manager_queue_log_custom(struct mansession *s, const struct message *m)
static struct ast_manager_event_blob * queue_caller_leave_to_ami(struct stasis_message *message)
Definition app_queue.c:2297
static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *gosub, int ringing)
Definition app_queue.c:7208
static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
Definition app_queue.c:8150
unsigned int stop
Definition app_sla.c:342
ast_mutex_t lock
Definition app_sla.c:337
#define var
Definition ast_expr2f.c:605
Persistent data storage (akin to *doze registry)
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition db.c:335
int ast_db_get_allocated(const char *family, const char *key, char **out)
Get key value specified by family/key as a heap allocated string.
Definition db.c:431
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition db.c:472
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition db.c:635
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition db.c:695
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition asterisk.h:40
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition astmm.h:191
#define ast_log
Definition astobj2.c:42
int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy all object references in the src container into the dest container.
#define ao2_iterator_next(iter)
Definition astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition astobj2.h:1532
@ CMP_MATCH
Definition astobj2.h:1027
@ CMP_STOP
Definition astobj2.h:1028
#define OBJ_KEY
Definition astobj2.h:1151
#define OBJ_POINTER
Definition astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition astobj2.h:363
#define ao2_t_iterator_next(iter, tag)
Definition astobj2.h:1909
#define ao2_t_find(container, arg, flags, tag)
Definition astobj2.h:1734
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition astobj2.h:1693
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition astobj2.h:1578
@ AO2_ITERATOR_UNLINK
Definition astobj2.h:1863
@ AO2_ITERATOR_DONTLOCK
Assume that the ao2_container is already locked.
Definition astobj2.h:1852
#define ao2_find(container, arg, flags)
Definition astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define AO2_STRING_FIELD_SORT_FN(stype, field)
Creates a sort function for a structure string field.
Definition astobj2.h:2064
#define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a red-black tree container.
Definition astobj2.h:1349
#define ao2_unlock(a)
Definition astobj2.h:729
#define ao2_lock(a)
Definition astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ao2_t_alloc(data_size, destructor_fn, debug_msg)
Definition astobj2.h:407
@ OBJ_SEARCH_PARTIAL_KEY
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition astobj2.h:1116
@ OBJ_SEARCH_OBJECT
The arg parameter is an object of the same type.
Definition astobj2.h:1087
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition astobj2.h:1063
@ OBJ_NODATA
Definition astobj2.h:1044
@ OBJ_SEARCH_MASK
Search option field mask.
Definition astobj2.h:1072
@ OBJ_MULTIPLE
Definition astobj2.h:1049
@ OBJ_UNLINK
Definition astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition astobj2.h:1101
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition astobj2.h:1327
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition astobj2.h:1303
@ AST_BRIDGE_TRANSFER_SUCCESS
Definition bridge.h:1104
After Bridge Execution API.
void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto)
Set channel to go on in the dialplan after the bridge.
static void ringing(struct ast_channel *chan)
Helper method to send a ringing indication to a channel in a bridge.
Basic bridge subclass API.
#define AST_TRANSFERER_ROLE_NAME
@ AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM
@ AST_BRIDGE_FLAG_MERGE_INHIBIT_TO
@ AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
Check if a role exists on a channel.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
#define AST_PRES_ALLOWED
Definition callerid.h:432
@ AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER
Definition callerid.h:554
#define AST_PRES_RESTRICTION
Definition callerid.h:431
Internal Asterisk hangup causes.
#define AST_CAUSE_ANSWERED_ELSEWHERE
Definition causes.h:114
static int priority
static int connected
Definition cdr_pgsql.c:73
static int available(struct dahdi_pvt **pvt, int is_specific_channel)
static const char type[]
static int call(void *data)
General Asterisk PBX channel definitions.
void ast_channel_exten_set(struct ast_channel *chan, const char *value)
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition channel.c:3213
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
void ast_channel_appl_set(struct ast_channel *chan, const char *value)
void ast_party_redirecting_init(struct ast_party_redirecting *init)
Initialize the given redirecting structure.
Definition channel.c:2109
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Definition channel.c:6518
int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const void *connected_info, int frame)
Run a connected line interception subroutine and update a channel's connected line information.
Definition channel.c:10444
void ast_party_number_init(struct ast_party_number *init)
Initialize the given number structure.
Definition channel.c:1631
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition channel.c:2574
@ AST_CHANNEL_REQUESTOR_BRIDGE_PEER
Definition channel.h:1525
int ast_party_id_presentation(const struct ast_party_id *id)
Determine the overall presentation value for the given party.
Definition channel.c:1808
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition channel.c:2059
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
void ast_channel_set_caller_event(struct ast_channel *chan, const struct ast_party_caller *caller, const struct ast_set_party_caller *update)
Set the caller id information in the Asterisk channel and generate an AMI event if the caller id name...
Definition channel.c:7446
struct ast_channel * ast_waitfor_n(struct ast_channel **chan, int n, int *ms)
Waits for input on a group of channels Wait for input on an array of channels for a given # of millis...
Definition channel.c:3195
#define ast_channel_lock(chan)
Definition channel.h:2989
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
Make the frame formats of two channels compatible.
Definition channel.c:6777
struct ast_format_cap * ast_channel_nativeformats(const struct ast_channel *chan)
void ast_channel_data_set(struct ast_channel *chan, const char *value)
@ AST_FEATURE_AUTOMIXMON
Definition channel.h:1089
@ AST_FEATURE_REDIRECT
Definition channel.h:1084
@ AST_FEATURE_PARKCALL
Definition channel.h:1088
@ AST_FEATURE_AUTOMON
Definition channel.h:1087
@ AST_FEATURE_DISCONNECT
Definition channel.h:1085
struct ast_party_redirecting * ast_channel_redirecting(struct ast_channel *chan)
void ast_party_connected_line_copy(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src)
Copy the source connected line information to the destination connected line.
Definition channel.c:2018
void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src, const struct ast_set_party_connected_line *update)
Set the connected line information based on another connected line source.
Definition channel.c:2041
int ast_channel_priority(const struct ast_channel *chan)
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition channel.h:2996
struct ast_party_connected_line * ast_channel_connected(struct ast_channel *chan)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
Inherit datastores from a parent to a child.
Definition channel.c:2359
void ast_channel_req_accountcodes(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
Setup new channel accountcodes from the requestor channel after ast_request().
Definition channel.c:6491
const char * ast_channel_context(const struct ast_channel *chan)
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition channel.c:4312
void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected, const struct ast_set_party_connected_line *update)
Indicate that the connected line information has changed.
Definition channel.c:9199
ast_channel_adsicpe
Definition channel.h:888
void ast_party_caller_set_init(struct ast_party_caller *init, const struct ast_party_caller *guide)
Initialize the given caller structure using the given guide for a set update operation.
Definition channel.c:1986
void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
Set caller ID number, name and ANI and generate AMI event.
Definition channel.c:7408
int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const void *redirecting_info, int is_frame)
Run a redirecting interception subroutine and update a channel's redirecting information.
Definition channel.c:10489
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
Inherits channel variable from parent to child channel.
Definition channel.c:6833
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition channel.c:1417
int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected)
Parse connected line indication frame data.
Definition channel.c:8891
int ast_channel_supports_html(struct ast_channel *channel)
Checks for HTML support on a channel.
Definition channel.c:6680
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition channel.c:446
int ast_channel_hangupcause(const struct ast_channel *chan)
int64_t ast_channel_get_duration_ms(struct ast_channel *chan)
Obtain how long it's been, in milliseconds, since the channel was created.
Definition channel.c:2854
struct ast_party_dialed * ast_channel_dialed(struct ast_channel *chan)
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
Indicates condition of channel, with payload.
Definition channel.c:4710
void ast_channel_update_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting, const struct ast_set_party_redirecting *update)
Indicate that the redirecting id has changed.
Definition channel.c:10390
#define AST_CHANNEL_NAME
Definition channel.h:173
struct timeval * ast_channel_whentohangup(struct ast_channel *chan)
void ast_party_number_free(struct ast_party_number *doomed)
Destroy the party number contents.
Definition channel.c:1678
#define AST_MAX_CONTEXT
Definition channel.h:135
void ast_party_connected_line_init(struct ast_party_connected_line *init)
Initialize the given connected line structure.
Definition channel.c:2009
int ast_channel_sendurl(struct ast_channel *channel, const char *url)
Sends a URL on a given link Send URL on link.
Definition channel.c:6692
const char * ast_channel_language(const struct ast_channel *chan)
void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
Destroy the redirecting information contents.
Definition channel.c:2166
void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_party_caller *src)
Copy the caller information to the connected line information.
Definition channel.c:8399
void ast_channel_req_accountcodes_precious(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
Setup new channel accountcodes from the requestor channel after ast_request().
Definition channel.c:6496
const char * ast_channel_call_forward(const struct ast_channel *chan)
int ast_pre_call(struct ast_channel *chan, const char *sub_args)
Execute a Gosub call on the channel before a call is placed.
Definition channel.c:6501
int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block)
Sets an option on a channel.
Definition channel.c:7496
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide)
Initialize the given connected line structure using the given guide for a set update operation.
Definition channel.c:2032
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer)
Put chan into autoservice while hanging up peer.
void ast_channel_adsicpe_set(struct ast_channel *chan, enum ast_channel_adsicpe value)
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition channel.c:4332
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition channel.c:1561
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition channel.c:6411
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition channel.h:2990
#define AST_MAX_EXTENSION
Definition channel.h:134
void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src)
Copy the source redirecting information to the destination redirecting.
Definition channel.c:2122
#define MAX_MUSICCLASS
Definition channel.h:175
ast_channel_state
ast_channel states
@ AST_STATE_UP
size_t current
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
#define RESULT_SUCCESS
Definition cli.h:40
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
#define CLI_FAILURE
Definition cli.h:46
#define RESULT_FAILURE
Definition cli.h:42
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
short word
#define SENTINEL
Definition compiler.h:87
Local proxy channel special access.
struct stasis_message_type * ast_local_optimization_end_type(void)
Message type for when a local channel optimization completes.
struct stasis_message_type * ast_local_optimization_begin_type(void)
Message type for when a local channel optimization begins.
Device state management.
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
@ AST_DEVSTATE_CACHABLE
Definition devicestate.h:70
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
const char * ast_devstate2str(enum ast_device_state devstate) attribute_pure
Convert device state to text string for output.
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
ast_device_state
Device States.
Definition devicestate.h:52
@ AST_DEVICE_RINGINUSE
Definition devicestate.h:60
@ AST_DEVICE_INUSE
Definition devicestate.h:55
@ AST_DEVICE_UNKNOWN
Definition devicestate.h:53
@ AST_DEVICE_ONHOLD
Definition devicestate.h:61
@ AST_DEVICE_RINGING
Definition devicestate.h:59
@ AST_DEVICE_INVALID
Definition devicestate.h:57
@ AST_DEVICE_BUSY
Definition devicestate.h:56
@ AST_DEVICE_NOT_INUSE
Definition devicestate.h:54
@ AST_DEVICE_UNAVAILABLE
Definition devicestate.h:58
Dialing API.
const char * ast_hangup_cause_to_dial_status(int hangup_cause)
Convert a hangup cause to a publishable dial status.
Definition dial.c:752
char buf[BUFSIZE]
Definition eagi_proxy.c:66
#define abs(x)
Definition f2c.h:195
long int flag
Definition f2c.h:83
Call Parking and Pickup API Includes code and algorithms from the Zapata library.
int ast_bridge_call_with_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, unsigned int flags)
Bridge a call, and add additional flags to the bridge.
Definition features.c:604
Generic File Format Support. Should be included by clients of the file handling routines....
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition file.c:223
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition file.c:1312
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition file.c:1148
#define AST_DIGIT_ANY
Definition file.h:48
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition file.c:1874
static const char name[]
Definition format_mp3.c:68
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition manager.c:2042
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:2000
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition manager.c:2078
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition manager.c:450
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition manager.c:2032
struct ast_str * ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
Convert a JSON object into an AMI compatible string.
Definition manager.c:551
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1661
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition manager.c:2086
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1921
static struct stasis_topic * manager_topic
A stasis_topic that all topics AMI cares about will be forwarded to.
Definition manager.c:182
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7716
struct stasis_message_type * ast_channel_masquerade_type(void)
Message type for when a channel is being masqueraded.
struct stasis_topic * ast_channel_topic_all(void)
A topic which publishes the events for all channels.
struct ast_multi_channel_blob * ast_multi_channel_blob_create(struct ast_json *blob)
Create a ast_multi_channel_blob suitable for a stasis_message.
struct ast_channel_snapshot * ast_multi_channel_blob_get_channel(struct ast_multi_channel_blob *obj, const char *role)
Retrieve a channel snapshot associated with a specific role from a ast_multi_channel_blob.
struct stasis_message_type * stasis_subscription_change_type(void)
Gets the message type for subscription change notices.
void ast_channel_publish_cached_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
Publish a channel blob message using the latest snapshot from the cache.
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object,...
struct stasis_message_type * ast_channel_agent_logoff_type(void)
Message type for agent logoff on a channel.
struct ast_channel_snapshot * ast_channel_snapshot_create(struct ast_channel *chan)
Generate a snapshot of the channel state. This is an ao2 object, so ao2_cleanup() to deallocate.
void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer, const char *dialstring, const char *dialstatus)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
void ast_channel_stage_snapshot_done(struct ast_channel *chan)
Clear flag to indicate channel snapshot is being staged, and publish snapshot.
void ast_multi_channel_blob_add_channel(struct ast_multi_channel_blob *obj, const char *role, struct ast_channel_snapshot *snapshot)
Add a ast_channel_snapshot to a ast_multi_channel_blob object.
struct stasis_message_type * ast_channel_hangup_request_type(void)
Message type for when a hangup is requested on a channel.
struct stasis_message_type * ast_channel_agent_login_type(void)
Message type for agent login on a channel.
struct ast_json * ast_multi_channel_blob_get_json(struct ast_multi_channel_blob *obj)
Retrieve the JSON blob from a ast_multi_channel_blob. Returned ast_json is still owned by obj.
void ast_channel_stage_snapshot(struct ast_channel *chan)
Set flag to indicate channel snapshot is being staged.
void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_channel *peer, struct ast_channel *forwarded, const char *dialstring, const char *dialstatus, const char *forward)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define END_OPTIONS
struct stasis_topic * ast_queue_topic(const char *queuename)
Get the Stasis Message Bus API topic for queue messages for a particular queue name.
Definition main/app.c:3350
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define BEGIN_OPTIONS
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
struct stasis_topic * ast_queue_topic_all(void)
Get the Stasis Message Bus API topic for queue messages.
Definition main/app.c:3345
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
Run a subroutine on a channel, placing an optional second channel into autoservice.
Definition main/app.c:297
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition main/app.c:3067
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition extconf.c:3324
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition extconf.c:3272
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
#define CONFIG_STATUS_FILEUNCHANGED
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition extconf.c:1213
#define AST_FRAME_DTMF
#define AST_OPTION_TONE_VERIFY
#define ast_frfree(fr)
@ AST_FRAME_CONTROL
@ AST_CONTROL_OFFHOOK
@ AST_CONTROL_REDIRECTING
@ AST_CONTROL_CONGESTION
@ AST_CONTROL_ANSWER
@ AST_CONTROL_RINGING
@ AST_CONTROL_HANGUP
@ AST_CONTROL_CONNECTED_LINE
@ AST_CONTROL_PVT_CAUSE_CODE
#define ast_debug(level,...)
Log a DEBUG message.
void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt,...)
Definition logger.c:957
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition json.c:73
struct ast_json_payload * ast_json_payload_create(struct ast_json *json)
Create an ao2 object to pass json blobs as data payloads for stasis.
Definition json.c:756
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition json.c:612
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition json.c:283
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition json.c:407
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition json.c:332
AST_JSON_INT_T ast_json_int_t
Primarily used to cast when packing to an "I" type.
Definition json.h:87
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
#define AST_LIST_LOCK(head)
Locks a list.
Definition linkedlists.h:40
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Asterisk locking-related definitions:
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition lock.h:611
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition lock.h:764
int errno
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition manager.h:193
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
Definition manager.c:10198
#define EVENT_FLAG_AGENT
Definition manager.h:80
struct ast_str * ast_manager_build_channel_state_string_prefix(const struct ast_channel_snapshot *snapshot, const char *prefix)
Generate the AMI message body from a channel snapshot.
int ast_max_forwards_decrement(struct ast_channel *chan)
Decrement the max forwards count for a particular channel.
int ast_max_forwards_get(struct ast_channel *chan)
Get the current max forwards for a particular channel.
loadable MixMonitor functionality
int ast_start_mixmonitor(struct ast_channel *chan, const char *filename, const char *options)
Start a mixmonitor on a channel with the given parameters.
Definition mixmonitor.c:74
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_DEVSTATE_CONSUMER
Definition module.h:347
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition pbx_app.c:404
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition module.h:640
Music on hold handling.
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition channel.c:7840
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition channel.c:7850
Core PBX routines and definitions.
const struct ast_include * ast_walk_context_includes(const struct ast_context *con, const struct ast_include *inc)
Definition pbx.c:8694
struct ast_context * ast_context_find(const char *name)
Find a context.
Definition extconf.c:4170
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition pbx.c:4211
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition pbx_app.c:483
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
const char * ast_get_include_name(const struct ast_include *include)
Definition pbx_include.c:50
int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *data)
Parse and set multiple channel variables, where the pairs are separated by the ',' character,...
@ AST_EXTENSION_REMOVED
Definition pbx.h:62
@ AST_EXTENSION_RINGING
Definition pbx.h:68
@ AST_EXTENSION_NOT_INUSE
Definition pbx.h:64
@ AST_EXTENSION_INUSE
Definition pbx.h:65
@ AST_EXTENSION_UNAVAILABLE
Definition pbx.h:67
@ AST_EXTENSION_ONHOLD
Definition pbx.h:69
@ AST_EXTENSION_BUSY
Definition pbx.h:66
@ AST_EXTENSION_DEACTIVATED
Definition pbx.h:63
const char * ast_get_context_name(struct ast_context *con)
Definition ael_main.c:421
#define ast_custom_function_register(acf)
Register a custom function.
Definition pbx.h:1563
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition pbx.c:8825
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
Definition pbx.c:4226
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_extension_state_del(int id, ast_state_cb_type change_cb)
Deletes a state change watcher by ID.
Definition pbx.c:3892
@ AST_HINT_UPDATE_DEVICE
Definition pbx.h:91
int ast_extension_state_add(const char *context, const char *exten, ast_state_cb_type change_cb, void *data)
Add watcher for extension states.
Definition pbx.c:3859
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition ael_main.c:165
int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
Uses hint and devicestate callback to get the state of an extension.
Definition pbx.c:3206
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition ael_main.c:211
static struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
static char url[512]
static void to_ami(struct ast_sip_subscription *sub, struct ast_str **buf)
static struct @522 args
#define NULL
Definition resample.c:96
Say numbers and dates (maybe words one day too)
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition channel.c:8341
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition stasis.h:1515
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition stasis.h:297
struct stasis_forward * stasis_forward_cancel(struct stasis_forward *forward)
Definition stasis.c:1615
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition stasis.c:1090
#define STASIS_MESSAGE_TYPE_DEFN_LOCAL(name,...)
Boiler-plate messaging macro for defining local message types.
Definition stasis.h:1467
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition stasis.c:1144
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition stasis.h:1493
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
Determine whether a message is the final message to be received on a subscription.
Definition stasis.c:1241
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition stasis.c:1201
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
Definition stasis.c:1645
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition stasis.c:1578
#define stasis_subscribe(topic, callback, data)
Definition stasis.h:649
struct stasis_message_type * ast_channel_entered_bridge_type(void)
Message type for ast_channel enter bridge blob messages.
struct stasis_message_type * ast_blind_transfer_type(void)
Message type for ast_blind_transfer_message.
struct stasis_message_type * ast_attended_transfer_type(void)
Message type for ast_attended_transfer_message.
struct stasis_topic * ast_bridge_topic_all(void)
A topic which publishes the events for all bridges.
@ AST_ATTENDED_TRANSFER_DEST_FAIL
@ AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE
@ AST_ATTENDED_TRANSFER_DEST_LOCAL_APP
@ AST_ATTENDED_TRANSFER_DEST_LINK
@ AST_ATTENDED_TRANSFER_DEST_APP
@ AST_ATTENDED_TRANSFER_DEST_THREEWAY
#define stasis_message_router_create(topic)
Create a new message router object.
void stasis_message_router_unsubscribe(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic.
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
void stasis_message_router_unsubscribe_and_join(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic, blocking until the final message has been processed.
#define stasis_message_router_create_pool(topic)
Create a new message router object.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
#define AST_STRING_FIELD(name)
Declare a string field.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition strings.h:1139
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition strings.c:238
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition utils.c:2233
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition strings.h:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
@ AST_STRSEP_TRIM
Definition strings.h:256
@ AST_STRSEP_STRIP
Definition strings.h:255
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition utils.c:2250
#define ast_str_alloca(init_len)
Definition strings.h:848
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition strings.h:1113
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
Definition strings.h:1303
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition strings.h:223
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition utils.c:1869
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition astobj2.h:1821
ast_app: A registered application
Definition pbx_app.c:45
Message representing attended transfer.
enum ast_attended_transfer_dest_type dest_type
union ast_attended_transfer_message::@301 dest
struct ast_channel_snapshot * links[2]
struct ast_bridge_channel_snapshot_pair to_transfer_target
enum ast_transfer_result result
struct ast_bridge_channel_snapshot_pair to_transferee
char bridge[AST_UUID_STR_LEN]
Message published during a blind transfer.
char exten[AST_MAX_EXTENSION]
struct ast_bridge_snapshot * bridge
enum ast_transfer_result result
char context[AST_MAX_CONTEXT]
Blob of data associated with a bridge.
struct ast_bridge_snapshot * bridge
struct ast_channel_snapshot * channel
struct ast_bridge_snapshot * bridge_snapshot
bridge configuration
Definition channel.h:1096
void * end_bridge_callback_data
Definition channel.h:1111
struct ast_flags features_callee
Definition channel.h:1098
struct ast_flags features_caller
Definition channel.h:1097
void(* end_bridge_callback)(void *)
Definition channel.h:1110
void(* end_bridge_callback_data_fixup)(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
Definition channel.h:1115
const ast_string_field uniqueid
Definition bridge.h:332
Blob of data associated with a channel.
struct ast_channel_snapshot * snapshot
struct ast_json * blob
const ast_string_field uniqueid
const ast_string_field name
Structure representing a snapshot of channel state.
struct ast_channel_snapshot_base * base
struct ast_channel_snapshot_caller * caller
Structure to describe a channel "technology", ie a channel driver See for examples:
Definition channel.h:648
const char *const type
Definition channel.h:649
Main Channel structure associated with a channel.
const struct ast_channel_tech * tech
char context[AST_MAX_CONTEXT]
descriptor for a cli entry.
Definition cli.h:171
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
ast_context: An extension context
Definition pbx.c:299
Data structure associated with a custom dialplan function.
Definition pbx.h:118
const char * name
Definition pbx.h:119
Definition astdb.h:31
struct ast_db_entry * next
Definition astdb.h:32
char * key
Definition astdb.h:33
The structure that contains device state.
enum ast_device_state state
const struct ast_eid * eid
The EID of the server where this message originated.
Structure used to handle boolean flags.
Definition utils.h:220
unsigned int flags
Definition utils.h:221
Format capabilities structure, holds formats + preference order + etc.
Definition format_cap.c:54
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@235 data
ast_include: include= support in extensions.conf
Definition pbx_include.c:37
struct ast_json * json
Definition json.h:1083
Abstract JSON element (object, array, string, int, ...).
Struct containing info for an AMI event to send out.
Definition manager.h:504
A multi channel blob data structure for multi_channel_blob stasis messages.
Caller Party information.
Definition channel.h:420
struct ast_party_id id
Caller party ID.
Definition channel.h:422
struct ast_party_id ani
Automatic Number Identification (ANI)
Definition channel.h:429
Connected Line/Party information.
Definition channel.h:458
int source
Information about the source of an update.
Definition channel.h:484
struct ast_party_id id
Connected party ID.
Definition channel.h:460
struct ast_party_id ani
Automatic Number Identification (ANI)
Definition channel.h:467
char * str
Subscriber phone number (Malloced)
Definition channel.h:388
struct ast_party_dialed::@217 number
Dialed/Called number.
int transit_network_select
Transit Network Select.
Definition channel.h:399
struct ast_party_name name
Subscriber name.
Definition channel.h:342
struct ast_party_number number
Subscriber phone number.
Definition channel.h:344
unsigned char valid
TRUE if the name information is valid/present.
Definition channel.h:281
char * str
Subscriber name (Malloced)
Definition channel.h:266
unsigned char valid
TRUE if the number information is valid/present.
Definition channel.h:299
char * str
Subscriber phone number (Malloced)
Definition channel.h:293
Redirecting Line information. RDNIS (Redirecting Directory Number Information Service) Where a call d...
Definition channel.h:524
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
Definition channel.h:529
Support for dynamic strings.
Definition strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
int autopause
Definition app_queue.c:1691
const char * name
Definition app_queue.c:1692
unsigned int autopauseunavail
Definition app_queue.c:2007
const ast_string_field sound_thereare
Definition app_queue.c:1987
unsigned int setinterfacevar
Definition app_queue.c:1993
struct ast_str * sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS]
Definition app_queue.c:1989
const ast_string_field sound_callerannounce
Definition app_queue.c:1987
int announcefrequency
Definition app_queue.c:2011
const ast_string_field sound_reporthold
Definition app_queue.c:1987
unsigned int announceholdtime
Definition app_queue.c:1999
const ast_string_field sound_holdtime
Definition app_queue.c:1987
unsigned int dead
Definition app_queue.c:1990
unsigned int reportholdtime
Definition app_queue.c:1996
unsigned int setqueueentryvar
Definition app_queue.c:1995
const ast_string_field sound_seconds
Definition app_queue.c:1987
unsigned int timeoutrestart
Definition app_queue.c:1998
struct ao2_container * members
Definition app_queue.c:2045
int periodicannouncefrequency
Definition app_queue.c:2014
const ast_string_field sound_thanks
Definition app_queue.c:1987
unsigned int announceposition_only_up
Definition app_queue.c:2001
unsigned int setqueuevar
Definition app_queue.c:1994
int announcepositionlimit
Definition app_queue.c:2010
unsigned int announce_to_first_user
Definition app_queue.c:1992
int randomperiodicannounce
Definition app_queue.c:2016
int periodicannouncestartdelay
Definition app_queue.c:2013
const ast_string_field defaultrule
Definition app_queue.c:1987
int log_restricted_caller_id
Definition app_queue.c:2043
struct queue_ent * head
Definition app_queue.c:2046
unsigned int realtime
Definition app_queue.c:2003
const ast_string_field queue_quantity2
Definition app_queue.c:1987
int servicelevel
Definition app_queue.c:2023
const ast_string_field moh
Definition app_queue.c:1987
struct call_queue::@54 list
int minannouncefrequency
Definition app_queue.c:2012
enum empty_conditions leavewhenempty
Definition app_queue.c:2009
int penaltymemberslimit
Definition app_queue.c:2029
unsigned int found
Definition app_queue.c:2004
const ast_string_field context
Definition app_queue.c:1987
const ast_string_field sound_calls
Definition app_queue.c:1987
unsigned int ringinuse
Definition app_queue.c:1991
int callsabandoned
Definition app_queue.c:2021
int roundingseconds
Definition app_queue.c:2017
int numperiodicannounce
Definition app_queue.c:2015
const ast_string_field sound_minute
Definition app_queue.c:1987
int callscompleted
Definition app_queue.c:2020
int callsabandonedinsl
Definition app_queue.c:2022
const ast_string_field sound_minutes
Definition app_queue.c:1987
unsigned int announceposition
Definition app_queue.c:2000
const ast_string_field queue_quantity1
Definition app_queue.c:1987
const ast_string_field membergosub
Definition app_queue.c:1987
char monfmt[8]
Definition app_queue.c:2025
enum empty_conditions joinempty
Definition app_queue.c:2008
unsigned int wrapped
Definition app_queue.c:1997
struct call_queue::@55 rules
const ast_string_field name
Definition app_queue.c:1987
int callscompletedinsl
Definition app_queue.c:2024
int memberdelay
Definition app_queue.c:2040
const ast_string_field sound_next
Definition app_queue.c:1987
unsigned int autopausebusy
Definition app_queue.c:2006
const ast_string_field announce
Definition app_queue.c:1987
int autopausedelay
Definition app_queue.c:2035
int timeoutpriority
Definition app_queue.c:2036
unsigned int relativeperiodicannounce
Definition app_queue.c:2005
We define a custom "local user" structure because we use it not only for keeping track of what is in ...
Definition app_queue.c:1821
unsigned int dial_callerid_absent
Definition app_queue.c:1835
unsigned int block_connected_update
Definition app_queue.c:1833
struct ast_aoc_decoded * aoc_s_rate_list
Definition app_queue.c:1838
struct ast_party_connected_line connected
Definition app_queue.c:1829
char interface[256]
Definition app_queue.c:1825
char * orig_chan_name
Definition app_queue.c:1840
struct callattempt * call_next
Definition app_queue.c:1823
struct ast_channel * chan
Definition app_queue.c:1824
unsigned int stillgoing
Definition app_queue.c:1837
struct callattempt * q_next
Definition app_queue.c:1822
struct member * member
Definition app_queue.c:1827
unsigned int pending_connected_update
Definition app_queue.c:1831
structure to hold extensions
Structure representing relevant data during a local channel optimization.
Definition app_queue.c:6391
const char * source_chan_uniqueid
Definition app_queue.c:6393
unsigned int id
Definition app_queue.c:6397
In case you didn't read that giant block of text above the mansession_session struct,...
Definition manager.c:323
Channel datastore data for max forwards.
char interface[AST_CHANNEL_NAME]
Definition app_queue.c:1880
unsigned int dead
Definition app_queue.c:1901
unsigned int delme
Definition app_queue.c:1902
int queuepos
Definition app_queue.c:1893
time_t starttime
Definition app_queue.c:1896
time_t lastcall
Definition app_queue.c:1897
int dynamic
Definition app_queue.c:1888
time_t logintime
Definition app_queue.c:1899
char membername[80]
Definition app_queue.c:1885
char rt_uniqueid[80]
Definition app_queue.c:1903
int calls
Definition app_queue.c:1887
int status
Definition app_queue.c:1890
int penalty
Definition app_queue.c:1886
int paused
Definition app_queue.c:1891
unsigned int ringinuse
Definition app_queue.c:1904
int wrapuptime
Definition app_queue.c:1895
int callcompletedinsl
Definition app_queue.c:1894
char state_exten[AST_MAX_EXTENSION]
Definition app_queue.c:1881
char state_context[AST_MAX_CONTEXT]
Definition app_queue.c:1882
int realtime
Definition app_queue.c:1889
int state_id
Definition app_queue.c:1884
time_t lastpause
Definition app_queue.c:1898
char reason_paused[80]
Definition app_queue.c:1892
struct call_queue * lastqueue
Definition app_queue.c:1900
char state_interface[AST_CHANNEL_NAME]
Definition app_queue.c:1883
Number structure.
int raise_respect_min
Definition app_queue.c:1936
struct penalty_rule::@53 list
struct call_queue * q
Definition app_queue.c:7024
struct ast_channel * chan
Definition app_queue.c:7025
struct timeval start_time
Definition app_queue.c:7026
int raise_respect_min
Definition app_queue.c:1865
char digits[AST_MAX_EXTENSION]
Definition app_queue.c:1849
int valid_digits
Definition app_queue.c:1851
time_t last_pos
Definition app_queue.c:1858
time_t last_periodic_announce_time
Definition app_queue.c:1856
time_t expire
Definition app_queue.c:1869
unsigned int withdraw
Definition app_queue.c:1871
struct penalty_rule * pr
Definition app_queue.c:1875
struct queue_ent::@52 qe_rules
int max_penalty
Definition app_queue.c:1862
int raise_penalty
Definition app_queue.c:1864
struct ast_channel * chan
Definition app_queue.c:1873
int min_penalty
Definition app_queue.c:1863
int ring_when_ringing
Definition app_queue.c:1855
int cancel_answered_elsewhere
Definition app_queue.c:1870
char announce[PATH_MAX]
Definition app_queue.c:1847
char * withdraw_info
Definition app_queue.c:1872
int linwrapped
Definition app_queue.c:1867
char moh[MAX_MUSICCLASS]
Definition app_queue.c:1846
int last_pos_said
Definition app_queue.c:1854
char context[AST_MAX_CONTEXT]
Definition app_queue.c:1848
int last_periodic_announce_sound
Definition app_queue.c:1857
const char * predial_callee
Definition app_queue.c:1850
time_t start
Definition app_queue.c:1868
struct call_queue * parent
Definition app_queue.c:1845
struct queue_ent * next
Definition app_queue.c:1876
User data for stasis subscriptions used for queue calls.
Definition app_queue.c:6415
const ast_string_field caller_uniqueid
Definition app_queue.c:6423
const ast_string_field member_uniqueid
Definition app_queue.c:6423
struct local_optimization member_optimize
Definition app_queue.c:6445
struct stasis_message_router * channel_router
Definition app_queue.c:6441
struct call_queue * queue
Definition app_queue.c:6425
const ast_string_field bridge_uniqueid
Definition app_queue.c:6423
struct stasis_message_router * bridge_router
Definition app_queue.c:6439
struct local_optimization caller_optimize
Definition app_queue.c:6443
struct member * member
Definition app_queue.c:6427
char name[80]
Definition app_queue.c:2052
struct rule_list::@57 list
struct rule_list::@56 rules
Forwarding information.
Definition stasis.c:1598
const char * name
Definition app_queue.c:1677
int strategy
Definition app_queue.c:1676
int value
Definition syslog.c:37
An API for managing task processing threads that can be shared across modules.
Handy terminal functions for vt* terms.
const char * ast_term_reset(void)
Returns the terminal reset code.
Definition term.c:357
#define COLOR_CYAN
Definition term.h:62
#define COLOR_MAGENTA
Definition term.h:60
#define COLOR_BROWN
Definition term.h:56
const char * ast_term_color(int fgcolor, int bgcolor)
Return a color sequence string.
Definition term.c:341
#define COLOR_BLACK
Definition term.h:50
#define COLOR_RED
Definition term.h:52
#define COLOR_GREEN
Definition term.h:54
static struct test_options options
static struct test_val a
static struct test_val c
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition utils.c:2315
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
Support for translation of data formats. translate.c.
FILE * out
Definition utils/frame.c:33
FILE * in
Definition utils/frame.c:33
Utility functions.
#define ast_test_flag(p, flag)
Definition utils.h:64
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_assert(a)
Definition utils.h:779
long int ast_random(void)
Definition utils.c:2346
#define ast_set_flag(p, flag)
Definition utils.h:71
#define ARRAY_LEN(a)
Definition utils.h:706
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition utils.c:2377
#define AST_FLAGS_ALL
Definition utils.h:217