Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
app_queue.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2018, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief True call queues with optional send URL on answer
22 *
23 * \author Mark Spencer <markster@digium.com>
24 *
25 * \par Development notes
26 * \note 2004-11-25: Persistent Dynamic Members added by:
27 * NetNation Communications (www.netnation.com)
28 * Kevin Lindsay <kevinl@netnation.com>
29 *
30 * Each dynamic agent in each queue is now stored in the astdb.
31 * When asterisk is restarted, each agent will be automatically
32 * readded into their recorded queues. This feature can be
33 * configured with the 'persistent_members=<1|0>' setting in the
34 * '[general]' category in queues.conf. The default is on.
35 *
36 * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
37 *
38 * \note These features added by David C. Troy <dave@toad.net>:
39 * - Per-queue holdtime calculation
40 * - Estimated holdtime announcement
41 * - Position announcement
42 * - Abandoned/completed call counters
43 * - Failout timer passed as optional app parameter
44 *
45 * Patch Version 1.07 2003-12-24 01
46 *
47 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
48 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
49 *
50 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
51 * by Matthew Enger <m.enger@xi.com.au>
52 *
53 * \ingroup applications
54 */
55
56/*! \li \ref app_queues.c uses configuration file \ref queues.conf
57 * \addtogroup configuration_file
58 */
59
60/*! \page queues.conf queues.conf
61 * \verbinclude queues.conf.sample
62 */
63
64/*** MODULEINFO
65 <support_level>core</support_level>
66 ***/
67
68#include "asterisk.h"
69
70#include <sys/time.h>
71#include <signal.h>
72#include <netinet/in.h>
73#include <ctype.h>
74
75#include "asterisk/lock.h"
76#include "asterisk/file.h"
77#include "asterisk/channel.h"
78#include "asterisk/pbx.h"
79#include "asterisk/app.h"
81#include "asterisk/module.h"
82#include "asterisk/translate.h"
83#include "asterisk/say.h"
84#include "asterisk/features.h"
86#include "asterisk/cli.h"
87#include "asterisk/manager.h"
88#include "asterisk/config.h"
89#include "asterisk/utils.h"
90#include "asterisk/causes.h"
91#include "asterisk/astdb.h"
94#include "asterisk/astobj2.h"
95#include "asterisk/strings.h"
97#include "asterisk/aoc.h"
98#include "asterisk/callerid.h"
99#include "asterisk/term.h"
100#include "asterisk/dial.h"
105#include "asterisk/core_local.h"
106#include "asterisk/mixmonitor.h"
109
110/*!
111 * \par Please read before modifying this file.
112 * There are three locks which are regularly used
113 * throughout this file, the queue list lock, the lock
114 * for each individual queue, and the interface list lock.
115 * Please be extra careful to always lock in the following order
116 * 1) queue list lock
117 * 2) individual queue lock
118 * 3) interface list lock
119 * This order has sort of "evolved" over the lifetime of this
120 * application, but it is now in place this way, so please adhere
121 * to this order!
122 */
123
124/*** DOCUMENTATION
125 <application name="Queue" language="en_US">
126 <since>
127 <version>0.2.0</version>
128 </since>
129 <synopsis>
130 Queue a call for a call queue.
131 </synopsis>
132 <syntax>
133 <parameter name="queuename" required="true" />
134 <parameter name="options">
135 <optionlist>
136 <option name="b" argsep="^">
137 <para>Before initiating an outgoing call, <literal>Gosub</literal> to the specified
138 location using the newly created channel. The <literal>Gosub</literal> will be
139 executed for each destination channel.</para>
140 <argument name="context" required="false" />
141 <argument name="exten" required="false" />
142 <argument name="priority" required="true" hasparams="optional" argsep="^">
143 <argument name="arg1" multiple="true" required="true" />
144 <argument name="argN" />
145 </argument>
146 </option>
147 <option name="B" argsep="^">
148 <para>Before initiating the outgoing call(s), <literal>Gosub</literal> to the
149 specified location using the current channel.</para>
150 <argument name="context" required="false" />
151 <argument name="exten" required="false" />
152 <argument name="priority" required="true" hasparams="optional" argsep="^">
153 <argument name="arg1" multiple="true" required="true" />
154 <argument name="argN" />
155 </argument>
156 </option>
157 <option name="C">
158 <para>Mark all calls as "answered elsewhere" when cancelled.</para>
159 </option>
160 <option name="c">
161 <para>Continue in the dialplan if the callee hangs up.</para>
162 </option>
163 <option name="d">
164 <para>Data-quality (modem) call (minimum delay).</para>
165 <para>This option only applies to DAHDI channels. By default,
166 DTMF is verified by muting audio TX/RX to verify the tone
167 is still present. This option disables that behavior.</para>
168 </option>
169 <option name="F" argsep="^">
170 <argument name="context" required="false" />
171 <argument name="exten" required="false" />
172 <argument name="priority" required="true" />
173 <para>When the caller hangs up, transfer the <emphasis>called member</emphasis>
174 to the specified destination and <emphasis>start</emphasis> execution at that location.</para>
175 <para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
176 prefixed with one or two underbars ('_').</para>
177 <para>NOTE: Using this option from a GoSub() might not make sense as there would be no return points.</para>
178 </option>
179 <option name="h">
180 <para>Allow <emphasis>callee</emphasis> to hang up by pressing <literal>*</literal>.</para>
181 </option>
182 <option name="H">
183 <para>Allow <emphasis>caller</emphasis> to hang up by pressing <literal>*</literal>.</para>
184 </option>
185 <option name="i">
186 <para>Ignore call forward requests from queue members and do nothing
187 when they are requested.</para>
188 </option>
189 <option name="I">
190 <para>Asterisk will ignore any connected line update requests or any redirecting party
191 update requests it may receive on this dial attempt.</para>
192 </option>
193 <option name="k">
194 <para>Allow the <emphasis>called</emphasis> party to enable parking of the call by sending
195 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
196 </option>
197 <option name="K">
198 <para>Allow the <emphasis>calling</emphasis> party to enable parking of the call by sending
199 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
200 </option>
201 <option name="m">
202 <para>Custom music on hold class to use, which will override the music on hold class configured
203 in <filename>queues.conf</filename>, if specified.</para>
204 <para>Note that CHANNEL(musicclass), if set, will still override this option.</para>
205 </option>
206 <option name="n">
207 <para>No retries on the timeout; will exit this application and
208 go to the next step.</para>
209 </option>
210 <option name="r">
211 <para>Ring instead of playing MOH. Periodic Announcements are still made, if applicable.</para>
212 </option>
213 <option name="R">
214 <para>Ring instead of playing MOH when a member channel is actually ringing.</para>
215 </option>
216 <option name="t">
217 <para>Allow the <emphasis>called</emphasis> user to transfer the calling user.</para>
218 </option>
219 <option name="T">
220 <para>Allow the <emphasis>calling</emphasis> user to transfer the call.</para>
221 </option>
222 <option name="x">
223 <para>Allow the <emphasis>called</emphasis> user to write the conversation
224 to disk via MixMonitor.</para>
225 </option>
226 <option name="X">
227 <para>Allow the <emphasis>calling</emphasis> user to write the conversation to
228 disk via MixMonitor.</para>
229 </option>
230 </optionlist>
231 </parameter>
232 <parameter name="URL">
233 <para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
234 </parameter>
235 <parameter name="announceoverride" argsep="&amp;">
236 <para>Announcement file(s) to play to agent before bridging
237 call, overriding the announcement(s) configured in
238 <filename>queues.conf</filename>, if any.</para>
239 <para>Ampersand separated list of filenames. If the filename
240 is a relative filename (it does not begin with a slash), it
241 will be searched for in the Asterisk sounds directory. If the
242 filename is able to be parsed as a URL, Asterisk will
243 download the file and then begin playback on it. To include a
244 literal <literal>&amp;</literal> in the URL you can enclose
245 the URL in single quotes.</para>
246 <argument name="announceoverride" required="true" />
247 <argument name="announceoverride2" multiple="true" />
248 </parameter>
249 <parameter name="timeout">
250 <para>Will cause the queue to fail out after a specified number of
251 seconds, checked between each <filename>queues.conf</filename> <replaceable>timeout</replaceable> and
252 <replaceable>retry</replaceable> cycle.</para>
253 </parameter>
254 <parameter name="AGI">
255 <para>Will setup an AGI script to be executed on the calling party's channel once they are
256 connected to a queue member.</para>
257 </parameter>
258 <parameter name="gosub">
259 <para>Will run a gosub on the called party's channel (the queue member)
260 once the parties are connected. The subroutine execution starts in the
261 named context at the s exten and priority 1.</para>
262 </parameter>
263 <parameter name="rule">
264 <para>Will cause the queue's defaultrule to be overridden by the rule specified.</para>
265 </parameter>
266 <parameter name="position">
267 <para>Attempt to enter the caller into the queue at the numerical position specified. <literal>1</literal>
268 would attempt to enter the caller at the head of the queue, and <literal>3</literal> would attempt to place
269 the caller third in the queue.</para>
270 </parameter>
271 </syntax>
272 <description>
273 <para>In addition to transferring the call, a call may be parked and then picked
274 up by another user.</para>
275 <para>This application will return to the dialplan if the queue does not exist, or
276 any of the join options cause the caller to not enter the queue.</para>
277 <para>This application does not automatically answer and should be preceded
278 by an application such as Answer(), Progress(), or Ringing().</para>
279 <para>This application sets the following channel variables upon completion:</para>
280 <variablelist>
281 <variable name="QUEUESTATUS">
282 <para>The status of the call as a text string.</para>
283 <value name="TIMEOUT" />
284 <value name="FULL" />
285 <value name="JOINEMPTY" />
286 <value name="LEAVEEMPTY" />
287 <value name="JOINUNAVAIL" />
288 <value name="LEAVEUNAVAIL" />
289 <value name="CONTINUE" />
290 <value name="WITHDRAW" />
291 </variable>
292 <variable name="ABANDONED">
293 <para>If the call was not answered by an agent this variable will be TRUE.</para>
294 <value name="TRUE" />
295 </variable>
296 <variable name="DIALEDPEERNUMBER">
297 <para>Resource of the agent that was dialed set on the outbound channel.</para>
298 </variable>
299 <variable name="QUEUE_WITHDRAW_INFO">
300 <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>
301 </variable>
302 </variablelist>
303 </description>
304 <see-also>
305 <ref type="application">Queue</ref>
306 <ref type="application">QueueLog</ref>
307 <ref type="application">AddQueueMember</ref>
308 <ref type="application">RemoveQueueMember</ref>
309 <ref type="application">PauseQueueMember</ref>
310 <ref type="application">UnpauseQueueMember</ref>
311 <ref type="function">QUEUE_VARIABLES</ref>
312 <ref type="function">QUEUE_MEMBER</ref>
313 <ref type="function">QUEUE_MEMBER_COUNT</ref>
314 <ref type="function">QUEUE_EXISTS</ref>
315 <ref type="function">QUEUE_GET_CHANNEL</ref>
316 <ref type="function">QUEUE_WAITING_COUNT</ref>
317 <ref type="function">QUEUE_MEMBER_LIST</ref>
318 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
319 </see-also>
320 </application>
321 <application name="AddQueueMember" language="en_US">
322 <since>
323 <version>0.5.0</version>
324 </since>
325 <synopsis>
326 Dynamically adds queue members.
327 </synopsis>
328 <syntax>
329 <parameter name="queuename" required="true" />
330 <parameter name="interface" />
331 <parameter name="penalty" />
332 <parameter name="options">
333 <optionlist>
334 <option name="p">
335 <para>Add queue member in paused state.</para>
336 </option>
337 <option name="r">
338 <argument name="reason" required="true" />
339 <para>Specify a reason why the member is in paused state.</para>
340 </option>
341 </optionlist>
342 </parameter>
343 <parameter name="membername" />
344 <parameter name="stateinterface" />
345 <parameter name="wrapuptime" />
346 </syntax>
347 <description>
348 <para>Dynamically adds interface to an existing queue. If the interface is
349 already in the queue it will return an error.</para>
350 <para>This application sets the following channel variable upon completion:</para>
351 <variablelist>
352 <variable name="AQMSTATUS">
353 <para>The status of the attempt to add a queue member as a text string.</para>
354 <value name="ADDED" />
355 <value name="MEMBERALREADY" />
356 <value name="NOSUCHQUEUE" />
357 </variable>
358 </variablelist>
359 </description>
360 <see-also>
361 <ref type="application">Queue</ref>
362 <ref type="application">QueueLog</ref>
363 <ref type="application">AddQueueMember</ref>
364 <ref type="application">RemoveQueueMember</ref>
365 <ref type="application">PauseQueueMember</ref>
366 <ref type="application">UnpauseQueueMember</ref>
367 <ref type="function">QUEUE_VARIABLES</ref>
368 <ref type="function">QUEUE_MEMBER</ref>
369 <ref type="function">QUEUE_MEMBER_COUNT</ref>
370 <ref type="function">QUEUE_EXISTS</ref>
371 <ref type="function">QUEUE_GET_CHANNEL</ref>
372 <ref type="function">QUEUE_WAITING_COUNT</ref>
373 <ref type="function">QUEUE_MEMBER_LIST</ref>
374 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
375 </see-also>
376 </application>
377 <application name="RemoveQueueMember" language="en_US">
378 <since>
379 <version>0.5.0</version>
380 </since>
381 <synopsis>
382 Dynamically removes queue members.
383 </synopsis>
384 <syntax>
385 <parameter name="queuename" required="true" />
386 <parameter name="interface" />
387 </syntax>
388 <description>
389 <para>If the interface is <emphasis>NOT</emphasis> in the queue it will return an error.</para>
390 <para>This application sets the following channel variable upon completion:</para>
391 <variablelist>
392 <variable name="RQMSTATUS">
393 <value name="REMOVED" />
394 <value name="NOTINQUEUE" />
395 <value name="NOSUCHQUEUE" />
396 <value name="NOTDYNAMIC" />
397 </variable>
398 </variablelist>
399 <example title="Remove queue member">
400 same => n,RemoveQueueMember(techsupport,SIP/3000)
401 </example>
402 </description>
403 <see-also>
404 <ref type="application">Queue</ref>
405 <ref type="application">QueueLog</ref>
406 <ref type="application">AddQueueMember</ref>
407 <ref type="application">RemoveQueueMember</ref>
408 <ref type="application">PauseQueueMember</ref>
409 <ref type="application">UnpauseQueueMember</ref>
410 <ref type="function">QUEUE_VARIABLES</ref>
411 <ref type="function">QUEUE_MEMBER</ref>
412 <ref type="function">QUEUE_MEMBER_COUNT</ref>
413 <ref type="function">QUEUE_EXISTS</ref>
414 <ref type="function">QUEUE_GET_CHANNEL</ref>
415 <ref type="function">QUEUE_WAITING_COUNT</ref>
416 <ref type="function">QUEUE_MEMBER_LIST</ref>
417 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
418 </see-also>
419 </application>
420 <application name="PauseQueueMember" language="en_US">
421 <since>
422 <version>1.2.0</version>
423 </since>
424 <synopsis>
425 Pauses a queue member.
426 </synopsis>
427 <syntax>
428 <parameter name="queuename" />
429 <parameter name="interface" required="true" />
430 <parameter name="options" />
431 <parameter name="reason">
432 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
433 </parameter>
434 </syntax>
435 <description>
436 <para>Pauses (blocks calls for) a queue member. The given interface will be paused in the given queue.
437 This prevents any calls from being sent from the queue to the interface until it is
438 unpaused with UnpauseQueueMember or the manager interface. If no queuename is given,
439 the interface is paused in every queue it is a member of. The application will fail if the
440 interface is not found.</para>
441 <para>This application sets the following channel variable upon completion:</para>
442 <variablelist>
443 <variable name="PQMSTATUS">
444 <para>The status of the attempt to pause a queue member as a text string.</para>
445 <value name="PAUSED" />
446 <value name="NOTFOUND" />
447 </variable>
448 </variablelist>
449 <example title="Pause queue member">
450 same => n,PauseQueueMember(,SIP/3000)
451 </example>
452 </description>
453 <see-also>
454 <ref type="application">Queue</ref>
455 <ref type="application">QueueLog</ref>
456 <ref type="application">AddQueueMember</ref>
457 <ref type="application">RemoveQueueMember</ref>
458 <ref type="application">PauseQueueMember</ref>
459 <ref type="application">UnpauseQueueMember</ref>
460 <ref type="function">QUEUE_VARIABLES</ref>
461 <ref type="function">QUEUE_MEMBER</ref>
462 <ref type="function">QUEUE_MEMBER_COUNT</ref>
463 <ref type="function">QUEUE_EXISTS</ref>
464 <ref type="function">QUEUE_GET_CHANNEL</ref>
465 <ref type="function">QUEUE_WAITING_COUNT</ref>
466 <ref type="function">QUEUE_MEMBER_LIST</ref>
467 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
468 </see-also>
469 </application>
470 <application name="UnpauseQueueMember" language="en_US">
471 <since>
472 <version>1.2.0</version>
473 </since>
474 <synopsis>
475 Unpauses a queue member.
476 </synopsis>
477 <syntax>
478 <parameter name="queuename" />
479 <parameter name="interface" required="true" />
480 <parameter name="options" />
481 <parameter name="reason">
482 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
483 </parameter>
484 </syntax>
485 <description>
486 <para>Unpauses (resumes calls to) a queue member. This is the counterpart to <literal>PauseQueueMember()</literal>
487 and operates exactly the same way, except it unpauses instead of pausing the given interface.</para>
488 <para>This application sets the following channel variable upon completion:</para>
489 <variablelist>
490 <variable name="UPQMSTATUS">
491 <para>The status of the attempt to unpause a queue member as a text string.</para>
492 <value name="UNPAUSED" />
493 <value name="NOTFOUND" />
494 </variable>
495 </variablelist>
496 <example title="Unpause queue member">
497 same => n,UnpauseQueueMember(,SIP/3000)
498 </example>
499 </description>
500 <see-also>
501 <ref type="application">Queue</ref>
502 <ref type="application">QueueLog</ref>
503 <ref type="application">AddQueueMember</ref>
504 <ref type="application">RemoveQueueMember</ref>
505 <ref type="application">PauseQueueMember</ref>
506 <ref type="application">UnpauseQueueMember</ref>
507 <ref type="function">QUEUE_VARIABLES</ref>
508 <ref type="function">QUEUE_MEMBER</ref>
509 <ref type="function">QUEUE_MEMBER_COUNT</ref>
510 <ref type="function">QUEUE_EXISTS</ref>
511 <ref type="function">QUEUE_GET_CHANNEL</ref>
512 <ref type="function">QUEUE_WAITING_COUNT</ref>
513 <ref type="function">QUEUE_MEMBER_LIST</ref>
514 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
515 </see-also>
516 </application>
517 <application name="QueueLog" language="en_US">
518 <since>
519 <version>1.4.0</version>
520 </since>
521 <synopsis>
522 Writes to the queue_log file.
523 </synopsis>
524 <syntax>
525 <parameter name="queuename" required="true" />
526 <parameter name="uniqueid" required="true" />
527 <parameter name="agent" required="true" />
528 <parameter name="event" required="true" />
529 <parameter name="additionalinfo" />
530 </syntax>
531 <description>
532 <para>Allows you to write your own events into the queue log.</para>
533 <example title="Log custom queue event">
534 same => n,QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)
535 </example>
536 </description>
537 <see-also>
538 <ref type="application">Queue</ref>
539 <ref type="application">QueueLog</ref>
540 <ref type="application">AddQueueMember</ref>
541 <ref type="application">RemoveQueueMember</ref>
542 <ref type="application">PauseQueueMember</ref>
543 <ref type="application">UnpauseQueueMember</ref>
544 <ref type="function">QUEUE_VARIABLES</ref>
545 <ref type="function">QUEUE_MEMBER</ref>
546 <ref type="function">QUEUE_MEMBER_COUNT</ref>
547 <ref type="function">QUEUE_EXISTS</ref>
548 <ref type="function">QUEUE_GET_CHANNEL</ref>
549 <ref type="function">QUEUE_WAITING_COUNT</ref>
550 <ref type="function">QUEUE_MEMBER_LIST</ref>
551 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
552 </see-also>
553 </application>
554 <application name="QueueUpdate" language="en_US">
555 <since>
556 <version>15.0.0</version>
557 </since>
558 <synopsis>
559 Writes to the queue_log file for outbound calls and updates Realtime Data.
560 Is used at h extension to be able to have all the parameters.
561 </synopsis>
562 <syntax>
563 <parameter name="queuename" required="true" />
564 <parameter name="uniqueid" required="true" />
565 <parameter name="agent" required="true" />
566 <parameter name="status" required="true" />
567 <parameter name="talktime" required="true" />
568 <parameter name="params" required="false" />
569 </syntax>
570 <description>
571 <para>Allows you to write Outbound events into the queue log.</para>
572 <example title="Write outbound event into queue log">
573 exten => h,1,QueueUpdate(${QUEUE}, ${UNIQUEID}, ${AGENT}, ${DIALSTATUS}, ${ANSWEREDTIME}, ${DIALEDTIME} | ${DIALEDNUMBER})
574 </example>
575 </description>
576 </application>
577 <function name="QUEUE_VARIABLES" language="en_US">
578 <since>
579 <version>1.6.0</version>
580 </since>
581 <synopsis>
582 Return Queue information in variables.
583 </synopsis>
584 <syntax>
585 <parameter name="queuename" required="true">
586 <enumlist>
587 <enum name="QUEUEMAX">
588 <para>Maximum number of calls allowed.</para>
589 </enum>
590 <enum name="QUEUESTRATEGY">
591 <para>The strategy of the queue.</para>
592 </enum>
593 <enum name="QUEUECALLS">
594 <para>Number of calls currently in the queue.</para>
595 </enum>
596 <enum name="QUEUEHOLDTIME">
597 <para>Current average hold time.</para>
598 </enum>
599 <enum name="QUEUECOMPLETED">
600 <para>Number of completed calls for the queue.</para>
601 </enum>
602 <enum name="QUEUEABANDONED">
603 <para>Number of abandoned calls.</para>
604 </enum>
605 <enum name="QUEUESRVLEVEL">
606 <para>Queue service level.</para>
607 </enum>
608 <enum name="QUEUESRVLEVELPERF">
609 <para>Current service level performance.</para>
610 </enum>
611 </enumlist>
612 </parameter>
613 </syntax>
614 <description>
615 <para>Makes the following queue variables available.</para>
616 <para>Returns <literal>0</literal> if queue is found and setqueuevar is defined, <literal>-1</literal> otherwise.</para>
617 </description>
618 <see-also>
619 <ref type="application">Queue</ref>
620 <ref type="application">QueueLog</ref>
621 <ref type="application">AddQueueMember</ref>
622 <ref type="application">RemoveQueueMember</ref>
623 <ref type="application">PauseQueueMember</ref>
624 <ref type="application">UnpauseQueueMember</ref>
625 <ref type="function">QUEUE_VARIABLES</ref>
626 <ref type="function">QUEUE_MEMBER</ref>
627 <ref type="function">QUEUE_MEMBER_COUNT</ref>
628 <ref type="function">QUEUE_EXISTS</ref>
629 <ref type="function">QUEUE_GET_CHANNEL</ref>
630 <ref type="function">QUEUE_WAITING_COUNT</ref>
631 <ref type="function">QUEUE_MEMBER_LIST</ref>
632 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
633 </see-also>
634 </function>
635 <function name="QUEUE_MEMBER" language="en_US">
636 <since>
637 <version>1.6.0</version>
638 </since>
639 <synopsis>
640 Provides a count of queue members based on the provided criteria, or updates a
641 queue member's settings.
642 </synopsis>
643 <syntax>
644 <parameter name="queuename" required="false" />
645 <parameter name="option" required="true">
646 <enumlist>
647 <enum name="logged">
648 <para>Returns the number of logged-in members for the specified queue.</para>
649 </enum>
650 <enum name="free">
651 <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>
652 </enum>
653 <enum name="ready">
654 <para>Returns the number of logged-in members for the specified queue that are immediately available to answer a call.</para>
655 </enum>
656 <enum name="count">
657 <para>Returns the total number of members for the specified queue.</para>
658 </enum>
659 <enum name="penalty">
660 <para>Gets or sets queue member penalty. If
661 <replaceable>queuename</replaceable> is not specified
662 when setting the penalty then the penalty is set in all queues
663 the interface is a member.</para>
664 </enum>
665 <enum name="paused">
666 <para>Gets or sets queue member paused status. If
667 <replaceable>queuename</replaceable> is not specified
668 when setting the paused status then the paused status is set
669 in all queues the interface is a member.</para>
670 </enum>
671 <enum name="ringinuse">
672 <para>Gets or sets queue member ringinuse. If
673 <replaceable>queuename</replaceable> is not specified
674 when setting ringinuse then ringinuse is set
675 in all queues the interface is a member.</para>
676 </enum>
677 </enumlist>
678 </parameter>
679 <parameter name="interface" required="false" />
680 </syntax>
681 <description>
682 <para>Allows access to queue counts [R] and member information [R/W].</para>
683 <para><replaceable>queuename</replaceable> is required for all read operations.</para>
684 <para><replaceable>interface</replaceable> is required for all member operations.</para>
685 </description>
686 <see-also>
687 <ref type="application">Queue</ref>
688 <ref type="application">QueueLog</ref>
689 <ref type="application">AddQueueMember</ref>
690 <ref type="application">RemoveQueueMember</ref>
691 <ref type="application">PauseQueueMember</ref>
692 <ref type="application">UnpauseQueueMember</ref>
693 <ref type="function">QUEUE_VARIABLES</ref>
694 <ref type="function">QUEUE_MEMBER</ref>
695 <ref type="function">QUEUE_MEMBER_COUNT</ref>
696 <ref type="function">QUEUE_EXISTS</ref>
697 <ref type="function">QUEUE_GET_CHANNEL</ref>
698 <ref type="function">QUEUE_WAITING_COUNT</ref>
699 <ref type="function">QUEUE_MEMBER_LIST</ref>
700 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
701 </see-also>
702 </function>
703 <function name="QUEUE_MEMBER_COUNT" language="en_US">
704 <since>
705 <version>1.4.0</version>
706 </since>
707 <synopsis>
708 Count number of members answering a queue.
709 </synopsis>
710 <syntax>
711 <parameter name="queuename" required="true" />
712 </syntax>
713 <description>
714 <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
715 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
716 </description>
717 <see-also>
718 <ref type="application">Queue</ref>
719 <ref type="application">QueueLog</ref>
720 <ref type="application">AddQueueMember</ref>
721 <ref type="application">RemoveQueueMember</ref>
722 <ref type="application">PauseQueueMember</ref>
723 <ref type="application">UnpauseQueueMember</ref>
724 <ref type="function">QUEUE_VARIABLES</ref>
725 <ref type="function">QUEUE_MEMBER</ref>
726 <ref type="function">QUEUE_MEMBER_COUNT</ref>
727 <ref type="function">QUEUE_EXISTS</ref>
728 <ref type="function">QUEUE_GET_CHANNEL</ref>
729 <ref type="function">QUEUE_WAITING_COUNT</ref>
730 <ref type="function">QUEUE_MEMBER_LIST</ref>
731 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
732 </see-also>
733 </function>
734 <function name="QUEUE_EXISTS" language="en_US">
735 <since>
736 <version>1.8.0</version>
737 </since>
738 <synopsis>
739 Check if a named queue exists on this server
740 </synopsis>
741 <syntax>
742 <parameter name="queuename" />
743 </syntax>
744 <description>
745 <para>Returns 1 if the specified queue exists, 0 if it does not</para>
746 </description>
747 <see-also>
748 <ref type="application">Queue</ref>
749 <ref type="application">QueueLog</ref>
750 <ref type="application">AddQueueMember</ref>
751 <ref type="application">RemoveQueueMember</ref>
752 <ref type="application">PauseQueueMember</ref>
753 <ref type="application">UnpauseQueueMember</ref>
754 <ref type="function">QUEUE_VARIABLES</ref>
755 <ref type="function">QUEUE_MEMBER</ref>
756 <ref type="function">QUEUE_MEMBER_COUNT</ref>
757 <ref type="function">QUEUE_EXISTS</ref>
758 <ref type="function">QUEUE_GET_CHANNEL</ref>
759 <ref type="function">QUEUE_WAITING_COUNT</ref>
760 <ref type="function">QUEUE_MEMBER_LIST</ref>
761 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
762 </see-also>
763 </function>
764 <function name="QUEUE_GET_CHANNEL" language="en_US">
765 <since>
766 <version>14.0.0</version>
767 </since>
768 <synopsis>
769 Return caller at the specified position in a queue.
770 </synopsis>
771 <syntax>
772 <parameter name="queuename" required="true" />
773 <parameter name="position" />
774 </syntax>
775 <description>
776 <para>Returns the caller channel at <replaceable>position</replaceable> in the specified <replaceable>queuename</replaceable>.</para>
777 <para>If <replaceable>position</replaceable> is unspecified the first channel is returned.</para>
778 </description>
779 <see-also>
780 <ref type="application">Queue</ref>
781 <ref type="application">QueueLog</ref>
782 <ref type="application">AddQueueMember</ref>
783 <ref type="application">RemoveQueueMember</ref>
784 <ref type="application">PauseQueueMember</ref>
785 <ref type="application">UnpauseQueueMember</ref>
786 <ref type="function">QUEUE_VARIABLES</ref>
787 <ref type="function">QUEUE_MEMBER</ref>
788 <ref type="function">QUEUE_MEMBER_COUNT</ref>
789 <ref type="function">QUEUE_EXISTS</ref>
790 <ref type="function">QUEUE_WAITING_COUNT</ref>
791 <ref type="function">QUEUE_MEMBER_LIST</ref>
792 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
793 </see-also>
794 </function>
795 <function name="QUEUE_WAITING_COUNT" language="en_US">
796 <since>
797 <version>1.4.0</version>
798 </since>
799 <synopsis>
800 Count number of calls currently waiting in a queue.
801 </synopsis>
802 <syntax>
803 <parameter name="queuename" />
804 </syntax>
805 <description>
806 <para>Returns the number of callers currently waiting in the specified <replaceable>queuename</replaceable>.</para>
807 </description>
808 <see-also>
809 <ref type="application">Queue</ref>
810 <ref type="application">QueueLog</ref>
811 <ref type="application">AddQueueMember</ref>
812 <ref type="application">RemoveQueueMember</ref>
813 <ref type="application">PauseQueueMember</ref>
814 <ref type="application">UnpauseQueueMember</ref>
815 <ref type="function">QUEUE_VARIABLES</ref>
816 <ref type="function">QUEUE_MEMBER</ref>
817 <ref type="function">QUEUE_MEMBER_COUNT</ref>
818 <ref type="function">QUEUE_EXISTS</ref>
819 <ref type="function">QUEUE_GET_CHANNEL</ref>
820 <ref type="function">QUEUE_WAITING_COUNT</ref>
821 <ref type="function">QUEUE_MEMBER_LIST</ref>
822 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
823 </see-also>
824 </function>
825 <function name="QUEUE_MEMBER_LIST" language="en_US">
826 <since>
827 <version>1.4.0</version>
828 </since>
829 <synopsis>
830 Returns a list of interfaces on a queue.
831 </synopsis>
832 <syntax>
833 <parameter name="queuename" required="true" />
834 </syntax>
835 <description>
836 <para>Returns a comma-separated list of members associated with the specified <replaceable>queuename</replaceable>.</para>
837 </description>
838 <see-also>
839 <ref type="application">Queue</ref>
840 <ref type="application">QueueLog</ref>
841 <ref type="application">AddQueueMember</ref>
842 <ref type="application">RemoveQueueMember</ref>
843 <ref type="application">PauseQueueMember</ref>
844 <ref type="application">UnpauseQueueMember</ref>
845 <ref type="function">QUEUE_VARIABLES</ref>
846 <ref type="function">QUEUE_MEMBER</ref>
847 <ref type="function">QUEUE_MEMBER_COUNT</ref>
848 <ref type="function">QUEUE_EXISTS</ref>
849 <ref type="function">QUEUE_GET_CHANNEL</ref>
850 <ref type="function">QUEUE_WAITING_COUNT</ref>
851 <ref type="function">QUEUE_MEMBER_LIST</ref>
852 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
853 </see-also>
854 </function>
855 <function name="QUEUE_MEMBER_PENALTY" language="en_US">
856 <since>
857 <version>1.6.0</version>
858 </since>
859 <synopsis>
860 Gets or sets queue members penalty.
861 </synopsis>
862 <syntax>
863 <parameter name="queuename" required="true" />
864 <parameter name="interface" required="true" />
865 </syntax>
866 <description>
867 <para>Gets or sets queue members penalty.</para>
868 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
869 </description>
870 <see-also>
871 <ref type="application">Queue</ref>
872 <ref type="application">QueueLog</ref>
873 <ref type="application">AddQueueMember</ref>
874 <ref type="application">RemoveQueueMember</ref>
875 <ref type="application">PauseQueueMember</ref>
876 <ref type="application">UnpauseQueueMember</ref>
877 <ref type="function">QUEUE_VARIABLES</ref>
878 <ref type="function">QUEUE_MEMBER</ref>
879 <ref type="function">QUEUE_MEMBER_COUNT</ref>
880 <ref type="function">QUEUE_EXISTS</ref>
881 <ref type="function">QUEUE_GET_CHANNEL</ref>
882 <ref type="function">QUEUE_WAITING_COUNT</ref>
883 <ref type="function">QUEUE_MEMBER_LIST</ref>
884 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
885 </see-also>
886 </function>
887 <manager name="QueueStatus" language="en_US">
888 <since>
889 <version>0.5.0</version>
890 </since>
891 <synopsis>
892 Show queue status.
893 </synopsis>
894 <syntax>
895 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
896 <parameter name="Queue">
897 <para>Limit the response to the status of the specified queue.</para>
898 </parameter>
899 <parameter name="Member">
900 <para>Limit the response to the status of the specified member.</para>
901 </parameter>
902 </syntax>
903 <description>
904 <para>Check the status of one or more queues.</para>
905 </description>
906 </manager>
907 <manager name="QueueSummary" language="en_US">
908 <since>
909 <version>1.6.0</version>
910 </since>
911 <synopsis>
912 Show queue summary.
913 </synopsis>
914 <syntax>
915 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
916 <parameter name="Queue">
917 <para>Queue for which the summary is requested.</para>
918 </parameter>
919 </syntax>
920 <description>
921 <para>Request the manager to send a QueueSummary event.</para>
922 </description>
923 </manager>
924 <manager name="QueueAdd" language="en_US">
925 <since>
926 <version>1.0.0</version>
927 </since>
928 <synopsis>
929 Add interface to queue.
930 </synopsis>
931 <syntax>
932 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
933 <parameter name="Queue" required="true">
934 <para>Queue's name.</para>
935 </parameter>
936 <parameter name="Interface" required="true">
937 <para>The name of the interface (tech/name) to add to the queue.</para>
938 </parameter>
939 <parameter name="Penalty">
940 <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>
941 </parameter>
942 <parameter name="Paused">
943 <para>To pause or not the member initially (true/false or 1/0).</para>
944 </parameter>
945 <parameter name="Reason" required="false">
946 <para>Text description why the member is paused.</para>
947 </parameter>
948 <parameter name="MemberName">
949 <para>Text alias for the interface.</para>
950 </parameter>
951 <parameter name="StateInterface" />
952 </syntax>
953 <description>
954 </description>
955 </manager>
956 <manager name="QueueRemove" language="en_US">
957 <since>
958 <version>1.0.0</version>
959 </since>
960 <synopsis>
961 Remove interface from queue.
962 </synopsis>
963 <syntax>
964 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
965 <parameter name="Queue" required="true">
966 <para>The name of the queue to take action on.</para>
967 </parameter>
968 <parameter name="Interface" required="true">
969 <para>The interface (tech/name) to remove from queue.</para>
970 </parameter>
971 </syntax>
972 <description>
973 </description>
974 </manager>
975 <manager name="QueuePause" language="en_US">
976 <since>
977 <version>1.2.0</version>
978 </since>
979 <synopsis>
980 Makes a queue member temporarily unavailable.
981 </synopsis>
982 <syntax>
983 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
984 <parameter name="Interface" required="true">
985 <para>The name of the interface (tech/name) to pause or unpause.</para>
986 </parameter>
987 <parameter name="Paused" required="true">
988 <para>Pause or unpause the interface. Set to 'true' to pause the member or 'false' to unpause.</para>
989 </parameter>
990 <parameter name="Queue" required="false">
991 <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>
992 </parameter>
993 <parameter name="Reason" required="false">
994 <para>Text description, returned in the event QueueMemberPaused.</para>
995 </parameter>
996 </syntax>
997 <description>
998 <para>Pause or unpause a member in a queue.</para>
999 </description>
1000 </manager>
1001 <manager name="QueueLog" language="en_US">
1002 <since>
1003 <version>1.6.0</version>
1004 </since>
1005 <synopsis>
1006 Adds custom entry in queue_log.
1007 </synopsis>
1008 <syntax>
1009 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1010 <parameter name="Queue" required="true" />
1011 <parameter name="Event" required="true" />
1012 <parameter name="Uniqueid" />
1013 <parameter name="Interface" />
1014 <parameter name="Message" />
1015 </syntax>
1016 <description>
1017 </description>
1018 </manager>
1019 <manager name="QueuePenalty" language="en_US">
1020 <since>
1021 <version>1.6.0</version>
1022 </since>
1023 <synopsis>
1024 Set the penalty for a queue member.
1025 </synopsis>
1026 <syntax>
1027 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1028 <parameter name="Interface" required="true">
1029 <para>The interface (tech/name) of the member whose penalty to change.</para>
1030 </parameter>
1031 <parameter name="Penalty" required="true">
1032 <para>The new penalty (number) for the member. Must be nonnegative.</para>
1033 </parameter>
1034 <parameter name="Queue">
1035 <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>
1036 </parameter>
1037 </syntax>
1038 <description>
1039 <para>Change the penalty of a queue member</para>
1040 </description>
1041 </manager>
1042 <manager name="QueueMemberRingInUse" language="en_US">
1043 <since>
1044 <version>11.0.0</version>
1045 </since>
1046 <synopsis>
1047 Set the ringinuse value for a queue member.
1048 </synopsis>
1049 <syntax>
1050 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1051 <parameter name="Interface" required="true" />
1052 <parameter name="RingInUse" required="true" />
1053 <parameter name="Queue" />
1054 </syntax>
1055 <description>
1056 </description>
1057 </manager>
1058 <manager name="QueueRule" language="en_US">
1059 <since>
1060 <version>1.6.0</version>
1061 </since>
1062 <synopsis>
1063 Queue Rules.
1064 </synopsis>
1065 <syntax>
1066 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1067 <parameter name="Rule">
1068 <para>The name of the rule in queuerules.conf whose contents to list.</para>
1069 </parameter>
1070 </syntax>
1071 <description>
1072 <para>List queue rules defined in queuerules.conf</para>
1073 </description>
1074 </manager>
1075 <manager name="QueueReload" language="en_US">
1076 <since>
1077 <version>1.6.2.0</version>
1078 </since>
1079 <synopsis>
1080 Reload a queue, queues, or any sub-section of a queue or queues.
1081 </synopsis>
1082 <syntax>
1083 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1084 <parameter name="Queue">
1085 <para>The name of the queue to take action on. If no queue name is specified, then all queues are affected.</para>
1086 </parameter>
1087 <parameter name="Members">
1088 <para>Whether to reload the queue's members.</para>
1089 <enumlist>
1090 <enum name="yes" />
1091 <enum name="no" />
1092 </enumlist>
1093 </parameter>
1094 <parameter name="Rules">
1095 <para>Whether to reload queuerules.conf</para>
1096 <enumlist>
1097 <enum name="yes" />
1098 <enum name="no" />
1099 </enumlist>
1100 </parameter>
1101 <parameter name="Parameters">
1102 <para>Whether to reload the other queue options.</para>
1103 <enumlist>
1104 <enum name="yes" />
1105 <enum name="no" />
1106 </enumlist>
1107 </parameter>
1108 </syntax>
1109 <description>
1110 </description>
1111 </manager>
1112 <manager name="QueueReset" language="en_US">
1113 <since>
1114 <version>1.6.2.0</version>
1115 </since>
1116 <synopsis>
1117 Reset queue statistics.
1118 </synopsis>
1119 <syntax>
1120 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1121 <parameter name="Queue">
1122 <para>The name of the queue on which to reset statistics.</para>
1123 </parameter>
1124 </syntax>
1125 <description>
1126 <para>Reset the statistics for a queue.</para>
1127 </description>
1128 </manager>
1129 <manager name="QueueChangePriorityCaller" language="en_US">
1130 <since>
1131 <version>15.0.0</version>
1132 </since>
1133 <synopsis>
1134 Change priority of a caller on queue.
1135 </synopsis>
1136 <syntax>
1137 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1138 <parameter name="Queue" required="true">
1139 <para>The name of the queue to take action on.</para>
1140 </parameter>
1141 <parameter name="Caller" required="true">
1142 <para>The caller (channel) to change priority on queue.</para>
1143 </parameter>
1144
1145 <parameter name="Priority" required="true">
1146 <para>Priority value for change for caller on queue.</para>
1147 </parameter>
1148 <parameter name="Immediate">
1149 <para>When set to yes will cause the priority change to be reflected immediately, causing the channel to change position within the queue.</para>
1150 </parameter>
1151 </syntax>
1152 <description>
1153 </description>
1154 </manager>
1155 <manager name="QueueWithdrawCaller" language="en_US">
1156 <since>
1157 <version>19.3.0</version>
1158 <version>18.11.0</version>
1159 <version>16.25.0</version>
1160 </since>
1161 <synopsis>
1162 Request to withdraw a caller from the queue back to the dialplan.
1163 </synopsis>
1164 <syntax>
1165 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1166 <parameter name="Queue" required="true">
1167 <para>The name of the queue to take action on.</para>
1168 </parameter>
1169 <parameter name="Caller" required="true">
1170 <para>The caller (channel) to withdraw from the queue.</para>
1171 </parameter>
1172 <parameter name="WithdrawInfo" required="false">
1173 <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>
1174 </parameter>
1175 </syntax>
1176 <description>
1177 </description>
1178 </manager>
1179
1180 <managerEvent language="en_US" name="QueueParams">
1181 <managerEventInstance class="EVENT_FLAG_AGENT">
1182 <since>
1183 <version>16.24.0</version>
1184 <version>18.10.0</version>
1185 <version>19.2.0</version>
1186 </since>
1187 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1188 <syntax>
1189 <parameter name="Max">
1190 <para>The name of the queue.</para>
1191 </parameter>
1192 <parameter name="Strategy">
1193 <para>The strategy of the queue.</para>
1194 </parameter>
1195 <parameter name="Calls">
1196 <para>The queue member's channel technology or location.</para>
1197 </parameter>
1198 <parameter name="Holdtime">
1199 <para>The queue's hold time.</para>
1200 </parameter>
1201 <parameter name="TalkTime">
1202 <para>The queue's talk time.</para>
1203 </parameter>
1204 <parameter name="Completed">
1205 <para>The queue's completion time.</para>
1206 </parameter>
1207 <parameter name="Abandoned">
1208 <para>The queue's call abandonment metric.</para>
1209 </parameter>
1210 <parameter name="ServiceLevelPerf">
1211 <para>Primary service level performance metric.</para>
1212 </parameter>
1213 <parameter name="ServiceLevelPerf2">
1214 <para>Secondary service level performance metric.</para>
1215 </parameter>
1216 </syntax>
1217 <see-also>
1218 <ref type="managerEvent">QueueMember</ref>
1219 <ref type="managerEvent">QueueEntry</ref>
1220 </see-also>
1221 </managerEventInstance>
1222 </managerEvent>
1223 <managerEvent language="en_US" name="QueueEntry">
1224 <managerEventInstance class="EVENT_FLAG_AGENT">
1225 <since>
1226 <version>16.24.0</version>
1227 <version>18.10.0</version>
1228 <version>19.2.0</version>
1229 </since>
1230 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1231 <syntax>
1232 <parameter name="Queue">
1233 <para>The name of the queue.</para>
1234 </parameter>
1235 <parameter name="Position">
1236 <para>The caller's position within the queue.</para>
1237 </parameter>
1238 <parameter name="Channel">
1239 <para>The name of the caller's channel.</para>
1240 </parameter>
1241 <parameter name="Uniqueid">
1242 <para>The unique ID of the channel.</para>
1243 </parameter>
1244 <parameter name="CallerIDNum">
1245 <para>The Caller ID number.</para>
1246 </parameter>
1247 <parameter name="CallerIDName">
1248 <para>The Caller ID name.</para>
1249 </parameter>
1250 <parameter name="ConnectedLineNum">
1251 <para>The bridged party's number.</para>
1252 </parameter>
1253 <parameter name="ConnectedLineName">
1254 <para>The bridged party's name.</para>
1255 </parameter>
1256 <parameter name="Wait">
1257 <para>The caller's wait time.</para>
1258 </parameter>
1259 <parameter name="Priority">
1260 <para>The caller's priority within the queue.</para>
1261 </parameter>
1262 </syntax>
1263 <see-also>
1264 <ref type="managerEvent">QueueParams</ref>
1265 <ref type="managerEvent">QueueMember</ref>
1266 </see-also>
1267 </managerEventInstance>
1268 </managerEvent>
1269 <managerEvent language="en_US" name="QueueMemberStatus">
1270 <managerEventInstance class="EVENT_FLAG_AGENT">
1271 <since>
1272 <version>12.0.0</version>
1273 </since>
1274 <synopsis>Raised when a Queue member's status has changed.</synopsis>
1275 <syntax>
1276 <parameter name="Queue">
1277 <para>The name of the queue.</para>
1278 </parameter>
1279 <parameter name="MemberName">
1280 <para>The name of the queue member.</para>
1281 </parameter>
1282 <parameter name="Interface">
1283 <para>The queue member's channel technology or location.</para>
1284 </parameter>
1285 <parameter name="StateInterface">
1286 <para>Channel technology or location from which to read device state changes.</para>
1287 </parameter>
1288 <parameter name="Membership">
1289 <enumlist>
1290 <enum name="dynamic"/>
1291 <enum name="realtime"/>
1292 <enum name="static"/>
1293 </enumlist>
1294 </parameter>
1295 <parameter name="Penalty">
1296 <para>The penalty associated with the queue member.</para>
1297 </parameter>
1298 <parameter name="CallsTaken">
1299 <para>The number of calls this queue member has serviced.</para>
1300 </parameter>
1301 <parameter name="LastCall">
1302 <para>The time this member last took a call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1303 </parameter>
1304 <parameter name="LastPause">
1305 <para>The time when started last paused the queue member.</para>
1306 </parameter>
1307 <parameter name="LoginTime">
1308 <para>The time this member logged in to the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1309 </parameter>
1310 <parameter name="InCall">
1311 <para>Set to 1 if member is in call. Set to 0 after LastCall time is updated.</para>
1312 <enumlist>
1313 <enum name="0"/>
1314 <enum name="1"/>
1315 </enumlist>
1316 </parameter>
1317 <parameter name="Status">
1318 <para>The numeric device state status of the queue member.</para>
1319 <enumlist>
1320 <enum name="0"><para>AST_DEVICE_UNKNOWN</para></enum>
1321 <enum name="1"><para>AST_DEVICE_NOT_INUSE</para></enum>
1322 <enum name="2"><para>AST_DEVICE_INUSE</para></enum>
1323 <enum name="3"><para>AST_DEVICE_BUSY</para></enum>
1324 <enum name="4"><para>AST_DEVICE_INVALID</para></enum>
1325 <enum name="5"><para>AST_DEVICE_UNAVAILABLE</para></enum>
1326 <enum name="6"><para>AST_DEVICE_RINGING</para></enum>
1327 <enum name="7"><para>AST_DEVICE_RINGINUSE</para></enum>
1328 <enum name="8"><para>AST_DEVICE_ONHOLD</para></enum>
1329 </enumlist>
1330 </parameter>
1331 <parameter name="Paused">
1332 <enumlist>
1333 <enum name="0"/>
1334 <enum name="1"/>
1335 </enumlist>
1336 </parameter>
1337 <parameter name="PausedReason">
1338 <para>If set when paused, the reason the queue member was paused.</para>
1339 </parameter>
1340 <parameter name="Ringinuse">
1341 <enumlist>
1342 <enum name="0"/>
1343 <enum name="1"/>
1344 </enumlist>
1345 </parameter>
1346 <parameter name="Wrapuptime">
1347 <para>The Wrapup Time of the queue member. If this value is set will override the wrapup time of queue.</para>
1348 </parameter>
1349 </syntax>
1350 </managerEventInstance>
1351 </managerEvent>
1352 <managerEvent language="en_US" name="QueueMemberAdded">
1353 <managerEventInstance class="EVENT_FLAG_AGENT">
1354 <since>
1355 <version>12.0.0</version>
1356 </since>
1357 <synopsis>Raised when a member is added to the queue.</synopsis>
1358 <syntax>
1359 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1360 </syntax>
1361 <see-also>
1362 <ref type="managerEvent">QueueMemberRemoved</ref>
1363 <ref type="application">AddQueueMember</ref>
1364 </see-also>
1365 </managerEventInstance>
1366 </managerEvent>
1367 <managerEvent language="en_US" name="QueueMemberRemoved">
1368 <managerEventInstance class="EVENT_FLAG_AGENT">
1369 <since>
1370 <version>12.0.0</version>
1371 </since>
1372 <synopsis>Raised when a member is removed from the queue.</synopsis>
1373 <syntax>
1374 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1375 </syntax>
1376 <see-also>
1377 <ref type="managerEvent">QueueMemberAdded</ref>
1378 <ref type="application">RemoveQueueMember</ref>
1379 </see-also>
1380 </managerEventInstance>
1381 </managerEvent>
1382 <managerEvent language="en_US" name="QueueMemberPause">
1383 <managerEventInstance class="EVENT_FLAG_AGENT">
1384 <since>
1385 <version>12.2.0</version>
1386 </since>
1387 <synopsis>Raised when a member is paused/unpaused in the queue.</synopsis>
1388 <syntax>
1389 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1390 </syntax>
1391 <see-also>
1392 <ref type="application">PauseQueueMember</ref>
1393 <ref type="application">UnpauseQueueMember</ref>
1394 </see-also>
1395 </managerEventInstance>
1396 </managerEvent>
1397 <managerEvent language="en_US" name="QueueMemberPenalty">
1398 <managerEventInstance class="EVENT_FLAG_AGENT">
1399 <since>
1400 <version>12.0.0</version>
1401 </since>
1402 <synopsis>Raised when a member's penalty is changed.</synopsis>
1403 <syntax>
1404 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1405 </syntax>
1406 <see-also>
1407 <ref type="function">QUEUE_MEMBER</ref>
1408 </see-also>
1409 </managerEventInstance>
1410 </managerEvent>
1411 <managerEvent language="en_US" name="QueueMemberRinginuse">
1412 <managerEventInstance class="EVENT_FLAG_AGENT">
1413 <since>
1414 <version>12.0.0</version>
1415 </since>
1416 <synopsis>Raised when a member's ringinuse setting is changed.</synopsis>
1417 <syntax>
1418 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1419 </syntax>
1420 <see-also>
1421 <ref type="function">QUEUE_MEMBER</ref>
1422 </see-also>
1423 </managerEventInstance>
1424 </managerEvent>
1425 <managerEvent language="en_US" name="QueueCallerJoin">
1426 <managerEventInstance class="EVENT_FLAG_AGENT">
1427 <since>
1428 <version>12.0.0</version>
1429 </since>
1430 <synopsis>Raised when a caller joins a Queue.</synopsis>
1431 <syntax>
1432 <channel_snapshot/>
1433 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1434 <parameter name="Position">
1435 <para>This channel's current position in the queue.</para>
1436 </parameter>
1437 <parameter name="Count">
1438 <para>The total number of channels in the queue.</para>
1439 </parameter>
1440 </syntax>
1441 <see-also>
1442 <ref type="managerEvent">QueueCallerLeave</ref>
1443 <ref type="application">Queue</ref>
1444 </see-also>
1445 </managerEventInstance>
1446 </managerEvent>
1447 <managerEvent language="en_US" name="QueueCallerLeave">
1448 <managerEventInstance class="EVENT_FLAG_AGENT">
1449 <since>
1450 <version>12.0.0</version>
1451 </since>
1452 <synopsis>Raised when a caller leaves a Queue.</synopsis>
1453 <syntax>
1454 <channel_snapshot/>
1455 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1456 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Count'])" />
1457 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1458 </syntax>
1459 <see-also>
1460 <ref type="managerEvent">QueueCallerJoin</ref>
1461 </see-also>
1462 </managerEventInstance>
1463 </managerEvent>
1464 <managerEvent language="en_US" name="QueueCallerAbandon">
1465 <managerEventInstance class="EVENT_FLAG_AGENT">
1466 <since>
1467 <version>12.0.0</version>
1468 </since>
1469 <synopsis>Raised when a caller abandons the queue.</synopsis>
1470 <syntax>
1471 <channel_snapshot/>
1472 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1473 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1474 <parameter name="OriginalPosition">
1475 <para>The channel's original position in the queue.</para>
1476 </parameter>
1477 <parameter name="HoldTime">
1478 <para>The time the channel was in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1479 </parameter>
1480 </syntax>
1481 </managerEventInstance>
1482 </managerEvent>
1483 <managerEvent language="en_US" name="AgentCalled">
1484 <managerEventInstance class="EVENT_FLAG_AGENT">
1485 <since>
1486 <version>12.0.0</version>
1487 </since>
1488 <synopsis>Raised when an queue member is notified of a caller in the queue.</synopsis>
1489 <syntax>
1490 <channel_snapshot/>
1491 <channel_snapshot prefix="Dest"/>
1492 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1493 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1494 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1495 </syntax>
1496 <see-also>
1497 <ref type="managerEvent">AgentRingNoAnswer</ref>
1498 <ref type="managerEvent">AgentComplete</ref>
1499 <ref type="managerEvent">AgentConnect</ref>
1500 </see-also>
1501 </managerEventInstance>
1502 </managerEvent>
1503 <managerEvent language="en_US" name="AgentRingNoAnswer">
1504 <managerEventInstance class="EVENT_FLAG_AGENT">
1505 <since>
1506 <version>12.0.0</version>
1507 </since>
1508 <synopsis>Raised when a queue member is notified of a caller in the queue and fails to answer.</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 <parameter name="RingTime">
1516 <para>The time the queue member was rung, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1517 </parameter>
1518 </syntax>
1519 <see-also>
1520 <ref type="managerEvent">AgentCalled</ref>
1521 </see-also>
1522 </managerEventInstance>
1523 </managerEvent>
1524 <managerEvent language="en_US" name="AgentComplete">
1525 <managerEventInstance class="EVENT_FLAG_AGENT">
1526 <since>
1527 <version>12.0.0</version>
1528 </since>
1529 <synopsis>Raised when a queue member has finished servicing a caller in the queue.</synopsis>
1530 <syntax>
1531 <channel_snapshot/>
1532 <channel_snapshot prefix="Dest"/>
1533 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1534 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1535 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1536 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1537 <parameter name="TalkTime">
1538 <para>The time the queue member talked with the caller in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1539 </parameter>
1540 <parameter name="Reason">
1541 <enumlist>
1542 <enum name="caller"/>
1543 <enum name="agent"/>
1544 <enum name="transfer"/>
1545 </enumlist>
1546 </parameter>
1547 </syntax>
1548 <see-also>
1549 <ref type="managerEvent">AgentCalled</ref>
1550 <ref type="managerEvent">AgentConnect</ref>
1551 </see-also>
1552 </managerEventInstance>
1553 </managerEvent>
1554 <managerEvent language="en_US" name="AgentDump">
1555 <managerEventInstance class="EVENT_FLAG_AGENT">
1556 <since>
1557 <version>12.0.0</version>
1558 </since>
1559 <synopsis>Raised when a queue member hangs up on a caller in the queue.</synopsis>
1560 <syntax>
1561 <channel_snapshot/>
1562 <channel_snapshot prefix="Dest"/>
1563 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1564 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1565 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1566 </syntax>
1567 <see-also>
1568 <ref type="managerEvent">AgentCalled</ref>
1569 <ref type="managerEvent">AgentConnect</ref>
1570 </see-also>
1571 </managerEventInstance>
1572 </managerEvent>
1573 <managerEvent language="en_US" name="AgentConnect">
1574 <managerEventInstance class="EVENT_FLAG_AGENT">
1575 <since>
1576 <version>12.0.0</version>
1577 </since>
1578 <synopsis>Raised when a queue member answers and is bridged to a caller in the queue.</synopsis>
1579 <syntax>
1580 <channel_snapshot/>
1581 <channel_snapshot prefix="Dest"/>
1582 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1583 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1584 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1585 <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='RingTime'])" />
1586 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1587 </syntax>
1588 <see-also>
1589 <ref type="managerEvent">AgentCalled</ref>
1590 <ref type="managerEvent">AgentComplete</ref>
1591 <ref type="managerEvent">AgentDump</ref>
1592 </see-also>
1593 </managerEventInstance>
1594 </managerEvent>
1595 ***/
1596
1597enum {
1599 OPT_GO_ON = (1 << 1),
1608 OPT_NO_RETRY = (1 << 10),
1609 OPT_RINGING = (1 << 11),
1620};
1621
1622enum {
1627 /* note: this entry _MUST_ be the last one in the enum */
1630
1655
1656
1658 AQMFLAG_PAUSED = (1 << 1),
1659 AQMFLAG_REASON = (1 << 2),
1660};
1661
1664 AQM_OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
1665};
1666
1671
1672enum {
1681};
1682
1683enum {
1688
1694};
1695
1696static const struct strategy {
1698 const char *name;
1699} strategies[] = {
1700 { QUEUE_STRATEGY_RINGALL, "ringall" },
1701 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
1702 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
1703 { QUEUE_STRATEGY_RANDOM, "random" },
1704 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
1705 { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
1706 { QUEUE_STRATEGY_LINEAR, "linear" },
1707 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
1708 { QUEUE_STRATEGY_RRORDERED, "rrordered"},
1710
1711static const struct autopause {
1713 const char *name;
1714} autopausesmodes [] = {
1715 { QUEUE_AUTOPAUSE_OFF,"no" },
1716 { QUEUE_AUTOPAUSE_ON, "yes" },
1717 { QUEUE_AUTOPAUSE_ALL,"all" },
1719
1720#define DEFAULT_RETRY 5
1721#define DEFAULT_TIMEOUT 15
1722#define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
1723#define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
1724/*!
1725 * \brief The minimum number of seconds between position announcements.
1726 * \note The default value of 15 provides backwards compatibility.
1727 */
1728#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
1729
1730#define MAX_QUEUE_BUCKETS 53
1731
1732#define RES_OKAY 0 /*!< Action completed */
1733#define RES_EXISTS (-1) /*!< Entry already exists */
1734#define RES_OUTOFMEMORY (-2) /*!< Out of memory */
1735#define RES_NOSUCHQUEUE (-3) /*!< No such queue */
1736#define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
1737#define RES_NOT_CALLER (-5) /*!< Caller not found */
1738
1739static char *app = "Queue";
1740
1741static char *app_aqm = "AddQueueMember" ;
1742
1743static char *app_rqm = "RemoveQueueMember" ;
1744
1745static char *app_pqm = "PauseQueueMember" ;
1746
1747static char *app_upqm = "UnpauseQueueMember" ;
1748
1749static char *app_ql = "QueueLog" ;
1750
1751static char *app_qupd = "QueueUpdate";
1752
1753/*! \brief Persistent Members astdb family */
1754static const char * const pm_family = "Queue/PersistentMembers";
1755
1756/*! \brief queues.conf [general] option */
1758
1759/*! \brief Records that one or more queues use weight */
1760static int use_weight;
1761
1762/*! \brief queues.conf [general] option */
1764
1765/*! \brief queues.conf [general] option */
1767
1768/*! \brief queues.conf [general] option */
1770
1771/*! \brief queuerules.conf [general] option */
1773
1774/*! \brief Subscription to device state change messages */
1776
1777/*! \brief queues.conf [general] option */
1779
1780/*! \brief queues.conf [general] option */
1782
1783/*! \brief queues.conf [general] option */
1785
1786/*! \brief queues.conf [general] option */
1788
1789/*! \brief name of the ringinuse field in the realtime database */
1791
1792/*! \brief does realtime backend support reason_paused */
1794
1805};
1806
1807static const struct {
1809 char *text;
1810} queue_results[] = {
1811 { QUEUE_UNKNOWN, "UNKNOWN" },
1812 { QUEUE_TIMEOUT, "TIMEOUT" },
1813 { QUEUE_JOINEMPTY,"JOINEMPTY" },
1814 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
1815 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
1816 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
1817 { QUEUE_FULL, "FULL" },
1818 { QUEUE_CONTINUE, "CONTINUE" },
1819 { QUEUE_WITHDRAW, "WITHDRAW" },
1821
1825};
1826
1827/*! \brief We define a custom "local user" structure because we
1828 * use it not only for keeping track of what is in use but
1829 * also for keeping track of who we're dialing.
1830 *
1831 * There are two "links" defined in this structure, q_next and call_next.
1832 * q_next links ALL defined callattempt structures into a linked list. call_next is
1833 * a link which allows for a subset of the callattempts to be traversed. This subset
1834 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
1835 * also is helpful so that queue logs are always accurate in the case where a call to
1836 * a member times out, especially if using the ringall strategy.
1837*/
1838
1843 char interface[256]; /*!< An Asterisk dial string (not a channel name) */
1846 /*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
1848 /*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
1850 /*! TRUE if the connected line update is blocked. */
1852 /*! TRUE if caller id is not available for connected line */
1853 unsigned int dial_callerid_absent:1;
1854 /*! TRUE if the call is still active */
1855 unsigned int stillgoing:1;
1857 /*! Original channel name. Must be freed. Could be NULL if allocation failed. */
1859};
1860
1861
1863 struct call_queue *parent; /*!< What queue is our parent */
1864 char moh[MAX_MUSICCLASS]; /*!< Name of musiconhold to be used */
1865 char announce[PATH_MAX]; /*!< Announcement to play for member when call is answered */
1866 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
1867 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
1868 const char *predial_callee; /*!< Gosub app arguments for outgoing calls. NULL if not supplied. */
1869 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
1870 int pos; /*!< Where we are in the queue */
1871 int prio; /*!< Our priority */
1872 int last_pos_said; /*!< Last position we told the user */
1873 int ring_when_ringing; /*!< Should we only use ring indication when a channel is ringing? */
1874 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
1875 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
1876 time_t last_pos; /*!< Last time we told the user their position */
1877 int opos; /*!< Where we started in the queue */
1878 int handled; /*!< Whether our call was handled */
1879 int pending; /*!< Non-zero if we are attempting to call a member */
1880 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
1881 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
1882 int raise_penalty; /*!< Float lower penalty members to a minimum penalty */
1883 int linpos; /*!< If using linear strategy, what position are we at? */
1884 int linwrapped; /*!< Is the linpos wrapped? */
1885 time_t start; /*!< When we started holding */
1886 time_t expire; /*!< When this entry should expire (time out of queue) */
1887 int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
1888 unsigned int withdraw:1; /*!< Should this call exit the queue at its next iteration? Used for QueueWithdrawCaller */
1889 char *withdraw_info; /*!< Optional info passed by the caller of QueueWithdrawCaller */
1890 struct ast_channel *chan; /*!< Our channel */
1891 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
1892 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
1893 struct queue_ent *next; /*!< The next queue entry */
1894};
1895
1896struct member {
1897 char interface[AST_CHANNEL_NAME]; /*!< Technology/Location to dial to reach this member*/
1898 char state_exten[AST_MAX_EXTENSION]; /*!< Extension to get state from (if using hint) */
1899 char state_context[AST_MAX_CONTEXT]; /*!< Context to use when getting state (if using hint) */
1900 char state_interface[AST_CHANNEL_NAME]; /*!< Technology/Location from which to read devicestate changes */
1901 int state_id; /*!< Extension state callback id (if using hint) */
1902 char membername[80]; /*!< Member name to use in queue logs */
1903 int penalty; /*!< Are we a last resort? */
1904 int calls; /*!< Number of calls serviced by this member */
1905 int dynamic; /*!< Are we dynamically added? */
1906 int realtime; /*!< Is this member realtime? */
1907 int status; /*!< Status of queue member */
1908 int paused; /*!< Are we paused (not accepting calls)? */
1909 char reason_paused[80]; /*!< Reason of paused if member is paused */
1910 int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
1911 int callcompletedinsl; /*!< Whether the current call was completed within service level */
1912 int wrapuptime; /*!< Wrapup Time */
1913 time_t starttime; /*!< The time at which the member answered the current caller. */
1914 time_t lastcall; /*!< When last successful call was hungup */
1915 time_t lastpause; /*!< When started the last pause */
1916 time_t logintime; /*!< The time when started the login */
1917 struct call_queue *lastqueue; /*!< Last queue we received a call */
1918 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
1919 unsigned int delme:1; /*!< Flag to delete entry on reload */
1920 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
1921 unsigned int ringinuse:1; /*!< Flag to ring queue members even if their status is 'inuse' */
1922};
1923
1933};
1934
1938};
1939
1940/* values used in multi-bit flags in call_queue */
1941#define ANNOUNCEHOLDTIME_ALWAYS 1
1942#define ANNOUNCEHOLDTIME_ONCE 2
1943#define QUEUE_EVENT_VARIABLES 3
1944
1946 int time; /*!< Number of seconds that need to pass before applying this rule */
1947 int max_value; /*!< The amount specified in the penalty rule for max penalty */
1948 int min_value; /*!< The amount specified in the penalty rule for min penalty */
1949 int raise_value; /*!< The amount specified in the penalty rule for min penalty */
1950 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
1951 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1952 int raise_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1953 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
1954};
1955
1956#define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
1957#define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
1958#define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
1959#define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
1960
1963 /*! Queue name */
1965 /*! Music on Hold class */
1967 /*! Announcement to play when call is answered */
1969 /*! Exit context */
1971 /*! Gosub to run upon member connection */
1973 /*! Default rule to use if none specified in call to Queue() */
1975 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
1977 /*! Sound file: "There are currently" (def. queue-thereare) */
1979 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
1981 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
1983 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
1985 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
1987 /*! Sound file: "minutes." (def. queue-minutes) */
1989 /*! Sound file: "minute." (def. queue-minute) */
1991 /*! Sound file: "seconds." (def. queue-seconds) */
1993 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
1995 /*! Sound file: Custom announce for caller, no default */
1997 /*! Sound file: "Hold time" (def. queue-reporthold) */
2000 /*! Sound files: Custom announce, no default */
2002 unsigned int dead:1;
2003 unsigned int ringinuse:1;
2004 unsigned int announce_to_first_user:1; /*!< Whether or not we announce to the first user in a queue */
2005 unsigned int setinterfacevar:1;
2006 unsigned int setqueuevar:1;
2007 unsigned int setqueueentryvar:1;
2008 unsigned int reportholdtime:1;
2009 unsigned int wrapped:1;
2010 unsigned int timeoutrestart:1;
2011 unsigned int announceholdtime:2;
2012 unsigned int announceposition:3;
2013 unsigned int announceposition_only_up:1; /*!< Only announce position if it has improved */
2015 unsigned int realtime:1;
2016 unsigned int found:1;
2018 unsigned int autopausebusy:1;
2019 unsigned int autopauseunavail:1;
2022 int announcepositionlimit; /*!< How many positions we announce? */
2023 int announcefrequency; /*!< How often to announce their position */
2024 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
2025 int periodicannouncestartdelay; /*!< How long into the queue should the periodic accouncement start */
2026 int periodicannouncefrequency; /*!< How often to play periodic announcement */
2027 int numperiodicannounce; /*!< The number of periodic announcements configured */
2028 int randomperiodicannounce; /*!< Are periodic announcements randomly chosen */
2029 int roundingseconds; /*!< How many seconds do we round to? */
2030 int holdtime; /*!< Current avg holdtime, based on an exponential average */
2031 int talktime; /*!< Current avg talktime, based on the same exponential average */
2032 int callscompleted; /*!< Number of queue calls completed */
2033 int callsabandoned; /*!< Number of queue calls abandoned */
2034 int callsabandonedinsl; /*!< Number of queue calls abandoned in servicelevel */
2035 int servicelevel; /*!< seconds setting for servicelevel*/
2036 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
2037 char monfmt[8]; /*!< Format to use when recording calls */
2038 int count; /*!< How many entries */
2039 int maxlen; /*!< Max number of entries */
2040 int wrapuptime; /*!< Wrapup Time */
2041 int penaltymemberslimit; /*!< Disregard penalty when queue has fewer than this many members */
2042
2043 int retry; /*!< Retry calling everyone after this amount of time */
2044 int timeout; /*!< How long to wait for an answer */
2045 int weight; /*!< Respective weight */
2046 int autopause; /*!< Auto pause queue members if they fail to answer */
2047 int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
2048 int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
2049
2050 /* Queue strategy things */
2051 int rrpos; /*!< Round Robin - position */
2052 int memberdelay; /*!< Seconds to delay connecting member to caller */
2053 int autofill; /*!< Ignore the head call status and ring an available agent */
2054
2055 int log_restricted_caller_id:1; /*!< Whether log Restricted Caller ID */
2056
2057 struct ao2_container *members; /*!< Head of the list of members */
2058 struct queue_ent *head; /*!< Head of the list of callers */
2059 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
2060 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
2061};
2062
2064 char name[80];
2067};
2068
2070
2071static struct ao2_container *queues;
2072
2073static void update_realtime_members(struct call_queue *q);
2074static struct member *interface_exists(struct call_queue *q, const char *interface);
2075static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
2076static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime);
2077
2078static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface);
2079/*! \brief sets the QUEUESTATUS channel variable */
2080static void set_queue_result(struct ast_channel *chan, enum queue_result res)
2081{
2082 int i;
2083
2084 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
2085 if (queue_results[i].id == res) {
2086 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
2087 return;
2088 }
2089 }
2090}
2091
2092static const char *int2strat(int strategy)
2093{
2094 int x;
2095
2096 for (x = 0; x < ARRAY_LEN(strategies); x++) {
2097 if (strategy == strategies[x].strategy) {
2098 return strategies[x].name;
2099 }
2100 }
2101
2102 return "<unknown>";
2103}
2104
2105static int strat2int(const char *strategy)
2106{
2107 int x;
2108
2109 for (x = 0; x < ARRAY_LEN(strategies); x++) {
2110 if (!strcasecmp(strategy, strategies[x].name)) {
2111 return strategies[x].strategy;
2112 }
2113 }
2114
2115 return -1;
2116}
2117
2118static int autopause2int(const char *autopause)
2119{
2120 int x;
2121 /*This 'double check' that default value is OFF */
2123 return QUEUE_AUTOPAUSE_OFF;
2124 }
2125
2126 /*This 'double check' is to ensure old values works */
2127 if(ast_true(autopause)) {
2128 return QUEUE_AUTOPAUSE_ON;
2129 }
2130
2131 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
2132 if (!strcasecmp(autopause, autopausesmodes[x].name)) {
2133 return autopausesmodes[x].autopause;
2134 }
2135 }
2136
2137 /*This 'double check' that default value is OFF */
2138 return QUEUE_AUTOPAUSE_OFF;
2139}
2140
2141static int queue_hash_cb(const void *obj, const int flags)
2142{
2143 const struct call_queue *q = obj;
2144
2145 return ast_str_case_hash(q->name);
2146}
2147
2148static int queue_cmp_cb(void *obj, void *arg, int flags)
2149{
2150 struct call_queue *q = obj, *q2 = arg;
2151 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
2152}
2153
2154/*!
2155 * \brief Return wrapuptime
2156 *
2157 * This function checks if wrapuptime in member is set and return this value.
2158 * Otherwise return value the wrapuptime in the queue configuration
2159 * \return integer value
2160 */
2161static int get_wrapuptime(struct call_queue *q, struct member *member)
2162{
2163 if (member->wrapuptime) {
2164 return member->wrapuptime;
2165 }
2166 return q->wrapuptime;
2167}
2168
2169/*! \internal
2170 * \brief ao2_callback, Decreases queuepos of all followers with a queuepos greater than arg.
2171 * \param obj the member being acted on
2172 * \param arg pointer to an integer containing the position value that was removed and requires reduction for anything above
2173 * \param flag unused
2174 */
2175static int queue_member_decrement_followers(void *obj, void *arg, int flag)
2176{
2177 struct member *mem = obj;
2178 int *decrement_followers_after = arg;
2179
2180 if (mem->queuepos > *decrement_followers_after) {
2181 mem->queuepos--;
2182 }
2183
2184 return 0;
2185}
2186
2187/*! \internal
2188 * \brief ao2_callback, finds members in a queue marked for deletion and in a cascading fashion runs queue_member_decrement_followers
2189 * on them. This callback should always be ran before performing mass unlinking of delmarked members from queues.
2190 * \param obj member being acted on
2191 * \param arg pointer to the queue members are being removed from
2192 * \param flag unused
2193 */
2194static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
2195{
2196 struct member *mem = obj;
2197 struct call_queue *queue = arg;
2198 int rrpos = mem->queuepos;
2199
2200 if (mem->delme) {
2202 }
2203
2204 return 0;
2205}
2206
2207/*! \internal
2208 * \brief Use this to decrement followers during removal of a member
2209 * \param queue which queue the member is being removed from
2210 * \param mem which member is being removed from the queue
2211 */
2212static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
2213{
2214 int pos = mem->queuepos;
2215
2216 /* 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
2217 * who would have been next otherwise. */
2218 if (pos < queue->rrpos) {
2219 queue->rrpos--;
2220 }
2221
2223}
2224
2225#define queue_ref(q) ao2_bump(q)
2226#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2227#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2228#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2229#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2230#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2231
2232/*! \brief Set variables of queue */
2233static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
2234{
2235 char interfacevar[256]="";
2236 float sl = 0;
2237
2238 ao2_lock(q);
2239
2240 if (q->setqueuevar) {
2241 sl = 0;
2242 if (q->callscompleted > 0) {
2243 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
2244 }
2245
2246 snprintf(interfacevar, sizeof(interfacevar),
2247 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2249
2250 ao2_unlock(q);
2251
2252 pbx_builtin_setvar_multiple(chan, interfacevar);
2253 } else {
2254 ao2_unlock(q);
2255 }
2256}
2257
2258/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
2259static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
2260{
2261 struct queue_ent *cur;
2262
2263 if (!q || !new)
2264 return;
2265 if (prev) {
2266 cur = prev->next;
2267 prev->next = new;
2268 } else {
2269 cur = q->head;
2270 q->head = new;
2271 }
2272 new->next = cur;
2273
2274 /* every queue_ent must have a reference to it's parent call_queue, this
2275 * reference does not go away until the end of the queue_ent's life, meaning
2276 * that even when the queue_ent leaves the call_queue this ref must remain. */
2277 if (!new->parent) {
2278 queue_ref(q);
2279 new->parent = q;
2280 }
2281 new->pos = ++(*pos);
2282 new->opos = *pos;
2283}
2284
2286{
2288 RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
2289 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2290
2291 channel_string = ast_manager_build_channel_state_string(obj->snapshot);
2292 event_string = ast_manager_str_from_json_object(obj->blob, NULL);
2293 if (!channel_string || !event_string) {
2294 return NULL;
2295 }
2296
2298 "%s"
2299 "%s",
2300 ast_str_buffer(channel_string),
2301 ast_str_buffer(event_string));
2302}
2303
2305{
2306 return queue_channel_to_ami("QueueCallerJoin", message);
2307}
2308
2310{
2311 return queue_channel_to_ami("QueueCallerLeave", message);
2312}
2313
2315{
2316 return queue_channel_to_ami("QueueCallerAbandon", message);
2317}
2318
2319STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_join_type,
2321 );
2322STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_leave_type,
2324 );
2325STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_abandon_type,
2327 );
2328
2330{
2331 struct ast_json_payload *payload = stasis_message_data(message);
2332 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2333
2334 event_string = ast_manager_str_from_json_object(payload->json, NULL);
2335 if (!event_string) {
2336 return NULL;
2337 }
2338
2340 "%s",
2341 ast_str_buffer(event_string));
2342}
2343
2345{
2346 return queue_member_to_ami("QueueMemberStatus", message);
2347}
2348
2350{
2351 return queue_member_to_ami("QueueMemberAdded", message);
2352}
2353
2355{
2356 return queue_member_to_ami("QueueMemberRemoved", message);
2357}
2358
2360{
2361 return queue_member_to_ami("QueueMemberPause", message);
2362}
2363
2365{
2366 return queue_member_to_ami("QueueMemberPenalty", message);
2367}
2368
2370{
2371 return queue_member_to_ami("QueueMemberRinginuse", message);
2372}
2373
2374STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_status_type,
2376 );
2377STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_added_type,
2379 );
2380STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_removed_type,
2382 );
2383STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_pause_type,
2385 );
2386STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_penalty_type,
2388 );
2389STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_ringinuse_type,
2391 );
2392
2394{
2397 struct ast_channel_snapshot *agent;
2398 RAII_VAR(struct ast_str *, caller_event_string, NULL, ast_free);
2399 RAII_VAR(struct ast_str *, agent_event_string, NULL, ast_free);
2400 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2401
2403 if (caller) {
2404 caller_event_string = ast_manager_build_channel_state_string(caller);
2405 if (!caller_event_string) {
2406 ast_log(LOG_NOTICE, "No caller event string, bailing\n");
2407 return NULL;
2408 }
2409 }
2410
2411 agent = ast_multi_channel_blob_get_channel(obj, "agent");
2412 if (agent) {
2413 agent_event_string = ast_manager_build_channel_state_string_prefix(agent, "Dest");
2414 if (!agent_event_string) {
2415 ast_log(LOG_NOTICE, "No agent event string, bailing\n");
2416 return NULL;
2417 }
2418 }
2419
2421 if (!event_string) {
2422 return NULL;
2423 }
2424
2426 "%s"
2427 "%s"
2428 "%s",
2429 caller_event_string ? ast_str_buffer(caller_event_string) : "",
2430 agent_event_string ? ast_str_buffer(agent_event_string) : "",
2431 ast_str_buffer(event_string));
2432}
2433
2435{
2436 return queue_multi_channel_to_ami("AgentCalled", message);
2437}
2438
2440{
2441 return queue_multi_channel_to_ami("AgentConnect", message);
2442}
2443
2445{
2446 return queue_multi_channel_to_ami("AgentComplete", message);
2447}
2448
2450{
2451 return queue_multi_channel_to_ami("AgentDump", message);
2452}
2453
2455{
2456 return queue_multi_channel_to_ami("AgentRingNoAnswer", message);
2457}
2458
2459STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_called_type,
2461 );
2462STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_connect_type,
2464 );
2465STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_complete_type,
2467 );
2470 );
2471STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_ringnoanswer_type,
2473 );
2474
2476 struct ast_channel_snapshot *caller_snapshot,
2477 struct ast_channel_snapshot *agent_snapshot,
2478 struct stasis_message_type *type, struct ast_json *blob)
2479{
2480 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
2481 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2482
2483 if (!type) {
2484 return;
2485 }
2486
2487 payload = ast_multi_channel_blob_create(blob);
2488 if (!payload) {
2489 return;
2490 }
2491
2492 if (caller_snapshot) {
2493 ast_multi_channel_blob_add_channel(payload, "caller", caller_snapshot);
2494 } else {
2495 ast_debug(1, "Empty caller_snapshot; sending incomplete event\n");
2496 }
2497
2498 if (agent_snapshot) {
2499 ast_multi_channel_blob_add_channel(payload, "agent", agent_snapshot);
2500 }
2501
2502 msg = stasis_message_create(type, payload);
2503 if (!msg) {
2504 return;
2505 }
2506
2507 stasis_publish(topic, msg);
2508}
2509
2510static void queue_publish_multi_channel_blob(struct ast_channel *caller, struct ast_channel *agent,
2511 struct stasis_message_type *type, struct ast_json *blob)
2512{
2513 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
2514 RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
2515
2516 ast_channel_lock(caller);
2517 caller_snapshot = ast_channel_snapshot_create(caller);
2518 ast_channel_unlock(caller);
2519 ast_channel_lock(agent);
2520 agent_snapshot = ast_channel_snapshot_create(agent);
2521 ast_channel_unlock(agent);
2522
2523 if (!caller_snapshot || !agent_snapshot) {
2524 return;
2525 }
2526
2528 agent_snapshot, type, blob);
2529}
2530
2531/*!
2532 * \internal
2533 * \brief Publish the member blob.
2534 * \since 12.0.0
2535 *
2536 * \param type Stasis message type to publish.
2537 * \param blob The information being published.
2538 *
2539 * \note The json blob reference is passed to this function.
2540 */
2542{
2543 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
2544 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2545
2546 if (!blob || !type) {
2547 ast_json_unref(blob);
2548 return;
2549 }
2550
2551 payload = ast_json_payload_create(blob);
2552 ast_json_unref(blob);
2553 if (!payload) {
2554 return;
2555 }
2556
2557 msg = stasis_message_create(type, payload);
2558 if (!msg) {
2559 return;
2560 }
2561
2563}
2564
2565static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
2566{
2567 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}",
2568 "Queue", q->name,
2569 "MemberName", mem->membername,
2570 "Interface", mem->interface,
2571 "StateInterface", mem->state_interface,
2572 "Membership", (mem->dynamic ? "dynamic" : (mem->realtime ? "realtime" : "static")),
2573 "Penalty", mem->penalty,
2574 "CallsTaken", mem->calls,
2575 "LastCall", (int)mem->lastcall,
2576 "LastPause", (int)mem->lastpause,
2577 "LoginTime", (int)mem->logintime,
2578 "InCall", mem->starttime ? 1 : 0,
2579 "Status", mem->status,
2580 "Paused", mem->paused,
2581 "PausedReason", mem->reason_paused,
2582 "Ringinuse", mem->ringinuse,
2583 "Wrapuptime", mem->wrapuptime);
2584}
2585
2586/*! \brief Check if members are available
2587 *
2588 * This function checks to see if members are available to be called. If any member
2589 * is available, the function immediately returns 0. If no members are available,
2590 * then -1 is returned.
2591 */
2592static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
2593{
2594 struct member *member;
2595 struct ao2_iterator mem_iter;
2596
2597 ao2_lock(q);
2598 mem_iter = ao2_iterator_init(q->members, 0);
2599 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
2600 int penalty = member->penalty;
2601 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2602 ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
2603 penalty = raise_penalty;
2604 }
2605 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2606 if (conditions & QUEUE_EMPTY_PENALTY) {
2607 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
2608 continue;
2609 }
2610 }
2611
2612 switch (devstate ? ast_device_state(member->state_interface) : member->status) {
2613 case AST_DEVICE_INVALID:
2614 if (conditions & QUEUE_EMPTY_INVALID) {
2615 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
2616 break;
2617 }
2618 goto default_case;
2620 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2621 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
2622 break;
2623 }
2624 goto default_case;
2625 case AST_DEVICE_INUSE:
2626 if (conditions & QUEUE_EMPTY_INUSE) {
2627 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
2628 break;
2629 }
2630 goto default_case;
2631 case AST_DEVICE_RINGING:
2632 if (conditions & QUEUE_EMPTY_RINGING) {
2633 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
2634 break;
2635 }
2636 goto default_case;
2637 case AST_DEVICE_UNKNOWN:
2638 if (conditions & QUEUE_EMPTY_UNKNOWN) {
2639 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
2640 break;
2641 }
2642 /* Fall-through */
2643 default:
2644 default_case:
2645 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2646 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
2647 break;
2648 } else if ((conditions & QUEUE_EMPTY_WRAPUP)
2649 && member->lastcall
2650 && get_wrapuptime(q, member)
2651 && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
2652 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2653 member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
2654 break;
2655 } else {
2656 ao2_ref(member, -1);
2657 ao2_iterator_destroy(&mem_iter);
2658 ao2_unlock(q);
2659 ast_debug(4, "%s is available.\n", member->membername);
2660 return 0;
2661 }
2662 break;
2663 }
2664 }
2665 ao2_iterator_destroy(&mem_iter);
2666 ao2_unlock(q);
2667
2668 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2669 /* member state still may be RINGING due to lag in event message - check again with device state */
2670 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2671 }
2672 return -1;
2673}
2674
2675/*
2676 * A "pool" of member objects that calls are currently pending on. If an
2677 * agent is a member of multiple queues it's possible for that agent to be
2678 * called by each of the queues at the same time. This happens because device
2679 * state is slow to notify the queue app of one of it's member's being rung.
2680 * This "pool" allows us to track which members are currently being rung while
2681 * we wait on the device state change.
2682 */
2684#define MAX_CALL_ATTEMPT_BUCKETS 353
2685
2686static int pending_members_hash(const void *obj, const int flags)
2687{
2688 const struct member *object;
2689 const char *key;
2690
2691 switch (flags & OBJ_SEARCH_MASK) {
2692 case OBJ_SEARCH_KEY:
2693 key = obj;
2694 break;
2695 case OBJ_SEARCH_OBJECT:
2696 object = obj;
2697 key = object->interface;
2698 break;
2699 default:
2700 ast_assert(0);
2701 return 0;
2702 }
2703 return ast_str_case_hash(key);
2704}
2705
2706static int pending_members_cmp(void *obj, void *arg, int flags)
2707{
2708 const struct member *object_left = obj;
2709 const struct member *object_right = arg;
2710 const char *right_key = arg;
2711 int cmp;
2712
2713 switch (flags & OBJ_SEARCH_MASK) {
2714 case OBJ_SEARCH_OBJECT:
2715 right_key = object_right->interface;
2716 /* Fall through */
2717 case OBJ_SEARCH_KEY:
2718 cmp = strcasecmp(object_left->interface, right_key);
2719 break;
2721 /* Not supported by container. */
2722 ast_assert(0);
2723 return 0;
2724 default:
2725 cmp = 0;
2726 break;
2727 }
2728 if (cmp) {
2729 return 0;
2730 }
2731 return CMP_MATCH;
2732}
2733
2734static void pending_members_remove(struct member *mem)
2735{
2736 ast_debug(3, "Removed %s from pending_members\n", mem->membername);
2738}
2739
2740/*! \brief set a member's status based on device state of that member's state_interface.
2741 *
2742 * Lock interface list find sc, iterate through each queues queue_member list for member to
2743 * update state inside queues
2744*/
2745static void update_status(struct call_queue *q, struct member *m, const int status)
2746{
2747 if (m->status != status) {
2748 /* If this member has transitioned to being available then update their queue
2749 * information. If they are currently in a call then the leg to the agent will be
2750 * considered done and the call finished.
2751 */
2754 }
2755
2756 m->status = status;
2757
2758 /* Remove the member from the pending members pool only when the status changes.
2759 * This is not done unconditionally because we can occasionally see multiple
2760 * device state notifications of not in use after a previous call has ended,
2761 * including after we have initiated a new call. This is more likely to
2762 * happen when there is latency in the connection to the member.
2763 */
2765
2766 queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2767 }
2768}
2769
2770/*!
2771 * \internal
2772 * \brief Determine if a queue member is available
2773 * \retval 1 if the member is available
2774 * \retval 0 if the member is not available
2775 */
2776static int is_member_available(struct call_queue *q, struct member *mem)
2777{
2778 int available = 0;
2779 int wrapuptime;
2780
2781 switch (mem->status) {
2782 case AST_DEVICE_INVALID:
2784 break;
2785 case AST_DEVICE_INUSE:
2786 case AST_DEVICE_BUSY:
2787 case AST_DEVICE_RINGING:
2789 case AST_DEVICE_ONHOLD:
2790 if (!mem->ringinuse) {
2791 break;
2792 }
2793 /* else fall through */
2795 case AST_DEVICE_UNKNOWN:
2796 if (!mem->paused) {
2797 available = 1;
2798 }
2799 break;
2800 }
2801
2802 /* Let wrapuptimes override device state availability */
2803 wrapuptime = get_wrapuptime(q, mem);
2804 if (mem->lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->lastcall)) {
2805 available = 0;
2806 }
2807 return available;
2808}
2809
2810/*! \brief set a member's status based on device state of that member's interface*/
2811static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
2812{
2813 struct ao2_iterator miter, qiter;
2814 struct ast_device_state_message *dev_state;
2815 struct member *m;
2816 struct call_queue *q;
2817 char interface[80], *slash_pos;
2818 int found = 0; /* Found this member in any queue */
2819 int found_member; /* Found this member in this queue */
2820 int avail = 0; /* Found an available member in this queue */
2821
2823 return;
2824 }
2825
2826 dev_state = stasis_message_data(msg);
2827 if (dev_state->eid) {
2828 /* ignore non-aggregate states */
2829 return;
2830 }
2831
2832 qiter = ao2_iterator_init(queues, 0);
2833 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
2834 ao2_lock(q);
2835
2836 avail = 0;
2837 found_member = 0;
2838 miter = ao2_iterator_init(q->members, 0);
2839 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2840 if (!found_member) {
2841 ast_copy_string(interface, m->state_interface, sizeof(interface));
2842
2843 if ((slash_pos = strchr(interface, '/'))) {
2844 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) {
2845 *slash_pos = '\0';
2846 }
2847 }
2848
2849 if (!strcasecmp(interface, dev_state->device)) {
2850 found_member = 1;
2851 update_status(q, m, dev_state->state);
2852 }
2853 }
2854
2855 /* check every member until we find one NOT_INUSE */
2856 if (!avail) {
2857 avail = is_member_available(q, m);
2858 }
2859 if (avail && found_member) {
2860 /* early exit as we've found an available member and the member of interest */
2861 ao2_ref(m, -1);
2862 break;
2863 }
2864 }
2865
2866 if (found_member) {
2867 found = 1;
2868 if (avail) {
2870 } else {
2872 }
2873 }
2874
2875 ao2_iterator_destroy(&miter);
2876
2877 ao2_unlock(q);
2878 queue_t_unref(q, "Done with iterator");
2879 }
2880 ao2_iterator_destroy(&qiter);
2881
2882 if (found) {
2883 ast_debug(1, "Device '%s' changed to state '%u' (%s)\n",
2884 dev_state->device,
2885 dev_state->state,
2886 ast_devstate2str(dev_state->state));
2887 } else {
2888 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",
2889 dev_state->device,
2890 dev_state->state,
2891 ast_devstate2str(dev_state->state));
2892 }
2893
2894 return;
2895}
2896
2897/*! \brief Helper function which converts from extension state to device state values */
2899{
2900 switch (state) {
2903 break;
2906 break;
2907 case AST_EXTENSION_BUSY:
2909 break;
2912 break;
2915 break;
2918 break;
2921 break;
2924 break;
2927 default:
2929 break;
2930 }
2931
2932 return state;
2933}
2934
2935/*!
2936 * \brief Returns if one context includes another context
2937 *
2938 * \param parent Parent context to search for child
2939 * \param child Context to check for inclusion in parent
2940 *
2941 * This function recursively checks if the context child is included in the context parent.
2942 *
2943 * \retval 1 if child is included in parent
2944 * \retval 0 if not
2945 */
2946static int context_included(const char *parent, const char *child);
2947static int context_included(const char *parent, const char *child)
2948{
2949 struct ast_context *c = NULL;
2950
2951 c = ast_context_find(parent);
2952 if (!c) {
2953 /* well, if parent doesn't exist, how can the child be included in it? */
2954 return 0;
2955 }
2956 if (!strcmp(ast_get_context_name(c), parent)) {
2957 /* found the context of the hint app_queue is using. Now, see
2958 if that context includes the one that just changed state */
2959 struct ast_include *inc = NULL;
2960
2961 while ((inc = (struct ast_include*) ast_walk_context_includes(c, inc))) {
2962 const char *includename = ast_get_include_name(inc);
2963 if (!strcasecmp(child, includename)) {
2964 return 1;
2965 }
2966 /* recurse on this context, for nested includes. The
2967 PBX extension parser will prevent infinite recursion. */
2968 if (context_included(includename, child)) {
2969 return 1;
2970 }
2971 }
2972 }
2973 return 0;
2974}
2975
2976static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
2977{
2978 struct ao2_iterator miter, qiter;
2979 struct member *m;
2980 struct call_queue *q;
2981 int state = info->exten_state;
2982 int found = 0, device_state = extensionstate2devicestate(state);
2983
2984 /* only interested in extension state updates involving device states */
2985 if (info->reason != AST_HINT_UPDATE_DEVICE) {
2986 return 0;
2987 }
2988
2989 qiter = ao2_iterator_init(queues, 0);
2990 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
2991 ao2_lock(q);
2992
2993 miter = ao2_iterator_init(q->members, 0);
2994 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2995 if (!strcmp(m->state_exten, exten) &&
2997 /* context could be included in m->state_context. We need to check. */
2998 found = 1;
2999 update_status(q, m, device_state);
3000 }
3001 }
3002 ao2_iterator_destroy(&miter);
3003
3004 ao2_unlock(q);
3005 queue_t_unref(q, "Done with iterator");
3006 }
3007 ao2_iterator_destroy(&qiter);
3008
3009 if (found) {
3010 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
3011 } else {
3012 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",
3013 exten, context, device_state, ast_devstate2str(device_state));
3014 }
3015
3016 return 0;
3017}
3018
3019/*! \brief Return the current state of a member */
3020static int get_queue_member_status(struct member *cur)
3021{
3023}
3024
3025static void destroy_queue_member_cb(void *obj)
3026{
3027 struct member *mem = obj;
3028
3029 if (mem->state_id != -1) {
3031 }
3032}
3033
3034/*! \brief allocate space for new queue member and set fields based on parameters passed */
3035static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
3036{
3037 struct member *cur;
3038
3039 if ((cur = ao2_alloc(sizeof(*cur), destroy_queue_member_cb))) {
3040 cur->ringinuse = ringinuse;
3041 cur->penalty = penalty;
3042 cur->paused = paused;
3043 cur->wrapuptime = wrapuptime;
3044 if (paused) {
3045 time(&cur->lastpause); /* Update time of last pause */
3046 }
3047 time(&cur->logintime);
3048 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
3051 } else {
3053 }
3055 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
3056 } else {
3057 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
3058 }
3059 if (!strchr(cur->interface, '/')) {
3060 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
3061 }
3062 if (!strncmp(cur->state_interface, "hint:", 5)) {
3063 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
3064 char *exten = strsep(&context, "@") + 5;
3065
3066 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
3067 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
3068
3070 } else {
3071 cur->state_id = -1;
3072 }
3073 cur->status = get_queue_member_status(cur);
3074 }
3075
3076 return cur;
3077}
3078
3079
3080static int compress_char(const char c)
3081{
3082 if (c < 32) {
3083 return 0;
3084 } else if (c > 96) {
3085 return c - 64;
3086 }
3087 return c - 32;
3088}
3089
3090static int member_hash_fn(const void *obj, const int flags)
3091{
3092 const struct member *mem = obj;
3093 const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
3094 const char *chname = strchr(interface, '/');
3095 int ret = 0, i;
3096
3097 if (!chname) {
3098 chname = interface;
3099 }
3100 for (i = 0; i < 5 && chname[i]; i++) {
3101 ret += compress_char(chname[i]) << (i * 6);
3102 }
3103 return ret;
3104}
3105
3106static int member_cmp_fn(void *obj1, void *obj2, int flags)
3107{
3108 struct member *mem1 = obj1;
3109 struct member *mem2 = obj2;
3110 const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;
3111
3112 return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
3113}
3114
3115/*!
3116 * \brief Initialize Queue default values.
3117 * \note the queue's lock must be held before executing this function
3118*/
3119static void init_queue(struct call_queue *q)
3120{
3121 int i;
3122 struct penalty_rule *pr_iter;
3123
3124 q->dead = 0;
3125 q->retry = DEFAULT_RETRY;
3127 q->maxlen = 0;
3128
3129 ast_string_field_set(q, announce, "");
3131 ast_string_field_set(q, membergosub, "");
3132 ast_string_field_set(q, defaultrule, "");
3133
3134 q->announcefrequency = 0;
3136 q->announceholdtime = 1;
3138 q->announcepositionlimit = 10; /* Default 10 positions */
3139 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
3140 q->roundingseconds = 0; /* Default - don't announce seconds */
3141 q->servicelevel = 0;
3142 q->ringinuse = 1;
3144 q->setinterfacevar = 0;
3145 q->setqueuevar = 0;
3146 q->setqueueentryvar = 0;
3148 q->monfmt[0] = '\0';
3149 q->reportholdtime = 0;
3150 q->wrapuptime = 0;
3151 q->penaltymemberslimit = 0;
3152 q->joinempty = 0;
3153 q->leavewhenempty = 0;
3154 q->memberdelay = 0;
3155 q->weight = 0;
3156 q->timeoutrestart = 0;
3160 q->numperiodicannounce = 0;
3163 q->autopausebusy = 0;
3164 q->autopauseunavail = 0;
3166 q->autopausedelay = 0;
3168 if (!q->members) {
3170 /* linear strategy depends on order, so we have to place all members in a list */
3172 } else {
3175 }
3176 }
3177 q->found = 1;
3178
3179 ast_string_field_set(q, moh, "");
3180 ast_string_field_set(q, sound_next, "queue-youarenext");
3181 ast_string_field_set(q, sound_thereare, "queue-thereare");
3182 ast_string_field_set(q, sound_calls, "queue-callswaiting");
3183 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
3184 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
3185 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
3186 ast_string_field_set(q, sound_minutes, "queue-minutes");
3187 ast_string_field_set(q, sound_minute, "queue-minute");
3188 ast_string_field_set(q, sound_seconds, "queue-seconds");
3189 ast_string_field_set(q, sound_thanks, "queue-thankyou");
3190 ast_string_field_set(q, sound_callerannounce, "");
3191 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
3192
3193 if (!q->sound_periodicannounce[0]) {
3195 }
3196
3197 if (q->sound_periodicannounce[0]) {
3198 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
3199 }
3200
3201 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3202 if (q->sound_periodicannounce[i]) {
3203 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
3204 }
3205 }
3206
3207 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
3208 ast_free(pr_iter);
3209 }
3210
3211 /* On restart assume no members are available.
3212 * The queue_avail hint is a boolean state to indicate whether a member is available or not.
3213 *
3214 * This seems counter intuitive, but is required to light a BLF
3215 * AST_DEVICE_INUSE indicates no members are available.
3216 * AST_DEVICE_NOT_INUSE indicates a member is available.
3217 */
3219}
3220
3221static void clear_queue(struct call_queue *q)
3222{
3223 q->holdtime = 0;
3224 q->callscompleted = 0;
3225 q->callsabandoned = 0;
3226 q->callscompletedinsl = 0;
3227 q->callsabandonedinsl = 0;
3228 q->talktime = 0;
3229
3230 if (q->members) {
3231 struct member *mem;
3232 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3233 while ((mem = ao2_iterator_next(&mem_iter))) {
3234 mem->calls = 0;
3235 mem->callcompletedinsl = 0;
3236 mem->lastcall = 0;
3237 mem->starttime = 0;
3238 ao2_ref(mem, -1);
3239 }
3240 ao2_iterator_destroy(&mem_iter);
3241 }
3242}
3243
3244/*!
3245 * \brief Change queue penalty by adding rule.
3246 *
3247 * Check rule for errors with time or formatting, see if rule is relative to rest
3248 * of queue, iterate list of rules to find correct insertion point, insert and return.
3249 * \retval -1 on failure
3250 * \retval 0 on success
3251 * \note Call this with the rule_lists locked
3252*/
3253static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
3254{
3255 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3256 struct penalty_rule *rule = NULL, *rule_iter;
3257 struct rule_list *rl_iter;
3258 int penaltychangetime, inserted = 0;
3259
3260 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
3261 return -1;
3262 }
3263
3264 contentdup = ast_strdupa(content);
3265
3266 if (!(maxstr = strchr(contentdup, ','))) {
3267 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3268 ast_free(rule);
3269 return -1;
3270 }
3271
3272 *maxstr++ = '\0';
3273 if ((minstr = strchr(maxstr,','))) {
3274 *minstr++ = '\0';
3275 if ((raisestr = strchr(minstr,','))) {
3276 *raisestr++ = '\0';
3277 }
3278 } else {
3279 raisestr = NULL;
3280 }
3281
3282 timestr = contentdup;
3283 if ((penaltychangetime = atoi(timestr)) < 0) {
3284 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3285 ast_free(rule);
3286 return -1;
3287 }
3288
3289 rule->time = penaltychangetime;
3290
3291 /* The last check will evaluate true if either no penalty change is indicated for a given rule
3292 * OR if a min penalty change is indicated but no max penalty change is */
3293 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
3294 rule->max_relative = 1;
3295 }
3296
3297 rule->max_value = atoi(maxstr);
3298
3299 if (!ast_strlen_zero(minstr)) {
3300 if (*minstr == '+' || *minstr == '-') {
3301 rule->min_relative = 1;
3302 }
3303 rule->min_value = atoi(minstr);
3304 } else { /*there was no minimum specified, so assume this means no change*/
3305 rule->min_relative = 1;
3306 }
3307
3308 if (!ast_strlen_zero(raisestr)) {
3309 if (*raisestr == '+' || *raisestr == '-') {
3310 rule->raise_relative = 1;
3311 }
3312 rule->raise_value = atoi(raisestr);
3313 } else { /*there was no raise specified, so assume this means no change*/
3314 rule->raise_relative = 1;
3315 }
3316
3317 /*We have the rule made, now we need to insert it where it belongs*/
3318 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
3319 if (strcasecmp(rl_iter->name, list_name)) {
3320 continue;
3321 }
3322
3323 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
3324 if (rule->time < rule_iter->time) {
3326 inserted = 1;
3327 break;
3328 }
3329 }
3331
3332 if (!inserted) {
3333 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
3334 inserted = 1;
3335 }
3336
3337 break;
3338 }
3339
3340 if (!inserted) {
3341 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
3342 ast_free(rule);
3343 return -1;
3344 }
3345 return 0;
3346}
3347
3348/*!
3349 * \brief Load queue rules from realtime.
3350 *
3351 * Check rule for errors with time or formatting, see if rule is relative to rest
3352 * of queue, iterate list of rules to find correct insertion point, insert and return.
3353 * \retval -1 on failure
3354 * \retval 0 on success
3355 * \note Call this with the rule_lists locked
3356*/
3357static int load_realtime_rules(void)
3358{
3359 struct ast_config *cfg;
3360 struct rule_list *rl_iter, *new_rl;
3361 struct penalty_rule *pr_iter;
3362 char *rulecat = NULL;
3363
3364 if (!ast_check_realtime("queue_rules")) {
3365 ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
3366 return 0;
3367 }
3368 if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
3369 ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
3370 return 0;
3371 }
3372 while ((rulecat = ast_category_browse(cfg, rulecat))) {
3373 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3374 int penaltychangetime, rule_exists = 0, inserted = 0;
3375 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3376 int min_relative = 0, max_relative = 0, raise_relative = 0;
3377 struct penalty_rule *new_penalty_rule = NULL;
3378
3379 rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
3380 if (ast_strlen_zero(rule_name)) {
3381 continue;
3382 }
3383
3384 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
3385 if (!(strcasecmp(rl_iter->name, rule_name))) {
3386 rule_exists = 1;
3387 new_rl = rl_iter;
3388 break;
3389 }
3390 }
3391 if (!rule_exists) {
3392 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
3393 ast_config_destroy(cfg);
3394 return -1;
3395 }
3396 ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
3398 }
3399 timestr = ast_variable_retrieve(cfg, rulecat, "time");
3400 if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
3401 ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
3402 (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
3403 continue;
3404 }
3405 if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
3406 ast_config_destroy(cfg);
3407 return -1;
3408 }
3409 if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
3410 ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
3411 max_penalty = 0;
3412 max_relative = 1;
3413 } else {
3414 if (*maxstr == '+' || *maxstr == '-') {
3415 max_relative = 1;
3416 }
3417 }
3418 if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
3419 ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
3420 min_penalty = 0;
3421 min_relative = 1;
3422 } else {
3423 if (*minstr == '+' || *minstr == '-') {
3424 min_relative = 1;
3425 }
3426 }
3427 if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
3428 ast_strlen_zero(raisestr) || sscanf(raisestr, "%30d", &raise_penalty) != 1) {
3429 raise_penalty = 0;
3430 raise_relative = 1;
3431 } else {
3432 if (*raisestr == '+' || *raisestr == '-') {
3433 raise_relative = 1;
3434 }
3435 }
3436 new_penalty_rule->time = penaltychangetime;
3437 new_penalty_rule->max_relative = max_relative;
3438 new_penalty_rule->max_value = max_penalty;
3439 new_penalty_rule->min_relative = min_relative;
3440 new_penalty_rule->min_value = min_penalty;
3441 new_penalty_rule->raise_relative = raise_relative;
3442 new_penalty_rule->raise_value = raise_penalty;
3443 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
3444 if (new_penalty_rule->time < pr_iter->time) {
3445 AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
3446 inserted = 1;
3447 }
3448 }
3450 if (!inserted) {
3451 AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
3452 }
3453 }
3454
3455 ast_config_destroy(cfg);
3456 return 0;
3457}
3458
3459static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
3460{
3461 char *value_copy = ast_strdupa(value);
3462 char *option = NULL;
3463 while ((option = strsep(&value_copy, ","))) {
3464 if (!strcasecmp(option, "paused")) {
3465 *empty |= QUEUE_EMPTY_PAUSED;
3466 } else if (!strcasecmp(option, "penalty")) {
3467 *empty |= QUEUE_EMPTY_PENALTY;
3468 } else if (!strcasecmp(option, "inuse")) {
3469 *empty |= QUEUE_EMPTY_INUSE;
3470 } else if (!strcasecmp(option, "ringing")) {
3471 *empty |= QUEUE_EMPTY_RINGING;
3472 } else if (!strcasecmp(option, "invalid")) {
3473 *empty |= QUEUE_EMPTY_INVALID;
3474 } else if (!strcasecmp(option, "wrapup")) {
3475 *empty |= QUEUE_EMPTY_WRAPUP;
3476 } else if (!strcasecmp(option, "unavailable")) {
3477 *empty |= QUEUE_EMPTY_UNAVAILABLE;
3478 } else if (!strcasecmp(option, "unknown")) {
3479 *empty |= QUEUE_EMPTY_UNKNOWN;
3480 } else if (!strcasecmp(option, "loose")) {
3482 } else if (!strcasecmp(option, "strict")) {
3484 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
3486 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
3487 *empty = 0;
3488 } else {
3489 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
3490 }
3491 }
3492}
3493
3494/*! \brief Configure a queue parameter.
3495 *
3496 * The failunknown flag is set for config files (and static realtime) to show
3497 * errors for unknown parameters. It is cleared for dynamic realtime to allow
3498 * extra fields in the tables.
3499 * \note For error reporting, line number is passed for .conf static configuration,
3500 * for Realtime queues, linenum is -1.
3501*/
3502static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
3503{
3504 if (!strcasecmp(param, "musicclass") ||
3505 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
3506 ast_string_field_set(q, moh, val);
3507 } else if (!strcasecmp(param, "announce")) {
3508 ast_string_field_set(q, announce, val);
3509 } else if (!strcasecmp(param, "context")) {
3511 } else if (!strcasecmp(param, "timeout")) {
3512 q->timeout = atoi(val);
3513 if (q->timeout < 0) {
3515 }
3516 } else if (!strcasecmp(param, "ringinuse")) {
3517 q->ringinuse = ast_true(val);
3518 } else if (!strcasecmp(param, "setinterfacevar")) {
3520 } else if (!strcasecmp(param, "setqueuevar")) {
3521 q->setqueuevar = ast_true(val);
3522 } else if (!strcasecmp(param, "setqueueentryvar")) {
3524 } else if (!strcasecmp(param, "monitor-format")) {
3525 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
3526 } else if (!strcasecmp(param, "membergosub")) {
3527 ast_string_field_set(q, membergosub, val);
3528 } else if (!strcasecmp(param, "queue-youarenext")) {
3529 ast_string_field_set(q, sound_next, val);
3530 } else if (!strcasecmp(param, "queue-thereare")) {
3531 ast_string_field_set(q, sound_thereare, val);
3532 } else if (!strcasecmp(param, "queue-callswaiting")) {
3533 ast_string_field_set(q, sound_calls, val);
3534 } else if (!strcasecmp(param, "queue-quantity1")) {
3535 ast_string_field_set(q, queue_quantity1, val);
3536 } else if (!strcasecmp(param, "queue-quantity2")) {
3537 ast_string_field_set(q, queue_quantity2, val);
3538 } else if (!strcasecmp(param, "queue-holdtime")) {
3539 ast_string_field_set(q, sound_holdtime, val);
3540 } else if (!strcasecmp(param, "queue-minutes")) {
3541 ast_string_field_set(q, sound_minutes, val);
3542 } else if (!strcasecmp(param, "queue-minute")) {
3543 ast_string_field_set(q, sound_minute, val);
3544 } else if (!strcasecmp(param, "queue-seconds")) {
3545 ast_string_field_set(q, sound_seconds, val);
3546 } else if (!strcasecmp(param, "queue-thankyou")) {
3547 ast_string_field_set(q, sound_thanks, val);
3548 } else if (!strcasecmp(param, "queue-callerannounce")) {
3549 ast_string_field_set(q, sound_callerannounce, val);
3550 } else if (!strcasecmp(param, "queue-reporthold")) {
3551 ast_string_field_set(q, sound_reporthold, val);
3552 } else if (!strcasecmp(param, "announce-frequency")) {
3553 q->announcefrequency = atoi(val);
3554 } else if (!strcasecmp(param, "announce-to-first-user")) {
3556 } else if (!strcasecmp(param, "min-announce-frequency")) {
3557 q->minannouncefrequency = atoi(val);
3558 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
3559 } else if (!strcasecmp(param, "announce-round-seconds")) {
3560 q->roundingseconds = atoi(val);
3561 /* Rounding to any other values just doesn't make sense... */
3562 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
3563 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
3564 if (linenum >= 0) {
3565 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3566 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3567 val, param, q->name, linenum);
3568 } else {
3569 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3570 "using 0 instead for queue '%s'\n", val, param, q->name);
3571 }
3572 q->roundingseconds=0;
3573 }
3574 } else if (!strcasecmp(param, "announce-holdtime")) {
3575 if (!strcasecmp(val, "once")) {
3577 } else if (ast_true(val)) {
3579 } else {
3580 q->announceholdtime = 0;
3581 }
3582 } else if (!strcasecmp(param, "announce-position")) {
3583 if (!strcasecmp(val, "limit")) {
3585 } else if (!strcasecmp(val, "more")) {
3587 } else if (ast_true(val)) {
3589 } else {
3591 }
3592 } else if (!strcasecmp(param, "announce-position-only-up")) {
3594 } else if (!strcasecmp(param, "announce-position-limit")) {
3595 q->announcepositionlimit = atoi(val);
3596 } else if (!strcasecmp(param, "periodic-announce")) {
3597 if (strchr(val, ',')) {
3598 char *s, *buf = ast_strdupa(val);
3599 unsigned int i = 0;
3600
3601 while ((s = strsep(&buf, ",|"))) {
3602 if (!q->sound_periodicannounce[i]) {
3604 }
3605 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
3606 i++;
3607 if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
3608 break;
3609 }
3610 }
3611 q->numperiodicannounce = i;
3612 } else {
3613 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
3614 q->numperiodicannounce = 1;
3615 }
3616 } else if (!strcasecmp(param, "periodic-announce-startdelay")) {
3618 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
3619 q->periodicannouncefrequency = atoi(val);
3620 } else if (!strcasecmp(param, "relative-periodic-announce")) {
3622 } else if (!strcasecmp(param, "random-periodic-announce")) {
3624 } else if (!strcasecmp(param, "retry")) {
3625 q->retry = atoi(val);
3626 if (q->retry <= 0) {
3627 q->retry = DEFAULT_RETRY;
3628 }
3629 } else if (!strcasecmp(param, "wrapuptime")) {
3630 q->wrapuptime = atoi(val);
3631 } else if (!strcasecmp(param, "penaltymemberslimit")) {
3632 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
3633 q->penaltymemberslimit = 0;
3634 }
3635 } else if (!strcasecmp(param, "autofill")) {
3636 q->autofill = ast_true(val);
3637 } else if (!strcasecmp(param, "autopause")) {
3639 } else if (!strcasecmp(param, "autopausedelay")) {
3640 q->autopausedelay = atoi(val);
3641 } else if (!strcasecmp(param, "autopausebusy")) {
3643 } else if (!strcasecmp(param, "autopauseunavail")) {
3645 } else if (!strcasecmp(param, "maxlen")) {
3646 q->maxlen = atoi(val);
3647 if (q->maxlen < 0) {
3648 q->maxlen = 0;
3649 }
3650 } else if (!strcasecmp(param, "servicelevel")) {
3651 q->servicelevel= atoi(val);
3652 } else if (!strcasecmp(param, "strategy")) {
3653 int strategy;
3654
3655 /* We are a static queue and already have set this, no need to do it again */
3656 if (failunknown) {
3657 return;
3658 }
3660 if (strategy < 0) {
3661 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3662 val, q->name);
3664 }
3665 if (strategy == q->strategy) {
3666 return;
3667 }
3669 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
3670 return;
3671 }
3672 q->strategy = strategy;
3673 } else if (!strcasecmp(param, "joinempty")) {
3675 } else if (!strcasecmp(param, "leavewhenempty")) {
3677 } else if (!strcasecmp(param, "reportholdtime")) {
3679 } else if (!strcasecmp(param, "memberdelay")) {
3680 q->memberdelay = atoi(val);
3681 } else if (!strcasecmp(param, "weight")) {
3682 q->weight = atoi(val);
3683 } else if (!strcasecmp(param, "timeoutrestart")) {
3685 } else if (!strcasecmp(param, "defaultrule")) {
3686 ast_string_field_set(q, defaultrule, val);
3687 } else if (!strcasecmp(param, "timeoutpriority")) {
3688 if (!strcasecmp(val, "conf")) {
3690 } else {
3692 }
3693 } else if (!strcasecmp(param, "log-restricted-caller-id")) {
3695 } else if (failunknown) {
3696 if (linenum >= 0) {
3697 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3698 q->name, param, linenum);
3699 } else {
3700 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
3701 }
3702 }
3703}
3704
3705
3706#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3707#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3708#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3709
3710/*! \internal
3711 * \brief If adding a single new member to a queue, use this function instead of ao2_linking.
3712 * This adds round robin queue position data for a fresh member as well as links it.
3713 * \param queue Which queue the member is being added to
3714 * \param mem Which member is being added to the queue
3715 */
3716static void member_add_to_queue(struct call_queue *queue, struct member *mem)
3717{
3718 ao2_lock(queue->members);
3719 mem->queuepos = ao2_container_count(queue->members);
3720 ao2_link(queue->members, mem);
3722 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", queue->name, mem->interface);
3723 ao2_unlock(queue->members);
3724}
3725
3726/*! \internal
3727 * \brief If removing a single member from a queue, use this function instead of ao2_unlinking.
3728 * This will perform round robin queue position reordering for the remaining members.
3729 * \param queue Which queue the member is being removed from
3730 * \param mem Which member is being removed from the queue
3731 */
3732static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
3733{
3735 ao2_lock(queue->members);
3738 ao2_unlink(queue->members, mem);
3739 ao2_unlock(queue->members);
3740}
3741
3742/*!
3743 * \brief Find rt member record to update otherwise create one.
3744 *
3745 * Search for member in queue, if found update penalty/paused state,
3746 * if no member exists create one flag it as a RT member and add to queue member list.
3747*/
3748static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
3749{
3750 struct member *m;
3751 struct ao2_iterator mem_iter;
3752 int penalty = 0;
3753 int paused = 0;
3754 int found = 0;
3755 int wrapuptime = 0;
3756 int ringinuse = q->ringinuse;
3757
3758 const char *config_val;
3759 const char *interface = ast_variable_retrieve(member_config, category, "interface");
3760 const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
3761 const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
3762 const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
3763 const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
3764 const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
3765 const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
3766 const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
3767
3768 if (ast_strlen_zero(rt_uniqueid)) {
3769 ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
3770 S_OR(membername, "NULL"));
3771 return;
3772 }
3773
3774 if (ast_strlen_zero(interface)) {
3775 ast_log(LOG_WARNING, "Realtime field 'interface' is empty for member %s\n",
3776 S_OR(membername, "NULL"));
3777 return;
3778 }
3779
3780 if (penalty_str) {
3781 penalty = atoi(penalty_str);
3782 if ((penalty < 0) && negative_penalty_invalid) {
3783 return;
3784 } else if (penalty < 0) {
3785 penalty = 0;
3786 }
3787 }
3788
3789 if (paused_str) {
3790 paused = atoi(paused_str);
3791 if (paused < 0) {
3792 paused = 0;
3793 }
3794 }
3795
3796 if (wrapuptime_str) {
3797 wrapuptime = atoi(wrapuptime_str);
3798 if (wrapuptime < 0) {
3799 wrapuptime = 0;
3800 }
3801 }
3802
3803 if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3804 if (ast_true(config_val)) {
3805 ringinuse = 1;
3806 } else if (ast_false(config_val)) {
3807 ringinuse = 0;
3808 } else {
3809 ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
3810 }
3811 }
3812
3813 /* Find member by realtime uniqueid and update */
3814 mem_iter = ao2_iterator_init(q->members, 0);
3815 while ((m = ao2_iterator_next(&mem_iter))) {
3816 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
3817 m->dead = 0; /* Do not delete this one. */
3818 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3819 if (paused_str) {
3820 m->paused = paused;
3821 if (paused && m->lastpause == 0) {
3822 time(&m->lastpause); /* XXX: Should this come from realtime? */
3823 }
3825 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, m->interface);
3826 }
3827 if (strcasecmp(state_interface, m->state_interface)) {
3828 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
3829 }
3830 m->penalty = penalty;
3831 m->ringinuse = ringinuse;
3832 m->wrapuptime = wrapuptime;
3834 ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
3835 }
3836 found = 1;
3837 ao2_ref(m, -1);
3838 break;
3839 }
3840 ao2_ref(m, -1);
3841 }
3842 ao2_iterator_destroy(&mem_iter);
3843
3844 /* Create a new member */
3845 if (!found) {
3846 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3847 m->dead = 0;
3848 m->realtime = 1;
3849 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3850 if (!ast_strlen_zero(reason_paused)) {
3851 ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
3852 }
3854 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3855 } else {
3856 ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3857 }
3858 member_add_to_queue(q, m);
3859 ao2_ref(m, -1);
3860 m = NULL;
3861 }
3862 }
3863}
3864
3865/*! \brief Iterate through queue's member list and delete them */
3866static void free_members(struct call_queue *q, int all)
3867{
3868 /* Free non-dynamic members */
3869 struct member *cur;
3870 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3871
3872 while ((cur = ao2_iterator_next(&mem_iter))) {
3873 if (all || !cur->dynamic) {
3875 }
3876 ao2_ref(cur, -1);
3877 }
3878 ao2_iterator_destroy(&mem_iter);
3879}
3880
3881/*! \brief Free queue's member list then its string fields */
3882static void destroy_queue(void *obj)
3883{
3884 struct call_queue *q = obj;
3885 int i;
3886
3887 free_members(q, 1);
3889 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3890 if (q->sound_periodicannounce[i]) {
3892 }
3893 }
3894 ao2_ref(q->members, -1);
3895}
3896
3897static struct call_queue *alloc_queue(const char *queuename)
3898{
3899 struct call_queue *q;
3900
3901 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
3902 if (ast_string_field_init(q, 64)) {
3903 queue_t_unref(q, "String field allocation failed");
3904 return NULL;
3905 }
3906 ast_string_field_set(q, name, queuename);
3907 }
3908 return q;
3909}
3910
3911/*!
3912 * \brief Reload a single queue via realtime.
3913 *
3914 * Check for statically defined queue first, check if deleted RT queue,
3915 * check for new RT queue, if queue vars are not defined init them with defaults.
3916 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
3917 * \retval the queue,
3918 * \retval NULL if it doesn't exist.
3919 * \note Should be called with the "queues" container locked.
3920*/
3921static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
3922{
3923 struct ast_variable *v;
3924 struct call_queue *q, tmpq = {
3925 .name = queuename,
3926 };
3927 struct member *m;
3928 struct ao2_iterator mem_iter;
3929 char *category = NULL;
3930 const char *tmp_name;
3931 char *tmp;
3932 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
3933
3934 /* Static queues override realtime. */
3935 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
3936 ao2_lock(q);
3937 if (!q->realtime) {
3938 if (q->dead) {
3939 ao2_unlock(q);
3940 queue_t_unref(q, "Queue is dead; can't return it");
3941 return NULL;
3942 }
3943 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
3944 ao2_unlock(q);
3945 return q;
3946 }
3947 } else if (!member_config) {
3948 /* Not found in the list, and it's not realtime ... */
3949 return NULL;
3950 }
3951 /* Check if queue is defined in realtime. */
3952 if (!queue_vars) {
3953 /* Delete queue from in-core list if it has been deleted in realtime. */
3954 if (q) {
3955 /*! \note Hmm, can't seem to distinguish a DB failure from a not
3956 found condition... So we might delete an in-core queue
3957 in case of DB failure. */
3958 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
3959
3960 q->dead = 1;
3961 /* Delete if unused (else will be deleted when last caller leaves). */
3962 queues_t_unlink(queues, q, "Unused; removing from container");
3963 ao2_unlock(q);
3964 queue_t_unref(q, "Queue is dead; can't return it");
3965 }
3966 return NULL;
3967 }
3968
3969 /* Create a new queue if an in-core entry does not exist yet. */
3970 if (!q) {
3971 struct ast_variable *tmpvar = NULL;
3972 if (!(q = alloc_queue(queuename))) {
3973 return NULL;
3974 }
3975 ao2_lock(q);
3976 clear_queue(q);
3977 q->realtime = 1;
3978 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
3979 * will allocate the members properly
3980 */
3981 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
3982 if (!strcasecmp(tmpvar->name, "strategy")) {
3983 q->strategy = strat2int(tmpvar->value);
3984 if (q->strategy < 0) {
3985 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3986 tmpvar->value, q->name);
3988 }
3989 break;
3990 }
3991 }
3992 /* We traversed all variables and didn't find a strategy */
3993 if (!tmpvar) {
3995 }
3996 queues_t_link(queues, q, "Add queue to container");
3997 }
3998 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
3999
4000 memset(tmpbuf, 0, sizeof(tmpbuf));
4001 for (v = queue_vars; v; v = v->next) {
4002 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
4003 if (strchr(v->name, '_')) {
4004 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
4005 tmp_name = tmpbuf;
4006 tmp = tmpbuf;
4007 while ((tmp = strchr(tmp, '_'))) {
4008 *tmp++ = '-';
4009 }
4010 } else {
4011 tmp_name = v->name;
4012 }
4013
4014 /* NULL values don't get returned from realtime; blank values should
4015 * still get set. If someone doesn't want a value to be set, they
4016 * should set the realtime column to NULL, not blank. */
4017 queue_set_param(q, tmp_name, v->value, -1, 0);
4018 }
4019
4020 /* Temporarily set realtime members dead so we can detect deleted ones. */
4021 mem_iter = ao2_iterator_init(q->members, 0);
4022 while ((m = ao2_iterator_next(&mem_iter))) {
4023 if (m->realtime) {
4024 m->dead = 1;
4025 }
4026 ao2_ref(m, -1);
4027 }
4028 ao2_iterator_destroy(&mem_iter);
4029
4030 while ((category = ast_category_browse(member_config, category))) {
4031 rt_handle_member_record(q, category, member_config);
4032 }
4033
4034 /* Delete all realtime members that have been deleted in DB. */
4035 mem_iter = ao2_iterator_init(q->members, 0);
4036 while ((m = ao2_iterator_next(&mem_iter))) {
4037 if (m->dead) {
4039 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4040 } else {
4041 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4042 }
4044 }
4045 ao2_ref(m, -1);
4046 }
4047 ao2_iterator_destroy(&mem_iter);
4048
4049 ao2_unlock(q);
4050
4051 return q;
4052}
4053
4054/*!
4055 * note */
4056
4057/*!
4058 * \internal
4059 * \brief Returns reference to the named queue. If the queue is realtime, it will load the queue as well.
4060 * \param queuename - name of the desired queue
4061 *
4062 * \retval the queue
4063 * \retval NULL if it doesn't exist
4064 */
4065static struct call_queue *find_load_queue_rt_friendly(const char *queuename)
4066{
4067 struct ast_variable *queue_vars;
4068 struct ast_config *member_config = NULL;
4069 struct call_queue *q = NULL, tmpq = {
4070 .name = queuename,
4071 };
4072 int prev_weight = 0;
4073
4074 /* Find the queue in the in-core list first. */
4075 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
4076
4077 if (!q || q->realtime) {
4078 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
4079 queue operations while waiting for the DB.
4080
4081 This will be two separate database transactions, so we might
4082 see queue parameters as they were before another process
4083 changed the queue and member list as it was after the change.
4084 Thus we might see an empty member list when a queue is
4085 deleted. In practise, this is unlikely to cause a problem. */
4086
4087 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
4088 if (queue_vars) {
4089 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
4090 if (!member_config) {
4091 ast_debug(1, "No queue_members defined in config extconfig.conf\n");
4092 member_config = ast_config_new();
4093 }
4094 }
4095 if (q) {
4096 prev_weight = q->weight ? 1 : 0;
4097 queue_t_unref(q, "Need to find realtime queue");
4098 }
4099
4100 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
4101 ast_config_destroy(member_config);
4102 ast_variables_destroy(queue_vars);
4103
4104 /* update the use_weight value if the queue's has gained or lost a weight */
4105 if (q) {
4106 if (!q->weight && prev_weight) {
4108 }
4109 if (q->weight && !prev_weight) {
4111 }
4112 }
4113 /* Other cases will end up with the proper value for use_weight */
4114 } else {
4116 }
4117 return q;
4118}
4119
4120/*!
4121 * \internal
4122 * \brief Load queues and members from realtime.
4123 *
4124 * \param queuename - name of the desired queue to load or empty if need to load all queues
4125*/
4126static void load_realtime_queues(const char *queuename)
4127{
4128 struct ast_config *cfg = NULL;
4129 char *category = NULL;
4130 const char *name = NULL;
4131 struct call_queue *q = NULL;
4132
4133 if (!ast_check_realtime("queues")) {
4134 return;
4135 }
4136
4137 if (ast_strlen_zero(queuename)) {
4138 if ((cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
4139 while ((category = ast_category_browse(cfg, category))) {
4140 name = ast_variable_retrieve(cfg, category, "name");
4142 queue_unref(q);
4143 }
4144 }
4145 ast_config_destroy(cfg);
4146 }
4147 } else {
4148 if ((q = find_load_queue_rt_friendly(queuename))) {
4149 queue_unref(q);
4150 }
4151 }
4152}
4153
4154static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
4155{
4156 int ret = -1;
4157
4158 if (ast_strlen_zero(mem->rt_uniqueid)) {
4159 return ret;
4160 }
4161
4162 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) >= 0) {
4163 ret = 0;
4164 }
4165
4166 return ret;
4167}
4168
4169
4171{
4172 struct ast_config *member_config = NULL;
4173 struct member *m;
4174 char *category = NULL;
4175 struct ao2_iterator mem_iter;
4176
4177 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
4178 /* This queue doesn't have realtime members. If the queue still has any realtime
4179 * members in memory, they need to be removed.
4180 */
4181 ao2_lock(q);
4182 mem_iter = ao2_iterator_init(q->members, 0);
4183 while ((m = ao2_iterator_next(&mem_iter))) {
4184 if (m->realtime) {
4186 }
4187 ao2_ref(m, -1);
4188 }
4189 ao2_iterator_destroy(&mem_iter);
4190 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
4191 ao2_unlock(q);
4192 return;
4193 }
4194
4195 ao2_lock(q);
4196
4197 /* Temporarily set realtime members dead so we can detect deleted ones.*/
4198 mem_iter = ao2_iterator_init(q->members, 0);
4199 while ((m = ao2_iterator_next(&mem_iter))) {
4200 if (m->realtime) {
4201 m->dead = 1;
4202 }
4203 ao2_ref(m, -1);
4204 }
4205 ao2_iterator_destroy(&mem_iter);
4206
4207 while ((category = ast_category_browse(member_config, category))) {
4208 rt_handle_member_record(q, category, member_config);
4209 }
4210
4211 /* Delete all realtime members that have been deleted in DB. */
4212 mem_iter = ao2_iterator_init(q->members, 0);
4213 while ((m = ao2_iterator_next(&mem_iter))) {
4214 if (m->dead) {
4216 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4217 } else {
4218 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4219 }
4221 }
4222 ao2_ref(m, -1);
4223 }
4224 ao2_iterator_destroy(&mem_iter);
4225 ao2_unlock(q);
4226 ast_config_destroy(member_config);
4227}
4228
4229static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
4230{
4231 struct call_queue *q;
4232 struct queue_ent *cur, *prev = NULL;
4233 int res = -1;
4234 int pos = 0;
4235 int inserted = 0;
4236
4237 if (!(q = find_load_queue_rt_friendly(queuename))) {
4238 return res;
4239 }
4240 ao2_lock(q);
4241
4242 /* This is our one */
4243 if (q->joinempty) {
4244 int status = 0;
4245 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, qe->raise_penalty, q->joinempty, 0))) {
4246 *reason = QUEUE_JOINEMPTY;
4247 ao2_unlock(q);
4248 queue_t_unref(q, "Done with realtime queue");
4249 return res;
4250 }
4251 }
4252 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) {
4253 *reason = QUEUE_FULL;
4254 } else if (*reason == QUEUE_UNKNOWN) {
4255 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4256
4257 /* There's space for us, put us at the right position inside
4258 * the queue.
4259 * Take into account the priority of the calling user */
4260 inserted = 0;
4261 prev = NULL;
4262 cur = q->head;
4263 while (cur) {
4264 /* We have higher priority than the current user, enter
4265 * before him, after all the other users with priority
4266 * higher or equal to our priority. */
4267 if ((!inserted) && (qe->prio > cur->prio)) {
4268 insert_entry(q, prev, qe, &pos);
4269 inserted = 1;
4270 }
4271 /* <= is necessary for the position comparison because it may not be possible to enter
4272 * at our desired position since higher-priority callers may have taken the position we want
4273 */
4274 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
4275 insert_entry(q, prev, qe, &pos);
4276 inserted = 1;
4277 /*pos is incremented inside insert_entry, so don't need to add 1 here*/
4278 if (position < pos) {
4279 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
4280 }
4281 }
4282 cur->pos = ++pos;
4283 prev = cur;
4284 cur = cur->next;
4285 }
4286 /* No luck, join at the end of the queue */
4287 if (!inserted) {
4288 insert_entry(q, prev, qe, &pos);
4289 }
4290 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
4291 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
4292 ast_copy_string(qe->context, q->context, sizeof(qe->context));
4293 q->count++;
4294 if (q->count == 1) {
4296 }
4297
4298 res = 0;
4299
4300 blob = ast_json_pack("{s: s, s: i, s: i}",
4301 "Queue", q->name,
4302 "Position", qe->pos,
4303 "Count", q->count);
4304 ast_channel_publish_cached_blob(qe->chan, queue_caller_join_type(), blob);
4305 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, ast_channel_name(qe->chan), qe->pos );
4306 }
4307 ao2_unlock(q);
4308 queue_t_unref(q, "Done with realtime queue");
4309
4310 return res;
4311}
4312
4313static int play_file(struct ast_channel *chan, const char *filename)
4314{
4315 int res;
4316
4317 if (ast_strlen_zero(filename)) {
4318 return 0;
4319 }
4320
4321 if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
4322 return 0;
4323 }
4324
4326
4327 res = ast_streamfile(chan, filename, ast_channel_language(chan));
4328 if (!res) {
4330 }
4331
4333
4334 return res;
4335}
4336
4337/*!
4338 * \brief Check for valid exit from queue via goto
4339 * \retval 0 if failure
4340 * \retval 1 if successful
4341*/
4342static int valid_exit(struct queue_ent *qe, char digit)
4343{
4344 int digitlen = strlen(qe->digits);
4345
4346 /* Prevent possible buffer overflow */
4347 if (digitlen < sizeof(qe->digits) - 2) {
4348 qe->digits[digitlen] = digit;
4349 qe->digits[digitlen + 1] = '\0';
4350 } else {
4351 qe->digits[0] = '\0';
4352 return 0;
4353 }
4354
4355 /* If there's no context to goto, short-circuit */
4356 if (ast_strlen_zero(qe->context)) {
4357 return 0;
4358 }
4359
4360 /* If the extension is bad, then reset the digits to blank */
4361 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
4363 qe->digits[0] = '\0';
4364 return 0;
4365 }
4366
4367 /* We have an exact match */
4368 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
4369 qe->valid_digits = 1;
4370 /* Return 1 on a successful goto */
4371 return 1;
4372 }
4373
4374 return 0;
4375}
4376
4377static int say_position(struct queue_ent *qe, int ringing)
4378{
4379 int res = 0, say_thanks = 0;
4380 long avgholdmins, avgholdsecs;
4381 time_t now;
4382
4383 /* Let minannouncefrequency seconds pass between the start of each position announcement */
4384 time(&now);
4385 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) {
4386 return 0;
4387 }
4388
4389 /* If either our position has changed, or we are over the freq timer, say position */
4390 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) {
4391 return 0;
4392 }
4393
4394 /* Only announce if the caller's queue position has improved since last time */
4395 if (qe->parent->announceposition_only_up && qe->last_pos_said <= qe->pos) {
4396 return 0;
4397 }
4398
4399 if (ringing) {
4400 ast_indicate(qe->chan,-1);
4401 } else {
4402 ast_moh_stop(qe->chan);
4403 }
4404
4408 qe->pos <= qe->parent->announcepositionlimit)) {
4409 say_thanks = 1;
4410 /* Say we're next, if we are */
4411 if (qe->pos == 1) {
4412 res = play_file(qe->chan, qe->parent->sound_next);
4413 if (!res) {
4414 goto posout;
4415 }
4416 /* Say there are more than N callers */
4418 res = (
4419 play_file(qe->chan, qe->parent->queue_quantity1) ||
4421 ast_channel_language(qe->chan), NULL) || /* Needs gender */
4423 /* Say there are currently N callers waiting */
4424 } else {
4425 res = (
4426 play_file(qe->chan, qe->parent->sound_thereare) ||
4428 ast_channel_language(qe->chan), "n") || /* Needs gender */
4429 play_file(qe->chan, qe->parent->sound_calls));
4430 }
4431 if (res) {
4432 goto playout;
4433 }
4434 }
4435 /* Round hold time to nearest minute */
4436 avgholdmins = labs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
4437
4438 /* If they have specified a rounding then round the seconds as well */
4439 if (qe->parent->roundingseconds) {
4440 avgholdsecs = (labs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
4441 avgholdsecs *= qe->parent->roundingseconds;
4442 } else {
4443 avgholdsecs = 0;
4444 }
4445
4446 ast_verb(3, "Hold time for %s is %ld minute(s) %ld seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
4447
4448 /* If the hold time is >1 min, if it's enabled, and if it's not
4449 supposed to be only once and we have already said it, say it */
4450 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
4453 say_thanks = 1;
4454 res = play_file(qe->chan, qe->parent->sound_holdtime);
4455 if (res) {
4456 goto playout;
4457 }
4458
4459 if (avgholdmins >= 1) {
4460 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4461 if (res) {
4462 goto playout;
4463 }
4464
4465 if (avgholdmins == 1) {
4466 res = play_file(qe->chan, qe->parent->sound_minute);
4467 if (res) {
4468 goto playout;
4469 }
4470 } else {
4471 res = play_file(qe->chan, qe->parent->sound_minutes);
4472 if (res) {
4473 goto playout;
4474 }
4475 }
4476 }
4477 if (avgholdsecs >= 1) {
4478 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4479 if (res) {
4480 goto playout;
4481 }
4482
4483 res = play_file(qe->chan, qe->parent->sound_seconds);
4484 if (res) {
4485 goto playout;
4486 }
4487 }
4488 }
4489
4490posout:
4491 if (qe->parent->announceposition) {
4492 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
4493 ast_channel_name(qe->chan), qe->parent->name, qe->pos);
4494 }
4495 if (say_thanks) {
4496 res = play_file(qe->chan, qe->parent->sound_thanks);
4497 }
4498playout:
4499
4500 if ((res > 0 && !valid_exit(qe, res))) {
4501 res = 0;
4502 }
4503
4504 /* Set our last_pos indicators */
4505 qe->last_pos = now;
4506 qe->last_pos_said = qe->pos;
4507
4508 /* Don't restart music on hold if we're about to exit the caller from the queue */
4509 if (!res) {
4510 if (ringing) {
4512 } else {
4513 ast_moh_start(qe->chan, qe->moh, NULL);
4514 }
4515 }
4516 return res;
4517}
4518
4519static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
4520{
4521 int oldvalue;
4522
4523 /* Calculate holdtime using an exponential average */
4524 /* Thanks to SRT for this contribution */
4525 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
4526
4527 ao2_lock(qe->parent);
4528 if ((qe->parent->callscompleted + qe->parent->callsabandoned) == 0) {
4529 qe->parent->holdtime = newholdtime;
4530 } else {
4531 oldvalue = qe->parent->holdtime;
4532 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4533 }
4534 ao2_unlock(qe->parent);
4535}
4536
4537/*! \brief Caller leaving queue.
4538 *
4539 * Search the queue to find the leaving client, if found remove from queue
4540 * create manager event, move others up the queue.
4541*/
4542static void leave_queue(struct queue_ent *qe)
4543{
4544 struct call_queue *q;
4545 struct queue_ent *current, *prev = NULL;
4546 struct penalty_rule *pr_iter;
4547 int pos = 0;
4548
4549 if (!(q = qe->parent)) {
4550 return;
4551 }
4552 queue_t_ref(q, "Copy queue pointer from queue entry");
4553 ao2_lock(q);
4554
4555 prev = NULL;
4556 for (current = q->head; current; current = current->next) {
4557 if (current == qe) {
4558 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4559 char posstr[20];
4560 q->count--;
4561 if (!q->count) {
4563 }
4564
4565 blob = ast_json_pack("{s: s, s: i, s: i}",
4566 "Queue", q->name,
4567 "Position", qe->pos,
4568 "Count", q->count);
4569 ast_channel_publish_cached_blob(qe->chan, queue_caller_leave_type(), blob);
4570 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
4571 /* Take us out of the queue */
4572 if (prev) {
4573 prev->next = current->next;
4574 } else {
4575 q->head = current->next;
4576 }
4577 /* Free penalty rules */
4578 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
4579 ast_free(pr_iter);
4580 }
4581 qe->pr = NULL;
4582 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
4583 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
4584 } else {
4585 /* Renumber the people after us in the queue based on a new count */
4586 current->pos = ++pos;
4587 prev = current;
4588 }
4589 }
4590 ao2_unlock(q);
4591
4592 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
4593 if (q->realtime) {
4594 struct ast_variable *var;
4595 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
4596 q->dead = 1;
4597 } else {
4599 }
4600 }
4601
4602 if (q->dead) {
4603 /* It's dead and nobody is in it, so kill it */
4604 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
4605 }
4606 /* unref the explicit ref earlier in the function */
4607 queue_t_unref(q, "Expire copied reference");
4608}
4609
4610/*!
4611 * \internal
4612 * \brief Destroy the given callattempt structure and free it.
4613 * \since 1.8
4614 *
4615 * \param doomed callattempt structure to destroy.
4616 */
4617static void callattempt_free(struct callattempt *doomed)
4618{
4619 if (doomed->member) {
4620 ao2_ref(doomed->member, -1);
4621 }
4623 ast_free(doomed->orig_chan_name);
4624 ast_free(doomed);
4625}
4626
4627static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
4628{
4629 struct callattempt *cur;
4630
4631 for (cur = outgoing; cur; cur = cur->q_next) {
4632 if (cur->chan && cur->chan != exception) {
4634 }
4635 }
4636}
4637
4638/*! \brief Hang up a list of outgoing calls */
4639static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
4640{
4641 struct callattempt *oo;
4642
4643 while (outgoing) {
4644 /* If someone else answered the call we should indicate this in the CANCEL */
4645 /* Hangup any existing lines we have open */
4646 if (outgoing->chan && (outgoing->chan != exception)) {
4647 if (exception || cancel_answered_elsewhere) {
4649 }
4650 ast_channel_publish_dial(qe->chan, outgoing->chan, outgoing->interface, "CANCEL");
4651
4652 /* When dialing channels it is possible that they may not ever
4653 * leave the not in use state (Local channels in particular) by
4654 * the time we cancel them. If this occurs but we know they were
4655 * dialed we explicitly remove them from the pending members
4656 * container so that subsequent call attempts occur.
4657 */
4658 if (outgoing->member->status == AST_DEVICE_NOT_INUSE) {
4660 }
4661
4662 ast_hangup(outgoing->chan);
4663 }
4664 oo = outgoing;
4665 outgoing = outgoing->q_next;
4667 callattempt_free(oo);
4668 }
4669}
4670
4671/*!
4672 * \brief Get the number of members available to accept a call.
4673 *
4674 * \note The queue passed in should be locked prior to this function call
4675 *
4676 * \param[in] q The queue for which we are counting the number of available members
4677 * \return Return the number of available members in queue q
4678 */
4680{
4681 struct member *mem;
4682 int avl = 0;
4683 struct ao2_iterator mem_iter;
4684
4685 mem_iter = ao2_iterator_init(q->members, 0);
4686 while ((mem = ao2_iterator_next(&mem_iter))) {
4687
4688 avl += is_member_available(q, mem);
4689 ao2_ref(mem, -1);
4690
4691 /* If autofill is not enabled or if the queue's strategy is ringall, then
4692 * we really don't care about the number of available members so much as we
4693 * do that there is at least one available.
4694 *
4695 * In fact, we purposely will return from this function stating that only
4696 * one member is available if either of those conditions hold. That way,
4697 * functions which determine what action to take based on the number of available
4698 * members will operate properly. The reasoning is that even if multiple
4699 * members are available, only the head caller can actually be serviced.
4700 */
4701 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4702 break;
4703 }
4704 }
4705 ao2_iterator_destroy(&mem_iter);
4706
4707 return avl;
4708}
4709
4710/* traverse all defined queues which have calls waiting and contain this member
4711 return 0 if no other queue has precedence (higher weight) or 1 if found */
4712static int compare_weight(struct call_queue *rq, struct member *member)
4713{
4714 struct call_queue *q;
4715 struct member *mem;
4716 int found = 0;
4717 struct ao2_iterator queue_iter;
4718
4719 queue_iter = ao2_iterator_init(queues, 0);
4720 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4721 if (q == rq) { /* don't check myself, could deadlock */
4722 queue_t_unref(q, "Done with iterator");
4723 continue;
4724 }
4725 ao2_lock(q);
4726 if (q->count && q->members) {
4727 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4728 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4729 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
4730 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);
4731 found = 1;
4732 }
4733 ao2_ref(mem, -1);
4734 }
4735 }
4736 ao2_unlock(q);
4737 queue_t_unref(q, "Done with iterator");
4738 if (found) {
4739 break;
4740 }
4741 }
4742 ao2_iterator_destroy(&queue_iter);
4743 return found;
4744}
4745
4746static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
4747{
4748 struct call_queue *q;
4749 struct member *mem;
4750 int is_longest_waiting = 1;
4751 struct ao2_iterator queue_iter;
4752 struct queue_ent *ch;
4753
4754 queue_iter = ao2_iterator_init(queues, 0);
4755 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4756 if (q == caller->parent) { /* don't check myself, could deadlock */
4757 queue_t_unref(q, "Done with iterator");
4758 continue;
4759 }
4760 ao2_lock(q);
4761 /*
4762 * If the other queue has equal weight, see if we should let that handle
4763 * their call first. If weights are not equal, compare_weights will step in.
4764 */
4765 if (q->weight == caller->parent->weight && q->count && q->members) {
4766 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4767 ast_debug(2, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4768
4769 /* Does this queue have a caller that's been waiting longer? */
4770 ch = q->head;
4771 while (ch) {
4772 /* If ch->pending, the other call (which may be waiting for a longer period of time),
4773 * is already ringing at another agent. Ignore such callers; otherwise, all agents
4774 * will be unused until the first caller is picked up.
4775 */
4776 if (ch->start < caller->start && !ch->pending) {
4777 ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",
4778 q->name, ch->pos, ch->start, caller->start);
4779 is_longest_waiting = 0;
4780 break;
4781 }
4782 ch = ch->next;
4783 }
4784 }
4785 }
4786 ao2_unlock(q);
4787 queue_t_unref(q, "Done with iterator");
4788 if (!is_longest_waiting) {
4789 break;
4790 }
4791 }
4792 ao2_iterator_destroy(&queue_iter);
4793 return is_longest_waiting;
4794}
4795
4796/*! \brief common hangup actions */
4797static void do_hang(struct callattempt *o)
4798{
4799 o->stillgoing = 0;
4800 ast_hangup(o->chan);
4802 o->chan = NULL;
4803}
4804
4805/*!
4806 * \internal
4807 * \brief Check if the member status is available.
4808 *
4809 * \param status Member status to check if available.
4810 *
4811 * \retval non-zero if the member status is available.
4812 */
4814{
4816}
4817
4818/*!
4819 * \internal
4820 * \brief Determine if can ring a queue entry.
4821 *
4822 * \param qe Queue entry to check.
4823 * \param call Member call attempt.
4824 *
4825 * \retval non-zero if an entry can be called.
4826 */
4827static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
4828{
4829 struct member *memberp = call->member;
4830 int wrapuptime;
4831
4832 if (memberp->paused) {
4833 ast_debug(1, "%s paused, can't receive call\n", call->interface);
4834 return 0;
4835 }
4836
4837 if (!memberp->ringinuse && !member_status_available(memberp->status)) {
4838 ast_debug(1, "%s not available, can't receive call\n", call->interface);
4839 return 0;
4840 }
4841
4842 if (memberp->lastqueue) {
4843 wrapuptime = get_wrapuptime(memberp->lastqueue, memberp);
4844 } else {
4845 wrapuptime = get_wrapuptime(qe->parent, memberp);
4846 }
4847 if (wrapuptime && (time(NULL) - memberp->lastcall) < wrapuptime) {
4848 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
4849 (memberp->lastqueue ? memberp->lastqueue->name : qe->parent->name),
4850 call->interface);
4851 return 0;
4852 }
4853
4854 if (use_weight && compare_weight(qe->parent, memberp)) {
4855 ast_debug(1, "Priority queue delaying call to %s:%s\n",
4856 qe->parent->name, call->interface);
4857 return 0;
4858 }
4859
4861 ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n",
4862 qe->parent->name, call->interface);
4863 return 0;
4864 }
4865
4866 if (!memberp->ringinuse) {
4867 struct member *mem;
4868
4870
4871 mem = ao2_find(pending_members, memberp,
4873 if (mem) {
4874 /*
4875 * If found that means this member is currently being attempted
4876 * from another calling thread, so stop trying from this thread
4877 */
4878 ast_debug(1, "%s has another call trying, can't receive call\n",
4879 call->interface);
4880 ao2_ref(mem, -1);
4882 return 0;
4883 }
4884
4885 /*
4886 * If not found add it to the container so another queue
4887 * won't attempt to call this member at the same time.
4888 */
4889 ast_debug(3, "Add %s to pending_members\n", memberp->membername);
4890 ao2_link(pending_members, memberp);
4892
4893 /*
4894 * The queue member is available. Get current status to be sure
4895 * because the device state and extension state callbacks may
4896 * not have updated the status yet.
4897 */
4899 ast_debug(1, "%s actually not available, can't receive call\n",
4900 call->interface);
4901 pending_members_remove(memberp);
4902 return 0;
4903 }
4904 }
4905
4906 return 1;
4907}
4908
4909/*!
4910 * \brief Part 2 of ring_one
4911 *
4912 * Does error checking before attempting to request a channel and call a member.
4913 * This function is only called from ring_one().
4914 * Failure can occur if:
4915 * - Agent on call
4916 * - Agent is paused
4917 * - Wrapup time not expired
4918 * - Priority by another queue
4919 *
4920 * \retval 1 on success to reach a free agent
4921 * \retval 0 on failure to get agent.
4922 */
4923static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
4924{
4925 int res;
4926 int status;
4927 char tech[256];
4928 char *location;
4929 struct ast_format_cap *nativeformats;
4930 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4931
4932 /* on entry here, we know that tmp->chan == NULL */
4933 if (!can_ring_entry(qe, tmp)) {
4934 tmp->stillgoing = 0;
4935 ++*busies;
4936 return 0;
4937 }
4938
4939 ast_copy_string(tech, tmp->interface, sizeof(tech));
4940 if ((location = strchr(tech, '/'))) {
4941 *location++ = '\0';
4942 } else {
4943 location = "";
4944 }
4945
4947 nativeformats = ao2_bump(ast_channel_nativeformats(qe->chan));
4949
4950 /* Request the peer */
4951 tmp->chan = ast_request(tech, nativeformats, NULL, qe->chan, location, &status);
4952 ao2_cleanup(nativeformats);
4953 if (!tmp->chan) { /* If we can't, just go on to the next call */
4954 ao2_lock(qe->parent);
4955 qe->parent->rrpos++;
4956 qe->linpos++;
4957 ao2_unlock(qe->parent);
4958
4960
4961 publish_dial_end_event(qe->chan, tmp, NULL, "BUSY");
4962 tmp->stillgoing = 0;
4963 ++*busies;
4964 return 0;
4965 }
4966
4967 ast_channel_lock_both(tmp->chan, qe->chan);
4968
4971 if (qe->cancel_answered_elsewhere) {
4973 }
4974 ast_channel_appl_set(tmp->chan, "AppQueue");
4975 ast_channel_data_set(tmp->chan, "(Outgoing Line)");
4976 memset(ast_channel_whentohangup(tmp->chan), 0, sizeof(*ast_channel_whentohangup(tmp->chan)));
4977
4978 /* If the new channel has no callerid, try to guess what it should be */
4979 if (!ast_channel_caller(tmp->chan)->id.number.valid) {
4981 struct ast_party_caller caller;
4982
4984 caller.id = ast_channel_connected(qe->chan)->id;
4985 caller.ani = ast_channel_connected(qe->chan)->ani;
4986 ast_channel_set_caller_event(tmp->chan, &caller, NULL);
4987 } else if (!ast_strlen_zero(ast_channel_dialed(qe->chan)->number.str)) {
4989 } else if (!ast_strlen_zero(ast_channel_exten(qe->chan))) {
4991 }
4992 tmp->dial_callerid_absent = 1;
4993 }
4994
4996
4998
5000
5001 /* Inherit specially named variables from parent channel */
5005
5006 /* Presense of ADSI CPE on outgoing channel follows ours */
5008
5009 /* Inherit context and extension */
5010 ast_channel_dialcontext_set(tmp->chan, ast_channel_context(qe->chan));
5012
5013 /* Save the original channel name to detect call pickup masquerading in. */
5015
5018
5019 /* location is tmp->interface where tech/ has been stripped, so it follow the same syntax as DIALEDPEERNUMBER in app_dial.c */
5020 pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", strlen(location) ? location : tmp->interface);
5021
5022 /* PREDIAL: Run gosub on the callee's channel */
5023 if (qe->predial_callee) {
5024 ast_pre_call(tmp->chan, qe->predial_callee);
5025 }
5026
5027 /* Place the call, but don't wait on the answer */
5028 if ((res = ast_call(tmp->chan, location, 0))) {
5029 /* Again, keep going even if there's an error */
5030 ast_verb(3, "Couldn't call %s\n", tmp->interface);
5031 do_hang(tmp);
5032 ++*busies;
5033 return 0;
5034 }
5035
5036 ast_channel_lock_both(tmp->chan, qe->chan);
5037
5038 blob = ast_json_pack("{s: s, s: s, s: s}",
5039 "Queue", qe->parent->name,
5040 "Interface", tmp->interface,
5041 "MemberName", tmp->member->membername);
5042 queue_publish_multi_channel_blob(qe->chan, tmp->chan, queue_agent_called_type(), blob);
5043
5045
5048
5049 ast_verb(3, "Called %s\n", tmp->interface);
5050
5051 return 1;
5052}
5053
5054/*! \brief find the entry with the best metric, or NULL */
5056{
5057 struct callattempt *best = NULL, *cur;
5058
5059 for (cur = outgoing; cur; cur = cur->q_next) {
5060 if (cur->stillgoing && /* Not already done */
5061 !cur->chan && /* Isn't already going */
5062 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
5063 best = cur;
5064 }
5065 }
5066
5067 return best;
5068}
5069
5070/*!
5071 * \brief Place a call to a queue member.
5072 *
5073 * Once metrics have been calculated for each member, this function is used
5074 * to place a call to the appropriate member (or members). The low-level
5075 * channel-handling and error detection is handled in ring_entry
5076 *
5077 * \retval 1 if a member was called successfully
5078 * \retval 0 otherwise
5079 */
5080static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
5081{
5082 int ret = 0;
5083 struct callattempt *cur;
5084
5085 if (qe->predial_callee) {
5087 for (cur = outgoing; cur; cur = cur->q_next) {
5088 if (cur->stillgoing && cur->chan) {
5090 }
5091 }
5092 }
5093
5094 while (ret == 0) {
5095 struct callattempt *best = find_best(outgoing);
5096 if (!best) {
5097 ast_debug(1, "Nobody left to try ringing in queue\n");
5098 break;
5099 }
5101 /* Ring everyone who shares this best metric (for ringall) */
5102 for (cur = outgoing; cur; cur = cur->q_next) {
5103 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
5104 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
5105 ret |= ring_entry(qe, cur, busies);
5106 if (qe->predial_callee && cur->chan) {
5108 }
5109 }
5110 }
5111 } else {
5112 /* Ring just the best channel */
5113 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
5114 ret = ring_entry(qe, best, busies);
5115 if (qe->predial_callee && best->chan) {
5117 }
5118 }
5119
5120 /* If we have timed out, break out */
5121 if (qe->expire && (time(NULL) >= qe->expire)) {
5122 ast_debug(1, "Queue timed out while ringing members.\n");
5123 ret = 0;
5124 break;
5125 }
5126 }
5127 if (qe->predial_callee) {
5128 for (cur = outgoing; cur; cur = cur->q_next) {
5129 if (cur->stillgoing && cur->chan) {
5131 }
5132 }
5134 }
5135
5136 return ret;
5137}
5138
5139/*! \brief Search for best metric and add to Round Robbin queue */
5140static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
5141{
5142 struct callattempt *best = find_best(outgoing);
5143
5144 if (best) {
5145 /* Ring just the best channel */
5146 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
5147 qe->parent->rrpos = best->metric % 1000;
5148 } else {
5149 /* Just increment rrpos */
5150 if (qe->parent->wrapped) {
5151 /* No more channels, start over */
5152 qe->parent->rrpos = 0;
5153 } else {
5154 /* Prioritize next entry */
5155 qe->parent->rrpos++;
5156 }
5157 }
5158 qe->parent->wrapped = 0;
5159
5160 return 0;
5161}
5162
5163/*! \brief Search for best metric and add to Linear queue */
5164static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
5165{
5166 struct callattempt *best = find_best(outgoing);
5167
5168 if (best) {
5169 /* Ring just the best channel */
5170 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
5171 qe->linpos = best->metric % 1000;
5172 } else {
5173 /* Just increment rrpos */
5174 if (qe->linwrapped) {
5175 /* No more channels, start over */
5176 qe->linpos = 0;
5177 } else {
5178 /* Prioritize next entry */
5179 qe->linpos++;
5180 }
5181 }
5182 qe->linwrapped = 0;
5183
5184 return 0;
5185}
5186
5187/*! \brief Playback announcement to queued members if period has elapsed */
5189{
5190 int res = 0;
5191 time_t now;
5192
5193 /* Get the current time */
5194 time(&now);
5195
5196 /* Check to see if it is time to announce */
5198 return 0;
5199 }
5200
5201 /* Stop the music on hold so we can play our own file */
5202 if (ringing) {
5203 ast_indicate(qe->chan,-1);
5204 } else {
5205 ast_moh_stop(qe->chan);
5206 }
5207
5208 ast_verb(3, "Playing periodic announcement\n");
5209
5211 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
5215 }
5216
5217 /* play the announcement */
5219
5220 if (res > 0 && !valid_exit(qe, res)) {
5221 res = 0;
5222 }
5223
5224 /* Resume Music on Hold if the caller is going to stay in the queue */
5225 if (!res) {
5226 if (ringing) {
5228 } else {
5229 ast_moh_start(qe->chan, qe->moh, NULL);
5230 }
5231 }
5232
5233 /* update last_periodic_announce_time */
5235 time(&qe->last_periodic_announce_time);
5236 } else {
5238 }
5239
5240 /* Update the current periodic announcement to the next announcement */
5241 if (!qe->parent->randomperiodicannounce) {
5243 }
5244
5245 return res;
5246}
5247
5248/*! \brief Record that a caller gave up on waiting in queue */
5249static void record_abandoned(struct queue_ent *qe)
5250{
5251 int callabandonedinsl = 0;
5252 time_t now;
5253
5254 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5255
5256 pbx_builtin_setvar_helper(qe->chan, "ABANDONED", "TRUE");
5257
5259 ao2_lock(qe->parent);
5260 blob = ast_json_pack("{s: s, s: i, s: i, s: i}",
5261 "Queue", qe->parent->name,
5262 "Position", qe->pos,
5263 "OriginalPosition", qe->opos,
5264 "HoldTime", (int)(time(NULL) - qe->start));
5265
5266
5267 time(&now);
5268 callabandonedinsl = ((now - qe->start) <= qe->parent->servicelevel);
5269 if (callabandonedinsl) {
5271 }
5272
5273 qe->parent->callsabandoned++;
5274 ao2_unlock(qe->parent);
5275
5276 ast_channel_publish_cached_blob(qe->chan, queue_caller_abandon_type(), blob);
5277}
5278
5279/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
5280static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, char *interface, char *membername, int autopause)
5281{
5282 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5283
5284 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
5285
5286 /* Stop ringing, and resume MOH if specified */
5287 if (qe->ring_when_ringing) {
5288 ast_indicate(qe->chan, -1);
5289 ast_moh_start(qe->chan, qe->moh, NULL);
5290 }
5291
5292 blob = ast_json_pack("{s: s, s: s, s: s, s: i}",
5293 "Queue", qe->parent->name,
5294 "Interface", interface,
5295 "MemberName", membername,
5296 "RingTime", rnatime);
5297 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_ringnoanswer_type(), blob);
5298
5299 ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), membername, "RINGNOANSWER", "%d", rnatime);
5301 if (qe->parent->autopausedelay > 0) {
5302 struct member *mem;
5303 ao2_lock(qe->parent);
5304 if ((mem = interface_exists(qe->parent, interface))) {
5305 time_t idletime = time(&idletime)-mem->lastcall;
5306 if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
5307 ao2_unlock(qe->parent);
5308 ao2_ref(mem, -1);
5309 return;
5310 }
5311 ao2_ref(mem, -1);
5312 }
5313 ao2_unlock(qe->parent);
5314 }
5315 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
5316 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
5317 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
5318 interface, qe->parent->name);
5319 } else {
5320 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
5321 }
5322 } else {
5323 /* If queue autopause is mode all, just don't send any queue to stop.
5324 * the function will stop in all queues */
5325 if (!set_member_paused("", interface, "Auto-Pause", 1)) {
5326 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
5327 interface, qe->parent->name);
5328 } else {
5329 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
5330 }
5331 }
5332 }
5333 return;
5334}
5335
5336/*!
5337 * \internal
5338 * \brief Update connected line on chan from peer.
5339 * \since 13.6.0
5340 *
5341 * \param chan Channel to get connected line updated.
5342 * \param peer Channel providing connected line information.
5343 * \param is_caller Non-zero if chan is the calling channel.
5344 */
5345static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
5346{
5347 struct ast_party_connected_line connected_caller;
5348
5349 ast_party_connected_line_init(&connected_caller);
5350
5351 ast_channel_lock(peer);
5353 ast_channel_unlock(peer);
5355 if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)) {
5356 ast_channel_update_connected_line(chan, &connected_caller, NULL);
5357 }
5358 ast_party_connected_line_free(&connected_caller);
5359}
5360
5361#define AST_MAX_WATCHERS 256
5362/*!
5363 * \brief Wait for a member to answer the call
5364 *
5365 * \param[in] qe the queue_ent corresponding to the caller in the queue
5366 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
5367 * \param[in] to the amount of time (in milliseconds) to wait for a response
5368 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
5369 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
5370 * \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
5371 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
5372 *
5373 * \todo eventually all call forward logic should be integrated into and replaced by ast_call_forward()
5374 */
5375static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
5376{
5377 const char *queue = qe->parent->name;
5378 struct callattempt *o, *start = NULL, *prev = NULL;
5379 int status;
5380 int numbusies = prebusies;
5381 int numnochan = 0;
5382 int stillgoing = 0;
5383 int orig = *to;
5384 struct ast_frame *f;
5385 struct callattempt *peer = NULL;
5386 struct ast_channel *winner;
5387 struct ast_channel *in = qe->chan;
5388 char on[80] = "";
5389 char membername[80] = "";
5390 long starttime = 0;
5391 long endtime = 0;
5392 char *inchan_name;
5393 struct timeval start_time_tv = ast_tvnow();
5394 int canceled_by_caller = 0; /* 1 when caller hangs up or press digit or press * */
5395
5397 inchan_name = ast_strdupa(ast_channel_name(qe->chan));
5399
5400 starttime = (long) time(NULL);
5401
5402 while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
5403 int numlines, retry, pos = 1;
5404 struct ast_channel *watchers[AST_MAX_WATCHERS];
5405 watchers[0] = in;
5406 start = NULL;
5407
5408 for (retry = 0; retry < 2; retry++) {
5409 numlines = 0;
5410 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
5411 if (o->stillgoing) { /* Keep track of important channels */
5412 stillgoing = 1;
5413 if (o->chan) {
5414 if (pos < AST_MAX_WATCHERS) {
5415 watchers[pos++] = o->chan;
5416 }
5417 if (!start) {
5418 start = o;
5419 } else {
5420 prev->call_next = o;
5421 }
5422 prev = o;
5423 }
5424 } else if (prev) {
5425 prev->call_next = NULL;
5426 }
5427 numlines++;
5428 }
5429 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
5430 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) {
5431 break;
5432 }
5433 /* On "ringall" strategy we only move to the next penalty level
5434 when *all* ringing phones are done in the current penalty level */
5435 ring_one(qe, outgoing, &numbusies);
5436 /* and retry... */
5437 }
5438 if (pos == 1 /* not found */) {
5439 if (numlines == (numbusies + numnochan)) {
5440 ast_debug(1, "Everyone is busy at this time\n");
5441 } else {
5442 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
5443 }
5444 *to = 0;
5445 return NULL;
5446 }
5447
5448 /* Poll for events from both the incoming channel as well as any outgoing channels */
5449 winner = ast_waitfor_n(watchers, pos, to);
5450
5451 /* Service all of the outgoing channels */
5452 for (o = start; o; o = o->call_next) {
5453 /* We go with a fixed buffer here instead of using ast_strdupa. Using
5454 * ast_strdupa in a loop like this one can cause a stack overflow
5455 */
5456 char ochan_name[AST_CHANNEL_NAME];
5457
5458 if (o->chan) {
5460 ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
5462 }
5463 if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
5464 if (!peer) {
5465 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5466 if (o->orig_chan_name
5467 && strcmp(o->orig_chan_name, ochan_name)) {
5468 /*
5469 * The channel name changed so we must generate COLP update.
5470 * Likely because a call pickup channel masqueraded in.
5471 */
5473 } else if (!o->block_connected_update) {
5474 if (o->pending_connected_update) {
5477 }
5478 } else if (!o->dial_callerid_absent) {
5480 }
5481 }
5482 if (o->aoc_s_rate_list) {
5483 size_t encoded_size;
5484 struct ast_aoc_encoded *encoded;
5485 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5486 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5487 ast_aoc_destroy_encoded(encoded);
5488 }
5489 }
5490 peer = o;
5491 }
5492 } else if (o->chan && (o->chan == winner)) {
5493
5494 ast_copy_string(on, o->member->interface, sizeof(on));
5495 ast_copy_string(membername, o->member->membername, sizeof(membername));
5496
5497 /* Before processing channel, go ahead and check for forwarding */
5498 if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
5499 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
5501 "CANCEL", ast_channel_call_forward(o->chan));
5502 numnochan++;
5503 do_hang(o);
5504 winner = NULL;
5505 continue;
5507 struct ast_channel *original = o->chan;
5508 char forwarder[AST_CHANNEL_NAME];
5509 char tmpchan[256];
5510 char *stuff;
5511 char *tech;
5512 int failed = 0;
5513
5514 ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
5515 ast_copy_string(forwarder, ast_channel_name(o->chan), sizeof(forwarder));
5516 if ((stuff = strchr(tmpchan, '/'))) {
5517 *stuff++ = '\0';
5518 tech = tmpchan;
5519 } else {
5520 const char *forward_context;
5522 forward_context = pbx_builtin_getvar_helper(o->chan, "FORWARD_CONTEXT");
5523 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), forward_context ? forward_context : ast_channel_context(o->chan));
5525 stuff = tmpchan;
5526 tech = "Local";
5527 }
5528 if (!strcasecmp(tech, "Local")) {
5529 /*
5530 * Drop the connected line update block for local channels since
5531 * this is going to run dialplan and the user can change his
5532 * mind about what connected line information he wants to send.
5533 */
5535 }
5536
5537 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
5538 /* Setup parameters */
5540 if (!o->chan) {
5542 "Forwarding failed to create channel to dial '%s/%s'\n",
5543 tech, stuff);
5544 o->stillgoing = 0;
5545 numnochan++;
5546 } else {
5547 ast_channel_lock_both(o->chan, original);
5549 ast_channel_redirecting(original));
5551 ast_channel_unlock(original);
5552
5556 pbx_builtin_setvar_helper(o->chan, "FORWARDERNAME", forwarder);
5558
5559 if (o->pending_connected_update) {
5560 /*
5561 * Re-seed the callattempt's connected line information with
5562 * previously acquired connected line info from the queued
5563 * channel. The previously acquired connected line info could
5564 * have been set through the CONNECTED_LINE dialplan function.
5565 */
5568 }
5569
5572
5574
5577 /*
5578 * The call was not previously redirected so it is
5579 * now redirected from this number.
5580 */
5586 }
5587
5589
5594
5597 && !o->block_connected_update) {
5598 struct ast_party_redirecting redirecting;
5599
5600 /*
5601 * Redirecting updates to the caller make sense only on single
5602 * call at a time strategies.
5603 *
5604 * Need to re-evaluate if calling unlock is still required as we no longer
5605 * use macro.
5606 */
5607 ast_party_redirecting_init(&redirecting);
5610 if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0)) {
5611 ast_channel_update_redirecting(in, &redirecting, NULL);
5612 }
5613 ast_party_redirecting_free(&redirecting);
5614 } else {
5616 }
5617
5618 if (ast_call(o->chan, stuff, 0)) {
5619 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
5620 tech, stuff);
5621 failed = 1;
5622 }
5623 }
5624
5626 "CANCEL", ast_channel_call_forward(original));
5627 if (o->chan) {
5628 ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
5629 }
5630
5631 if (failed) {
5632 do_hang(o);
5633 numnochan++;
5634 }
5635
5636 /* Hangup the original channel now, in case we needed it */
5637 ast_hangup(winner);
5638 continue;
5639 }
5640 f = ast_read(winner);
5641 if (f) {
5642 if (f->frametype == AST_FRAME_CONTROL) {
5643 switch (f->subclass.integer) {
5644 case AST_CONTROL_ANSWER:
5645 /* This is our guy if someone answered. */
5646 if (!peer) {
5647 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5648 ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
5649 publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
5650 if (o->orig_chan_name
5651 && strcmp(o->orig_chan_name, ochan_name)) {
5652 /*
5653 * The channel name changed so we must generate COLP update.
5654 * Likely because a call pickup channel masqueraded in.
5655 */
5657 } else if (!o->block_connected_update) {
5658 if (o->pending_connected_update) {
5661 }
5662 } else if (!o->dial_callerid_absent) {
5664 }
5665 }
5666 if (o->aoc_s_rate_list) {
5667 size_t encoded_size;
5668 struct ast_aoc_encoded *encoded;
5669 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5670 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5671 ast_aoc_destroy_encoded(encoded);
5672 }
5673 }
5674 peer = o;
5675 }
5676 break;
5677 case AST_CONTROL_BUSY:
5678 ast_verb(3, "%s is busy\n", ochan_name);
5679 ast_channel_publish_dial(qe->chan, o->chan, on, "BUSY");
5680 endtime = (long) time(NULL);
5681 endtime -= starttime;
5682 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopausebusy);
5683 do_hang(o);
5685 if (qe->parent->timeoutrestart) {
5686 start_time_tv = ast_tvnow();
5687 }
5688 /* Have enough time for a queue member to answer? */
5689 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5690 ring_one(qe, outgoing, &numbusies);
5691 starttime = (long) time(NULL);
5692 }
5693 }
5694 numbusies++;
5695 break;
5697 ast_verb(3, "%s is circuit-busy\n", ochan_name);
5698 ast_channel_publish_dial(qe->chan, o->chan, on, "CONGESTION");
5699 endtime = (long) time(NULL);
5700 endtime -= starttime;
5701 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopauseunavail);
5702 do_hang(o);
5704 if (qe->parent->timeoutrestart) {
5705 start_time_tv = ast_tvnow();
5706 }
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 ringing\n", ochan_name);
5716
5717 ast_channel_publish_dial(qe->chan, o->chan, on, "RINGING");
5718
5719 /* Start ring indication when the channel is ringing, if specified */
5720 if (qe->ring_when_ringing) {
5721 ast_moh_stop(qe->chan);
5723 }
5724 break;
5726 /* Ignore going off hook */
5727 break;
5729 if (o->block_connected_update) {
5730 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
5731 break;
5732 }
5735
5736 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
5742 break;
5743 }
5744
5745 /*
5746 * Prevent using the CallerID from the outgoing channel since we
5747 * got a connected line update from it.
5748 */
5749 o->dial_callerid_absent = 1;
5750
5751 if (ast_channel_connected_line_sub(o->chan, in, f, 1)) {
5753 }
5754 break;
5755 case AST_CONTROL_AOC:
5756 {
5757 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
5758 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
5760 o->aoc_s_rate_list = decoded;
5761 } else {
5762 ast_aoc_destroy_decoded(decoded);
5763 }
5764 }
5765 break;
5768 /*
5769 * Redirecting updates to the caller make sense only on single
5770 * call at a time strategies.
5771 */
5772 break;
5773 }
5774 if (o->block_connected_update) {
5775 ast_verb(3, "Redirecting update to %s prevented\n",
5776 inchan_name);
5777 break;
5778 }
5779 ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
5780 ochan_name, inchan_name);
5781 if (ast_channel_redirecting_sub(o->chan, in, f, 1)) {
5783 }
5784 break;
5787 break;
5788 default:
5789 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
5790 break;
5791 }
5792 }
5793 ast_frfree(f);
5794 } else { /* ast_read() returned NULL */
5795 endtime = (long) time(NULL) - starttime;
5796 ast_channel_publish_dial(qe->chan, o->chan, on, "NOANSWER");
5797 rna(endtime * 1000, qe, o->chan, on, membername, 1);
5798 do_hang(o);
5800 if (qe->parent->timeoutrestart) {
5801 start_time_tv = ast_tvnow();
5802 }
5803 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5804 ring_one(qe, outgoing, &numbusies);
5805 starttime = (long) time(NULL);
5806 }
5807 }
5808 }
5809 }
5810 }
5811
5812 /* If we received an event from the caller, deal with it. */
5813 if (winner == in) {
5814 f = ast_read(in);
5815 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
5816 /* Got hung up */
5817 *to = -1;
5818 if (f) {
5819 if (f->data.uint32) {
5821 }
5822 ast_frfree(f);
5823 }
5824 canceled_by_caller = 1;
5825 } else if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
5826 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
5827 *to = 0;
5828 ast_frfree(f);
5829 canceled_by_caller = 1;
5830 } else if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
5831 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
5832 *to = 0;
5833 *digit = f->subclass.integer;
5834 ast_frfree(f);
5835 canceled_by_caller = 1;
5836 }
5837 /* When caller hung up or pressed * or digit. */
5838 if (canceled_by_caller) {
5840 for (o = start; o; o = o->call_next) {
5841 if (o->chan) {
5842 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));
5843 }
5844 }
5845 return NULL;
5846 }
5847
5848 /* Send the frame from the in channel to all outgoing channels. */
5849 for (o = start; o; o = o->call_next) {
5850 if (!o->stillgoing || !o->chan) {
5851 /* This outgoing channel has died so don't send the frame to it. */
5852 continue;
5853 }
5854 switch (f->frametype) {
5855 case AST_FRAME_CONTROL:
5856 switch (f->subclass.integer) {
5858 if (o->block_connected_update) {
5859 ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
5860 break;
5861 }
5862 if (ast_channel_connected_line_sub(in, o->chan, f, 1)) {
5864 }
5865 break;
5867 if (o->block_connected_update) {
5868 ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
5869 break;
5870 }
5871 if (ast_channel_redirecting_sub(in, o->chan, f, 1)) {
5873 }
5874 break;
5875 default:
5876 /* We are not going to do anything with this frame. */
5877 goto skip_frame;
5878 }
5879 break;
5880 default:
5881 /* We are not going to do anything with this frame. */
5882 goto skip_frame;
5883 }
5884 }
5885skip_frame:;
5886
5887 ast_frfree(f);
5888 }
5889 }
5890
5891 if (!*to) {
5892 for (o = start; o; o = o->call_next) {
5893 if (o->chan) {
5894 rna(orig, qe, o->chan, o->interface, o->member->membername, 1);
5895 }
5896 }
5897
5898 publish_dial_end_event(qe->chan, outgoing, NULL, "NOANSWER");
5899 }
5900
5901 return peer;
5902}
5903
5904/*!
5905 * \brief Check if we should start attempting to call queue members.
5906 *
5907 * A simple process, really. Count the number of members who are available
5908 * to take our call and then see if we are in a position in the queue at
5909 * which a member could accept our call.
5910 *
5911 * \param[in] qe The caller who wants to know if it is his turn
5912 * \retval 0 It is not our turn
5913 * \retval 1 It is our turn
5914 */
5915static int is_our_turn(struct queue_ent *qe)
5916{
5917 struct queue_ent *ch;
5918 int res;
5919 int avl;
5920 int idx = 0;
5921 /* This needs a lock. How many members are available to be served? */
5922 ao2_lock(qe->parent);
5923
5924 avl = num_available_members(qe->parent);
5925
5926 ch = qe->parent->head;
5927
5928 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
5929
5930 while ((idx < avl) && (ch) && (ch != qe)) {
5931 if (!ch->pending) {
5932 idx++;
5933 }
5934 ch = ch->next;
5935 }
5936
5937 ao2_unlock(qe->parent);
5938 /* If the queue entry is within avl [the number of available members] calls from the top ...
5939 * Autofill and position check added to support autofill=no (as only calls
5940 * from the front of the queue are valid when autofill is disabled)
5941 */
5942 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
5943 ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
5944 res = 1;
5945 } else {
5946 ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
5947 res = 0;
5948 }
5949
5950 /* Update realtime members if this is the first call and number of avalable members is 0 */
5951 if (avl == 0 && qe->pos == 1) {
5953 }
5954
5955 return res;
5956}
5957
5958/*!
5959 * \brief update rules for queues
5960 *
5961 * Calculate min/max penalties making sure if relative they stay within bounds.
5962 * Update queues penalty and set dialplan vars, goto next list entry.
5963*/
5964static void update_qe_rule(struct queue_ent *qe)
5965{
5966 int max_penalty = INT_MAX;
5967
5968 if (qe->max_penalty != INT_MAX) {
5969 char max_penalty_str[20];
5970
5971 if (qe->pr->max_relative) {
5972 max_penalty = qe->max_penalty + qe->pr->max_value;
5973 } else {
5974 max_penalty = qe->pr->max_value;
5975 }
5976
5977 /* a relative change to the penalty could put it below 0 */
5978 if (max_penalty < 0) {
5979 max_penalty = 0;
5980 }
5981
5982 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
5983 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
5985 ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
5986 qe->max_penalty, ast_channel_name(qe->chan), qe->pr->time);
5987 }
5988
5989 if (qe->min_penalty != INT_MAX) {
5990 char min_penalty_str[20];
5991 int min_penalty;
5992
5993 if (qe->pr->min_relative) {
5994 min_penalty = qe->min_penalty + qe->pr->min_value;
5995 } else {
5996 min_penalty = qe->pr->min_value;
5997 }
5998
5999 /* a relative change to the penalty could put it below 0 */
6000 if (min_penalty < 0) {
6001 min_penalty = 0;
6002 }
6003
6004 if (max_penalty != INT_MAX && min_penalty > max_penalty) {
6006 }
6007
6008 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
6009 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
6011 ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
6012 qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
6013 }
6014
6015 if (qe->raise_penalty != INT_MAX) {
6016 char raise_penalty_str[20];
6017 int raise_penalty;
6018
6019 if (qe->pr->raise_relative) {
6021 } else {
6023 }
6024
6025 /* a relative change to the penalty could put it below 0 */
6026 if (raise_penalty < 0) {
6027 raise_penalty = 0;
6028 }
6029
6030 if (max_penalty != INT_MAX && raise_penalty > max_penalty) {
6032 }
6033
6034 snprintf(raise_penalty_str, sizeof(raise_penalty_str), "%d", raise_penalty);
6035 pbx_builtin_setvar_helper(qe->chan, "QUEUE_RAISE_PENALTY", raise_penalty_str);
6037 ast_debug(3, "Setting raised penalty to %d for caller %s since %d seconds have elapsed\n",
6038 qe->raise_penalty, ast_channel_name(qe->chan), qe->pr->time);
6039 }
6040
6041 qe->pr = AST_LIST_NEXT(qe->pr, list);
6042}
6043
6044/*! \brief The waiting areas for callers who are not actively calling members
6045 *
6046 * This function is one large loop. This function will return if a caller
6047 * either exits the queue or it becomes that caller's turn to attempt calling
6048 * queue members. Inside the loop, we service the caller with periodic announcements,
6049 * holdtime announcements, etc. as configured in queues.conf
6050 *
6051 * \retval 0 if the caller's turn has arrived
6052 * \retval -1 if the caller should exit the queue.
6053 */
6054static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
6055{
6056 int res = 0;
6057
6058 /* This is the holding pen for callers 2 through maxlen */
6059 for (;;) {
6060
6061 /* A request to withdraw this call from the queue arrived */
6062 if (qe->withdraw) {
6063 *reason = QUEUE_WITHDRAW;
6064 res = 1;
6065 break;
6066 }
6067
6068 if (is_our_turn(qe)) {
6069 break;
6070 }
6071
6072 /* If we have timed out, break out */
6073 if (qe->expire && (time(NULL) >= qe->expire)) {
6074 *reason = QUEUE_TIMEOUT;
6075 break;
6076 }
6077
6078 if (qe->parent->leavewhenempty) {
6079 int status = 0;
6080
6082 record_abandoned(qe);
6083 *reason = QUEUE_LEAVEEMPTY;
6084 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));
6085 res = -1;
6086 qe->handled = -1;
6087 break;
6088 }
6089 }
6090
6091 /* Make a position announcement, if enabled */
6092 if (qe->parent->announcefrequency &&
6093 (res = say_position(qe,ringing))) {
6094 break;
6095 }
6096
6097 /* If we have timed out, break out */
6098 if (qe->expire && (time(NULL) >= qe->expire)) {
6099 *reason = QUEUE_TIMEOUT;
6100 break;
6101 }
6102
6103 /* Make a periodic announcement, if enabled */
6106 break;
6107
6108 /* see if we need to move to the next penalty level for this queue */
6109 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
6110 update_qe_rule(qe);
6111 }
6112
6113 /* If we have timed out, break out */
6114 if (qe->expire && (time(NULL) >= qe->expire)) {
6115 *reason = QUEUE_TIMEOUT;
6116 break;
6117 }
6118
6119 /* Wait a second before checking again */
6120 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
6121 if (res > 0 && !valid_exit(qe, res)) {
6122 res = 0;
6123 } else {
6124 break;
6125 }
6126 }
6127
6128 /* If we have timed out, break out */
6129 if (qe->expire && (time(NULL) >= qe->expire)) {
6130 *reason = QUEUE_TIMEOUT;
6131 break;
6132 }
6133 }
6134
6135 return res;
6136}
6137
6138/*!
6139 * \brief update the queue status
6140 * \retval 0 always
6141*/
6142static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
6143{
6144 int oldtalktime;
6145 int newtalktime = time(NULL) - starttime;
6146 struct member *mem;
6147 struct call_queue *qtmp;
6148 struct ao2_iterator queue_iter;
6149
6150 /* It is possible for us to be called when a call has already been considered terminated
6151 * and data updated, so to ensure we only act on the call that the agent is currently in
6152 * we check when the call was bridged.
6153 */
6154 if (!starttime || (member->starttime != starttime)) {
6155 return 0;
6156 }
6157
6158 if (shared_lastcall) {
6159 queue_iter = ao2_iterator_init(queues, 0);
6160 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
6161 ao2_lock(qtmp);
6162 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
6163 time(&mem->lastcall);
6164 mem->calls++;
6165 mem->callcompletedinsl = 0;
6166 mem->starttime = 0;
6167 mem->lastqueue = q;
6168 ao2_ref(mem, -1);
6169 }
6170 ao2_unlock(qtmp);
6171 queue_t_unref(qtmp, "Done with iterator");
6172 }
6173 ao2_iterator_destroy(&queue_iter);
6174 } else {
6175 ao2_lock(q);
6176 time(&member->lastcall);
6178 member->calls++;
6179 member->starttime = 0;
6180 member->lastqueue = q;
6181 ao2_unlock(q);
6182 }
6183 /* Member might never experience any direct status change (local
6184 * channel with forwarding in particular). If that's the case,
6185 * this is the last chance to remove it from pending or subsequent
6186 * calls will not occur.
6187 */
6189
6190 ao2_lock(q);
6191 q->callscompleted++;
6192 if (callcompletedinsl) {
6193 q->callscompletedinsl++;
6194 }
6195 if (q->callscompleted == 1) {
6196 q->talktime = newtalktime;
6197 } else {
6198 /* Calculate talktime using the same exponential average as holdtime code */
6199 oldtalktime = q->talktime;
6200 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
6201 }
6202 ao2_unlock(q);
6203 return 0;
6204}
6205
6206/*! \brief Calculate the metric of each member in the outgoing callattempts
6207 *
6208 * A numeric metric is given to each member depending on the ring strategy used
6209 * by the queue. Members with lower metrics will be called before members with
6210 * higher metrics
6211 * \retval -1 if penalties are exceeded
6212 * \retval 0 otherwise
6213 */
6214static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
6215{
6216 /* disregarding penalty on too few members? */
6217 int membercount = ao2_container_count(q->members);
6218 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
6219 int penalty = mem->penalty;
6220
6221 if (usepenalty) {
6222 if (qe->raise_penalty != INT_MAX && penalty < qe->raise_penalty) {
6223 /* Low penalty is raised up to the current minimum */
6224 penalty = qe->raise_penalty;
6225 }
6226 if ((qe->max_penalty != INT_MAX && penalty > qe->max_penalty) ||
6227 (qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) {
6228 return -1;
6229 }
6230 } else {
6231 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
6232 membercount, q->penaltymemberslimit);
6233 }
6234
6235 switch (q->strategy) {
6237 /* Everyone equal, except for penalty */
6238 tmp->metric = penalty * 1000000 * usepenalty;
6239 break;
6241 if (pos < qe->linpos) {
6242 tmp->metric = 1000 + pos;
6243 } else {
6244 if (pos > qe->linpos) {
6245 /* Indicate there is another priority */
6246 qe->linwrapped = 1;
6247 }
6248 tmp->metric = pos;
6249 }
6250 tmp->metric += penalty * 1000000 * usepenalty;
6251 break;
6254 pos = mem->queuepos;
6255 if (pos < q->rrpos) {
6256 tmp->metric = 1000 + pos;
6257 } else {
6258 if (pos > q->rrpos) {
6259 /* Indicate there is another priority */
6260 q->wrapped = 1;
6261 }
6262 tmp->metric = pos;
6263 }
6264 tmp->metric += penalty * 1000000 * usepenalty;
6265 break;
6267 tmp->metric = ast_random() % 1000;
6268 tmp->metric += penalty * 1000000 * usepenalty;
6269 break;
6271 tmp->metric = ast_random() % ((1 + penalty) * 1000);
6272 break;
6274 tmp->metric = mem->calls;
6275 tmp->metric += penalty * 1000000 * usepenalty;
6276 break;
6278 if (!mem->lastcall) {
6279 tmp->metric = 0;
6280 } else {
6281 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
6282 }
6283 tmp->metric += penalty * 1000000 * usepenalty;
6284 break;
6285 default:
6286 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
6287 break;
6288 }
6289 return 0;
6290}
6291
6295 TRANSFER
6297
6298/*! \brief Send out AMI message with member call completion status information */
6299static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller,
6300 struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart,
6301 time_t callstart, enum agent_complete_reason rsn)
6302{
6303 const char *reason = NULL; /* silence dumb compilers */
6304 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
6305
6306 switch (rsn) {
6307 case CALLER:
6308 reason = "caller";
6309 break;
6310 case AGENT:
6311 reason = "agent";
6312 break;
6313 case TRANSFER:
6314 reason = "transfer";
6315 break;
6316 }
6317
6318 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I, s: s}",
6319 "Queue", queuename,
6320 "Interface", member->interface,
6321 "MemberName", member->membername,
6322 "HoldTime", (ast_json_int_t)(callstart - holdstart),
6323 "TalkTime", (ast_json_int_t)(time(NULL) - callstart),
6324 "Reason", reason ?: "");
6325
6327 queue_agent_complete_type(), blob);
6328}
6329
6330static void queue_agent_cb(void *userdata, struct stasis_subscription *sub,
6331 struct stasis_message *msg)
6332{
6333 struct ast_channel_blob *agent_blob;
6334
6335 agent_blob = stasis_message_data(msg);
6336
6338 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6339 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6340 "AGENTLOGIN", "%s", agent_blob->snapshot->base->name);
6342 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6343 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6344 "AGENTLOGOFF", "%s|%ld", agent_blob->snapshot->base->name,
6345 (long) ast_json_integer_get(ast_json_object_get(agent_blob->blob, "logintime")));
6346 }
6347}
6348
6349/*!
6350 * \brief Structure representing relevant data during a local channel optimization
6351 *
6352 * The reason we care about local channel optimizations is that we want to be able
6353 * to accurately report when the caller and queue member have stopped talking to
6354 * each other. A local channel optimization can cause it to appear that the conversation
6355 * has stopped immediately after it has begun. By tracking that the relevant channels
6356 * to monitor have changed due to a local channel optimization, we can give accurate
6357 * reports.
6358 *
6359 * Local channel optimizations for queues are restricted from their normal operation.
6360 * Bridges created by queues can only be the destination of local channel optimizations,
6361 * not the source. In addition, move-swap local channel optimizations are the only
6362 * permitted types of local channel optimization.
6363 *
6364 * This data is populated when we are told that a local channel optimization begin
6365 * is occurring. When we get told the optimization has ended successfully, we then
6366 * apply the data here into the queue_stasis_data.
6367 */
6369 /*! The uniqueid of the channel that will be taking the place of the caller or member */
6371 /*! Indication of whether we think there is a local channel optimization in progress */
6373 /*! The identifier for this local channel optimization */
6374 unsigned int id;
6375};
6376
6377/*!
6378 * \brief User data for stasis subscriptions used for queue calls.
6379 *
6380 * app_queue subscribes to channel and bridge events for all bridged calls.
6381 * app_queue cares about the following events:
6382 *
6383 * \li bridge enter: To determine the unique ID of the bridge created for the call.
6384 * \li blind transfer: To send an appropriate agent complete event.
6385 * \li attended transfer: To send an appropriate agent complete event.
6386 * \li local optimization: To update caller and member unique IDs for the call.
6387 * \li hangup: To send an appropriate agent complete event.
6388 *
6389 * The stasis subscriptions last until we determine that the caller and the member
6390 * are no longer bridged with each other.
6391 */
6394 /*! The unique ID of the caller's channel. */
6396 /*! The unique ID of the queue member's channel */
6398 /*! The unique ID of the bridge created by the queue */
6401 /*! The relevant queue */
6403 /*! The queue member that has answered the call */
6405 /*! The time at which the caller entered the queue. Start of the caller's hold time */
6407 /*! The time at which the member answered the call. */
6409 /*! The original position of the caller when he entered the queue */
6411 /*! Indication if the call was answered within the configured service level of the queue */
6413 /*! Indicates if the stasis subscriptions are shutting down */
6415 /*! The stasis message router for bridge events */
6417 /*! The stasis message router for channel events */
6419 /*! Local channel optimization details for the caller */
6421 /*! Local channel optimization details for the member */
6423};
6424
6425/*!
6426 * \internal
6427 * \brief Free memory for a queue_stasis_data
6428 */
6429static void queue_stasis_data_destructor(void *obj)
6430{
6431 struct queue_stasis_data *queue_data = obj;
6432
6433 /* This can only happen if refcounts for this object have got severely messed up */
6434 ast_assert(queue_data->bridge_router == NULL);
6435 ast_assert(queue_data->channel_router == NULL);
6436
6437 ao2_cleanup(queue_data->member);
6438 queue_unref(queue_data->queue);
6439 ast_string_field_free_memory(queue_data);
6440}
6441
6442/*!
6443 * \internal
6444 * \brief End all stasis subscriptions on a queue_stasis_data
6445 */
6446static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
6447{
6448 SCOPED_AO2LOCK(lock, queue_data);
6449
6450 queue_data->dying = 1;
6452 queue_data->bridge_router = NULL;
6454 queue_data->channel_router = NULL;
6455}
6456
6457/*!
6458 * \internal
6459 * \brief Allocate a queue_stasis_data and initialize its data.
6460 */
6462 struct ast_channel *peer, struct member *mem, time_t holdstart,
6463 time_t starttime, int callcompletedinsl)
6464{
6465 struct queue_stasis_data *queue_data;
6466
6467 queue_data = ao2_alloc(sizeof(*queue_data), queue_stasis_data_destructor);
6468 if (!queue_data) {
6469 return NULL;
6470 }
6471
6472 if (ast_string_field_init(queue_data, 64)) {
6473 ao2_cleanup(queue_data);
6474 return NULL;
6475 }
6476
6479 queue_data->queue = queue_ref(qe->parent);
6480 queue_data->starttime = starttime;
6481 queue_data->holdstart = holdstart;
6483 queue_data->caller_pos = qe->opos;
6484 ao2_ref(mem, +1);
6485 queue_data->member = mem;
6486
6487 return queue_data;
6488}
6489
6490/*!
6491 * \internal
6492 * \brief Log an attended transfer in the queue log.
6493 *
6494 * Attended transfer queue log messages vary based on the method by which the
6495 * attended transfer was completed.
6496 *
6497 * \param queue_data Data pertaining to the particular call in the queue.
6498 * \param atxfer_msg The stasis attended transfer message data.
6499 */
6500static void log_attended_transfer(struct queue_stasis_data *queue_data,
6501 struct ast_attended_transfer_message *atxfer_msg)
6502{
6503 RAII_VAR(struct ast_str *, transfer_str, ast_str_create(32), ast_free);
6504
6505 if (!transfer_str) {
6506 ast_log(LOG_WARNING, "Unable to log attended transfer to queue log\n");
6507 return;
6508 }
6509
6510 switch (atxfer_msg->dest_type) {
6512 ast_str_set(&transfer_str, 0, "BRIDGE|%s", atxfer_msg->dest.bridge);
6513 break;
6516 ast_str_set(&transfer_str, 0, "APP|%s", atxfer_msg->dest.app);
6517 break;
6519 ast_str_set(&transfer_str, 0, "LINK|%s|%s", atxfer_msg->dest.links[0]->base->name,
6520 atxfer_msg->dest.links[1]->base->name);
6521 break;
6524 /* Threeways are headed off and should not be logged here */
6525 ast_assert(0);
6526 return;
6527 }
6528
6529 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername, "ATTENDEDTRANSFER", "%s|%ld|%ld|%d",
6530 ast_str_buffer(transfer_str),
6531 (long) (queue_data->starttime - queue_data->holdstart),
6532 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6533}
6534
6535/*!
6536 * \internal
6537 * \brief Handle a stasis bridge enter event.
6538 *
6539 * We track this particular event in order to learn what bridge
6540 * was created for the queue call.
6541 *
6542 * \param userdata Data pertaining to the particular call in the queue.
6543 * \param sub The stasis subscription on which the message occurred.
6544 * \param msg The stasis message for the bridge enter event
6545 */
6546static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub,
6547 struct stasis_message *msg)
6548{
6549 struct queue_stasis_data *queue_data = userdata;
6550 struct ast_bridge_blob *enter_blob = stasis_message_data(msg);
6551 SCOPED_AO2LOCK(lock, queue_data);
6552
6553 if (queue_data->dying) {
6554 return;
6555 }
6556
6557 if (!ast_strlen_zero(queue_data->bridge_uniqueid)) {
6558 return;
6559 }
6560
6561 if (!strcmp(enter_blob->channel->base->uniqueid, queue_data->caller_uniqueid)) {
6562 ast_string_field_set(queue_data, bridge_uniqueid,
6563 enter_blob->bridge->uniqueid);
6564 ast_debug(3, "Detected entry of caller channel %s into bridge %s\n",
6565 enter_blob->channel->base->name, queue_data->bridge_uniqueid);
6566 }
6567}
6568
6569/*!
6570 * \brief Handle a blind transfer event
6571 *
6572 * This event is important in order to be able to log the end of the
6573 * call to the queue log and to stasis.
6574 *
6575 * \param userdata Data pertaining to the particular call in the queue.
6576 * \param sub The stasis subscription on which the message occurred.
6577 * \param msg The stasis message for the blind transfer event
6578 */
6579static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub,
6580 struct stasis_message *msg)
6581{
6582 struct queue_stasis_data *queue_data = userdata;
6583 struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
6584 const char *exten;
6585 const char *context;
6586 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6587 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6588
6589 if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
6590 return;
6591 }
6592
6593 ao2_lock(queue_data);
6594
6595 if (queue_data->dying) {
6596 ao2_unlock(queue_data);
6597 return;
6598 }
6599
6600 if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
6601 strcmp(queue_data->bridge_uniqueid, transfer_msg->bridge->uniqueid)) {
6602 ao2_unlock(queue_data);
6603 return;
6604 }
6605
6606 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6607 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6608
6609 ao2_unlock(queue_data);
6610
6611 exten = transfer_msg->exten;
6612 context = transfer_msg->context;
6613
6614 ast_debug(3, "Detected blind transfer in queue %s\n", queue_data->queue->name);
6615 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6616 "BLINDTRANSFER", "%s|%s|%ld|%ld|%d",
6617 exten, context,
6618 (long) (queue_data->starttime - queue_data->holdstart),
6619 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6620
6621 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6622 queue_data->holdstart, queue_data->starttime, TRANSFER);
6623 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6624 queue_data->starttime);
6625 remove_stasis_subscriptions(queue_data);
6626}
6627
6628/*!
6629 * \brief Handle an attended transfer event
6630 *
6631 * This event is important in order to be able to log the end of the
6632 * call to the queue log and to stasis.
6633 *
6634 * \param userdata Data pertaining to the particular call in the queue.
6635 * \param sub The stasis subscription on which the message occurred.
6636 * \param msg The stasis message for the attended transfer event.
6637 */
6638static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub,
6639 struct stasis_message *msg)
6640{
6641 struct queue_stasis_data *queue_data = userdata;
6642 struct ast_attended_transfer_message *atxfer_msg = stasis_message_data(msg);
6643 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6644 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6645
6646 if (atxfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS ||
6648 return;
6649 }
6650
6651 ao2_lock(queue_data);
6652
6653 if (queue_data->dying) {
6654 ao2_unlock(queue_data);
6655 return;
6656 }
6657
6658 if (ast_strlen_zero(queue_data->bridge_uniqueid)) {
6659 ao2_unlock(queue_data);
6660 return;
6661 }
6662
6663 if ((!atxfer_msg->to_transferee.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6664 atxfer_msg->to_transferee.bridge_snapshot->uniqueid)) &&
6665 (!atxfer_msg->to_transfer_target.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6667 ao2_unlock(queue_data);
6668 return;
6669 }
6670
6671 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6672 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6673
6674 ao2_unlock(queue_data);
6675
6676 ast_debug(3, "Detected attended transfer in queue %s\n", queue_data->queue->name);
6677 log_attended_transfer(queue_data, atxfer_msg);
6678
6679 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6680 queue_data->holdstart, queue_data->starttime, TRANSFER);
6681 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6682 queue_data->starttime);
6683 remove_stasis_subscriptions(queue_data);
6684}
6685
6686/*!
6687 * \internal
6688 * \brief Callback for all stasis bridge events
6689 *
6690 * Based on the event and what bridge it is on, the task is farmed out to relevant
6691 * subroutines for further processing.
6692 */
6693static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub,
6694 struct stasis_message *msg)
6695{
6697 ao2_cleanup(userdata);
6698 }
6699}
6700
6701/*!
6702 * \internal
6703 * \brief Handler for the beginning of a local channel optimization
6704 *
6705 * This method gathers data relevant to the local channel optimization and stores
6706 * it to be used once the local optimization completes.
6707 *
6708 * \param userdata Data pertaining to the particular call in the queue.
6709 * \param sub The stasis subscription on which the message occurred.
6710 * \param msg The stasis message for the local optimization begin event
6711 */
6712static void handle_local_optimization_begin(void *userdata, struct stasis_subscription *sub,
6713 struct stasis_message *msg)
6714{
6715 struct queue_stasis_data *queue_data = userdata;
6716 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6717 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6718 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6719 struct ast_channel_snapshot *source = ast_multi_channel_blob_get_channel(optimization_blob, "source");
6720 struct local_optimization *optimization;
6721 unsigned int id;
6722 SCOPED_AO2LOCK(lock, queue_data);
6723
6724 if (queue_data->dying) {
6725 return;
6726 }
6727
6728 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6729 optimization = &queue_data->member_optimize;
6730 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6731 optimization = &queue_data->caller_optimize;
6732 } else {
6733 return;
6734 }
6735
6736 /* We only allow move-swap optimizations, so there had BETTER be a source */
6737 ast_assert(source != NULL);
6738
6739 optimization->source_chan_uniqueid = ast_strdup(source->base->uniqueid);
6740 if (!optimization->source_chan_uniqueid) {
6741 ast_log(LOG_ERROR, "Unable to track local channel optimization for channel %s. Expect further errors\n", local_one->base->name);
6742 return;
6743 }
6745
6746 optimization->id = id;
6747 optimization->in_progress = 1;
6748}
6749
6750/*!
6751 * \internal
6752 * \brief Handler for the end of a local channel optimization
6753 *
6754 * This method takes the data gathered during the local channel optimization begin
6755 * event and applies it to the queue stasis data appropriately. This generally involves
6756 * updating the caller or member unique ID with the channel that is taking the place of
6757 * the previous caller or member.
6758 *
6759 * \param userdata Data pertaining to the particular call in the queue.
6760 * \param sub The stasis subscription on which the message occurred.
6761 * \param msg The stasis message for the local optimization end event
6762 */
6763static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub,
6764 struct stasis_message *msg)
6765{
6766 struct queue_stasis_data *queue_data = userdata;
6767 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6768 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6769 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6770 struct local_optimization *optimization;
6771 int is_caller;
6772 unsigned int id;
6773 SCOPED_AO2LOCK(lock, queue_data);
6774
6775 if (queue_data->dying) {
6776 return;
6777 }
6778
6779 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6780 optimization = &queue_data->member_optimize;
6781 is_caller = 0;
6782 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6783 optimization = &queue_data->caller_optimize;
6784 is_caller = 1;
6785 } else {
6786 return;
6787 }
6788
6790
6791 if (!optimization->in_progress) {
6792 ast_log(LOG_WARNING, "Told of a local optimization end when we had no previous begin\n");
6793 return;
6794 }
6795
6796 if (id != optimization->id) {
6797 ast_log(LOG_WARNING, "Local optimization end event ID does not match begin (%u != %u)\n",
6798 id, optimization->id);
6799 return;
6800 }
6801
6802 if (is_caller) {
6803 ast_debug(3, "Local optimization: Changing queue caller uniqueid from %s to %s\n",
6804 queue_data->caller_uniqueid, optimization->source_chan_uniqueid);
6805 ast_string_field_set(queue_data, caller_uniqueid, optimization->source_chan_uniqueid);
6806 } else {
6807 ast_debug(3, "Local optimization: Changing queue member uniqueid from %s to %s\n",
6808 queue_data->member_uniqueid, optimization->source_chan_uniqueid);
6809 ast_string_field_set(queue_data, member_uniqueid, optimization->source_chan_uniqueid);
6810 }
6811
6812 optimization->in_progress = 0;
6813}
6814
6815/*!
6816 * \internal
6817 * \brief Handler for hangup stasis event
6818 *
6819 * This is how we determine that the caller or member has hung up and the call
6820 * has ended. An appropriate queue log and stasis message are raised in this
6821 * callback.
6822 *
6823 * \param userdata Data pertaining to the particular call in the queue.
6824 * \param sub The stasis subscription on which the message occurred.
6825 * \param msg The stasis message for the hangup event.
6826 */
6827static void handle_hangup(void *userdata, struct stasis_subscription *sub,
6828 struct stasis_message *msg)
6829{
6830 struct queue_stasis_data *queue_data = userdata;
6831 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6832 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6833 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6834 RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
6835 enum agent_complete_reason reason;
6836
6837 ao2_lock(queue_data);
6838
6839 if (queue_data->dying) {
6840 ao2_unlock(queue_data);
6841 return;
6842 }
6843
6844 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6845 reason = CALLER;
6846 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6847 reason = AGENT;
6848 } else {
6849 ao2_unlock(queue_data);
6850 return;
6851 }
6852
6853 chan = ast_channel_get_by_name(channel_blob->snapshot->base->name);
6854 if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
6855 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
6856 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
6857 /* Channel that is hanging up is doing it as part of a transfer.
6858 * We'll get a transfer event later
6859 */
6860 ao2_unlock(queue_data);
6861 return;
6862 }
6863
6864 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6865 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6866
6867 ao2_unlock(queue_data);
6868
6869 ast_debug(3, "Detected hangup of queue %s channel %s\n", reason == CALLER ? "caller" : "member",
6870 channel_blob->snapshot->base->name);
6871
6872 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6873 reason == CALLER ? "COMPLETECALLER" : "COMPLETEAGENT", "%ld|%ld|%d",
6874 (long) (queue_data->starttime - queue_data->holdstart),
6875 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6876
6877 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6878 queue_data->holdstart, queue_data->starttime, reason);
6879 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6880 queue_data->starttime);
6881 remove_stasis_subscriptions(queue_data);
6882}
6883
6884static void handle_masquerade(void *userdata, struct stasis_subscription *sub,
6885 struct stasis_message *msg)
6886{
6887 struct queue_stasis_data *queue_data = userdata;
6888 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6889 const char *new_channel_id;
6890
6891 new_channel_id = ast_json_string_get(ast_json_object_get(channel_blob->blob, "newchanneluniqueid"));
6892
6893 ao2_lock(queue_data);
6894
6895 if (queue_data->dying) {
6896 ao2_unlock(queue_data);
6897 return;
6898 }
6899
6900 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6901 ast_debug(1, "Replacing caller channel %s with %s due to masquerade\n", queue_data->caller_uniqueid, new_channel_id);
6902 ast_string_field_set(queue_data, caller_uniqueid, new_channel_id);
6903 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6904 ast_debug(1, "Replacing member channel %s with %s due to masquerade\n", queue_data->member_uniqueid, new_channel_id);
6905 ast_string_field_set(queue_data, member_uniqueid, new_channel_id);
6906 }
6907
6908 ao2_unlock(queue_data);
6909}
6910
6911/*!
6912 * \internal
6913 * \brief Callback for all stasis channel events
6914 *
6915 * Based on the event and the channels involved, the work is farmed out into
6916 * subroutines for further processing.
6917 */
6918static void queue_channel_cb(void *userdata, struct stasis_subscription *sub,
6919 struct stasis_message *msg)
6920{
6922 ao2_cleanup(userdata);
6923 }
6924}
6925
6926/*!
6927 * \internal
6928 * \brief Create stasis subscriptions for a particular call in the queue.
6929 *
6930 * These subscriptions are created once the call has been answered. The subscriptions
6931 * are put in place so that call progress may be tracked. Once the call can be determined
6932 * to have ended, then messages are logged to the queue log and stasis events are emitted.
6933 *
6934 * \param qe The queue entry representing the caller
6935 * \param peer The channel that has answered the call
6936 * \param mem The queue member that answered the call
6937 * \param holdstart The time at which the caller entered the queue
6938 * \param starttime The time at which the call was answered
6939 * \param callcompletedinsl Indicates if the call was answered within the configured service level of the queue.
6940 * \retval 0 Success
6941 * \retval non-zero Failure
6942 */
6943static int setup_stasis_subs(struct queue_ent *qe, struct ast_channel *peer, struct member *mem,
6944 time_t holdstart, time_t starttime, int callcompletedinsl)
6945{
6946 struct queue_stasis_data *queue_data = queue_stasis_data_alloc(qe, peer, mem, holdstart, starttime, callcompletedinsl);
6947
6948 if (!queue_data) {
6949 return -1;
6950 }
6951
6953 if (!queue_data->bridge_router) {
6954 ao2_ref(queue_data, -1);
6955 return -1;
6956 }
6957
6959 handle_bridge_enter, queue_data);
6961 handle_blind_transfer, queue_data);
6963 handle_attended_transfer, queue_data);
6965 queue_bridge_cb, queue_data);
6966
6968 if (!queue_data->channel_router) {
6969 /* Unsubscribing from the bridge router will remove the only ref of queue_data,
6970 * thus beginning the destruction process
6971 */
6973 queue_data->bridge_router = NULL;
6974 return -1;
6975 }
6976
6977 ao2_ref(queue_data, +1);
6981 handle_local_optimization_end, queue_data);
6983 handle_hangup, queue_data);
6985 handle_masquerade, queue_data);
6987 queue_channel_cb, queue_data);
6988
6989 return 0;
6990}
6991
6993 struct call_queue *q;
6995};
6996
6997static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
6998{
6999 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
7000 ao2_ref(qeb, +1);
7001 qeb->chan = originator;
7002}
7003
7004static void end_bridge_callback(void *data)
7005{
7006 struct queue_end_bridge *qeb = data;
7007 struct call_queue *q = qeb->q;
7008 struct ast_channel *chan = qeb->chan;
7009
7010 if (ao2_ref(qeb, -1) == 1) {
7011 set_queue_variables(q, chan);
7012 /* This unrefs the reference we made in try_calling when we allocated qeb */
7013 queue_t_unref(q, "Expire bridge_config reference");
7014 }
7015}
7016
7017/*!
7018 * \internal
7019 * \brief Setup the after bridge goto location on the peer.
7020 * \since 12.0.0
7021 *
7022 * \param chan Calling channel for bridge.
7023 * \param peer Peer channel for bridge.
7024 * \param opts Dialing option flags.
7025 * \param opt_args Dialing option argument strings.
7026 */
7027static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
7028{
7029 const char *context;
7030 const char *extension;
7031 int priority;
7032
7033 if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
7034 ast_channel_lock(chan);
7038 ast_channel_unlock(chan);
7040 opt_args[OPT_ARG_CALLEE_GO_ON]);
7041 }
7042}
7043
7044static void escape_and_substitute(struct ast_channel *chan, const char *input,
7045 char *output, size_t size)
7046{
7047 const char *m = input;
7048 char escaped[size];
7049 char *p;
7050
7051 for (p = escaped; p < escaped + size - 1; p++, m++) {
7052 switch (*m) {
7053 case '^':
7054 if (*(m + 1) == '{') {
7055 *p = '$';
7056 }
7057 break;
7058 case ',':
7059 *p++ = '\\';
7060 /* Fall through */
7061 default:
7062 *p = *m;
7063 }
7064 if (*m == '\0')
7065 break;
7066 }
7067
7068 if (p == escaped + size) {
7069 escaped[size - 1] = '\0';
7070 }
7071
7072 pbx_substitute_variables_helper(chan, escaped, output, size - 1);
7073}
7074
7075static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
7076{
7077 char escaped_filename[256];
7078 char file_with_ext[sizeof(escaped_filename) + sizeof(qe->parent->monfmt)];
7079 char mixmonargs[1512];
7080 char escaped_monitor_exec[1024];
7081 const char *monitor_options;
7082 const char *monitor_exec;
7083
7084 escaped_monitor_exec[0] = '\0';
7085
7086 if (filename) {
7087 escape_and_substitute(qe->chan, filename, escaped_filename, sizeof(escaped_filename));
7088 } else {
7089 ast_copy_string(escaped_filename, ast_channel_uniqueid(qe->chan), sizeof(escaped_filename));
7090 }
7091
7093 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
7094 monitor_exec = ast_strdupa(monitor_exec);
7095 }
7096 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
7097 monitor_options = ast_strdupa(monitor_options);
7098 } else {
7099 monitor_options = "";
7100 }
7102
7103 if (monitor_exec) {
7104 escape_and_substitute(qe->chan, monitor_exec, escaped_monitor_exec, sizeof(escaped_monitor_exec));
7105 }
7106
7107 snprintf(file_with_ext, sizeof(file_with_ext), "%s.%s", escaped_filename, qe->parent->monfmt);
7108
7109 if (!ast_strlen_zero(escaped_monitor_exec)) {
7110 snprintf(mixmonargs, sizeof(mixmonargs), "b%s,%s", monitor_options, escaped_monitor_exec);
7111 } else {
7112 snprintf(mixmonargs, sizeof(mixmonargs), "b%s", monitor_options);
7113 }
7114
7115 ast_debug(1, "Arguments being passed to MixMonitor: %s,%s\n", file_with_ext, mixmonargs);
7116
7117 if (ast_start_mixmonitor(qe->chan, file_with_ext, mixmonargs)) {
7118 ast_log(LOG_WARNING, "Unable to start mixmonitor. Is the MixMonitor app loaded?\n");
7119 }
7120}
7121
7122/*!
7123 * \internal
7124 * \brief A large function which calls members, updates statistics, and bridges the caller and a member
7125 *
7126 * Here is the process of this function
7127 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
7128 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member.
7129 * 3. Call ring_one to place a call to the appropriate member(s)
7130 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
7131 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
7132 * 6. Start the monitor or mixmonitor if the option is set
7133 * 7. Remove the caller from the queue to allow other callers to advance
7134 * 8. Bridge the call.
7135 * 9. Do any post processing after the call has disconnected.
7136 *
7137 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
7138 * \param[in] opts the options passed as the third parameter to the Queue() application
7139 * \param[in] opt_args the options passed as the third parameter to the Queue() application
7140 * \param[in] announceoverride filename to play to user when waiting
7141 * \param[in] url the url passed as the fourth parameter to the Queue() application
7142 * \param[in,out] tries the number of times we have tried calling queue members
7143 * \param[out] noption set if the call to Queue() has the 'n' option set.
7144 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
7145 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
7146 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
7147 */
7148static 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)
7149{
7150 struct member *cur;
7151 struct callattempt *outgoing = NULL; /* the list of calls we are building */
7152 int to, orig;
7153 char oldexten[AST_MAX_EXTENSION]="";
7154 char oldcontext[AST_MAX_CONTEXT]="";
7155 char queuename[256]="";
7156 struct ast_channel *peer;
7157 struct callattempt *lpeer;
7158 struct member *member;
7159 struct ast_app *application;
7160 int res = 0, bridge = 0;
7161 int numbusies = 0;
7162 int x=0;
7163 char *announce = NULL;
7164 char digit = 0;
7165 time_t now = time(NULL);
7166 struct ast_bridge_config bridge_config;
7167 char nondataquality = 1;
7168 char *agiexec = NULL;
7169 char *gosubexec = NULL;
7170 const char *monitorfilename;
7171 int forwardsallowed = 1;
7172 int block_connected_line = 0;
7173 struct ao2_iterator memi;
7175 int callcompletedinsl;
7176 time_t starttime;
7177
7178 memset(&bridge_config, 0, sizeof(bridge_config));
7179 time(&now);
7180
7181 /* If we've already exceeded our timeout, then just stop
7182 * This should be extremely rare. queue_exec will take care
7183 * of removing the caller and reporting the timeout as the reason.
7184 */
7185 if (qe->expire && now >= qe->expire) {
7186 res = 0;
7187 goto out;
7188 }
7189
7190 if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) {
7192 }
7193 if (ast_test_flag(&opts, OPT_CALLER_TRANSFER)) {
7195 }
7196 if (ast_test_flag(&opts, OPT_CALLEE_AUTOMON)) {
7198 }
7199 if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
7201 }
7202 if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
7203 nondataquality = 0;
7204 }
7205 if (ast_test_flag(&opts, OPT_CALLEE_HANGUP)) {
7207 }
7208 if (ast_test_flag(&opts, OPT_CALLER_HANGUP)) {
7210 }
7211 if (ast_test_flag(&opts, OPT_CALLEE_PARK)) {
7213 }
7214 if (ast_test_flag(&opts, OPT_CALLER_PARK)) {
7216 }
7217 if (ast_test_flag(&opts, OPT_NO_RETRY)) {
7220 (*tries)++;
7221 } else {
7222 *tries = ao2_container_count(qe->parent->members);
7223 }
7224 *noption = 1;
7225 }
7226 if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
7227 forwardsallowed = 0;
7228 }
7230 block_connected_line = 1;
7231 }
7234 }
7237 }
7238 if (ast_test_flag(&opts, OPT_MARK_AS_ANSWERED)) {
7240 }
7241
7242 /* if the calling channel has AST_CAUSE_ANSWERED_ELSEWHERE set, make sure this is inherited.
7243 (this is mainly to support unreal/local channels)
7244 */
7247 }
7248
7249 ao2_lock(qe->parent);
7250 ast_debug(1, "%s is trying to call a queue member.\n",
7251 ast_channel_name(qe->chan));
7252 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
7253 if (!ast_strlen_zero(qe->announce)) {
7254 announce = qe->announce;
7255 }
7256 if (!ast_strlen_zero(announceoverride)) {
7257 announce = announceoverride;
7258 }
7259
7260 memi = ao2_iterator_init(qe->parent->members, 0);
7261 while ((cur = ao2_iterator_next(&memi))) {
7262 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
7263 if (!tmp) {
7264 ao2_ref(cur, -1);
7265 ao2_iterator_destroy(&memi);
7266 ao2_unlock(qe->parent);
7267 goto out;
7268 }
7269
7270 /*
7271 * Seed the callattempt's connected line information with previously
7272 * acquired connected line info from the queued channel. The
7273 * previously acquired connected line info could have been set
7274 * through the CONNECTED_LINE dialplan function.
7275 */
7279
7280 tmp->block_connected_update = block_connected_line;
7281 tmp->stillgoing = 1;
7282 tmp->member = cur; /* Place the reference for cur into callattempt. */
7283 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
7284 /* Calculate the metric for the appropriate strategy. */
7285 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
7286 /* Put them in the list of outgoing thingies... We're ready now.
7287 XXX If we're forcibly removed, these outgoing calls won't get
7288 hung up XXX */
7289 tmp->q_next = outgoing;
7290 outgoing = tmp;
7291 } else {
7292 callattempt_free(tmp);
7293 }
7294 }
7295 ao2_iterator_destroy(&memi);
7296
7298 /* Application arguments have higher timeout priority (behaviour for <=1.6) */
7299 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) {
7300 to = (qe->expire - now) * 1000;
7301 } else {
7302 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
7303 }
7304 } else {
7305 /* Config timeout is higher priority thatn application timeout */
7306 if (qe->expire && qe->expire<=now) {
7307 to = 0;
7308 } else if (qe->parent->timeout) {
7309 to = qe->parent->timeout * 1000;
7310 } else {
7311 to = -1;
7312 }
7313 }
7314 orig = to;
7315 ++qe->pending;
7316 ao2_unlock(qe->parent);
7317 /* Call the queue members with the best metric now. */
7318 ring_one(qe, outgoing, &numbusies);
7319 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
7321 forwardsallowed);
7322
7323 ao2_lock(qe->parent);
7326
7327 }
7330 }
7331 ao2_unlock(qe->parent);
7332 peer = lpeer ? lpeer->chan : NULL;
7333 if (!peer) {
7334 qe->pending = 0;
7335 if (to) {
7336 /* Must gotten hung up */
7337 res = -1;
7338 } else {
7339 /* User exited by pressing a digit */
7340 res = digit;
7341 }
7342 if (res == -1) {
7343 ast_debug(1, "%s: Nobody answered.\n", ast_channel_name(qe->chan));
7344 }
7345 } else { /* peer is valid */
7346 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
7347 RAII_VAR(struct ast_str *, interfacevar, ast_str_create(325), ast_free);
7348 /* Ah ha! Someone answered within the desired timeframe. Of course after this
7349 we will always return with -1 so that it is hung up properly after the
7350 conversation. */
7351 if (!strcmp(ast_channel_tech(qe->chan)->type, "DAHDI")) {
7352 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7353 }
7354 if (!strcmp(ast_channel_tech(peer)->type, "DAHDI")) {
7355 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7356 }
7357 /* Update parameters for the queue */
7358 time(&now);
7359 recalc_holdtime(qe, (now - qe->start));
7360 member = lpeer->member;
7361 ao2_lock(qe->parent);
7362 callcompletedinsl = member->callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
7363 ao2_unlock(qe->parent);
7364 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
7365 ao2_ref(member, 1);
7367 outgoing = NULL;
7368 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
7369 int res2;
7370
7371 res2 = ast_autoservice_start(qe->chan);
7372 if (!res2) {
7373 if (qe->parent->memberdelay) {
7374 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
7375 res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
7376 }
7377 if (!res2 && announce) {
7378 char *front;
7379 char *announcefiles = ast_strdupa(announce);
7380 while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
7381 if (play_file(peer, front) < 0) {
7382 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
7383 }
7384 }
7385 }
7386 if (!res2 && qe->parent->reportholdtime) {
7387 if (!play_file(peer, qe->parent->sound_reporthold)) {
7388 long holdtime, holdtimesecs;
7389
7390 time(&now);
7391 holdtime = labs((now - qe->start) / 60);
7392 holdtimesecs = labs((now - qe->start) % 60);
7393 if (holdtime > 0) {
7394 ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7395 if (play_file(peer, qe->parent->sound_minutes) < 0) {
7396 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, ast_channel_name(peer));
7397 }
7398 }
7399 if (holdtimesecs > 1) {
7400 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7401 if (play_file(peer, qe->parent->sound_seconds) < 0) {
7402 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, ast_channel_name(peer));
7403 }
7404 }
7405 }
7406 }
7408 }
7409 if (ast_check_hangup(peer)) {
7410 /* Agent must have hung up */
7411 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", ast_channel_name(peer));
7412 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "AGENTDUMP", "%s", "");
7413
7414 blob = ast_json_pack("{s: s, s: s, s: s}",
7415 "Queue", queuename,
7416 "Interface", member->interface,
7417 "MemberName", member->membername);
7418 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_dump_type(), blob);
7419
7423 ao2_ref(member, -1);
7424 goto out;
7425 } else if (ast_check_hangup(qe->chan)) {
7426 /* Caller must have hung up just before being connected */
7427 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
7428 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
7429 record_abandoned(qe);
7430 qe->handled = -1;
7434 ao2_ref(member, -1);
7435 return -1;
7436 }
7437 }
7438 /* Stop music on hold */
7439 if (ringing) {
7440 ast_indicate(qe->chan,-1);
7441 } else {
7442 ast_moh_stop(qe->chan);
7443 }
7444
7445 /* Make sure channels are compatible */
7446 res = ast_channel_make_compatible(qe->chan, peer);
7447 if (res < 0) {
7448 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "SYSCOMPAT", "%s", "");
7449 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));
7450 record_abandoned(qe);
7454 ao2_ref(member, -1);
7455 return -1;
7456 }
7457
7458 /* Play announcement to the caller telling it's his turn if defined */
7460 if (play_file(qe->chan, qe->parent->sound_callerannounce)) {
7461 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
7462 }
7463 }
7464
7465 ao2_lock(qe->parent);
7466 /* if setinterfacevar is defined, make member variables available to the channel */
7467 /* use pbx_builtin_setvar to set a load of variables with one call */
7468 if (qe->parent->setinterfacevar && interfacevar) {
7469 ast_str_set(&interfacevar, 0, "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
7472 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7473 }
7474
7475 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
7476 /* use pbx_builtin_setvar to set a load of variables with one call */
7477 if (qe->parent->setqueueentryvar && interfacevar) {
7478 ast_str_set(&interfacevar, 0, "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
7479 (long) (time(NULL) - qe->start), qe->opos);
7481 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7482 }
7483
7484 ao2_unlock(qe->parent);
7485
7486 /* try to set queue variables if configured to do so*/
7488 set_queue_variables(qe->parent, peer);
7489
7490 setup_peer_after_bridge_goto(qe->chan, peer, &opts, opt_args);
7492 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
7493 monitorfilename = ast_strdupa(monitorfilename);
7494 }
7496
7497 /* Begin Monitoring */
7498 if (*qe->parent->monfmt) {
7499 setup_mixmonitor(qe, monitorfilename);
7500 }
7501 /* Drop out of the queue at this point, to prepare for next caller */
7502 leave_queue(qe);
7504 ast_debug(1, "app_queue: sendurl=%s.\n", url);
7505 ast_channel_sendurl(peer, url);
7506 }
7507
7508 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
7509 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
7510 if (!ast_strlen_zero(gosub)) {
7511 gosubexec = ast_strdupa(gosub);
7512 } else {
7513 if (qe->parent->membergosub) {
7514 gosubexec = ast_strdupa(qe->parent->membergosub);
7515 }
7516 }
7517
7518 if (!ast_strlen_zero(gosubexec)) {
7519 char *gosub_args = NULL;
7520 char *gosub_argstart;
7521
7522 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
7523
7524 gosub_argstart = strchr(gosubexec, ',');
7525 if (gosub_argstart) {
7526 const char *what_is_s = "s";
7527 *gosub_argstart = 0;
7528 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)) &&
7529 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7530 what_is_s = "~~s~~";
7531 }
7532 if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
7533 gosub_args = NULL;
7534 }
7535 *gosub_argstart = ',';
7536 } else {
7537 const char *what_is_s = "s";
7538 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)) &&
7539 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7540 what_is_s = "~~s~~";
7541 }
7542 if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
7543 gosub_args = NULL;
7544 }
7545 }
7546 if (gosub_args) {
7547 ast_app_exec_sub(qe->chan, peer, gosub_args, 0);
7548 ast_free(gosub_args);
7549 } else {
7550 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
7551 }
7552 }
7553
7554 if (!ast_strlen_zero(agi)) {
7555 ast_debug(1, "app_queue: agi=%s.\n", agi);
7556 application = pbx_findapp("agi");
7557 if (application) {
7558 agiexec = ast_strdupa(agi);
7559 pbx_exec(qe->chan, application, agiexec);
7560 } else {
7561 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
7562 }
7563 }
7564 qe->handled++;
7565
7566 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer),
7567 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
7568
7569 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I}",
7570 "Queue", queuename,
7571 "Interface", member->interface,
7572 "MemberName", member->membername,
7573 "HoldTime", (ast_json_int_t)(time(NULL) - qe->start),
7574 "RingTime", (ast_json_int_t)(orig - to > 0 ? (orig - to) / 1000 : 0));
7575 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_connect_type(), blob);
7576
7577 ast_copy_string(oldcontext, ast_channel_context(qe->chan), sizeof(oldcontext));
7578 ast_copy_string(oldexten, ast_channel_exten(qe->chan), sizeof(oldexten));
7579
7580 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
7581 queue_end_bridge->q = qe->parent;
7582 queue_end_bridge->chan = qe->chan;
7586 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
7587 * to make sure to increase the refcount of this queue so it cannot be freed until we
7588 * are done with it. We remove this reference in end_bridge_callback.
7589 */
7590 queue_t_ref(qe->parent, "For bridge_config reference");
7591 }
7592
7593 ao2_lock(qe->parent);
7594 time(&member->starttime);
7595 starttime = member->starttime;
7596 ao2_unlock(qe->parent);
7597 /* As a queue member may end up in multiple calls at once if a transfer occurs with
7598 * a Local channel in the mix we pass the current call information (starttime) to the
7599 * Stasis subscriptions so when they update the queue member data it becomes a noop
7600 * if this call is no longer between the caller and the queue member.
7601 */
7602 setup_stasis_subs(qe, peer, member, qe->start, starttime, callcompletedinsl);
7603 bridge = ast_bridge_call_with_flags(qe->chan, peer, &bridge_config,
7605
7606 res = bridge ? bridge : 1;
7607 ao2_ref(member, -1);
7608 }
7609out:
7611
7612 return res;
7613}
7614
7615static int wait_a_bit(struct queue_ent *qe)
7616{
7617 /* Don't need to hold the lock while we setup the outgoing calls */
7618 int retrywait = qe->parent->retry * 1000;
7619
7620 int res = ast_waitfordigit(qe->chan, retrywait);
7621 if (res > 0 && !valid_exit(qe, res)) {
7622 res = 0;
7623 }
7624
7625 return res;
7626}
7627
7628static struct member *interface_exists(struct call_queue *q, const char *interface)
7629{
7630 struct member *mem;
7631 struct ao2_iterator mem_iter;
7632
7633 if (!q) {
7634 return NULL;
7635 }
7636 mem_iter = ao2_iterator_init(q->members, 0);
7637 while ((mem = ao2_iterator_next(&mem_iter))) {
7638 if (!strcasecmp(interface, mem->interface)) {
7639 ao2_iterator_destroy(&mem_iter);
7640 return mem;
7641 }
7642 ao2_ref(mem, -1);
7643 }
7644 ao2_iterator_destroy(&mem_iter);
7645
7646 return NULL;
7647}
7648
7649
7650/*! \brief Dump all members in a specific queue to the database
7651 * \code
7652 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
7653 * \endcode
7654 */
7655static void dump_queue_members(struct call_queue *pm_queue)
7656{
7657 struct member *cur_member;
7658 struct ast_str *value;
7659 struct ao2_iterator mem_iter;
7660
7661 if (!pm_queue) {
7662 return;
7663 }
7664
7665 /* 4K is a reasonable default for most applications, but we grow to
7666 * accommodate more if necessary. */
7667 if (!(value = ast_str_create(4096))) {
7668 return;
7669 }
7670
7671 mem_iter = ao2_iterator_init(pm_queue->members, 0);
7672 while ((cur_member = ao2_iterator_next(&mem_iter))) {
7673 if (!cur_member->dynamic) {
7674 ao2_ref(cur_member, -1);
7675 continue;
7676 }
7677
7678 ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s;%s;%d",
7679 ast_str_strlen(value) ? "|" : "",
7680 cur_member->interface,
7681 cur_member->penalty,
7682 cur_member->paused,
7683 cur_member->membername,
7684 cur_member->state_interface,
7685 cur_member->reason_paused,
7686 cur_member->wrapuptime);
7687
7688 ao2_ref(cur_member, -1);
7689 }
7690 ao2_iterator_destroy(&mem_iter);
7691
7692 if (ast_str_strlen(value) && !cur_member) {
7693 if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) {
7694 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
7695 }
7696 } else {
7697 /* Delete the entry if the queue is empty or there is an error */
7698 ast_db_del(pm_family, pm_queue->name);
7699 }
7700
7701 ast_free(value);
7702}
7703
7704/*! \brief Remove member from queue
7705 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7706 * \retval RES_NOSUCHQUEUE queue does not exist
7707 * \retval RES_OKAY removed member from queue
7708 * \retval RES_EXISTS queue exists but no members
7709*/
7710static int remove_from_queue(const char *queuename, const char *interface)
7711{
7712 struct call_queue *q, tmpq = {
7713 .name = queuename,
7714 };
7715 struct member *mem, tmpmem;
7716 int res = RES_NOSUCHQUEUE;
7717
7718 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
7719 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
7720 ao2_lock(q);
7721 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
7722 /* XXX future changes should beware of this assumption!! */
7723 /*Change Penalty on realtime users*/
7725 update_realtime_member_field(mem, q->name, "penalty", "-1");
7726 } else if (!mem->dynamic) {
7727 ao2_ref(mem, -1);
7728 ao2_unlock(q);
7729 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
7730 return RES_NOT_DYNAMIC;
7731 }
7732 queue_publish_member_blob(queue_member_removed_type(), queue_member_blob_create(q, mem));
7733
7735 ao2_ref(mem, -1);
7736
7739 }
7740
7741 if (!num_available_members(q)) {
7743 }
7744
7745 res = RES_OKAY;
7746 } else {
7747 res = RES_EXISTS;
7748 }
7749 ao2_unlock(q);
7750 queue_t_unref(q, "Expiring temporary reference");
7751 }
7752
7753 return res;
7754}
7755
7756/*! \brief Add member to queue
7757 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7758 * \retval RES_NOSUCHQUEUE queue does not exist
7759 * \retval RES_OKAY added member from queue
7760 * \retval RES_EXISTS queue exists but no members
7761 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
7762*/
7763static 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)
7764{
7765 struct call_queue *q;
7766 struct member *new_member, *old_member;
7767 int res = RES_NOSUCHQUEUE;
7768
7769 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7770 * short-circuits if the queue is already in memory. */
7771 if (!(q = find_load_queue_rt_friendly(queuename))) {
7772 return res;
7773 }
7774
7775 ao2_lock(q);
7776 if ((old_member = interface_exists(q, interface)) == NULL) {
7778 new_member->dynamic = 1;
7779 if (reason_paused) {
7780 ast_copy_string(new_member->reason_paused, reason_paused, sizeof(new_member->reason_paused));
7781 }
7782 member_add_to_queue(q, new_member);
7783 queue_publish_member_blob(queue_member_added_type(), queue_member_blob_create(q, new_member));
7784
7785 if (is_member_available(q, new_member)) {
7787 }
7788
7789 ao2_ref(new_member, -1);
7790 new_member = NULL;
7791
7792 if (dump) {
7794 }
7795
7796 res = RES_OKAY;
7797 } else {
7798 res = RES_OUTOFMEMORY;
7799 }
7800 } else {
7801 ao2_ref(old_member, -1);
7802 res = RES_EXISTS;
7803 }
7804 ao2_unlock(q);
7805 queue_t_unref(q, "Expiring temporary reference");
7806
7807 return res;
7808}
7809
7810
7811/*! \brief Change priority caller into a queue
7812 * \retval RES_NOSUCHQUEUE queue does not exist
7813 * \retval RES_OKAY change priority
7814 * \retval RES_NOT_CALLER queue exists but no caller
7815*/
7816static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
7817{
7818 struct call_queue *q;
7819 struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
7820 int res = RES_NOSUCHQUEUE;
7821
7822 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7823 * short-circuits if the queue is already in memory. */
7824 if (!(q = find_load_queue_rt_friendly(queuename))) {
7825 return res;
7826 }
7827
7828 ao2_lock(q);
7829 res = RES_NOT_CALLER;
7830 for (current = q->head; current; current = current->next) {
7831 if (strcmp(ast_channel_name(current->chan), caller) == 0) {
7832 ast_debug(1, "%s Caller new priority %d in queue %s\n",
7833 caller, priority, queuename);
7834 current->prio = priority;
7835 if (immediate) {
7836 /* This caller is being immediately moved in the queue so remove them */
7837 if (prev) {
7838 prev->next = current->next;
7839 } else {
7840 q->head = current->next;
7841 }
7842 caller_qe = current;
7843 /* The position for all callers is not recalculated in here as it will
7844 * be updated when the moved caller is inserted back into the queue
7845 */
7846 }
7847 res = RES_OKAY;
7848 break;
7849 } else if (immediate) {
7850 prev = current;
7851 }
7852 }
7853
7854 if (caller_qe) {
7855 int inserted = 0, pos = 0;
7856
7857 /* If a caller queue entry exists, we are applying their priority immediately
7858 * and have to reinsert them at the correct position.
7859 */
7860 prev = NULL;
7861 current = q->head;
7862 while (current) {
7863 if (!inserted && (caller_qe->prio > current->prio)) {
7864 insert_entry(q, prev, caller_qe, &pos);
7865 inserted = 1;
7866 }
7867
7868 /* We always update the position as it may have changed */
7869 current->pos = ++pos;
7870
7871 /* Move to the next caller in the queue */
7872 prev = current;
7873 current = current->next;
7874 }
7875
7876 if (!inserted) {
7877 insert_entry(q, prev, caller_qe, &pos);
7878 }
7879 }
7880
7881 ao2_unlock(q);
7882 return res;
7883}
7884
7885
7886/*! \brief Request to withdraw a caller from a queue
7887 * \retval RES_NOSUCHQUEUE queue does not exist
7888 * \retval RES_OKAY withdraw request sent
7889 * \retval RES_NOT_CALLER queue exists but no caller
7890 * \retval RES_EXISTS a withdraw request was already sent for this caller (channel) and queue
7891*/
7892static int request_withdraw_caller_from_queue(const char *queuename, const char *caller, const char *withdraw_info)
7893{
7894 struct call_queue *q;
7895 struct queue_ent *qe;
7896 int res = RES_NOSUCHQUEUE;
7897
7898 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7899 * short-circuits if the queue is already in memory. */
7900 if (!(q = find_load_queue_rt_friendly(queuename))) {
7901 return res;
7902 }
7903
7904 ao2_lock(q);
7905 res = RES_NOT_CALLER;
7906 for (qe = q->head; qe; qe = qe->next) {
7907 if (!strcmp(ast_channel_name(qe->chan), caller)) {
7908 if (qe->withdraw) {
7909 ast_debug(1, "Ignoring duplicate withdraw request of caller %s from queue %s\n", caller, queuename);
7910 res = RES_EXISTS;
7911 } else {
7912 ast_debug(1, "Requested withdraw of caller %s from queue %s\n", caller, queuename);
7913 /* It is not possible to change the withdraw info by further withdraw requests for this caller (channel)
7914 in this queue, so we do not need to worry about a memory leak here. */
7915 if (withdraw_info) {
7917 }
7918 qe->withdraw = 1;
7919 res = RES_OKAY;
7920 }
7921 break;
7922 }
7923 }
7924 ao2_unlock(q);
7925 queue_unref(q);
7926
7927 return res;
7928}
7929
7930
7932{
7933 struct ast_json *json_blob = queue_member_blob_create(q, member);
7934
7935 if (!json_blob) {
7936 return -1;
7937 }
7938
7939 queue_publish_member_blob(queue_member_pause_type(), json_blob);
7940
7941 return 0;
7942}
7943
7944/*!
7945 * \internal
7946 * \brief Set the pause status of the specific queue member.
7947 *
7948 * \param q Which queue the member belongs.
7949 * \param mem Queue member being paused/unpaused.
7950 * \param reason Why is this happening (Can be NULL/empty for no reason given.)
7951 * \param paused Set to 1 if the member is being paused or 0 to unpause.
7952 *
7953 * \pre The q is locked on entry.
7954 */
7955static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
7956{
7957 if (mem->paused == paused) {
7958 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n",
7959 (paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
7960 }
7961
7962 if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid)) {
7964 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "reason_paused", S_OR(reason, ""), "paused", paused ? "1" : "0", SENTINEL) < 0) {
7965 ast_log(LOG_WARNING, "Failed update of realtime queue member %s:%s %spause and reason '%s'\n",
7966 q->name, mem->interface, (paused ? "" : "un"), S_OR(reason, ""));
7967 }
7968 } else {
7969 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "paused", paused ? "1" : "0", SENTINEL) < 0) {
7970 ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
7971 (paused ? "" : "un"), q->name, mem->interface);
7972 }
7973 }
7974 }
7975
7976 mem->paused = paused;
7977 if (paused) {
7978 time(&mem->lastpause); /* update last pause field */
7979 }
7980 if (paused && !ast_strlen_zero(reason)) {
7981 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
7982 } else {
7983 /* We end up filling this in again later (temporarily) but we need it
7984 * empty for now so that the intervening code - specifically
7985 * dump_queue_members() - has the correct view of things. */
7986 mem->reason_paused[0] = '\0';
7987 }
7988
7990 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
7991
7994 }
7995
7996 if (is_member_available(q, mem)) {
7998 "Queue:%s_avail", q->name);
7999 } else if (!num_available_members(q)) {
8001 "Queue:%s_avail", q->name);
8002 }
8003
8004 if (!paused && !ast_strlen_zero(reason)) {
8005 /* Because we've been unpaused with a 'reason' we need to ensure that
8006 * that reason is emitted when the subsequent PauseQueueMember event
8007 * is raised. So temporarily set it on the member and clear it out
8008 * again right after. */
8009 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
8010 }
8011
8012 ast_queue_log(q->name, "NONE", mem->membername, paused ? "PAUSE" : "UNPAUSE",
8013 "%s", mem->reason_paused);
8014
8016
8017 if (!paused) {
8018 mem->reason_paused[0] = '\0';
8019 }
8020}
8021
8022static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
8023{
8024 int found = 0;
8025 struct call_queue *q;
8026 struct ao2_iterator queue_iter;
8027
8028 if (ast_check_realtime("queues")) {
8029 load_realtime_queues(queuename);
8030 }
8031
8032 queue_iter = ao2_iterator_init(queues, 0);
8033 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
8034 ao2_lock(q);
8035 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
8036 struct member *mem;
8037
8038 if ((mem = interface_exists(q, interface))) {
8039 /*
8040 * Before we do the PAUSE/UNPAUSE, log if this was a
8041 * PAUSEALL/UNPAUSEALL but only on the first found entry.
8042 */
8043 ++found;
8044 if (found == 1
8045 && ast_strlen_zero(queuename)) {
8046 /*
8047 * XXX In all other cases, we use the queue name,
8048 * but since this affects all queues, we cannot.
8049 */
8050 ast_queue_log("NONE", "NONE", mem->membername,
8051 (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", S_OR(reason, ""));
8052 }
8053
8054 set_queue_member_pause(q, mem, reason, paused);
8055 ao2_ref(mem, -1);
8056 }
8057
8058 if (!ast_strlen_zero(queuename)) {
8059 ao2_unlock(q);
8060 queue_t_unref(q, "Done with iterator");
8061 break;
8062 }
8063 }
8064
8065 ao2_unlock(q);
8066 queue_t_unref(q, "Done with iterator");
8067 }
8068 ao2_iterator_destroy(&queue_iter);
8069
8070 return found ? RESULT_SUCCESS : RESULT_FAILURE;
8071}
8072
8073/*!
8074 * \internal
8075 * \brief helper function for set_member_penalty - given a queue, sets all member penalties with the interface
8076 * \param[in] q queue which is having its member's penalty changed - must be unlocked prior to calling
8077 * \param[in] interface String of interface used to search for queue members being changed
8078 * \param[in] penalty Value penalty is being changed to for the member.
8079 * \retval 0 if the there is no member with interface belonging to q and no change is made
8080 * \retval 1 if the there is a member with interface belonging to q and changes are made
8081 */
8082static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
8083{
8084 struct member *mem;
8085 int foundinterface = 0;
8086
8087 ao2_lock(q);
8088 if ((mem = interface_exists(q, interface))) {
8089 foundinterface++;
8090 if (mem->realtime) {
8091 char rtpenalty[80];
8092
8093 sprintf(rtpenalty, "%i", penalty);
8094 update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
8095 }
8096
8097 mem->penalty = penalty;
8098
8099 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
8100 queue_publish_member_blob(queue_member_penalty_type(), queue_member_blob_create(q, mem));
8101 ao2_ref(mem, -1);
8102 }
8103 ao2_unlock(q);
8104
8105 return foundinterface;
8106}
8107
8108/*!
8109 * \internal
8110 * \brief Set the ringinuse value of the specific queue member.
8111 *
8112 * \param q Which queue the member belongs.
8113 * \param mem Queue member being set.
8114 * \param ringinuse Set to 1 if the member is called when inuse.
8115 *
8116 * \pre The q is locked on entry.
8117 */
8118static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
8119{
8120 if (mem->realtime) {
8122 ringinuse ? "1" : "0");
8123 }
8124
8125 mem->ringinuse = ringinuse;
8126
8127 ast_queue_log(q->name, "NONE", mem->interface, "RINGINUSE", "%d", ringinuse);
8128 queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
8129}
8130
8132{
8133 struct member *mem;
8134 int foundinterface = 0;
8135
8136 ao2_lock(q);
8137 if ((mem = interface_exists(q, interface))) {
8138 foundinterface++;
8140 ao2_ref(mem, -1);
8141 }
8142 ao2_unlock(q);
8143
8144 return foundinterface;
8145}
8146
8147static int set_member_value_help_members(struct call_queue *q, const char *interface, int property, int value)
8148{
8149 switch(property) {
8150 case MEMBER_PENALTY:
8152
8153 case MEMBER_RINGINUSE:
8155
8156 default:
8157 ast_log(LOG_ERROR, "Attempted to set invalid property\n");
8158 return 0;
8159 }
8160}
8161
8162/*!
8163 * \internal
8164 * \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues.
8165 * \param[in] queuename If specified, only act on a member if it belongs to this queue
8166 * \param[in] interface Interface of queue member(s) having priority set.
8167 * \param[in] property Which queue property is being set
8168 * \param[in] value Value penalty is being changed to for each member
8169 */
8170static int set_member_value(const char *queuename, const char *interface, int property, int value)
8171{
8172 int foundinterface = 0, foundqueue = 0;
8173 struct call_queue *q;
8174 struct ast_config *queue_config = NULL;
8175 struct ao2_iterator queue_iter;
8176
8177 /* property dependent restrictions on values should be checked in this switch */
8178 switch (property) {
8179 case MEMBER_PENALTY:
8180 if (value < 0 && !negative_penalty_invalid) {
8181 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", value);
8182 return RESULT_FAILURE;
8183 }
8184 }
8185
8186 if (ast_strlen_zero(queuename)) { /* This means we need to iterate through all the queues. */
8187 if (ast_check_realtime("queues")) {
8188 queue_config = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
8189 if (queue_config) {
8190 char *category = NULL;
8191 while ((category = ast_category_browse(queue_config, category))) {
8192 const char *name = ast_variable_retrieve(queue_config, category, "name");
8193 if (ast_strlen_zero(name)) {
8194 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
8195 continue;
8196 }
8197 if ((q = find_load_queue_rt_friendly(name))) {
8198 foundqueue++;
8199 foundinterface += set_member_value_help_members(q, interface, property, value);
8200 queue_unref(q);
8201 }
8202 }
8203
8204 ast_config_destroy(queue_config);
8205 }
8206 }
8207
8208 /* After hitting realtime queues, go back and get the regular ones. */
8209 queue_iter = ao2_iterator_init(queues, 0);
8210 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
8211 foundqueue++;
8212 foundinterface += set_member_value_help_members(q, interface, property, value);
8213 queue_unref(q);
8214 }
8215 ao2_iterator_destroy(&queue_iter);
8216 } else { /* We actually have a queuename, so we can just act on the single queue. */
8217 if ((q = find_load_queue_rt_friendly(queuename))) {
8218 foundqueue++;
8219 foundinterface += set_member_value_help_members(q, interface, property, value);
8220 queue_unref(q);
8221 }
8222 }
8223
8224 if (foundinterface) {
8225 return RESULT_SUCCESS;
8226 } else if (!foundqueue) {
8227 ast_log (LOG_ERROR, "Invalid queuename\n");
8228 } else {
8229 ast_log (LOG_ERROR, "Invalid interface\n");
8230 }
8231
8232 return RESULT_FAILURE;
8233}
8234
8235/*!
8236 * \brief Gets members penalty.
8237 * \return Return the members penalty or RESULT_FAILURE on error.
8238 */
8239static int get_member_penalty(char *queuename, char *interface)
8240{
8241 int foundqueue = 0, penalty;
8242 struct call_queue *q;
8243 struct member *mem;
8244
8245 if ((q = find_load_queue_rt_friendly(queuename))) {
8246 foundqueue = 1;
8247 ao2_lock(q);
8248 if ((mem = interface_exists(q, interface))) {
8249 penalty = mem->penalty;
8250 ao2_ref(mem, -1);
8251 ao2_unlock(q);
8252 queue_t_unref(q, "Search complete");
8253 return penalty;
8254 }
8255 ao2_unlock(q);
8256 queue_t_unref(q, "Search complete");
8257 }
8258
8259 /* some useful debugging */
8260 if (foundqueue) {
8261 ast_log (LOG_ERROR, "Invalid queuename\n");
8262 } else {
8263 ast_log (LOG_ERROR, "Invalid interface\n");
8264 }
8265
8266 return RESULT_FAILURE;
8267}
8268
8269/*! \brief Reload dynamic queue members persisted into the astdb */
8270static void reload_queue_members(void)
8271{
8272 char *cur_ptr;
8273 const char *queue_name;
8274 char *member;
8275 char *interface;
8276 char *membername = NULL;
8277 char *state_interface;
8278 char *penalty_tok;
8279 int penalty = 0;
8280 char *paused_tok;
8281 int paused = 0;
8282 char *wrapuptime_tok;
8283 int wrapuptime = 0;
8284 char *reason_paused;
8285 struct ast_db_entry *db_tree;
8286 struct ast_db_entry *entry;
8287 struct call_queue *cur_queue;
8288 char *queue_data;
8289
8290 /* Each key in 'pm_family' is the name of a queue */
8291 db_tree = ast_db_gettree(pm_family, NULL);
8292 for (entry = db_tree; entry; entry = entry->next) {
8293
8294 queue_name = entry->key + strlen(pm_family) + 2;
8295
8296 {
8297 struct call_queue tmpq = {
8298 .name = queue_name,
8299 };
8300 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
8301 }
8302
8303 if (!cur_queue) {
8304 cur_queue = find_load_queue_rt_friendly(queue_name);
8305 }
8306
8307 if (!cur_queue) {
8308 /* If the queue no longer exists, remove it from the
8309 * database */
8310 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
8311 ast_db_del(pm_family, queue_name);
8312 continue;
8313 }
8314
8315 if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
8316 queue_t_unref(cur_queue, "Expire reload reference");
8317 continue;
8318 }
8319
8320 cur_ptr = queue_data;
8321 while ((member = strsep(&cur_ptr, ",|"))) {
8322 if (ast_strlen_zero(member)) {
8323 continue;
8324 }
8325
8326 interface = strsep(&member, ";");
8327 penalty_tok = strsep(&member, ";");
8328 paused_tok = strsep(&member, ";");
8329 membername = strsep(&member, ";");
8330 state_interface = strsep(&member, ";");
8331 reason_paused = strsep(&member, ";");
8332 wrapuptime_tok = strsep(&member, ";");
8333
8334 if (!penalty_tok) {
8335 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
8336 break;
8337 }
8338 penalty = strtol(penalty_tok, NULL, 10);
8339 if (errno == ERANGE) {
8340 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
8341 break;
8342 }
8343
8344 if (!paused_tok) {
8345 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
8346 break;
8347 }
8348 paused = strtol(paused_tok, NULL, 10);
8349 if ((errno == ERANGE) || paused < 0 || paused > 1) {
8350 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
8351 break;
8352 }
8353
8354 if (!ast_strlen_zero(wrapuptime_tok)) {
8355 wrapuptime = strtol(wrapuptime_tok, NULL, 10);
8356 if (errno == ERANGE) {
8357 ast_log(LOG_WARNING, "Error converting wrapuptime: %s: Out of range.\n", wrapuptime_tok);
8358 break;
8359 }
8360 }
8361
8362 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d ReasonPause: %s Wrapuptime: %d\n",
8363 queue_name, interface, membername, penalty, paused, reason_paused, wrapuptime);
8364
8365 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface, reason_paused, wrapuptime) == RES_OUTOFMEMORY) {
8366 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
8367 break;
8368 }
8369 }
8370 queue_t_unref(cur_queue, "Expire reload reference");
8371 ast_free(queue_data);
8372 }
8373
8374 if (db_tree) {
8375 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
8376 ast_db_freetree(db_tree);
8377 }
8378}
8379
8380/*! \brief PauseQueueMember application */
8381static int pqm_exec(struct ast_channel *chan, const char *data)
8382{
8383 char *parse;
8385 AST_APP_ARG(queuename);
8386 AST_APP_ARG(interface);
8388 AST_APP_ARG(reason);
8389 );
8390
8391 if (ast_strlen_zero(data)) {
8392 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
8393 return -1;
8394 }
8395
8396 parse = ast_strdupa(data);
8397
8399
8400 if (ast_strlen_zero(args.interface)) {
8401 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
8402 return -1;
8403 }
8404
8405 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
8406 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
8407 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
8408 return 0;
8409 }
8410
8411 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
8412
8413 return 0;
8414}
8415
8416/*! \brief UnpauseQueueMember application */
8417static int upqm_exec(struct ast_channel *chan, const char *data)
8418{
8419 char *parse;
8421 AST_APP_ARG(queuename);
8422 AST_APP_ARG(interface);
8424 AST_APP_ARG(reason);
8425 );
8426
8427 if (ast_strlen_zero(data)) {
8428 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
8429 return -1;
8430 }
8431
8432 parse = ast_strdupa(data);
8433
8435
8436 if (ast_strlen_zero(args.interface)) {
8437 ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
8438 return -1;
8439 }
8440
8441 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
8442 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
8443 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
8444 return 0;
8445 }
8446
8447 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
8448
8449 return 0;
8450}
8451
8452/*! \brief RemoveQueueMember application */
8453static int rqm_exec(struct ast_channel *chan, const char *data)
8454{
8455 int res=-1;
8456 char *parse, *temppos = NULL;
8457 struct member *mem = NULL;
8458
8460 AST_APP_ARG(queuename);
8462 );
8463
8464
8465 if (ast_strlen_zero(data)) {
8466 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
8467 return -1;
8468 }
8469
8470 parse = ast_strdupa(data);
8471
8473
8474 if (ast_strlen_zero(args.interface)) {
8475 args.interface = ast_strdupa(ast_channel_name(chan));
8476 temppos = strrchr(args.interface, '-');
8477 if (temppos) {
8478 *temppos = '\0';
8479 }
8480 }
8481
8482 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
8483
8485 mem = find_member_by_queuename_and_interface(args.queuename, args.interface);
8486 }
8487
8488 switch (remove_from_queue(args.queuename, args.interface)) {
8489 case RES_OKAY:
8490 if (!mem || ast_strlen_zero(mem->membername)) {
8491 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "REMOVEMEMBER", "%s", "");
8492 } else {
8493 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), mem->membername, "REMOVEMEMBER", "%s", "");
8494 }
8495 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
8496 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
8497 res = 0;
8498 break;
8499 case RES_EXISTS:
8500 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
8501 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
8502 res = 0;
8503 break;
8504 case RES_NOSUCHQUEUE:
8505 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
8506 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
8507 res = 0;
8508 break;
8509 case RES_NOT_DYNAMIC:
8510 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
8511 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
8512 res = 0;
8513 break;
8514 }
8515
8516 if (mem) {
8517 ao2_ref(mem, -1);
8518 }
8519
8520 return res;
8521}
8522
8523/*! \brief AddQueueMember application */
8524static int aqm_exec(struct ast_channel *chan, const char *data)
8525{
8526 int res=-1;
8527 char *parse, *tmp, *temppos = NULL, *reason = NULL;
8529 AST_APP_ARG(queuename);
8536 );
8537 int penalty = 0;
8538 int paused = 0;
8539 int wrapuptime;
8540 struct ast_flags flags = { 0 };
8541
8542 if (ast_strlen_zero(data)) {
8543 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface][,wrapuptime]]]]])\n");
8544 return -1;
8545 }
8546
8547 parse = ast_strdupa(data);
8548
8550
8551 if (args.options) {
8552 char *opts[AQM_OPT_ARG_ARRAY_SIZE] = { NULL, };
8553 ast_app_parse_options(aqm_opts, &flags, opts, args.options);
8555 paused = 1;
8557 reason = ast_strdupa(opts[AQM_OPT_ARG_PAUSE_REASON]);
8558 }
8559 }
8560 }
8561
8562 if (ast_strlen_zero(args.interface)) {
8563 args.interface = ast_strdupa(ast_channel_name(chan));
8564 temppos = strrchr(args.interface, '-');
8565 if (temppos) {
8566 *temppos = '\0';
8567 }
8568 }
8569
8570 if (!ast_strlen_zero(args.penalty)) {
8571 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
8572 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
8573 penalty = 0;
8574 }
8575 }
8576
8577 if (!ast_strlen_zero(args.wrapuptime)) {
8578 tmp = args.wrapuptime;
8579 ast_strip(tmp);
8580 wrapuptime = atoi(tmp);
8581 if (wrapuptime < 0) {
8582 wrapuptime = 0;
8583 }
8584 } else {
8585 wrapuptime = 0;
8586 }
8587
8588 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, paused, queue_persistent_members, args.state_interface, reason, wrapuptime)) {
8589 case RES_OKAY:
8590 if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
8591 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
8592 } else {
8593 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
8594 }
8595 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
8596 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
8597 res = 0;
8598 break;
8599 case RES_EXISTS:
8600 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
8601 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
8602 res = 0;
8603 break;
8604 case RES_NOSUCHQUEUE:
8605 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
8606 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
8607 res = 0;
8608 break;
8609 case RES_OUTOFMEMORY:
8610 ast_log(LOG_ERROR, "Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
8611 break;
8612 }
8613
8614 return res;
8615}
8616
8617/*! \brief QueueLog application */
8618static int ql_exec(struct ast_channel *chan, const char *data)
8619{
8620 char *parse;
8621
8623 AST_APP_ARG(queuename);
8624 AST_APP_ARG(uniqueid);
8625 AST_APP_ARG(membername);
8627 AST_APP_ARG(params);
8628 );
8629
8630 if (ast_strlen_zero(data)) {
8631 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
8632 return -1;
8633 }
8634
8635 parse = ast_strdupa(data);
8636
8638
8639 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
8640 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
8641 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
8642 return -1;
8643 }
8644
8645 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
8646 "%s", args.params ? args.params : "");
8647
8648 return 0;
8649}
8650
8651/*! \brief Copy rule from global list into specified queue */
8652static void copy_rules(struct queue_ent *qe, const char *rulename)
8653{
8654 struct penalty_rule *pr_iter;
8655 struct rule_list *rl_iter;
8656 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
8658 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
8659 if (!strcasecmp(rl_iter->name, tmp)) {
8660 break;
8661 }
8662 }
8663 if (rl_iter) {
8664 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
8665 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
8666 if (!new_pr) {
8667 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
8668 break;
8669 }
8670 new_pr->time = pr_iter->time;
8671 new_pr->max_value = pr_iter->max_value;
8672 new_pr->min_value = pr_iter->min_value;
8673 new_pr->raise_value = pr_iter->raise_value;
8674 new_pr->max_relative = pr_iter->max_relative;
8675 new_pr->min_relative = pr_iter->min_relative;
8676 new_pr->raise_relative = pr_iter->raise_relative;
8677 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
8678 }
8679 }
8681}
8682
8683/*!\brief The starting point for all queue calls
8684 *
8685 * The process involved here is to
8686 * 1. Parse the options specified in the call to Queue()
8687 * 2. Join the queue
8688 * 3. Wait in a loop until it is our turn to try calling a queue member
8689 * 4. Attempt to call a queue member
8690 * 5. If 4. did not result in a bridged call, then check for between
8691 * call options such as periodic announcements etc.
8692 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
8693 * exit the queue.
8694 */
8695static int queue_exec(struct ast_channel *chan, const char *data)
8696{
8697 int res=-1;
8698 int ringing=0;
8699 const char *user_priority;
8700 const char *max_penalty_str;
8701 const char *min_penalty_str;
8702 const char *raise_penalty_str;
8703 int prio;
8704 int qcontinue = 0;
8705 int max_penalty, min_penalty, raise_penalty;
8706 enum queue_result reason = QUEUE_UNKNOWN;
8707 /* whether to exit Queue application after the timeout hits */
8708 int tries = 0;
8709 int noption = 0;
8710 char *parse;
8711 int makeannouncement = 0;
8712 int position = 0;
8714 AST_APP_ARG(queuename);
8717 AST_APP_ARG(announceoverride);
8718 AST_APP_ARG(queuetimeoutstr);
8719 AST_APP_ARG(agi);
8720 AST_APP_ARG(gosub);
8722 AST_APP_ARG(position);
8723 );
8724 /* Our queue entry */
8725 struct queue_ent qe = { 0 };
8726 struct ast_flags opts = { 0, };
8727 char *opt_args[OPT_ARG_ARRAY_SIZE];
8728 int max_forwards;
8729 int cid_allow;
8730
8731 if (ast_strlen_zero(data)) {
8732 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,gosub[,rule[,position]]]]]]]]\n");
8733 return -1;
8734 }
8735
8736 ast_channel_lock(chan);
8738 ast_channel_unlock(chan);
8739
8740 if (max_forwards <= 0) {
8741 ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
8742 return -1;
8743 }
8744
8745 parse = ast_strdupa(data);
8747
8748 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, gosub: %s, rule: %s, position: %s\n",
8749 args.queuename,
8750 S_OR(args.options, ""),
8751 S_OR(args.url, ""),
8752 S_OR(args.announceoverride, ""),
8753 S_OR(args.queuetimeoutstr, ""),
8754 S_OR(args.agi, ""),
8755 S_OR(args.gosub, ""),
8756 S_OR(args.rule, ""),
8757 S_OR(args.position, ""));
8758
8759 if (!ast_strlen_zero(args.options)) {
8760 ast_app_parse_options(queue_exec_options, &opts, opt_args, args.options);
8761 }
8762
8763 /* Setup our queue entry */
8764 qe.start = time(NULL);
8765
8766 pbx_builtin_setvar_helper(chan, "ABANDONED", NULL);
8767
8768 /* set the expire time based on the supplied timeout; */
8769 if (!ast_strlen_zero(args.queuetimeoutstr)) {
8770 qe.expire = qe.start + atoi(args.queuetimeoutstr);
8771 } else {
8772 qe.expire = 0;
8773 }
8774
8775 /* Get the priority from the variable ${QUEUE_PRIO} */
8776 ast_channel_lock(chan);
8777 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
8778 if (user_priority) {
8779 if (sscanf(user_priority, "%30d", &prio) == 1) {
8780 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
8781 } else {
8782 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
8783 user_priority, ast_channel_name(chan));
8784 prio = 0;
8785 }
8786 } else {
8787 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
8788 prio = 0;
8789 }
8790
8791 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
8792
8793 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
8794 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
8795 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
8796 } else {
8797 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
8798 max_penalty_str, ast_channel_name(chan));
8799 max_penalty = INT_MAX;
8800 }
8801 } else {
8802 max_penalty = INT_MAX;
8803 }
8804
8805 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
8806 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
8807 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
8808 } else {
8809 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
8810 min_penalty_str, ast_channel_name(chan));
8811 min_penalty = INT_MAX;
8812 }
8813 } else {
8814 min_penalty = INT_MAX;
8815 }
8816
8817 if ((raise_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_RAISE_PENALTY"))) {
8818 if (sscanf(raise_penalty_str, "%30d", &raise_penalty) == 1) {
8819 ast_debug(1, "%s: Got raise penalty %d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), raise_penalty);
8820 } else {
8821 ast_log(LOG_WARNING, "${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n",
8822 raise_penalty_str, ast_channel_name(chan));
8823 raise_penalty = INT_MAX;
8824 }
8825 } else {
8826 raise_penalty = INT_MAX;
8827 }
8828 ast_channel_unlock(chan);
8829
8830 if (ast_test_flag(&opts, OPT_RINGING)) {
8831 ringing = 1;
8832 }
8833
8834 if (ringing != 1 && ast_test_flag(&opts, OPT_RING_WHEN_RINGING)) {
8835 qe.ring_when_ringing = 1;
8836 }
8837
8838 if (ast_test_flag(&opts, OPT_GO_ON)) {
8839 qcontinue = 1;
8840 }
8841
8842 if (args.position) {
8843 position = atoi(args.position);
8844 if (position < 0) {
8845 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
8846 position = 0;
8847 }
8848 }
8849
8850 ast_debug(1, "queue: %s, expires: %ld, priority: %d\n",
8851 args.queuename, (long)qe.expire, prio);
8852
8853 qe.chan = chan;
8854 qe.prio = prio;
8855 qe.max_penalty = max_penalty;
8856 qe.min_penalty = min_penalty;
8857 qe.raise_penalty = raise_penalty;
8858 qe.last_pos_said = 0;
8859 qe.last_pos = 0;
8862 qe.valid_digits = 0;
8863 if (join_queue(args.queuename, &qe, &reason, position)) {
8864 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
8865 set_queue_result(chan, reason);
8866 return 0;
8867 }
8868 ast_assert(qe.parent != NULL);
8869
8870 if (qe.parent->periodicannouncestartdelay >= 0) {
8873 }
8874
8876
8877 if (log_caller_id_name) {
8878 char *escaped_cidname = NULL;
8879 /* Ensure caller ID name is valid and not NULL before processing */
8880 if (cid_allow && ast_channel_caller(chan)->id.name.valid && ast_channel_caller(chan)->id.name.str) {
8881 escaped_cidname = ast_strdupa(ast_channel_caller(chan)->id.name.str);
8882 /* Only iterate if '|' is found */
8883 if (strchr(escaped_cidname, '|')) {
8884 for (char *p = escaped_cidname; *p; p++) {
8885 if (*p == '|') {
8886 *p = '_';
8887 }
8888 }
8889 }
8890 }
8891
8892 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d|%s",
8893 S_OR(args.url, ""),
8894 S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8895 qe.opos,
8896 S_OR(escaped_cidname, ""));
8897 } else {
8898 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
8899 S_OR(args.url, ""),
8900 S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8901 qe.opos);
8902 }
8903
8904 /* PREDIAL: Preprocess any callee gosub arguments. */
8906 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
8909 }
8910
8911 /* PREDIAL: Run gosub on the caller's channel */
8913 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
8915 ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
8916 }
8917
8918 /* Music on hold class override */
8921 ast_copy_string(qe.moh, opt_args[OPT_ARG_MUSICONHOLD_CLASS], sizeof(qe.moh));
8922 }
8923
8924 copy_rules(&qe, args.rule);
8925 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
8926check_turns:
8927 if (ringing) {
8929 } else {
8930 ast_moh_start(chan, qe.moh, NULL);
8931 }
8932
8933 /* This is the wait loop for callers 2 through maxlen */
8934 res = wait_our_turn(&qe, ringing, &reason);
8935 if (res) {
8936 goto stop;
8937 }
8938
8939 makeannouncement = qe.parent->announce_to_first_user;
8940
8941 for (;;) {
8942 /* This is the wait loop for the head caller*/
8943 /* To exit, they may get their call answered; */
8944 /* they may dial a digit from the queue context; */
8945 /* or, they may timeout. */
8946
8947 /* A request to withdraw this call from the queue arrived */
8948 if (qe.withdraw) {
8949 reason = QUEUE_WITHDRAW;
8950 res = 1;
8951 break;
8952 }
8953
8954 /* Leave if we have exceeded our queuetimeout */
8955 if (qe.expire && (time(NULL) >= qe.expire)) {
8956 record_abandoned(&qe);
8957 reason = QUEUE_TIMEOUT;
8958 res = 0;
8959 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
8960 qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8961 break;
8962 }
8963
8964 if (makeannouncement) {
8965 /* Make a position announcement, if enabled */
8966 if (qe.parent->announcefrequency) {
8967 if ((res = say_position(&qe, ringing))) {
8968 goto stop;
8969 }
8970 }
8971 }
8972 makeannouncement = 1;
8973
8974 /* Make a periodic announcement, if enabled */
8976 if ((res = say_periodic_announcement(&qe, ringing))) {
8977 goto stop;
8978 }
8979 }
8980
8981 /* A request to withdraw this call from the queue arrived */
8982 if (qe.withdraw) {
8983 reason = QUEUE_WITHDRAW;
8984 res = 1;
8985 break;
8986 }
8987
8988 /* Leave if we have exceeded our queuetimeout */
8989 if (qe.expire && (time(NULL) >= qe.expire)) {
8990 record_abandoned(&qe);
8991 reason = QUEUE_TIMEOUT;
8992 res = 0;
8993 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
8994 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8995 break;
8996 }
8997
8998 /* see if we need to move to the next penalty level for this queue */
8999 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
9000 update_qe_rule(&qe);
9001 }
9002
9003 /* Try calling all queue members for 'timeout' seconds */
9004 res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.gosub, ringing);
9005 if (res) {
9006 goto stop;
9007 }
9008
9009 if (qe.parent->leavewhenempty) {
9010 int status = 0;
9012 record_abandoned(&qe);
9013 reason = QUEUE_LEAVEEMPTY;
9014 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
9015 res = 0;
9016 break;
9017 }
9018 }
9019
9020 /* exit after 'timeout' cycle if 'n' option enabled */
9021 if (noption && tries >= ao2_container_count(qe.parent->members)) {
9022 ast_verb(3, "Exiting on time-out cycle\n");
9023 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
9024 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9025 record_abandoned(&qe);
9026 reason = QUEUE_TIMEOUT;
9027 res = 0;
9028 break;
9029 }
9030
9031
9032 /* Leave if we have exceeded our queuetimeout */
9033 if (qe.expire && (time(NULL) >= qe.expire)) {
9034 record_abandoned(&qe);
9035 reason = QUEUE_TIMEOUT;
9036 res = 0;
9037 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));
9038 break;
9039 }
9040
9041 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
9042 res = wait_a_bit(&qe);
9043 if (res) {
9044 goto stop;
9045 }
9046
9047 /* If using dynamic realtime members, we should regenerate the member list for this queue */
9049
9050 /* Since this is a priority queue and
9051 * it is not sure that we are still at the head
9052 * of the queue, go and check for our turn again.
9053 */
9054 if (!is_our_turn(&qe)) {
9055 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.chan));
9056 goto check_turns;
9057 }
9058 }
9059
9060stop:
9061 if (res) {
9062 if (reason == QUEUE_WITHDRAW) {
9063 record_abandoned(&qe);
9064 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 : "");
9065 if (qe.withdraw_info) {
9066 pbx_builtin_setvar_helper(qe.chan, "QUEUE_WITHDRAW_INFO", qe.withdraw_info);
9067 }
9068 res = 0;
9069 } else if (res < 0) {
9070 if (!qe.handled) {
9071 record_abandoned(&qe);
9072 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
9073 "%d|%d|%ld", qe.pos, qe.opos,
9074 (long) (time(NULL) - qe.start));
9075 res = -1;
9076 } else if (reason == QUEUE_LEAVEEMPTY) {
9077 /* Return back to dialplan, don't hang up */
9078 res = 0;
9079 } else if (qcontinue) {
9080 reason = QUEUE_CONTINUE;
9081 res = 0;
9082 }
9083 } else if (qe.valid_digits) {
9084 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
9085 "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9086 }
9087 }
9088
9089 /* Free the optional withdraw info if present */
9090 /* This is done here to catch all cases. e.g. if the call eventually wasn't withdrawn, e.g. answered */
9091 if (qe.withdraw_info) {
9093 qe.withdraw_info = NULL;
9094 }
9095
9096 /* Don't allow return code > 0 */
9097 if (res >= 0) {
9098 res = 0;
9099 if (ringing) {
9100 ast_indicate(chan, -1);
9101 } else {
9102 ast_moh_stop(chan);
9103 }
9104 ast_stopstream(chan);
9105 }
9106
9108
9109 leave_queue(&qe);
9110 if (reason != QUEUE_UNKNOWN)
9111 set_queue_result(chan, reason);
9112
9113 /*
9114 * every queue_ent is given a reference to it's parent
9115 * call_queue when it joins the queue. This ref must be taken
9116 * away right before the queue_ent is destroyed. In this case
9117 * the queue_ent is about to be returned on the stack
9118 */
9119 qe.parent = queue_unref(qe.parent);
9120
9121 return res;
9122}
9123
9124/*!
9125 * \brief create interface var with all queue details.
9126 * \retval 0 on success
9127 * \retval -1 on error
9128*/
9129static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9130{
9131 int res = -1;
9132 struct call_queue *q;
9133 char interfacevar[256] = "";
9134 float sl = 0;
9135
9136 if (ast_strlen_zero(data)) {
9137 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9138 return -1;
9139 }
9140
9141 if ((q = find_load_queue_rt_friendly(data))) {
9142 ao2_lock(q);
9143 if (q->setqueuevar) {
9144 sl = 0;
9145 res = 0;
9146
9147 if (q->callscompleted > 0) {
9148 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
9149 }
9150
9151 snprintf(interfacevar, sizeof(interfacevar),
9152 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
9154
9155 pbx_builtin_setvar_multiple(chan, interfacevar);
9156 }
9157
9158 ao2_unlock(q);
9159 queue_t_unref(q, "Done with QUEUE() function");
9160 } else {
9161 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9162 }
9163
9164 snprintf(buf, len, "%d", res);
9165
9166 return 0;
9167}
9168
9169/*!
9170 * \brief Check if a given queue exists
9171 *
9172 */
9173static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9174{
9175 struct call_queue *q;
9176
9177 buf[0] = '\0';
9178
9179 if (ast_strlen_zero(data)) {
9180 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9181 return -1;
9182 }
9184 snprintf(buf, len, "%d", q != NULL? 1 : 0);
9185 if (q) {
9186 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
9187 }
9188
9189 return 0;
9190}
9191
9192static struct member *get_interface_helper(struct call_queue *q, const char *interface)
9193{
9194 struct member *m;
9195
9197 ast_log(LOG_ERROR, "QUEUE_MEMBER: Missing required interface argument.\n");
9198 return NULL;
9199 }
9200
9202 if (!m) {
9203 ast_log(LOG_ERROR, "Queue member interface '%s' not in queue '%s'.\n",
9204 interface, q->name);
9205 }
9206 return m;
9207}
9208
9209/*!
9210 * \brief Get number either busy / free / ready or total members of a specific queue
9211 * \brief Get or set member properties penalty / paused / ringinuse
9212 * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ringinuse)
9213 * \retval -1 on error
9214 */
9215static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9216{
9217 int count = 0;
9218 struct member *m;
9219 struct ao2_iterator mem_iter;
9220 struct call_queue *q;
9221
9223 AST_APP_ARG(queuename);
9224 AST_APP_ARG(option);
9225 AST_APP_ARG(interface);
9226 );
9227 /* Make sure the returned value on error is zero length string. */
9228 buf[0] = '\0';
9229
9230 if (ast_strlen_zero(data)) {
9232 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9233 cmd);
9234 return -1;
9235 }
9236
9238
9239 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
9241 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9242 cmd);
9243 return -1;
9244 }
9245
9246 if ((q = find_load_queue_rt_friendly(args.queuename))) {
9247 ao2_lock(q);
9248 if (!strcasecmp(args.option, "logged")) {
9249 mem_iter = ao2_iterator_init(q->members, 0);
9250 while ((m = ao2_iterator_next(&mem_iter))) {
9251 /* Count the agents who are logged in and presently answering calls */
9252 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9253 count++;
9254 }
9255 ao2_ref(m, -1);
9256 }
9257 ao2_iterator_destroy(&mem_iter);
9258 } else if (!strcasecmp(args.option, "free")) {
9259 mem_iter = ao2_iterator_init(q->members, 0);
9260 while ((m = ao2_iterator_next(&mem_iter))) {
9261 /* Count the agents who are logged in and presently answering calls */
9262 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
9263 count++;
9264 }
9265 ao2_ref(m, -1);
9266 }
9267 ao2_iterator_destroy(&mem_iter);
9268 } else if (!strcasecmp(args.option, "ready")) {
9269 time_t now;
9270 time(&now);
9271 mem_iter = ao2_iterator_init(q->members, 0);
9272 while ((m = ao2_iterator_next(&mem_iter))) {
9273 /* Count the agents who are logged in, not paused and not wrapping up */
9274 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
9275 !(m->lastcall && get_wrapuptime(q, m) && ((now - get_wrapuptime(q, m)) < m->lastcall))) {
9276 count++;
9277 }
9278 ao2_ref(m, -1);
9279 }
9280 ao2_iterator_destroy(&mem_iter);
9281 } else if (!strcasecmp(args.option, "count")) {
9283 } else if (!strcasecmp(args.option, "penalty")) {
9284 m = get_interface_helper(q, args.interface);
9285 if (m) {
9286 count = m->penalty;
9287 ao2_ref(m, -1);
9288 }
9289 } else if (!strcasecmp(args.option, "paused")) {
9290 m = get_interface_helper(q, args.interface);
9291 if (m) {
9292 count = m->paused;
9293 ao2_ref(m, -1);
9294 }
9295 } else if ((!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9296 || !strcasecmp(args.option, "ringinuse"))) {
9297 m = get_interface_helper(q, args.interface);
9298 if (m) {
9299 count = m->ringinuse;
9300 ao2_ref(m, -1);
9301 }
9302 } else {
9303 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9304 }
9305 ao2_unlock(q);
9306 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
9307 } else {
9308 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9309 }
9310
9311 snprintf(buf, len, "%d", count);
9312
9313 return 0;
9314}
9315
9316/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse. */
9317static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9318{
9319 int memvalue;
9320
9322 AST_APP_ARG(queuename);
9323 AST_APP_ARG(option);
9324 AST_APP_ARG(interface);
9325 );
9326
9327 if (ast_strlen_zero(data)) {
9329 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9330 cmd);
9331 return -1;
9332 }
9333
9335
9336 if (ast_strlen_zero(args.option)
9337 || ast_strlen_zero(args.interface)) {
9339 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9340 cmd);
9341 return -1;
9342 }
9343
9344 /*
9345 * If queuename is empty then the option will be
9346 * set for the interface in all queues.
9347 */
9348
9349 memvalue = atoi(value);
9350 if (!strcasecmp(args.option, "penalty")) {
9351 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
9352 ast_log(LOG_ERROR, "Invalid interface, queue, or penalty\n");
9353 return -1;
9354 }
9355 } else if (!strcasecmp(args.option, "paused")) {
9356 memvalue = (memvalue <= 0) ? 0 : 1;
9357 if (set_member_paused(args.queuename, args.interface, NULL, memvalue)) {
9358 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9359 return -1;
9360 }
9361 } else if (!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9362 || !strcasecmp(args.option, "ringinuse")) {
9363 memvalue = (memvalue <= 0) ? 0 : 1;
9364 if (set_member_value(args.queuename, args.interface, MEMBER_RINGINUSE, memvalue)) {
9365 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9366 return -1;
9367 }
9368 } else {
9369 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9370 return -1;
9371 }
9372 return 0;
9373}
9374
9375/*!
9376 * \brief Get the total number of members in a specific queue (Deprecated)
9377 * \retval number of members
9378 * \retval -1 on error
9379*/
9380static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9381{
9382 int count = 0;
9383 struct member *m;
9384 struct call_queue *q;
9385 struct ao2_iterator mem_iter;
9386 static int depflag = 1;
9387
9388 if (depflag) {
9389 depflag = 0;
9390 ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
9391 }
9392
9393 if (ast_strlen_zero(data)) {
9394 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9395 return -1;
9396 }
9397
9398 if ((q = find_load_queue_rt_friendly(data))) {
9399 ao2_lock(q);
9400 mem_iter = ao2_iterator_init(q->members, 0);
9401 while ((m = ao2_iterator_next(&mem_iter))) {
9402 /* Count the agents who are logged in and presently answering calls */
9403 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9404 count++;
9405 }
9406 ao2_ref(m, -1);
9407 }
9408 ao2_iterator_destroy(&mem_iter);
9409 ao2_unlock(q);
9410 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
9411 } else {
9412 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9413 }
9414
9415 snprintf(buf, len, "%d", count);
9416
9417 return 0;
9418}
9419
9420/*! \brief Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue */
9421static int queue_function_queuegetchannel(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9422{
9423 int position;
9424 char *parse;
9425 struct call_queue *q;
9426 struct ast_variable *var;
9427
9429 AST_APP_ARG(queuename);
9430 AST_APP_ARG(position);
9431 );
9432
9433 buf[0] = '\0';
9434
9435 if (ast_strlen_zero(data)) {
9436 ast_log(LOG_ERROR, "Missing argument. QUEUE_GET_CHANNEL(<queuename>,<position>)\n");
9437 return -1;
9438 }
9439
9440 parse = ast_strdupa(data);
9442
9443 if (ast_strlen_zero(args.queuename)) {
9444 ast_log (LOG_ERROR, "The <queuename> parameter is required.\n");
9445 return -1;
9446 }
9447
9448 if (ast_strlen_zero(args.position)) {
9449 position = 1;
9450 } else {
9451 if (sscanf(args.position, "%30d", &position) != 1) {
9452 ast_log (LOG_ERROR, "<position> parameter must be an integer.\n");
9453 return -1;
9454 }
9455 if (position < 1) {
9456 ast_log (LOG_ERROR, "<position> parameter must be an integer greater than zero.\n");
9457 return -1;
9458 }
9459 }
9460
9461 {
9462 struct call_queue tmpq = {
9463 .name = args.queuename,
9464 };
9465
9466 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_GET_CHANNEL()");
9467 }
9468 if (q) {
9469 ao2_lock(q);
9470 if (q->count >= position) {
9471 struct queue_ent *qe;
9472
9473 for (qe = q->head; qe; qe = qe->next) {
9474 if (qe->pos == position) {
9476 break;
9477 }
9478 }
9479 }
9480 ao2_unlock(q);
9481 queue_t_unref(q, "Done with reference in QUEUE_GET_CHANNEL()");
9482 return 0;
9483 }
9484
9485 var = ast_load_realtime("queues", "name", args.queuename, SENTINEL);
9486 if (var) {
9487 /* if the queue is realtime but was not found in memory, this
9488 * means that the queue had been deleted from memory since it was
9489 * "dead."
9490 */
9492 return 0;
9493 }
9494
9495 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9496 return 0;
9497}
9498
9499/*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
9500static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9501{
9502 int count = 0;
9503 struct call_queue *q, tmpq = {
9504 .name = data,
9505 };
9506 struct ast_variable *var = NULL;
9507
9508 buf[0] = '\0';
9509
9510 if (ast_strlen_zero(data)) {
9511 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
9512 return -1;
9513 }
9514
9515 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
9516 ao2_lock(q);
9517 count = q->count;
9518 ao2_unlock(q);
9519 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
9520 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
9521 /* if the queue is realtime but was not found in memory, this
9522 * means that the queue had been deleted from memory since it was
9523 * "dead." This means it has a 0 waiting count
9524 */
9525 count = 0;
9527 } else {
9528 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9529 }
9530
9531 snprintf(buf, len, "%d", count);
9532
9533 return 0;
9534}
9535
9536/*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
9537static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9538{
9539 struct call_queue *q;
9540 struct member *m;
9541
9542 /* Ensure an otherwise empty list doesn't return garbage */
9543 buf[0] = '\0';
9544
9545 if (ast_strlen_zero(data)) {
9546 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
9547 return -1;
9548 }
9549
9550 if ((q = find_load_queue_rt_friendly(data))) {
9551 int buflen = 0, count = 0;
9552 struct ao2_iterator mem_iter;
9553
9554 ao2_lock(q);
9555 mem_iter = ao2_iterator_init(q->members, 0);
9556 while ((m = ao2_iterator_next(&mem_iter))) {
9557 /* strcat() is always faster than printf() */
9558 if (count++) {
9559 strncat(buf + buflen, ",", len - buflen - 1);
9560 buflen++;
9561 }
9562 strncat(buf + buflen, m->interface, len - buflen - 1);
9563 buflen += strlen(m->interface);
9564 /* Safeguard against overflow (negative length) */
9565 if (buflen >= len - 2) {
9566 ao2_ref(m, -1);
9567 ast_log(LOG_WARNING, "Truncating list\n");
9568 break;
9569 }
9570 ao2_ref(m, -1);
9571 }
9572 ao2_iterator_destroy(&mem_iter);
9573 ao2_unlock(q);
9574 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
9575 } else
9576 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9577
9578 /* We should already be terminated, but let's make sure. */
9579 buf[len - 1] = '\0';
9580
9581 return 0;
9582}
9583
9584/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
9585static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9586{
9587 int penalty;
9589 AST_APP_ARG(queuename);
9590 AST_APP_ARG(interface);
9591 );
9592 /* Make sure the returned value on error is NULL. */
9593 buf[0] = '\0';
9594
9595 if (ast_strlen_zero(data)) {
9596 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9597 return -1;
9598 }
9599
9601
9602 if (args.argc < 2) {
9603 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9604 return -1;
9605 }
9606
9607 penalty = get_member_penalty (args.queuename, args.interface);
9608
9609 if (penalty >= 0) { /* remember that buf is already '\0' */
9610 snprintf (buf, len, "%d", penalty);
9611 }
9612
9613 return 0;
9614}
9615
9616/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
9617static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9618{
9619 int penalty;
9621 AST_APP_ARG(queuename);
9622 AST_APP_ARG(interface);
9623 );
9624
9625 if (ast_strlen_zero(data)) {
9626 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9627 return -1;
9628 }
9629
9631
9632 if (args.argc < 2) {
9633 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9634 return -1;
9635 }
9636
9637 penalty = atoi(value);
9638
9639 if (ast_strlen_zero(args.interface)) {
9640 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
9641 return -1;
9642 }
9643
9644 /* if queuename = NULL then penalty will be set for interface in all the queues. */
9645 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, penalty)) {
9646 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
9647 return -1;
9648 }
9649
9650 return 0;
9651}
9652
9654 .name = "QUEUE_EXISTS",
9655 .read = queue_function_exists,
9656};
9657
9659 .name = "QUEUE_VARIABLES",
9660 .read = queue_function_var,
9661};
9662
9664 .name = "QUEUE_MEMBER",
9666 .write = queue_function_mem_write,
9667};
9668
9670 .name = "QUEUE_MEMBER_COUNT",
9671 .read = queue_function_qac_dep,
9672};
9673
9675 .name = "QUEUE_GET_CHANNEL",
9677};
9678
9680 .name = "QUEUE_WAITING_COUNT",
9682};
9683
9685 .name = "QUEUE_MEMBER_LIST",
9687};
9688
9690 .name = "QUEUE_MEMBER_PENALTY",
9693};
9694
9695/*! Reset the global queue rules parameters even if there is no "general" section of queuerules.conf */
9697{
9698 realtime_rules = 0;
9699}
9700
9701/*! Set the global queue rules parameters as defined in the "general" section of queuerules.conf */
9703{
9704 const char *general_val = NULL;
9705 if ((general_val = ast_variable_retrieve(cfg, "general", "realtime_rules"))) {
9706 realtime_rules = ast_true(general_val);
9707 }
9708}
9709
9710/*! \brief Reload the rules defined in queuerules.conf
9711 *
9712 * \param reload If 1, then only process queuerules.conf if the file
9713 * has changed since the last time we inspected it.
9714 * \return Always returns AST_MODULE_LOAD_SUCCESS
9715 */
9717{
9718 struct ast_config *cfg;
9719 struct rule_list *rl_iter, *new_rl;
9720 struct penalty_rule *pr_iter;
9721 char *rulecat = NULL;
9722 struct ast_variable *rulevar = NULL;
9723 struct ast_flags config_flags = { (reload && !realtime_rules) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9724
9725 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
9726 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
9728 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9729 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
9731 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
9732 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n");
9734 }
9735
9737 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
9738 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
9739 ast_free(pr_iter);
9740 ast_free(rl_iter);
9741 }
9743 while ((rulecat = ast_category_browse(cfg, rulecat))) {
9744 if (!strcasecmp(rulecat, "general")) {
9746 continue;
9747 }
9748 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
9750 ast_config_destroy(cfg);
9752 } else {
9753 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
9754 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
9755 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
9756 if(!strcasecmp(rulevar->name, "penaltychange"))
9757 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
9758 else
9759 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
9760 }
9761 }
9762
9763 ast_config_destroy(cfg);
9764
9768 }
9769
9772}
9773
9774/*! Always set the global queue defaults, even if there is no "general" section in queues.conf */
9776{
9778 autofill_default = 0;
9779 montype_default = 0;
9780 shared_lastcall = 0;
9784}
9785
9786/*! Set the global queue parameters as defined in the "general" section of queues.conf */
9787static void queue_set_global_params(struct ast_config *cfg)
9788{
9789 const char *general_val = NULL;
9790 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
9791 queue_persistent_members = ast_true(general_val);
9792 }
9793 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) {
9794 autofill_default = ast_true(general_val);
9795 }
9796 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
9797 if (!strcasecmp(general_val, "mixmonitor"))
9798 montype_default = 1;
9799 }
9800 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) {
9801 shared_lastcall = ast_true(general_val);
9802 }
9803 if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid"))) {
9804 negative_penalty_invalid = ast_true(general_val);
9805 }
9806 if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
9807 log_membername_as_agent = ast_true(general_val);
9808 }
9809 if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) {
9811 }
9812 /* Apply log-caller-id-name in the same place as other global settings */
9813 if ((general_val = ast_variable_retrieve(cfg, "general", "log-caller-id-name"))) {
9814 log_caller_id_name = ast_true(general_val);
9815 }
9816}
9817
9818/*! \brief reload information pertaining to a single member
9819 *
9820 * This function is called when a member = line is encountered in
9821 * queues.conf.
9822 *
9823 * \param memberdata The part after member = in the config file
9824 * \param q The queue to which this member belongs
9825 */
9826static void reload_single_member(const char *memberdata, struct call_queue *q)
9827{
9828 char *membername, *interface, *state_interface, *tmp;
9829 char *parse;
9830 struct member *cur, *newm;
9831 struct member tmpmem;
9832 int penalty;
9833 int ringinuse;
9834 int wrapuptime;
9835 int paused;
9844 );
9845
9846 if (ast_strlen_zero(memberdata)) {
9847 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
9848 return;
9849 }
9850
9851 /* Add a new member */
9852 parse = ast_strdupa(memberdata);
9853
9855
9856 interface = args.interface;
9857 if (!ast_strlen_zero(args.penalty)) {
9858 tmp = args.penalty;
9859 ast_strip(tmp);
9860 penalty = atoi(tmp);
9861 if (penalty < 0) {
9862 penalty = 0;
9863 }
9864 } else {
9865 penalty = 0;
9866 }
9867
9868 if (!ast_strlen_zero(args.membername)) {
9869 membername = args.membername;
9870 ast_strip(membername);
9871 } else {
9872 membername = interface;
9873 }
9874
9875 if (!ast_strlen_zero(args.state_interface)) {
9876 state_interface = args.state_interface;
9877 ast_strip(state_interface);
9878 } else {
9879 state_interface = interface;
9880 }
9881
9882 if (!ast_strlen_zero(args.ringinuse)) {
9883 tmp = args.ringinuse;
9884 ast_strip(tmp);
9885 if (ast_true(tmp)) {
9886 ringinuse = 1;
9887 } else if (ast_false(tmp)) {
9888 ringinuse = 0;
9889 } else {
9890 ast_log(LOG_ERROR, "Member %s has an invalid ringinuse value. Using %s ringinuse value.\n",
9891 membername, q->name);
9892 ringinuse = q->ringinuse;
9893 }
9894 } else {
9895 ringinuse = q->ringinuse;
9896 }
9897
9898 if (!ast_strlen_zero(args.wrapuptime)) {
9899 tmp = args.wrapuptime;
9900 ast_strip(tmp);
9901 wrapuptime = atoi(tmp);
9902 if (wrapuptime < 0) {
9903 wrapuptime = 0;
9904 }
9905 } else {
9906 wrapuptime = 0;
9907 }
9908
9909 if (!ast_strlen_zero(args.paused)) {
9910 tmp = args.paused;
9911 ast_strip(tmp);
9912 if (ast_true(tmp)) {
9913 paused = 1;
9914 } else if (ast_false(tmp)) {
9915 paused = 0;
9916 } else {
9917 ast_log(LOG_ERROR, "Member %s has an invalid paused value.\n", membername);
9918 paused = 0;
9919 }
9920 } else {
9921 paused = 0;
9922 }
9923
9924 /* Find the old position in the list */
9925 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
9926 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
9927
9928 if (cur) {
9929 paused = cur->paused;
9930 }
9931
9932 if ((newm = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
9933 newm->wrapuptime = wrapuptime;
9934 if (cur) {
9935 ao2_lock(q->members);
9936 /* Round Robin Queue Position must be copied if this is replacing an existing member */
9937 newm->queuepos = cur->queuepos;
9938 /* Don't reset agent stats either */
9939 newm->calls = cur->calls;
9940 newm->lastcall = cur->lastcall;
9941
9942 ao2_link(q->members, newm);
9943 ao2_unlink(q->members, cur);
9944 ao2_unlock(q->members);
9945 } else {
9946 /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
9947 member_add_to_queue(q, newm);
9948 }
9949 ao2_ref(newm, -1);
9950 }
9951 newm = NULL;
9952
9953 if (cur) {
9954 ao2_ref(cur, -1);
9955 }
9956}
9957
9958static int mark_member_dead(void *obj, void *arg, int flags)
9959{
9960 struct member *member = obj;
9961 if (!member->dynamic && !member->realtime) {
9962 member->delme = 1;
9963 }
9964 return 0;
9965}
9966
9967static int kill_dead_members(void *obj, void *arg, int flags)
9968{
9969 struct member *member = obj;
9970
9971 if (!member->delme) {
9973 return 0;
9974 } else {
9975 return CMP_MATCH;
9976 }
9977}
9978
9979/*! \brief Reload information pertaining to a particular queue
9980 *
9981 * Once we have isolated a queue within reload_queues, we call this. This will either
9982 * reload information for the queue or if we're just reloading member information, we'll just
9983 * reload that without touching other settings within the queue
9984 *
9985 * \param cfg The configuration which we are reading
9986 * \param mask Tells us what information we need to reload
9987 * \param queuename The name of the queue we are reloading information from
9988 */
9989static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
9990{
9991 int new;
9992 struct call_queue *q = NULL;
9993 struct member *member;
9994 /*We're defining a queue*/
9995 struct call_queue tmpq = {
9996 .name = queuename,
9997 };
9998 const char *tmpvar;
9999 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
10000 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
10001 int prev_weight = 0;
10002 struct ast_variable *var;
10003 struct ao2_iterator mem_iter;
10004
10005 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
10006 if (queue_reload) {
10007 /* Make one then */
10008 if (!(q = alloc_queue(queuename))) {
10009 return;
10010 }
10011 } else {
10012 /* Since we're not reloading queues, this means that we found a queue
10013 * in the configuration file which we don't know about yet. Just return.
10014 */
10015 return;
10016 }
10017 new = 1;
10018 } else {
10019 new = 0;
10020 }
10021
10022 if (!new) {
10023 ao2_lock(q);
10024 prev_weight = q->weight ? 1 : 0;
10025 }
10026 /* Check if we already found a queue with this name in the config file */
10027 if (q->found) {
10028 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
10029 if (!new) {
10030 /* It should be impossible to *not* hit this case*/
10031 ao2_unlock(q);
10032 }
10033 queue_t_unref(q, "We exist! Expiring temporary pointer");
10034 return;
10035 }
10036 /* Due to the fact that the "linear" strategy will have a different allocation
10037 * scheme for queue members, we must devise the queue's strategy before other initializations.
10038 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
10039 * container used will have only a single bucket instead of the typical number.
10040 */
10041 if (queue_reload) {
10042 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
10043 q->strategy = strat2int(tmpvar);
10044 if (q->strategy < 0) {
10045 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
10046 tmpvar, q->name);
10048 }
10049 } else {
10051 }
10052 init_queue(q);
10053 }
10054 if (member_reload) {
10056 q->found = 1;
10057 }
10058
10059 /* On the first pass we just read the parameters of the queue */
10060 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
10061 if (queue_reload && strcasecmp(var->name, "member")) {
10062 queue_set_param(q, var->name, var->value, var->lineno, 1);
10063 }
10064 }
10065
10066 /* On the second pass, we read members */
10067 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
10068 if (member_reload && !strcasecmp(var->name, "member")) {
10069 reload_single_member(var->value, q);
10070 }
10071 }
10072
10073 /* Update ringinuse for dynamic members */
10074 if (member_reload) {
10075 ao2_lock(q->members);
10077 while ((member = ao2_iterator_next(&mem_iter))) {
10078 if (member->dynamic) {
10080 }
10081 ao2_ref(member, -1);
10082 }
10083 ao2_iterator_destroy(&mem_iter);
10084 ao2_unlock(q->members);
10085 }
10086
10087 /* At this point, we've determined if the queue has a weight, so update use_weight
10088 * as appropriate
10089 */
10090 if (!q->weight && prev_weight) {
10092 } else if (q->weight && !prev_weight) {
10094 }
10095
10096 /* Free remaining members marked as delme */
10097 if (member_reload) {
10098 ao2_lock(q->members);
10101 ao2_unlock(q->members);
10102 }
10103
10104 if (new) {
10105 queues_t_link(queues, q, "Add queue to container");
10106 } else {
10107 ao2_unlock(q);
10108 }
10109 queue_t_unref(q, "Expiring creation reference");
10110}
10111
10112static int mark_unfound(void *obj, void *arg, int flags)
10113{
10114 struct call_queue *q = obj;
10115 char *queuename = arg;
10116 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
10117 q->found = 0;
10118 }
10119 return 0;
10120}
10121
10122static int kill_if_unfound(void *obj, void *arg, int flags)
10123{
10124 struct call_queue *q = obj;
10125 char *queuename = arg;
10126 if (!q->realtime && !q->found && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
10127 q->dead = 1;
10128 return CMP_MATCH;
10129 } else {
10130 return 0;
10131 }
10132}
10133
10134/*! \brief reload the queues.conf file
10135 *
10136 * This function reloads the information in the general section of the queues.conf
10137 * file and potentially more, depending on the value of mask.
10138 *
10139 * \param reload 0 if we are calling this the first time, 1 every other time
10140 * \param mask Gives flags telling us what information to actually reload
10141 * \param queuename If set to a non-zero string, then only reload information from
10142 * that particular queue. Otherwise inspect all queues
10143 * \retval -1 Failure occurred
10144 * \retval 0 All clear!
10145 */
10146static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
10147{
10148 struct ast_config *cfg;
10149 char *cat;
10150 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10151 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
10152
10153 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
10154 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
10155 return -1;
10156 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
10157 return 0;
10158 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10159 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n");
10160 return -1;
10161 }
10162
10163 /* We've made it here, so it looks like we're doing operations on all queues. */
10165
10166 /* Mark non-realtime queues not found at the beginning. */
10167 ao2_callback(queues, OBJ_NODATA, mark_unfound, (char *) queuename);
10168
10169 /* Chug through config file. */
10170 cat = NULL;
10172 while ((cat = ast_category_browse(cfg, cat)) ) {
10173 if (!strcasecmp(cat, "general") && queue_reload) {
10175 continue;
10176 }
10177 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
10178 reload_single_queue(cfg, mask, cat);
10179 }
10180
10181 ast_config_destroy(cfg);
10182 if (queue_reload) {
10183 /* Unlink and mark dead all non-realtime queues that were not found in the configuration file. */
10185 }
10187 return 0;
10188}
10189
10190/*! \brief Facilitates resetting statistics for a queue
10191 *
10192 * This function actually does not reset any statistics, but
10193 * rather finds a call_queue struct which corresponds to the
10194 * passed-in queue name and passes that structure to the
10195 * clear_queue function. If no queuename is passed in, then
10196 * all queues will have their statistics reset.
10197 *
10198 * \param queuename The name of the queue to reset the statistics
10199 * for. If this is NULL or zero-length, then this means to reset
10200 * the statistics for all queues
10201 * \retval 0 always
10202 */
10203static int clear_stats(const char *queuename)
10204{
10205 struct call_queue *q;
10206 struct ao2_iterator queue_iter;
10207
10208 queue_iter = ao2_iterator_init(queues, 0);
10209 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10210 ao2_lock(q);
10211 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
10212 clear_queue(q);
10213 ao2_unlock(q);
10214 queue_t_unref(q, "Done with iterator");
10215 }
10216 ao2_iterator_destroy(&queue_iter);
10217 return 0;
10218}
10219
10220/*! \brief The command center for all reload operations
10221 *
10222 * Whenever any piece of queue information is to be reloaded, this function
10223 * is called. It interprets the flags set in the mask parameter and acts
10224 * based on how they are set.
10225 *
10226 * \param reload True if we are reloading information, false if we are loading
10227 * information for the first time.
10228 * \param mask A bitmask which tells the handler what actions to take
10229 * \param queuename The name of the queue on which we wish to take action
10230 * \retval 0 All reloads were successful
10231 * \retval non-zero There was a failure
10232 */
10233static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
10234{
10235 int res = 0;
10236
10237 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
10238 res |= reload_queue_rules(reload);
10239 }
10240 if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
10241 res |= clear_stats(queuename);
10242 }
10244 res |= reload_queues(reload, mask, queuename);
10245 }
10246 return res;
10247}
10248
10249/*! \brief direct output to manager or cli with proper terminator */
10250static void do_print(struct mansession *s, int fd, const char *str)
10251{
10252 if (s) {
10253 astman_append(s, "%s\r\n", str);
10254 } else {
10255 ast_cli(fd, "%s\n", str);
10256 }
10257}
10258
10259/*! \brief Print a single queue to AMI or the CLI */
10260static void print_queue(struct mansession *s, int fd, struct call_queue *q)
10261{
10262 float sl;
10263 float sl2;
10264 struct ao2_iterator mem_iter;
10265 struct ast_str *out = ast_str_alloca(512);
10266 time_t now = time(NULL);
10267
10268 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
10269 if (q->maxlen) {
10270 ast_str_append(&out, 0, "%d", q->maxlen);
10271 } else {
10272 ast_str_append(&out, 0, "unlimited");
10273 }
10274 sl = 0;
10275 sl2 = 0;
10276 if (q->callscompleted > 0) {
10277 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
10278 }
10279 if (q->callscompleted + q->callsabandoned > 0) {
10280 sl2 =100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted));
10281 }
10282
10283 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",
10285 do_print(s, fd, ast_str_buffer(out));
10286 if (!ao2_container_count(q->members)) {
10287 do_print(s, fd, " No Members");
10288 } else {
10289 struct member *mem;
10290
10291 do_print(s, fd, " Members: ");
10292 mem_iter = ao2_iterator_init(q->members, 0);
10293 while ((mem = ao2_iterator_next(&mem_iter))) {
10294 ast_str_set(&out, 0, " %s", mem->membername);
10295 if (strcasecmp(mem->membername, mem->interface)) {
10296 ast_str_append(&out, 0, " (%s", mem->interface);
10298 && strcmp(mem->state_interface, mem->interface)) {
10299 ast_str_append(&out, 0, " from %s", mem->state_interface);
10300 }
10301 ast_str_append(&out, 0, ")");
10302 }
10303 if (mem->penalty) {
10304 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
10305 }
10306
10307 ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
10308
10309 ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
10310 mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
10311 mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
10312 mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset());
10313
10314 if (mem->paused) {
10315 ast_str_append(&out, 0, " %s(paused%s%s was %ld secs ago)%s",
10317 ast_strlen_zero(mem->reason_paused) ? "" : ":",
10319 (long) (now - mem->lastpause),
10320 ast_term_reset());
10321 }
10322
10323 ast_str_append(&out, 0, " (%s%s%s)",
10328 if (mem->calls) {
10329 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
10330 mem->calls, (long) (now - mem->lastcall));
10331 } else {
10332 ast_str_append(&out, 0, " has taken no calls yet");
10333 }
10334 ast_str_append(&out, 0, " %s(login was %ld secs ago)%s",
10336 (long) (now - mem->logintime),
10337 ast_term_reset());
10338 do_print(s, fd, ast_str_buffer(out));
10339 ao2_ref(mem, -1);
10340 }
10341 ao2_iterator_destroy(&mem_iter);
10342 }
10343 if (!q->head) {
10344 do_print(s, fd, " No Callers");
10345 } else {
10346 struct queue_ent *qe;
10347 int pos = 1;
10348
10349 do_print(s, fd, " Callers: ");
10350 for (qe = q->head; qe; qe = qe->next) {
10351 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
10352 pos++, ast_channel_name(qe->chan), (long) (now - qe->start) / 60,
10353 (long) (now - qe->start) % 60, qe->prio);
10354 do_print(s, fd, ast_str_buffer(out));
10355 }
10356 }
10357 do_print(s, fd, ""); /* blank line between entries */
10358}
10359
10361
10362/*!
10363 * \brief Show queue(s) status and statistics
10364 *
10365 * List the queues strategy, calls processed, members logged in,
10366 * other queue statistics such as avg hold time.
10367*/
10368static char *__queues_show(struct mansession *s, int fd, int argc, const char * const *argv)
10369{
10370 struct call_queue *q;
10371 struct ast_str *out = ast_str_alloca(512);
10372 struct ao2_container *sorted_queues;
10373
10374 struct ao2_iterator queue_iter;
10375 int found = 0;
10376
10377 if (argc != 2 && argc != 3) {
10378 return CLI_SHOWUSAGE;
10379 }
10380
10381 if (argc == 3) { /* specific queue */
10382 if ((q = find_load_queue_rt_friendly(argv[2]))) {
10383 ao2_lock(q);
10384 print_queue(s, fd, q);
10385 ao2_unlock(q);
10386 queue_unref(q);
10387 } else {
10388 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
10389 do_print(s, fd, ast_str_buffer(out));
10390 }
10391 return CLI_SUCCESS;
10392 }
10393
10394 if (ast_check_realtime("queues")) {
10395 /* This block is to find any queues which are defined in realtime but
10396 * which have not yet been added to the in-core container
10397 */
10398 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
10399 if (cfg) {
10400 char *category = NULL;
10401 while ((category = ast_category_browse(cfg, category))) {
10402 const char *queuename = ast_variable_retrieve(cfg, category, "name");
10403 if (ast_strlen_zero(queuename)) {
10404 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
10405 continue;
10406 }
10407 if ((q = find_load_queue_rt_friendly(queuename))) {
10408 queue_t_unref(q, "Done with temporary pointer");
10409 }
10410 }
10411 ast_config_destroy(cfg);
10412 }
10413 }
10414
10415 /*
10416 * Snapping a copy of the container prevents having to lock both the queues container
10417 * and the queue itself at the same time. It also allows us to sort the entries.
10418 */
10419 sorted_queues = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, call_queue_sort_fn, NULL);
10420 if (!sorted_queues) {
10421 return CLI_SUCCESS;
10422 }
10423 if (ao2_container_dup(sorted_queues, queues, 0)) {
10424 ao2_ref(sorted_queues, -1);
10425 return CLI_SUCCESS;
10426 }
10427
10428 /*
10429 * No need to lock the container since it's temporary and static.
10430 * We also unlink the entries as we use them so the container is
10431 * empty when the iterator finishes. We can then just unref the container.
10432 */
10433 queue_iter = ao2_iterator_init(sorted_queues, AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK);
10434 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10435 struct call_queue *realtime_queue = NULL;
10436 ao2_lock(q);
10437 /* This check is to make sure we don't print information for realtime
10438 * queues which have been deleted from realtime but which have not yet
10439 * been deleted from the in-core container. Only do this if we're not
10440 * looking for a specific queue.
10441 */
10442 if (q->realtime) {
10443 realtime_queue = find_load_queue_rt_friendly(q->name);
10444 if (!realtime_queue) {
10445 ao2_unlock(q);
10446 queue_t_unref(q, "Done with iterator");
10447 continue;
10448 }
10449 queue_t_unref(realtime_queue, "Queue is already in memory");
10450 }
10451
10452 found = 1;
10453 print_queue(s, fd, q);
10454
10455 ao2_unlock(q);
10456 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
10457 }
10458 ao2_iterator_destroy(&queue_iter);
10459 ao2_ref(sorted_queues, -1);
10460 if (!found) {
10461 ast_str_set(&out, 0, "No queues.");
10462 do_print(s, fd, ast_str_buffer(out));
10463 }
10464 return CLI_SUCCESS;
10465}
10466
10467/*!
10468 * \brief Check if a given word is in a space-delimited list
10469 *
10470 * \param list Space delimited list of words
10471 * \param word The word used to search the list
10472 *
10473 * \note This function will not return 1 if the word is at the very end of the
10474 * list (followed immediately by a \0, not a space) since it is used for
10475 * checking tab-completion and a word at the end is still being tab-completed.
10476 *
10477 * \retval 1 if the word is found
10478 * \retval 0 if the word is not found
10479*/
10480static int word_in_list(const char *list, const char *word) {
10481 int list_len, word_len = strlen(word);
10482 const char *find, *end_find, *end_list;
10483
10484 /* strip whitespace from front */
10485 while(isspace(*list)) {
10486 list++;
10487 }
10488
10489 while((find = strstr(list, word))) {
10490 /* beginning of find starts inside another word? */
10491 if (find != list && *(find - 1) != ' ') {
10492 list = find;
10493 /* strip word from front */
10494 while(!isspace(*list) && *list != '\0') {
10495 list++;
10496 }
10497 /* strip whitespace from front */
10498 while(isspace(*list)) {
10499 list++;
10500 }
10501 continue;
10502 }
10503
10504 /* end of find ends inside another word or at very end of list? */
10505 list_len = strlen(list);
10506 end_find = find + word_len;
10507 end_list = list + list_len;
10508 if (end_find == end_list || *end_find != ' ') {
10509 list = find;
10510 /* strip word from front */
10511 while(!isspace(*list) && *list != '\0') {
10512 list++;
10513 }
10514 /* strip whitespace from front */
10515 while(isspace(*list)) {
10516 list++;
10517 }
10518 continue;
10519 }
10520
10521 /* terminating conditions satisfied, word at beginning or separated by ' ' */
10522 return 1;
10523 }
10524
10525 return 0;
10526}
10527
10528/*!
10529 * \brief Check if a given word is in a space-delimited list
10530 *
10531 * \param line The line as typed not including the current word being completed
10532 * \param word The word currently being completed
10533 * \param pos The number of completed words in line
10534 * \param state The nth desired completion option
10535 * \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.
10536 *
10537 * \return Returns the queue tab-completion for the given word and state
10538*/
10539static char *complete_queue(const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset)
10540{
10541 struct call_queue *q;
10542 char *ret = NULL;
10543 int which = 0;
10544 int wordlen = strlen(word);
10545 struct ao2_iterator queue_iter;
10546 const char *word_list = NULL;
10547
10548 /* for certain commands, already completed items should be left out of
10549 * the list */
10550 if (word_list_offset && strlen(line) >= word_list_offset) {
10551 word_list = line + word_list_offset;
10552 }
10553
10554 queue_iter = ao2_iterator_init(queues, 0);
10555 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10556 if (!strncasecmp(word, q->name, wordlen) && ++which > state
10557 && (!word_list_offset || !word_in_list(word_list, q->name))) {
10558 ret = ast_strdup(q->name);
10559 queue_t_unref(q, "Done with iterator");
10560 break;
10561 }
10562 queue_t_unref(q, "Done with iterator");
10563 }
10564 ao2_iterator_destroy(&queue_iter);
10565
10566 /* Pretend "rules" is at the end of the queues list in certain
10567 * circumstances since it is an alternate command that should be
10568 * tab-completable for "queue show" */
10569 if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
10570 ret = ast_strdup("rules");
10571 }
10572
10573 return ret;
10574}
10575
10576static char *complete_queue_show(const char *line, const char *word, int pos, int state)
10577{
10578 if (pos == 2) {
10579 return complete_queue(line, word, pos, state, 0);
10580 }
10581 return NULL;
10582}
10583
10584static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10585{
10586 switch ( cmd ) {
10587 case CLI_INIT:
10588 e->command = "queue show";
10589 e->usage =
10590 "Usage: queue show\n"
10591 " Provides summary information on a specified queue.\n";
10592 return NULL;
10593 case CLI_GENERATE:
10594 return complete_queue_show(a->line, a->word, a->pos, a->n);
10595 }
10596
10597 return __queues_show(NULL, a->fd, a->argc, a->argv);
10598}
10599
10600static int manager_queue_rule_show(struct mansession *s, const struct message *m)
10601{
10602 const char *rule = astman_get_header(m, "Rule");
10603 const char *id = astman_get_header(m, "ActionID");
10604 struct rule_list *rl_iter;
10605 struct penalty_rule *pr_iter;
10606
10607 astman_append(s, "Response: Success\r\n");
10608 if (!ast_strlen_zero(id)) {
10609 astman_append(s, "ActionID: %s\r\n", id);
10610 }
10611
10613 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
10614 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
10615 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
10616 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
10617 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 );
10618 }
10619 if (!ast_strlen_zero(rule)) {
10620 break;
10621 }
10622 }
10623 }
10625
10626 /*
10627 * Two blank lines instead of one because the Response and
10628 * ActionID headers used to not be present.
10629 */
10630 astman_append(s, "\r\n\r\n");
10631
10632 return RESULT_SUCCESS;
10633}
10634
10635/*! \brief Summary of queue info via the AMI */
10636static int manager_queues_summary(struct mansession *s, const struct message *m)
10637{
10638 time_t now;
10639 int qmemcount = 0;
10640 int qmemavail = 0;
10641 int qchancount = 0;
10642 int qlongestholdtime = 0;
10643 int qsummaries = 0;
10644 const char *id = astman_get_header(m, "ActionID");
10645 const char *queuefilter = astman_get_header(m, "Queue");
10646 char idText[256];
10647 struct call_queue *q;
10648 struct queue_ent *qe;
10649 struct member *mem;
10650 struct ao2_iterator queue_iter;
10651 struct ao2_iterator mem_iter;
10652
10653 if (ast_check_realtime("queues")) {
10654 load_realtime_queues(queuefilter);
10655 }
10656
10657 astman_send_listack(s, m, "Queue summary will follow", "start");
10658 time(&now);
10659 idText[0] = '\0';
10660 if (!ast_strlen_zero(id)) {
10661 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10662 }
10663 queue_iter = ao2_iterator_init(queues, 0);
10664 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10665 ao2_lock(q);
10666
10667 /* List queue properties */
10668 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10669 /* Reset the necessary local variables if no queuefilter is set*/
10670 qmemcount = 0;
10671 qmemavail = 0;
10672 qchancount = 0;
10673 qlongestholdtime = 0;
10674
10675 /* List Queue Members */
10676 mem_iter = ao2_iterator_init(q->members, 0);
10677 while ((mem = ao2_iterator_next(&mem_iter))) {
10678 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
10679 ++qmemcount;
10680 if (member_status_available(mem->status) && !mem->paused) {
10681 ++qmemavail;
10682 }
10683 }
10684 ao2_ref(mem, -1);
10685 }
10686 ao2_iterator_destroy(&mem_iter);
10687 for (qe = q->head; qe; qe = qe->next) {
10688 if ((now - qe->start) > qlongestholdtime) {
10689 qlongestholdtime = now - qe->start;
10690 }
10691 ++qchancount;
10692 }
10693 astman_append(s, "Event: QueueSummary\r\n"
10694 "Queue: %s\r\n"
10695 "LoggedIn: %d\r\n"
10696 "Available: %d\r\n"
10697 "Callers: %d\r\n"
10698 "HoldTime: %d\r\n"
10699 "TalkTime: %d\r\n"
10700 "LongestHoldTime: %d\r\n"
10701 "%s"
10702 "\r\n",
10703 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
10704 ++qsummaries;
10705 }
10706 ao2_unlock(q);
10707 queue_t_unref(q, "Done with iterator");
10708 }
10709 ao2_iterator_destroy(&queue_iter);
10710
10711 astman_send_list_complete_start(s, m, "QueueSummaryComplete", qsummaries);
10713
10714 return RESULT_SUCCESS;
10715}
10716
10717/*! \brief Queue status info via AMI */
10718static int manager_queues_status(struct mansession *s, const struct message *m)
10719{
10720 time_t now;
10721 int pos;
10722 int q_items = 0;
10723 const char *id = astman_get_header(m,"ActionID");
10724 const char *queuefilter = astman_get_header(m,"Queue");
10725 const char *memberfilter = astman_get_header(m,"Member");
10726 char idText[256];
10727 struct call_queue *q;
10728 struct queue_ent *qe;
10729 float sl = 0;
10730 float sl2 = 0;
10731 struct member *mem;
10732 struct ao2_iterator queue_iter;
10733 struct ao2_iterator mem_iter;
10734
10735 if (ast_check_realtime("queues")) {
10736 load_realtime_queues(queuefilter);
10737 }
10738
10739 astman_send_listack(s, m, "Queue status will follow", "start");
10740 time(&now);
10741 idText[0] = '\0';
10742 if (!ast_strlen_zero(id)) {
10743 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10744 }
10745
10746 queue_iter = ao2_iterator_init(queues, 0);
10747 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10748 ao2_lock(q);
10749
10750 /* List queue properties */
10751 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10752 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
10753 sl2 = (((q->callscompleted + q->callsabandoned) > 0) ? 100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted)) : 0);
10754
10755 astman_append(s, "Event: QueueParams\r\n"
10756 "Queue: %s\r\n"
10757 "Max: %d\r\n"
10758 "Strategy: %s\r\n"
10759 "Calls: %d\r\n"
10760 "Holdtime: %d\r\n"
10761 "TalkTime: %d\r\n"
10762 "Completed: %d\r\n"
10763 "Abandoned: %d\r\n"
10764 "ServiceLevel: %d\r\n"
10765 "ServicelevelPerf: %2.1f\r\n"
10766 "ServicelevelPerf2: %2.1f\r\n"
10767 "Weight: %d\r\n"
10768 "%s"
10769 "\r\n",
10770 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
10771 q->callsabandoned, q->servicelevel, sl, sl2, q->weight, idText);
10772 ++q_items;
10773
10774 /* List Queue Members */
10775 mem_iter = ao2_iterator_init(q->members, 0);
10776 while ((mem = ao2_iterator_next(&mem_iter))) {
10777 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
10778 astman_append(s, "Event: QueueMember\r\n"
10779 "Queue: %s\r\n"
10780 "Name: %s\r\n"
10781 "Location: %s\r\n"
10782 "StateInterface: %s\r\n"
10783 "Membership: %s\r\n"
10784 "Penalty: %d\r\n"
10785 "CallsTaken: %d\r\n"
10786 "LastCall: %d\r\n"
10787 "LastPause: %d\r\n"
10788 "LoginTime: %d\r\n"
10789 "InCall: %d\r\n"
10790 "Status: %d\r\n"
10791 "Paused: %d\r\n"
10792 "PausedReason: %s\r\n"
10793 "Wrapuptime: %d\r\n"
10794 "%s"
10795 "\r\n",
10796 q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
10797 mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, (int)mem->logintime, mem->starttime ? 1 : 0, mem->status,
10798 mem->paused, mem->reason_paused, mem->wrapuptime, idText);
10799 ++q_items;
10800 }
10801 ao2_ref(mem, -1);
10802 }
10803 ao2_iterator_destroy(&mem_iter);
10804
10805 /* List Queue Entries */
10806 pos = 1;
10807 for (qe = q->head; qe; qe = qe->next) {
10808 astman_append(s, "Event: QueueEntry\r\n"
10809 "Queue: %s\r\n"
10810 "Position: %d\r\n"
10811 "Channel: %s\r\n"
10812 "Uniqueid: %s\r\n"
10813 "CallerIDNum: %s\r\n"
10814 "CallerIDName: %s\r\n"
10815 "ConnectedLineNum: %s\r\n"
10816 "ConnectedLineName: %s\r\n"
10817 "Wait: %ld\r\n"
10818 "Priority: %d\r\n"
10819 "%s"
10820 "\r\n",
10821 q->name, pos++, ast_channel_name(qe->chan), ast_channel_uniqueid(qe->chan),
10826 (long) (now - qe->start), qe->prio, idText);
10827 ++q_items;
10828 }
10829 }
10830 ao2_unlock(q);
10831 queue_t_unref(q, "Done with iterator");
10832 }
10833 ao2_iterator_destroy(&queue_iter);
10834
10835 astman_send_list_complete_start(s, m, "QueueStatusComplete", q_items);
10837
10838 return RESULT_SUCCESS;
10839}
10840
10841static int manager_add_queue_member(struct mansession *s, const struct message *m)
10842{
10843 const char *queuename, *interface, *penalty_s, *paused_s, *reason, *membername, *state_interface, *wrapuptime_s;
10844 int paused, penalty, wrapuptime = 0;
10845
10846 queuename = astman_get_header(m, "Queue");
10847 interface = astman_get_header(m, "Interface");
10848 penalty_s = astman_get_header(m, "Penalty");
10849 paused_s = astman_get_header(m, "Paused");
10850 reason = astman_get_header(m, "Reason"); /* Optional */
10851 membername = astman_get_header(m, "MemberName");
10852 state_interface = astman_get_header(m, "StateInterface");
10853 wrapuptime_s = astman_get_header(m, "Wrapuptime");
10854
10855 if (ast_strlen_zero(queuename)) {
10856 astman_send_error(s, m, "'Queue' not specified.");
10857 return 0;
10858 }
10859
10860 if (ast_strlen_zero(interface)) {
10861 astman_send_error(s, m, "'Interface' not specified.");
10862 return 0;
10863 }
10864
10865 if (ast_strlen_zero(penalty_s)) {
10866 penalty = 0;
10867 } else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) {
10868 penalty = 0;
10869 }
10870
10871 if (ast_strlen_zero(wrapuptime_s)) {
10872 wrapuptime = 0;
10873 } else if (sscanf(wrapuptime_s, "%30d", &wrapuptime) != 1 || wrapuptime < 0) {
10874 wrapuptime = 0;
10875 }
10876
10877 if (ast_strlen_zero(paused_s)) {
10878 paused = 0;
10879 } else {
10880 paused = abs(ast_true(paused_s));
10881 }
10882
10883 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, reason, wrapuptime)) {
10884 case RES_OKAY:
10885 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
10886 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10887 } else {
10888 ast_queue_log(queuename, "MANAGER", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10889 }
10890 astman_send_ack(s, m, "Added interface to queue");
10891 break;
10892 case RES_EXISTS:
10893 astman_send_error(s, m, "Unable to add interface: Already there");
10894 break;
10895 case RES_NOSUCHQUEUE:
10896 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
10897 break;
10898 case RES_OUTOFMEMORY:
10899 astman_send_error(s, m, "Out of memory");
10900 break;
10901 }
10902
10903 return 0;
10904}
10905
10906static int manager_remove_queue_member(struct mansession *s, const struct message *m)
10907{
10908 const char *queuename, *interface;
10909 struct member *mem = NULL;
10910
10911 queuename = astman_get_header(m, "Queue");
10912 interface = astman_get_header(m, "Interface");
10913
10914 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
10915 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
10916 return 0;
10917 }
10918
10920 mem = find_member_by_queuename_and_interface(queuename, interface);
10921 }
10922
10923 switch (remove_from_queue(queuename, interface)) {
10924 case RES_OKAY:
10925 if (!mem || ast_strlen_zero(mem->membername)) {
10926 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
10927 } else {
10928 ast_queue_log(queuename, "MANAGER", mem->membername, "REMOVEMEMBER", "%s", "");
10929 }
10930 astman_send_ack(s, m, "Removed interface from queue");
10931 break;
10932 case RES_EXISTS:
10933 astman_send_error(s, m, "Unable to remove interface: Not there");
10934 break;
10935 case RES_NOSUCHQUEUE:
10936 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
10937 break;
10938 case RES_OUTOFMEMORY:
10939 astman_send_error(s, m, "Out of memory");
10940 break;
10941 case RES_NOT_DYNAMIC:
10942 astman_send_error(s, m, "Member not dynamic");
10943 break;
10944 }
10945
10946 if (mem) {
10947 ao2_ref(mem, -1);
10948 }
10949
10950 return 0;
10951}
10952
10953static int manager_pause_queue_member(struct mansession *s, const struct message *m)
10954{
10955 const char *queuename, *interface, *paused_s, *reason;
10956 int paused;
10957
10958 interface = astman_get_header(m, "Interface");
10959 paused_s = astman_get_header(m, "Paused");
10960 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
10961 reason = astman_get_header(m, "Reason"); /* Optional */
10962
10963 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
10964 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
10965 return 0;
10966 }
10967
10968 paused = abs(ast_true(paused_s));
10969
10970 if (set_member_paused(queuename, interface, reason, paused)) {
10971 astman_send_error(s, m, "Interface not found");
10972 } else {
10973 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
10974 }
10975 return 0;
10976}
10977
10978static int manager_queue_log_custom(struct mansession *s, const struct message *m)
10979{
10980 const char *queuename, *event, *message, *interface, *uniqueid;
10981
10982 queuename = astman_get_header(m, "Queue");
10983 uniqueid = astman_get_header(m, "UniqueId");
10984 interface = astman_get_header(m, "Interface");
10985 event = astman_get_header(m, "Event");
10986 message = astman_get_header(m, "Message");
10987
10988 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
10989 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
10990 return 0;
10991 }
10992
10993 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
10994 astman_send_ack(s, m, "Event added successfully");
10995
10996 return 0;
10997}
10998
10999static int manager_queue_reload(struct mansession *s, const struct message *m)
11000{
11001 struct ast_flags mask = {0,};
11002 const char *queuename = NULL;
11003 int header_found = 0;
11004
11005 queuename = astman_get_header(m, "Queue");
11006 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
11008 header_found = 1;
11009 }
11010 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
11012 header_found = 1;
11013 }
11014 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
11016 header_found = 1;
11017 }
11018
11019 if (!header_found) {
11021 }
11022
11023 if (!reload_handler(1, &mask, queuename)) {
11024 astman_send_ack(s, m, "Queue reloaded successfully");
11025 } else {
11026 astman_send_error(s, m, "Error encountered while reloading queue");
11027 }
11028 return 0;
11029}
11030
11031static int manager_queue_reset(struct mansession *s, const struct message *m)
11032{
11033 const char *queuename = NULL;
11034 struct ast_flags mask = {QUEUE_RESET_STATS,};
11035
11036 queuename = astman_get_header(m, "Queue");
11037
11038 if (!reload_handler(1, &mask, queuename)) {
11039 astman_send_ack(s, m, "Queue stats reset successfully");
11040 } else {
11041 astman_send_error(s, m, "Error encountered while resetting queue stats");
11042 }
11043 return 0;
11044}
11045
11046static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
11047{
11048 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
11049 switch (pos) {
11050 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
11051 return NULL;
11052 case 4: /* only one possible match, "to" */
11053 return state == 0 ? ast_strdup("to") : NULL;
11054 case 5: /* <queue> */
11055 return complete_queue(line, word, pos, state, 0);
11056 case 6: /* only one possible match, "penalty" */
11057 return state == 0 ? ast_strdup("penalty") : NULL;
11058 case 7:
11059 if (0 <= state && state < 100) { /* 0-99 */
11060 char *num;
11061 if ((num = ast_malloc(3))) {
11062 sprintf(num, "%d", state);
11063 }
11064 return num;
11065 } else {
11066 return NULL;
11067 }
11068 case 8: /* only one possible match, "as" */
11069 return state == 0 ? ast_strdup("as") : NULL;
11070 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
11071 return NULL;
11072 default:
11073 return NULL;
11074 }
11075}
11076
11077static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
11078{
11079 const char *queuename, *interface, *ringinuse_s;
11080 int ringinuse;
11081
11082 interface = astman_get_header(m, "Interface");
11083 ringinuse_s = astman_get_header(m, "RingInUse");
11084
11085 /* Optional - if not supplied, set the ringinuse value for the given Interface in all queues */
11086 queuename = astman_get_header(m, "Queue");
11087
11088 if (ast_strlen_zero(interface) || ast_strlen_zero(ringinuse_s)) {
11089 astman_send_error(s, m, "Need 'Interface' and 'RingInUse' parameters.");
11090 return 0;
11091 }
11092
11093 if (ast_true(ringinuse_s)) {
11094 ringinuse = 1;
11095 } else if (ast_false(ringinuse_s)) {
11096 ringinuse = 0;
11097 } else {
11098 astman_send_error(s, m, "'RingInUse' parameter must be a truth value (yes/no, on/off, 0/1, etc)");
11099 return 0;
11100 }
11101
11102 if (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11103 astman_send_error(s, m, "Invalid interface, queuename, or ringinuse value\n");
11104 } else {
11105 astman_send_ack(s, m, "Interface ringinuse set successfully");
11106 }
11107
11108 return 0;
11109}
11110
11111static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
11112{
11113 const char *queuename, *interface, *penalty_s;
11114 int penalty;
11115
11116 interface = astman_get_header(m, "Interface");
11117 penalty_s = astman_get_header(m, "Penalty");
11118 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
11119 queuename = astman_get_header(m, "Queue");
11120
11121 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
11122 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
11123 return 0;
11124 }
11125
11126 penalty = atoi(penalty_s);
11127
11128 if (set_member_value((char *)queuename, (char *)interface, MEMBER_PENALTY, penalty)) {
11129 astman_send_error(s, m, "Invalid interface, queuename or penalty");
11130 } else {
11131 astman_send_ack(s, m, "Interface penalty set successfully");
11132 }
11133
11134 return 0;
11135}
11136
11137static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
11138{
11139 const char *queuename, *caller, *priority_s, *immediate_s;
11140 int priority = 0, immediate = 0;
11141
11142 queuename = astman_get_header(m, "Queue");
11143 caller = astman_get_header(m, "Caller");
11144 priority_s = astman_get_header(m, "Priority");
11145 immediate_s = astman_get_header(m, "Immediate");
11146
11147 if (ast_strlen_zero(queuename)) {
11148 astman_send_error(s, m, "'Queue' not specified.");
11149 return 0;
11150 }
11151
11152 if (ast_strlen_zero(caller)) {
11153 astman_send_error(s, m, "'Caller' not specified.");
11154 return 0;
11155 }
11156
11157 if (ast_strlen_zero(priority_s)) {
11158 astman_send_error(s, m, "'Priority' not specified.");
11159 return 0;
11160 } else if (sscanf(priority_s, "%30d", &priority) != 1) {
11161 astman_send_error(s, m, "'Priority' need integer.");
11162 return 0;
11163 }
11164
11165 if (!ast_strlen_zero(immediate_s)) {
11166 immediate = ast_true(immediate_s);
11167 }
11168
11169 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
11170 case RES_OKAY:
11171 astman_send_ack(s, m, "Priority change for caller on queue");
11172 break;
11173 case RES_NOSUCHQUEUE:
11174 astman_send_error(s, m, "Unable to change priority caller on queue: No such queue");
11175 break;
11176 case RES_NOT_CALLER:
11177 astman_send_error(s, m, "Unable to change priority caller on queue: No such caller");
11178 break;
11179 }
11180
11181 return 0;
11182}
11183
11185{
11186 const char *queuename, *caller, *withdraw_info;
11187
11188 queuename = astman_get_header(m, "Queue");
11189 caller = astman_get_header(m, "Caller");
11190 withdraw_info = astman_get_header(m, "WithdrawInfo");
11191
11192 if (ast_strlen_zero(queuename)) {
11193 astman_send_error(s, m, "'Queue' not specified.");
11194 return 0;
11195 }
11196
11197 if (ast_strlen_zero(caller)) {
11198 astman_send_error(s, m, "'Caller' not specified.");
11199 return 0;
11200 }
11201
11202 switch (request_withdraw_caller_from_queue(queuename, caller, withdraw_info)) {
11203 case RES_OKAY:
11204 astman_send_ack(s, m, "Withdraw requested successfully");
11205 break;
11206 case RES_NOSUCHQUEUE:
11207 astman_send_error(s, m, "Unable to request withdraw from queue: No such queue");
11208 break;
11209 case RES_NOT_CALLER:
11210 astman_send_error(s, m, "Unable to request withdraw from queue: No such caller");
11211 break;
11212 case RES_EXISTS:
11213 astman_send_error(s, m, "Unable to request withdraw from queue: Already requested");
11214 break;
11215 }
11216
11217 return 0;
11218}
11219
11220
11221static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11222{
11223 const char *queuename, *interface, *membername = NULL, *state_interface = NULL, *reason = NULL;
11224 int penalty, paused = 0;
11225
11226 switch ( cmd ) {
11227 case CLI_INIT:
11228 e->command = "queue add member";
11229 e->usage =
11230 "Usage: queue add member <dial string> to <queue> [penalty <penalty> [as <membername> [state_interface <interface> [paused <reason>]]]]\n"
11231 " 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";
11232 return NULL;
11233 case CLI_GENERATE:
11234 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
11235 }
11236
11237 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12) && (a->argc != 14)) {
11238 return CLI_SHOWUSAGE;
11239 } else if (strcmp(a->argv[4], "to")) {
11240 return CLI_SHOWUSAGE;
11241 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
11242 return CLI_SHOWUSAGE;
11243 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
11244 return CLI_SHOWUSAGE;
11245 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
11246 return CLI_SHOWUSAGE;
11247 } else if ((a->argc == 14) && strcmp(a->argv[12], "paused")) {
11248 return CLI_SHOWUSAGE;
11249 }
11250
11251 queuename = a->argv[5];
11252 interface = a->argv[3];
11253 if (a->argc >= 8) {
11254 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
11255 if (penalty < 0) {
11256 ast_cli(a->fd, "Penalty must be >= 0\n");
11257 penalty = 0;
11258 }
11259 } else {
11260 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
11261 penalty = 0;
11262 }
11263 } else {
11264 penalty = 0;
11265 }
11266
11267 if (a->argc >= 10) {
11268 membername = a->argv[9];
11269 }
11270
11271 if (a->argc >= 12) {
11272 state_interface = a->argv[11];
11273 }
11274
11275 if (a->argc >= 14) {
11276 paused = 1;
11277 reason = a->argv[13];
11278 }
11279
11280 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, reason, 0)) {
11281 case RES_OKAY:
11282 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
11283 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
11284 } else {
11285 ast_queue_log(queuename, "CLI", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
11286 }
11287 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
11288 return CLI_SUCCESS;
11289 case RES_EXISTS:
11290 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
11291 return CLI_FAILURE;
11292 case RES_NOSUCHQUEUE:
11293 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
11294 return CLI_FAILURE;
11295 case RES_OUTOFMEMORY:
11296 ast_cli(a->fd, "Out of memory\n");
11297 return CLI_FAILURE;
11298 case RES_NOT_DYNAMIC:
11299 ast_cli(a->fd, "Member not dynamic\n");
11300 return CLI_FAILURE;
11301 default:
11302 return CLI_FAILURE;
11303 }
11304}
11305
11306static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
11307{
11308 int which = 0;
11309 struct call_queue *q;
11310 struct member *m;
11311 struct ao2_iterator queue_iter;
11312 struct ao2_iterator mem_iter;
11313 int wordlen = strlen(word);
11314
11315 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
11316 if (pos > 5 || pos < 3) {
11317 return NULL;
11318 }
11319 if (pos == 4) { /* only one possible match, 'from' */
11320 return (state == 0 ? ast_strdup("from") : NULL);
11321 }
11322
11323 if (pos == 5) { /* No need to duplicate code */
11324 return complete_queue(line, word, pos, state, 0);
11325 }
11326
11327 /* here is the case for 3, <member> */
11328 queue_iter = ao2_iterator_init(queues, 0);
11329 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
11330 ao2_lock(q);
11331 mem_iter = ao2_iterator_init(q->members, 0);
11332 while ((m = ao2_iterator_next(&mem_iter))) {
11333 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
11334 char *tmp;
11335 tmp = ast_strdup(m->interface);
11336 ao2_ref(m, -1);
11337 ao2_iterator_destroy(&mem_iter);
11338 ao2_unlock(q);
11339 queue_t_unref(q, "Done with iterator, returning interface name");
11340 ao2_iterator_destroy(&queue_iter);
11341 return tmp;
11342 }
11343 ao2_ref(m, -1);
11344 }
11345 ao2_iterator_destroy(&mem_iter);
11346 ao2_unlock(q);
11347 queue_t_unref(q, "Done with iterator");
11348 }
11349 ao2_iterator_destroy(&queue_iter);
11350
11351 return NULL;
11352}
11353
11354static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11355{
11356 const char *queuename, *interface;
11357 struct member *mem = NULL;
11358 char *res = CLI_FAILURE;
11359
11360 switch (cmd) {
11361 case CLI_INIT:
11362 e->command = "queue remove member";
11363 e->usage =
11364 "Usage: queue remove member <channel> from <queue>\n"
11365 " Remove a specific channel from a queue.\n";
11366 return NULL;
11367 case CLI_GENERATE:
11368 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
11369 }
11370
11371 if (a->argc != 6) {
11372 return CLI_SHOWUSAGE;
11373 } else if (strcmp(a->argv[4], "from")) {
11374 return CLI_SHOWUSAGE;
11375 }
11376
11377 queuename = a->argv[5];
11378 interface = a->argv[3];
11379
11381 mem = find_member_by_queuename_and_interface(queuename, interface);
11382 }
11383
11384 switch (remove_from_queue(queuename, interface)) {
11385 case RES_OKAY:
11386 if (!mem || ast_strlen_zero(mem->membername)) {
11387 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
11388 } else {
11389 ast_queue_log(queuename, "CLI", mem->membername, "REMOVEMEMBER", "%s", "");
11390 }
11391 ast_cli(a->fd, "Removed interface %s from queue '%s'\n", interface, queuename);
11392 res = CLI_SUCCESS;
11393 break;
11394 case RES_EXISTS:
11395 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
11396 break;
11397 case RES_NOSUCHQUEUE:
11398 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
11399 break;
11400 case RES_OUTOFMEMORY:
11401 ast_cli(a->fd, "Out of memory\n");
11402 break;
11403 case RES_NOT_DYNAMIC:
11404 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
11405 break;
11406 }
11407
11408 if (mem) {
11409 ao2_ref(mem, -1);
11410 }
11411
11412 return res;
11413}
11414
11415
11416
11417static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11418{
11419 const char *queuename, *caller;
11420 int priority, immediate = 0;
11421 char *res = CLI_FAILURE;
11422
11423 switch (cmd) {
11424 case CLI_INIT:
11425 e->command = "queue priority caller";
11426 e->usage =
11427 "Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
11428 " Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
11429 return NULL;
11430 case CLI_GENERATE:
11431 return NULL;
11432 }
11433
11434 if (a->argc < 8) {
11435 return CLI_SHOWUSAGE;
11436 } else if (strcmp(a->argv[4], "on")) {
11437 return CLI_SHOWUSAGE;
11438 } else if (strcmp(a->argv[6], "to")) {
11439 return CLI_SHOWUSAGE;
11440 } else if (sscanf(a->argv[7], "%30d", &priority) != 1) {
11441 ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
11442 return CLI_SHOWUSAGE;
11443 } else if (a->argc == 9) {
11444 if (strcmp(a->argv[8], "immediate")) {
11445 return CLI_SHOWUSAGE;
11446 }
11447 immediate = 1;
11448 }
11449
11450 caller = a->argv[3];
11451 queuename = a->argv[5];
11452
11453 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
11454 case RES_OKAY:
11455 res = CLI_SUCCESS;
11456 break;
11457 case RES_NOSUCHQUEUE:
11458 ast_cli(a->fd, "Unable change priority caller %s on queue '%s': No such queue\n", caller, queuename);
11459 break;
11460 case RES_NOT_CALLER:
11461 ast_cli(a->fd, "Unable to change priority caller '%s' on queue '%s': Not there\n", caller, queuename);
11462
11463 break;
11464 }
11465
11466 return res;
11467}
11468
11469
11470
11471static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
11472{
11473 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
11474 switch (pos) {
11475 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
11476 return NULL;
11477 case 4: /* only one possible match, "queue" */
11478 return state == 0 ? ast_strdup("queue") : NULL;
11479 case 5: /* <queue> */
11480 return complete_queue(line, word, pos, state, 0);
11481 case 6: /* "reason" */
11482 return state == 0 ? ast_strdup("reason") : NULL;
11483 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
11484 return NULL;
11485 default:
11486 return NULL;
11487 }
11488}
11489
11490static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11491{
11492 const char *queuename, *interface, *reason;
11493 int paused;
11494
11495 switch (cmd) {
11496 case CLI_INIT:
11497 e->command = "queue {pause|unpause} member";
11498 e->usage =
11499 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
11500 " Pause or unpause a queue member. Not specifying a particular queue\n"
11501 " will pause or unpause a member across all queues to which the member\n"
11502 " belongs.\n";
11503 return NULL;
11504 case CLI_GENERATE:
11505 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
11506 }
11507
11508 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
11509 return CLI_SHOWUSAGE;
11510 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
11511 return CLI_SHOWUSAGE;
11512 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
11513 return CLI_SHOWUSAGE;
11514 }
11515
11516
11517 interface = a->argv[3];
11518 queuename = a->argc >= 6 ? a->argv[5] : NULL;
11519 reason = a->argc == 8 ? a->argv[7] : NULL;
11520 paused = !strcasecmp(a->argv[1], "pause");
11521
11522 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
11523 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
11524 if (!ast_strlen_zero(queuename)) {
11525 ast_cli(a->fd, " in queue '%s'", queuename);
11526 }
11527 if (!ast_strlen_zero(reason)) {
11528 ast_cli(a->fd, " for reason '%s'", reason);
11529 }
11530 ast_cli(a->fd, "\n");
11531 return CLI_SUCCESS;
11532 } else {
11533 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
11534 if (!ast_strlen_zero(queuename)) {
11535 ast_cli(a->fd, " in queue '%s'", queuename);
11536 }
11537 if (!ast_strlen_zero(reason)) {
11538 ast_cli(a->fd, " for reason '%s'", reason);
11539 }
11540 ast_cli(a->fd, "\n");
11541 return CLI_FAILURE;
11542 }
11543}
11544
11545static char *complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
11546{
11547 /* 0 - queue; 1 - set; 2 - penalty/ringinuse; 3 - <value>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
11548 switch (pos) {
11549 case 4:
11550 if (state == 0) {
11551 return ast_strdup("on");
11552 } else {
11553 return NULL;
11554 }
11555 case 6:
11556 if (state == 0) {
11557 return ast_strdup("in");
11558 } else {
11559 return NULL;
11560 }
11561 case 7:
11562 return complete_queue(line, word, pos, state, 0);
11563 default:
11564 return NULL;
11565 }
11566}
11567
11568static char *handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11569{
11570 const char *queuename = NULL, *interface;
11571 int ringinuse;
11572
11573 switch (cmd) {
11574 case CLI_INIT:
11575 e->command = "queue set ringinuse";
11576 e->usage =
11577 "Usage: queue set ringinuse <yes/no> on <interface> [in <queue>]\n"
11578 " Set a member's ringinuse in the queue specified. If no queue is specified\n"
11579 " then that interface's penalty is set in all queues to which that interface is a member.\n";
11580 break;
11581 return NULL;
11582 case CLI_GENERATE:
11583 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11584 }
11585
11586 /* Sensible argument counts */
11587 if (a->argc != 6 && a->argc != 8) {
11588 return CLI_SHOWUSAGE;
11589 }
11590
11591 /* Uses proper indicational words */
11592 if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11593 return CLI_SHOWUSAGE;
11594 }
11595
11596 /* Set the queue name if applicable */
11597 if (a->argc == 8) {
11598 queuename = a->argv[7];
11599 }
11600
11601 /* Interface being set */
11602 interface = a->argv[5];
11603
11604 /* Check and set the ringinuse value */
11605 if (ast_true(a->argv[3])) {
11606 ringinuse = 1;
11607 } else if (ast_false(a->argv[3])) {
11608 ringinuse = 0;
11609 } else {
11610 return CLI_SHOWUSAGE;
11611 }
11612
11613 switch (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11614 case RESULT_SUCCESS:
11615 ast_cli(a->fd, "Set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11616 return CLI_SUCCESS;
11617 case RESULT_FAILURE:
11618 ast_cli(a->fd, "Failed to set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11619 return CLI_FAILURE;
11620 default:
11621 return CLI_FAILURE;
11622 }
11623}
11624
11625static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11626{
11627 const char *queuename = NULL, *interface;
11628 int penalty = 0;
11629
11630 switch (cmd) {
11631 case CLI_INIT:
11632 e->command = "queue set penalty";
11633 e->usage =
11634 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
11635 " Set a member's penalty in the queue specified. If no queue is specified\n"
11636 " then that interface's penalty is set in all queues to which that interface is a member\n";
11637 return NULL;
11638 case CLI_GENERATE:
11639 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11640 }
11641
11642 if (a->argc != 6 && a->argc != 8) {
11643 return CLI_SHOWUSAGE;
11644 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11645 return CLI_SHOWUSAGE;
11646 }
11647
11648 if (a->argc == 8) {
11649 queuename = a->argv[7];
11650 }
11651 interface = a->argv[5];
11652 penalty = atoi(a->argv[3]);
11653
11654 switch (set_member_value(queuename, interface, MEMBER_PENALTY, penalty)) {
11655 case RESULT_SUCCESS:
11656 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11657 return CLI_SUCCESS;
11658 case RESULT_FAILURE:
11659 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11660 return CLI_FAILURE;
11661 default:
11662 return CLI_FAILURE;
11663 }
11664}
11665
11666static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
11667{
11668 int which = 0;
11669 struct rule_list *rl_iter;
11670 int wordlen = strlen(word);
11671 char *ret = NULL;
11672 if (pos != 3) /* Wha? */ {
11673 return NULL;
11674 }
11675
11677 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11678 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
11679 ret = ast_strdup(rl_iter->name);
11680 break;
11681 }
11682 }
11684
11685 return ret;
11686}
11687
11688static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11689{
11690 const char *rule;
11691 struct rule_list *rl_iter;
11692 struct penalty_rule *pr_iter;
11693 switch (cmd) {
11694 case CLI_INIT:
11695 e->command = "queue show rules";
11696 e->usage =
11697 "Usage: queue show rules [rulename]\n"
11698 " Show the list of rules associated with rulename. If no\n"
11699 " rulename is specified, list all rules defined in queuerules.conf\n";
11700 return NULL;
11701 case CLI_GENERATE:
11702 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
11703 }
11704
11705 if (a->argc != 3 && a->argc != 4) {
11706 return CLI_SHOWUSAGE;
11707 }
11708
11709 rule = a->argc == 4 ? a->argv[3] : "";
11711 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11712 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
11713 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
11714 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
11715 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);
11716 }
11717 }
11718 }
11720 return CLI_SUCCESS;
11721}
11722
11723static char *handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11724{
11725 struct ast_flags mask = {QUEUE_RESET_STATS,};
11726 int i;
11727
11728 switch (cmd) {
11729 case CLI_INIT:
11730 e->command = "queue reset stats";
11731 e->usage =
11732 "Usage: queue reset stats [<queuenames>]\n"
11733 "\n"
11734 "Issuing this command will reset statistics for\n"
11735 "<queuenames>, or for all queues if no queue is\n"
11736 "specified.\n";
11737 return NULL;
11738 case CLI_GENERATE:
11739 if (a->pos >= 3) {
11740 return complete_queue(a->line, a->word, a->pos, a->n, 17);
11741 } else {
11742 return NULL;
11743 }
11744 }
11745
11746 if (a->argc < 3) {
11747 return CLI_SHOWUSAGE;
11748 }
11749
11750 if (a->argc == 3) {
11751 reload_handler(1, &mask, NULL);
11752 return CLI_SUCCESS;
11753 }
11754
11755 for (i = 3; i < a->argc; ++i) {
11756 reload_handler(1, &mask, a->argv[i]);
11757 }
11758
11759 return CLI_SUCCESS;
11760}
11761
11762static char *handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11763{
11764 struct ast_flags mask = {0,};
11765 int i;
11766
11767 switch (cmd) {
11768 case CLI_INIT:
11769 e->command = "queue reload {parameters|members|rules|all}";
11770 e->usage =
11771 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
11772 "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
11773 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
11774 "specified in order to know what information to reload. Below is an explanation\n"
11775 "of each of these qualifiers.\n"
11776 "\n"
11777 "\t'members' - reload queue members from queues.conf\n"
11778 "\t'parameters' - reload all queue options except for queue members\n"
11779 "\t'rules' - reload the queuerules.conf file\n"
11780 "\t'all' - reload queue rules, parameters, and members\n"
11781 "\n"
11782 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
11783 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
11784 "one queue is specified when using this command, reloading queue rules may cause\n"
11785 "other queues to be affected\n";
11786 return NULL;
11787 case CLI_GENERATE:
11788 if (a->pos >= 3) {
11789 /* find the point at which the list of queue names starts */
11790 const char *command_end = a->line + strlen("queue reload ");
11791 command_end = strchr(command_end, ' ');
11792 if (!command_end) {
11793 command_end = a->line + strlen(a->line);
11794 }
11795 return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
11796 } else {
11797 return NULL;
11798 }
11799 }
11800
11801 if (a->argc < 3)
11802 return CLI_SHOWUSAGE;
11803
11804 if (!strcasecmp(a->argv[2], "rules")) {
11806 } else if (!strcasecmp(a->argv[2], "members")) {
11808 } else if (!strcasecmp(a->argv[2], "parameters")) {
11810 } else if (!strcasecmp(a->argv[2], "all")) {
11812 }
11813
11814 if (a->argc == 3) {
11815 reload_handler(1, &mask, NULL);
11816 return CLI_SUCCESS;
11817 }
11818
11819 for (i = 3; i < a->argc; ++i) {
11820 reload_handler(1, &mask, a->argv[i]);
11821 }
11822
11823 return CLI_SUCCESS;
11824}
11825
11826/*!
11827 * \brief Update Queue with data of an outgoing call
11828*/
11829static int qupd_exec(struct ast_channel *chan, const char *data)
11830{
11831 int oldtalktime;
11832 char *parse;
11833 struct call_queue *q;
11834 struct member *mem;
11835 int newtalktime = 0;
11836
11838 AST_APP_ARG(queuename);
11839 AST_APP_ARG(uniqueid);
11840 AST_APP_ARG(agent);
11842 AST_APP_ARG(talktime);
11843 AST_APP_ARG(params););
11844
11845 if (ast_strlen_zero(data)) {
11846 ast_log(LOG_WARNING, "QueueUpdate requires arguments (queuename,uniqueid,agent,status,talktime,params[totaltime,callednumber])\n");
11847 return -1;
11848 }
11849
11850 parse = ast_strdupa(data);
11851
11853
11854 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) || ast_strlen_zero(args.agent) || ast_strlen_zero(args.status)) {
11855 ast_log(LOG_WARNING, "Missing argument to QueueUpdate (queuename,uniqueid,agent,status,talktime,params[totaltime|callednumber])\n");
11856 return -1;
11857 }
11858
11859 if (!ast_strlen_zero(args.talktime)) {
11860 newtalktime = atoi(args.talktime);
11861 }
11862
11863 q = find_load_queue_rt_friendly(args.queuename);
11864 if (!q) {
11865 ast_log(LOG_WARNING, "QueueUpdate could not find requested queue '%s'\n", args.queuename);
11866 return 0;
11867 }
11868
11869 ao2_lock(q);
11870 if (q->members) {
11871 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
11872 while ((mem = ao2_iterator_next(&mem_iter))) {
11873 if (!strcasecmp(mem->membername, args.agent)) {
11874 if (!strcasecmp(args.status, "ANSWER")) {
11875 oldtalktime = q->talktime;
11876 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
11877 time(&mem->lastcall);
11878 mem->calls++;
11879 mem->lastqueue = q;
11880 q->callscompleted++;
11881
11882 if (newtalktime <= q->servicelevel) {
11883 q->callscompletedinsl++;
11884 }
11885 } else {
11886
11887 time(&mem->lastcall);
11888 q->callsabandoned++;
11889 }
11890
11891 ast_queue_log(args.queuename, args.uniqueid, args.agent, "OUTCALL", "%s|%s|%s", args.status, args.talktime, args.params);
11892 }
11893
11894 ao2_ref(mem, -1);
11895 }
11896
11897 ao2_iterator_destroy(&mem_iter);
11898 }
11899
11900 ao2_unlock(q);
11901 queue_t_unref(q, "Done with temporary pointer");
11902
11903 return 0;
11904}
11905
11906static struct ast_cli_entry cli_queue[] = {
11907 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
11908 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
11909 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
11910 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
11911 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
11912 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
11913 AST_CLI_DEFINE(handle_queue_set_member_ringinuse, "Set ringinuse for a channel of a specified queue"),
11914 AST_CLI_DEFINE(handle_queue_reload, "Reload queues, members, queue rules, or parameters"),
11915 AST_CLI_DEFINE(handle_queue_reset, "Reset statistics for a queue"),
11916 AST_CLI_DEFINE(handle_queue_change_priority_caller, "Change priority caller on queue"),
11917};
11918
11921
11922static int unload_module(void)
11923{
11926
11928
11929 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_join_type);
11930 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_leave_type);
11931 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_abandon_type);
11932
11933 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_status_type);
11934 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_added_type);
11935 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_removed_type);
11936 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_pause_type);
11937 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_penalty_type);
11938 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_ringinuse_type);
11939
11940 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_called_type);
11941 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_connect_type);
11942 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_complete_type);
11943 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_dump_type);
11944 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_ringnoanswer_type);
11945
11947 ast_manager_unregister("QueueStatus");
11948 ast_manager_unregister("QueueRule");
11949 ast_manager_unregister("QueueSummary");
11950 ast_manager_unregister("QueueAdd");
11951 ast_manager_unregister("QueueRemove");
11952 ast_manager_unregister("QueuePause");
11953 ast_manager_unregister("QueueLog");
11954 ast_manager_unregister("QueueUpdate");
11955 ast_manager_unregister("QueuePenalty");
11956 ast_manager_unregister("QueueReload");
11957 ast_manager_unregister("QueueReset");
11958 ast_manager_unregister("QueueMemberRingInUse");
11959 ast_manager_unregister("QueueChangePriorityCaller");
11960 ast_manager_unregister("QueueWithdrawCaller");
11976
11978
11979 ast_unload_realtime("queue_members");
11982
11983 queues = NULL;
11984 return 0;
11985}
11986
11987/*!
11988 * \brief Load the module
11989 *
11990 * Module loading including tests for configuration or dependencies.
11991 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
11992 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
11993 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
11994 * configuration file or other non-critical problem return
11995 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
11996 */
11997static int load_module(void)
11998{
11999 int err = 0;
12000 struct ast_flags mask = {AST_FLAGS_ALL, };
12001 struct ast_config *member_config;
12002 struct stasis_topic *queue_topic;
12004
12007 if (!queues) {
12009 }
12010
12013 if (!pending_members) {
12014 unload_module();
12016 }
12017
12018 use_weight = 0;
12019
12020 if (reload_handler(0, &mask, NULL)) {
12021 unload_module();
12023 }
12024
12025 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, "reason_paused", RQ_CHAR, 80, SENTINEL);
12026
12027 /*
12028 * This section is used to determine which name for 'ringinuse' to use in realtime members
12029 * Necessary for supporting older setups.
12030 *
12031 * It also checks if 'reason_paused' exists in the realtime backend
12032 */
12033 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
12034 if (!member_config) {
12035 realtime_ringinuse_field = "ringinuse";
12036 } else {
12037 const char *config_val;
12038
12039 if ((config_val = ast_variable_retrieve(member_config, NULL, "ringinuse"))) {
12040 ast_log(LOG_NOTICE, "ringinuse field entries found in queue_members table. Using 'ringinuse'\n");
12041 realtime_ringinuse_field = "ringinuse";
12042 } else if ((config_val = ast_variable_retrieve(member_config, NULL, "ignorebusy"))) {
12043 ast_log(LOG_NOTICE, "ignorebusy field found in queue_members table with no ringinuse field. Using 'ignorebusy'\n");
12044 realtime_ringinuse_field = "ignorebusy";
12045 } else {
12046 ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
12047 realtime_ringinuse_field = "ringinuse";
12048 }
12049
12050 if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
12052 }
12053 }
12054 ast_config_destroy(member_config);
12055
12058 }
12059
12068 err |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
12069 err |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
12076 err |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
12077 err |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
12078 err |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
12079 err |= ast_manager_register_xml("QueueChangePriorityCaller", 0, manager_change_priority_caller_on_queue);
12089
12090 /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
12092 if (!device_state_sub) {
12093 err = -1;
12094 }
12097
12099 queue_topic = ast_queue_topic_all();
12100 if (!manager_topic || !queue_topic) {
12101 unload_module();
12103 }
12105 if (!topic_forwarder) {
12106 unload_module();
12108 }
12109
12112 unload_module();
12114 }
12116 if (!agent_router) {
12117 unload_module();
12119 }
12123 NULL);
12127 NULL);
12128
12129 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_join_type);
12130 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_leave_type);
12131 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_abandon_type);
12132
12133 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_status_type);
12134 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_added_type);
12135 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_removed_type);
12136 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_pause_type);
12137 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_penalty_type);
12138 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_ringinuse_type);
12139
12140 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_called_type);
12141 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_connect_type);
12142 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_complete_type);
12143 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_dump_type);
12144 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_ringnoanswer_type);
12145
12146 if (err) {
12147 unload_module();
12149 }
12151}
12152
12153static int reload(void)
12154{
12155 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
12156 ast_unload_realtime("queue_members");
12157 reload_handler(1, &mask, NULL);
12158 return 0;
12159}
12160
12161/*!
12162 * \brief Find a member by looking up queuename and interface.
12163 * \return member or NULL if member not found.
12164 */
12165static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface)
12166{
12167 struct member *mem = NULL;
12168 struct call_queue *q;
12169
12170 if ((q = find_load_queue_rt_friendly(queuename))) {
12171 ao2_lock(q);
12172 mem = ao2_find(q->members, interface, OBJ_KEY);
12173 ao2_unlock(q);
12174 queue_t_unref(q, "Expiring temporary reference.");
12175 }
12176 return mem;
12177}
12178
12180 .support_level = AST_MODULE_SUPPORT_CORE,
12181 .load = load_module,
12182 .unload = unload_module,
12183 .reload = reload,
12184 .load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
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:9658
static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6546
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:3035
static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:3732
static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
Definition: app_queue.c:4746
static void load_realtime_queues(const char *queuename)
Definition: app_queue.c:4126
static struct member * interface_exists(struct call_queue *q, const char *interface)
Definition: app_queue.c:7628
static int is_our_turn(struct queue_ent *qe)
Check if we should start attempting to call queue members.
Definition: app_queue.c:5915
static void record_abandoned(struct queue_ent *qe)
Record that a caller gave up on waiting in queue.
Definition: app_queue.c:5249
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:9215
static char * queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:10584
static int get_wrapuptime(struct call_queue *q, struct member *member)
Return wrapuptime.
Definition: app_queue.c:2161
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:3748
static int context_included(const char *parent, const char *child)
Returns if one context includes another context.
Definition: app_queue.c:2947
static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
Set variables of queue.
Definition: app_queue.c:2233
static int manager_queue_reset(struct mansession *s, const struct message *m)
Definition: app_queue.c:11031
static struct ast_manager_event_blob * queue_member_ringinuse_to_ami(struct stasis_message *message)
Definition: app_queue.c:2369
static struct ast_manager_event_blob * queue_member_penalty_to_ami(struct stasis_message *message)
Definition: app_queue.c:2364
member_properties
Definition: app_queue.c:1935
@ MEMBER_RINGINUSE
Definition: app_queue.c:1937
@ MEMBER_PENALTY
Definition: app_queue.c:1936
#define RES_NOT_CALLER
Definition: app_queue.c:1737
STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_join_type,.to_ami=queue_caller_join_to_ami,)
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:6943
#define ANNOUNCEPOSITION_MORE_THAN
Definition: app_queue.c:1958
static int pending_members_cmp(void *obj, void *arg, int flags)
Definition: app_queue.c:2706
static void queue_reset_global_params(void)
Definition: app_queue.c:9775
static int log_caller_id_name
queues.conf [general] option
Definition: app_queue.c:1787
static char * complete_queue_rule_show(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11666
static void dump_queue_members(struct call_queue *pm_queue)
Dump all members in a specific queue to the database.
Definition: app_queue.c:7655
#define QUEUE_UNPAUSED_DEVSTATE
Definition: app_queue.c:3707
static struct ast_custom_function queuemembercount_function
Definition: app_queue.c:9663
static struct ast_custom_function queuewaitingcount_function
Definition: app_queue.c:9679
static int play_file(struct ast_channel *chan, const char *filename)
Definition: app_queue.c:4313
static int queue_persistent_members
queues.conf [general] option
Definition: app_queue.c:1757
static const struct @50 queue_results[]
static int montype_default
queues.conf [general] option
Definition: app_queue.c:1766
static int mark_member_dead(void *obj, void *arg, int flags)
Definition: app_queue.c:9958
static char * app_pqm
Definition: app_queue.c:1745
static struct ast_custom_function queuememberlist_function
Definition: app_queue.c:9684
static struct ast_manager_event_blob * queue_channel_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2285
static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
Definition: app_queue.c:8118
static char * realtime_ringinuse_field
name of the ringinuse field in the realtime database
Definition: app_queue.c:1790
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
Check if members are available.
Definition: app_queue.c:2592
static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6693
static int remove_from_queue(const char *queuename, const char *interface)
Remove member from queue.
Definition: app_queue.c:7710
static int manager_add_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10841
#define MAX_PERIODIC_ANNOUNCEMENTS
Definition: app_queue.c:1723
static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
Definition: app_queue.c:3459
static int aqm_exec(struct ast_channel *chan, const char *data)
AddQueueMember application.
Definition: app_queue.c:8524
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
sets the QUEUESTATUS channel variable
Definition: app_queue.c:2080
static struct ast_manager_event_blob * queue_member_pause_to_ami(struct stasis_message *message)
Definition: app_queue.c:2359
static void leave_queue(struct queue_ent *qe)
Caller leaving queue.
Definition: app_queue.c:4542
#define MAX_QUEUE_BUCKETS
Definition: app_queue.c:1730
static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
The command center for all reload operations.
Definition: app_queue.c:10233
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:9173
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:9585
static char * complete_queue_pause_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11471
static void do_hang(struct callattempt *o)
common hangup actions
Definition: app_queue.c:4797
static int set_member_value(const char *queuename, const char *interface, int property, int value)
Definition: app_queue.c:8170
static void reload_single_member(const char *memberdata, struct call_queue *q)
reload information pertaining to a single member
Definition: app_queue.c:9826
static struct ast_manager_event_blob * queue_agent_ringnoanswer_to_ami(struct stasis_message *message)
Definition: app_queue.c:2454
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:6299
static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
reload the queues.conf file
Definition: app_queue.c:10146
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
Playback announcement to queued members if period has elapsed.
Definition: app_queue.c:5188
static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
Reload information pertaining to a particular queue.
Definition: app_queue.c:9989
static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
Definition: app_queue.c:7075
static struct ast_manager_event_blob * queue_member_removed_to_ami(struct stasis_message *message)
Definition: app_queue.c:2354
#define DEFAULT_RETRY
Definition: app_queue.c:1720
static int upqm_exec(struct ast_channel *chan, const char *data)
UnpauseQueueMember application.
Definition: app_queue.c:8417
static void clear_queue(struct call_queue *q)
Definition: app_queue.c:3221
static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle an attended transfer event.
Definition: app_queue.c:6638
static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
Definition: app_queue.c:4519
#define RES_NOT_DYNAMIC
Definition: app_queue.c:1736
static int compare_weight(struct call_queue *rq, struct member *member)
Definition: app_queue.c:4712
static char * handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11490
static int set_member_ringinuse_help_members(struct call_queue *q, const char *interface, int ringinuse)
Definition: app_queue.c:8131
static void queue_agent_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6330
queue_timeout_priority
Definition: app_queue.c:1822
@ TIMEOUT_PRIORITY_CONF
Definition: app_queue.c:1824
@ TIMEOUT_PRIORITY_APP
Definition: app_queue.c:1823
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:9537
static char * app_ql
Definition: app_queue.c:1749
static void queue_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9787
static void reload_queue_members(void)
Reload dynamic queue members persisted into the astdb.
Definition: app_queue.c:8270
static int rqm_exec(struct ast_channel *chan, const char *data)
RemoveQueueMember application.
Definition: app_queue.c:8453
static int valid_exit(struct queue_ent *qe, char digit)
Check for valid exit from queue via goto.
Definition: app_queue.c:4342
aqm_flags
Definition: app_queue.c:1657
@ AQMFLAG_REASON
Definition: app_queue.c:1659
@ AQMFLAG_PAUSED
Definition: app_queue.c:1658
static int is_member_available(struct call_queue *q, struct member *mem)
Definition: app_queue.c:2776
#define QUEUE_PAUSED_DEVSTATE
Definition: app_queue.c:3706
#define ANNOUNCEPOSITION_NO
Definition: app_queue.c:1957
static char * handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11625
static void handle_masquerade(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6884
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:6054
static void print_queue(struct mansession *s, int fd, struct call_queue *q)
Print a single queue to AMI or the CLI.
Definition: app_queue.c:10260
char * text
Definition: app_queue.c:1809
#define MAX_CALL_ATTEMPT_BUCKETS
Definition: app_queue.c:2684
static char * app_rqm
Definition: app_queue.c:1743
static void copy_rules(struct queue_ent *qe, const char *rulename)
Copy rule from global list into specified queue.
Definition: app_queue.c:8652
static int manager_queue_rule_show(struct mansession *s, const struct message *m)
Definition: app_queue.c:10600
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:7892
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
Definition: app_queue.c:8022
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:7816
static struct ao2_container * queues
Definition: app_queue.c:2071
static int negative_penalty_invalid
queues.conf [general] option
Definition: app_queue.c:1778
#define AST_MAX_WATCHERS
Definition: app_queue.c:5361
static int load_realtime_rules(void)
Load queue rules from realtime.
Definition: app_queue.c:3357
static void queue_rules_reset_global_params(void)
Definition: app_queue.c:9696
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
Definition: app_queue.c:6142
static void escape_and_substitute(struct ast_channel *chan, const char *input, char *output, size_t size)
Definition: app_queue.c:7044
#define RES_OUTOFMEMORY
Definition: app_queue.c:1734
static int member_hash_fn(const void *obj, const int flags)
Definition: app_queue.c:3090
static int member_cmp_fn(void *obj1, void *obj2, int flags)
Definition: app_queue.c:3106
#define queues_t_unlink(c, q, tag)
Definition: app_queue.c:2230
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY
The minimum number of seconds between position announcements.
Definition: app_queue.c:1728
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:4639
static void queue_stasis_data_destructor(void *obj)
Definition: app_queue.c:6429
static struct ast_manager_event_blob * queue_member_added_to_ami(struct stasis_message *message)
Definition: app_queue.c:2349
static struct ast_manager_event_blob * queue_agent_dump_to_ami(struct stasis_message *message)
Definition: app_queue.c:2449
static int extensionstate2devicestate(int state)
Helper function which converts from extension state to device state values.
Definition: app_queue.c:2898
#define queues_t_link(c, q, tag)
Definition: app_queue.c:2229
static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
Definition: app_queue.c:2976
static int manager_queue_reload(struct mansession *s, const struct message *m)
Definition: app_queue.c:10999
static int get_member_penalty(char *queuename, char *interface)
Gets members penalty.
Definition: app_queue.c:8239
static int realtime_rules
queuerules.conf [general] option
Definition: app_queue.c:1772
static void handle_local_optimization_begin(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6712
static int pqm_exec(struct ast_channel *chan, const char *data)
PauseQueueMember application.
Definition: app_queue.c:8381
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.
Definition: app_queue.c:10539
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:6214
static char * handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11723
static int qupd_exec(struct ast_channel *chan, const char *data)
Update Queue with data of an outgoing call.
Definition: app_queue.c:11829
static char * complete_queue_show(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:10576
static char * handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11568
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:9129
static int reload_queue_rules(int reload)
Reload the rules defined in queuerules.conf.
Definition: app_queue.c:9716
static char * complete_queue_add_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11046
static char * handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11417
static char * handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11688
static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
Definition: app_queue.c:11077
static char * complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11545
@ OPT_CALLER_AUTOMON
Definition: app_queue.c:1616
@ OPT_CALLEE_PARK
Definition: app_queue.c:1606
@ OPT_PREDIAL_CALLER
Definition: app_queue.c:1618
@ OPT_GO_ON
Definition: app_queue.c:1599
@ OPT_IGNORE_CONNECTEDLINE
Definition: app_queue.c:1605
@ OPT_CALLEE_AUTOMON
Definition: app_queue.c:1615
@ OPT_CALLEE_TRANSFER
Definition: app_queue.c:1611
@ OPT_CALLEE_GO_ON
Definition: app_queue.c:1601
@ OPT_MARK_AS_ANSWERED
Definition: app_queue.c:1598
@ OPT_IGNORE_CALL_FW
Definition: app_queue.c:1604
@ OPT_CALLER_PARK
Definition: app_queue.c:1607
@ OPT_NO_RETRY
Definition: app_queue.c:1608
@ OPT_DATA_QUALITY
Definition: app_queue.c:1600
@ OPT_CALLER_HANGUP
Definition: app_queue.c:1603
@ OPT_MUSICONHOLD_CLASS
Definition: app_queue.c:1619
@ OPT_CALLEE_AUTOMIXMON
Definition: app_queue.c:1613
@ OPT_CALLEE_HANGUP
Definition: app_queue.c:1602
@ OPT_CALLER_AUTOMIXMON
Definition: app_queue.c:1614
@ OPT_RINGING
Definition: app_queue.c:1609
@ OPT_CALLER_TRANSFER
Definition: app_queue.c:1612
@ OPT_PREDIAL_CALLEE
Definition: app_queue.c:1617
@ OPT_RING_WHEN_RINGING
Definition: app_queue.c:1610
static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
Definition: app_queue.c:4827
static const char * int2strat(int strategy)
Definition: app_queue.c:2092
#define RES_NOSUCHQUEUE
Definition: app_queue.c:1735
static void free_members(struct call_queue *q, int all)
Iterate through queue's member list and delete them.
Definition: app_queue.c:3866
static int publish_queue_member_pause(struct call_queue *q, struct member *member)
Definition: app_queue.c:7931
static void callattempt_free(struct callattempt *doomed)
Definition: app_queue.c:4617
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:5164
#define queue_t_unref(q, tag)
Definition: app_queue.c:2228
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:5375
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:2745
static char * app_qupd
Definition: app_queue.c:1751
#define queue_unref(q)
Definition: app_queue.c:2226
#define ANNOUNCEHOLDTIME_ALWAYS
Definition: app_queue.c:1941
static const struct ast_app_option queue_exec_options[128]
Definition: app_queue.c:1654
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:2510
static int shared_lastcall
queues.conf [general] option
Definition: app_queue.c:1769
static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
Definition: app_queue.c:11111
#define queue_t_ref(q, tag)
Definition: app_queue.c:2227
AO2_STRING_FIELD_SORT_FN(call_queue, name)
static char * __queues_show(struct mansession *s, int fd, int argc, const char *const *argv)
Show queue(s) status and statistics.
Definition: app_queue.c:10368
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:3502
#define ANNOUNCEPOSITION_LIMIT
Definition: app_queue.c:1959
static char * app_upqm
Definition: app_queue.c:1747
static int clear_stats(const char *queuename)
Facilitates resetting statistics for a queue.
Definition: app_queue.c:10203
static struct ast_custom_function queuememberpenalty_function
Definition: app_queue.c:9689
static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
Definition: app_queue.c:7955
static int queue_cmp_cb(void *obj, void *arg, int flags)
Definition: app_queue.c:2148
queue_result
Definition: app_queue.c:1795
@ QUEUE_FULL
Definition: app_queue.c:1802
@ QUEUE_UNKNOWN
Definition: app_queue.c:1796
@ QUEUE_WITHDRAW
Definition: app_queue.c:1804
@ QUEUE_CONTINUE
Definition: app_queue.c:1803
@ QUEUE_LEAVEEMPTY
Definition: app_queue.c:1799
@ QUEUE_LEAVEUNAVAIL
Definition: app_queue.c:1801
@ QUEUE_JOINUNAVAIL
Definition: app_queue.c:1800
@ QUEUE_JOINEMPTY
Definition: app_queue.c:1798
@ QUEUE_TIMEOUT
Definition: app_queue.c:1797
static int manager_request_withdraw_caller_from_queue(struct mansession *s, const struct message *m)
Definition: app_queue.c:11184
static char * app
Definition: app_queue.c:1739
static int set_member_value_help_members(struct call_queue *q, const char *interface, int property, int value)
Definition: app_queue.c:8147
static int queue_hash_cb(const void *obj, const int flags)
Definition: app_queue.c:2141
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:3921
static const char *const pm_family
Persistent Members astdb family.
Definition: app_queue.c:1754
#define queue_ref(q)
Definition: app_queue.c:2225
static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Get the total number of members in a specific queue (Deprecated)
Definition: app_queue.c:9380
static int log_membername_as_agent
queues.conf [general] option
Definition: app_queue.c:1781
static void update_qe_rule(struct queue_ent *qe)
update rules for queues
Definition: app_queue.c:5964
static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
Definition: app_queue.c:5345
static struct ast_manager_event_blob * queue_agent_called_to_ami(struct stasis_message *message)
Definition: app_queue.c:2434
static int queue_exec(struct ast_channel *chan, const char *data)
The starting point for all queue calls.
Definition: app_queue.c:8695
static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
Definition: app_queue.c:4229
static char * handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11354
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:5280
static const struct ast_app_option aqm_opts[128]
Definition: app_queue.c:1670
static void destroy_queue_member_cb(void *obj)
Definition: app_queue.c:3025
static void init_queue(struct call_queue *q)
Initialize Queue default values.
Definition: app_queue.c:3119
static struct ao2_container * pending_members
Definition: app_queue.c:2683
static struct ast_manager_event_blob * queue_multi_channel_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2393
static struct member * find_member_by_queuename_and_interface(const char *queuename, const char *interface)
Find a member by looking up queuename and interface.
Definition: app_queue.c:12165
static int force_longest_waiting_caller
queues.conf [general] option
Definition: app_queue.c:1784
#define RES_OKAY
Definition: app_queue.c:1732
static struct ast_manager_event_blob * queue_member_status_to_ami(struct stasis_message *message)
Definition: app_queue.c:2344
static int kill_dead_members(void *obj, void *arg, int flags)
Definition: app_queue.c:9967
#define DEFAULT_TIMEOUT
Definition: app_queue.c:1721
static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
Definition: app_queue.c:6446
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:9500
static int pending_members_hash(const void *obj, const int flags)
Definition: app_queue.c:2686
static int autofill_default
queues.conf [general] option
Definition: app_queue.c:1763
static int manager_pause_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10953
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:2259
#define ANNOUNCEHOLDTIME_ONCE
Definition: app_queue.c:1942
static struct ast_manager_event_blob * queue_caller_join_to_ami(struct stasis_message *message)
Definition: app_queue.c:2304
static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
Definition: app_queue.c:2194
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
Change queue penalty by adding rule.
Definition: app_queue.c:3253
static char * handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11762
static int num_available_members(struct call_queue *q)
Get the number of members available to accept a call.
Definition: app_queue.c:4679
static int ql_exec(struct ast_channel *chan, const char *data)
QueueLog application.
Definition: app_queue.c:8618
static int manager_remove_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10906
static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
Definition: app_queue.c:4154
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:5140
static struct stasis_message_router * agent_router
Definition: app_queue.c:11919
static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:2212
static struct member * get_interface_helper(struct call_queue *q, const char *interface)
Definition: app_queue.c:9192
static int mark_unfound(void *obj, void *arg, int flags)
Definition: app_queue.c:10112
queue_reload_mask
Definition: app_queue.c:1689
@ QUEUE_RELOAD_RULES
Definition: app_queue.c:1692
@ QUEUE_RELOAD_MEMBER
Definition: app_queue.c:1691
@ QUEUE_RESET_STATS
Definition: app_queue.c:1693
@ QUEUE_RELOAD_PARAMETERS
Definition: app_queue.c:1690
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:2811
static int load_module(void)
Load the module.
Definition: app_queue.c:11997
static void member_add_to_queue(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:3716
#define RECHECK
Definition: app_queue.c:1722
static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
Part 2 of ring_one.
Definition: app_queue.c:4923
static void pending_members_remove(struct member *mem)
Definition: app_queue.c:2734
static void end_bridge_callback(void *data)
Definition: app_queue.c:7004
enum queue_result id
Definition: app_queue.c:1808
static struct stasis_subscription * device_state_sub
Subscription to device state change messages.
Definition: app_queue.c:1775
static int manager_queues_summary(struct mansession *s, const struct message *m)
Summary of queue info via the AMI.
Definition: app_queue.c:10636
static struct call_queue * alloc_queue(const char *queuename)
Definition: app_queue.c:3897
static struct callattempt * find_best(struct callattempt *outgoing)
find the entry with the best metric, or NULL
Definition: app_queue.c:5055
static int get_queue_member_status(struct member *cur)
Return the current state of a member.
Definition: app_queue.c:3020
static int unload_module(void)
Definition: app_queue.c:11922
static int reload(void)
Definition: app_queue.c:12153
#define QUEUE_UNKNOWN_PAUSED_DEVSTATE
Definition: app_queue.c:3708
static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
Definition: app_queue.c:4627
static int manager_queues_status(struct mansession *s, const struct message *m)
Queue status info via AMI.
Definition: app_queue.c:10718
static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6763
static int say_position(struct queue_ent *qe, int ringing)
Definition: app_queue.c:4377
static int realtime_reason_paused
does realtime backend support reason_paused
Definition: app_queue.c:1793
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:6461
static void update_realtime_members(struct call_queue *q)
Definition: app_queue.c:4170
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:9617
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:2475
static struct ast_custom_function queuemembercount_dep
Definition: app_queue.c:9669
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:7763
static struct ast_manager_event_blob * queue_agent_connect_to_ami(struct stasis_message *message)
Definition: app_queue.c:2439
static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
Definition: app_queue.c:6997
empty_conditions
Definition: app_queue.c:1924
@ QUEUE_EMPTY_INVALID
Definition: app_queue.c:1930
@ QUEUE_EMPTY_UNKNOWN
Definition: app_queue.c:1931
@ QUEUE_EMPTY_PENALTY
Definition: app_queue.c:1925
@ QUEUE_EMPTY_RINGING
Definition: app_queue.c:1928
@ QUEUE_EMPTY_INUSE
Definition: app_queue.c:1927
@ QUEUE_EMPTY_UNAVAILABLE
Definition: app_queue.c:1929
@ QUEUE_EMPTY_WRAPUP
Definition: app_queue.c:1932
@ QUEUE_EMPTY_PAUSED
Definition: app_queue.c:1926
static struct ast_json * queue_member_blob_create(struct call_queue *q, struct member *mem)
Definition: app_queue.c:2565
static void queue_channel_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6918
static int use_weight
Records that one or more queues use weight.
Definition: app_queue.c:1760
#define ANNOUNCEPOSITION_YES
Definition: app_queue.c:1956
static int wait_a_bit(struct queue_ent *qe)
Definition: app_queue.c:7615
static char * app_aqm
Definition: app_queue.c:1741
static void destroy_queue(void *obj)
Free queue's member list then its string fields.
Definition: app_queue.c:3882
static int kill_if_unfound(void *obj, void *arg, int flags)
Definition: app_queue.c:10122
static void handle_hangup(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6827
static void do_print(struct mansession *s, int fd, const char *str)
direct output to manager or cli with proper terminator
Definition: app_queue.c:10250
static char * complete_queue_remove_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11306
static char * handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11221
#define RES_EXISTS
Definition: app_queue.c:1733
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:9317
static void queue_rules_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9702
static int member_status_available(int status)
Definition: app_queue.c:4813
static int queue_member_decrement_followers(void *obj, void *arg, int flag)
Definition: app_queue.c:2175
static struct ast_cli_entry cli_queue[]
Definition: app_queue.c:11906
@ QUEUE_STRATEGY_RINGALL
Definition: app_queue.c:1673
@ QUEUE_STRATEGY_RRMEMORY
Definition: app_queue.c:1677
@ QUEUE_STRATEGY_LINEAR
Definition: app_queue.c:1678
@ QUEUE_STRATEGY_LEASTRECENT
Definition: app_queue.c:1674
@ QUEUE_STRATEGY_RANDOM
Definition: app_queue.c:1676
@ QUEUE_STRATEGY_FEWESTCALLS
Definition: app_queue.c:1675
@ QUEUE_STRATEGY_RRORDERED
Definition: app_queue.c:1680
@ QUEUE_STRATEGY_WRANDOM
Definition: app_queue.c:1679
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:4065
static struct ast_custom_function queuegetchannel_function
Definition: app_queue.c:9674
static struct ast_manager_event_blob * queue_caller_abandon_to_ami(struct stasis_message *message)
Definition: app_queue.c:2314
static struct stasis_forward * topic_forwarder
Definition: app_queue.c:11920
static struct ast_custom_function queueexists_function
Definition: app_queue.c:9653
aqm_args
Definition: app_queue.c:1662
@ AQM_OPT_ARG_ARRAY_SIZE
Definition: app_queue.c:1664
@ AQM_OPT_ARG_PAUSE_REASON
Definition: app_queue.c:1663
static int strat2int(const char *strategy)
Definition: app_queue.c:2105
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:9421
static struct ast_manager_event_blob * queue_agent_complete_to_ami(struct stasis_message *message)
Definition: app_queue.c:2444
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:7027
static int word_in_list(const char *list, const char *word)
Check if a given word is in a space-delimited list.
Definition: app_queue.c:10480
static void log_attended_transfer(struct queue_stasis_data *queue_data, struct ast_attended_transfer_message *atxfer_msg)
Definition: app_queue.c:6500
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
Place a call to a queue member.
Definition: app_queue.c:5080
@ OPT_ARG_CALLEE_GO_ON
Definition: app_queue.c:1623
@ OPT_ARG_PREDIAL_CALLEE
Definition: app_queue.c:1624
@ OPT_ARG_MUSICONHOLD_CLASS
Definition: app_queue.c:1626
@ OPT_ARG_PREDIAL_CALLER
Definition: app_queue.c:1625
@ OPT_ARG_ARRAY_SIZE
Definition: app_queue.c:1628
static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle a blind transfer event.
Definition: app_queue.c:6579
static const struct autopause autopausesmodes[]
agent_complete_reason
Definition: app_queue.c:6292
@ AGENT
Definition: app_queue.c:6294
@ CALLER
Definition: app_queue.c:6293
@ TRANSFER
Definition: app_queue.c:6295
static struct ast_manager_event_blob * queue_member_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2329
@ QUEUE_AUTOPAUSE_ON
Definition: app_queue.c:1685
@ QUEUE_AUTOPAUSE_OFF
Definition: app_queue.c:1684
@ QUEUE_AUTOPAUSE_ALL
Definition: app_queue.c:1686
static int autopause2int(const char *autopause)
Definition: app_queue.c:2118
static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
Definition: app_queue.c:11137
static int compress_char(const char c)
Definition: app_queue.c:3080
static void queue_publish_member_blob(struct stasis_message_type *type, struct ast_json *blob)
Definition: app_queue.c:2541
static int manager_queue_log_custom(struct mansession *s, const struct message *m)
Definition: app_queue.c:10978
static struct ast_manager_event_blob * queue_caller_leave_to_ami(struct stasis_message *message)
Definition: app_queue.c:2309
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:7148
static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
Definition: app_queue.c:8082
unsigned int stop
Definition: app_sla.c:342
ast_mutex_t lock
Definition: app_sla.c:337
#define var
Definition: ast_expr2f.c:605
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1570
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_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.
Definition: bridge_after.c:622
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
Definition: bridge_basic.h:36
@ 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.
Definition: bridge_roles.c:394
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
enum cc_state state
Definition: ccss.c:399
static int priority
static int available(struct dahdi_pvt **pvt, int is_specific_channel)
Definition: chan_dahdi.c:13552
static const char type[]
Definition: chan_ooh323.c:109
static int call(void *data)
Definition: chan_pjsip.c:2395
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:3203
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
@ 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
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:2149
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Definition: channel.c:6478
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:10364
void ast_party_number_init(struct ast_party_number *init)
Initialize the given number structure.
Definition: channel.c:1671
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2570
@ AST_CHANNEL_REQUESTOR_BRIDGE_PEER
Definition: channel.h:1523
int ast_party_id_presentation(const struct ast_party_id *id)
Determine the overall presentation value for the given party.
Definition: channel.c:1848
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2099
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:7393
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:3185
#define ast_channel_lock(chan)
Definition: channel.h:2970
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
Make the frame formats of two channels compatible.
Definition: channel.c:6737
struct ast_format_cap * ast_channel_nativeformats(const struct ast_channel *chan)
void ast_channel_data_set(struct ast_channel *chan, const char *value)
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:2058
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:2081
int ast_channel_priority(const struct ast_channel *chan)
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2977
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:2397
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:6451
const char * ast_channel_context(const struct ast_channel *chan)
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4274
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:9119
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:2026
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:7355
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:10409
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:6793
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:8811
int ast_channel_supports_html(struct ast_channel *channel)
Checks for HTML support on a channel.
Definition: channel.c:6640
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
int ast_channel_hangupcause(const struct ast_channel *chan)
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:4670
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:10310
#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:1718
#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:2049
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:6652
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:2206
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:8319
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:6456
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:6461
const struct ast_channel_tech * ast_channel_tech(const struct ast_channel *chan)
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:7443
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:2072
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.
Definition: autoservice.c:349
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1481
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:4294
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1601
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:6371
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2971
#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:2162
#define MAX_MUSICCLASS
Definition: channel.h:175
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#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.
Definition: devicestate.c:513
const char * ast_devstate2str(enum ast_device_state devstate) attribute_pure
Convert device state to text string for output.
Definition: devicestate.c:240
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
Definition: devicestate.c:671
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:749
char connected
Definition: eagi_proxy.c:82
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:601
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:222
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1301
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1137
#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:1848
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:2028
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:1986
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:2064
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:454
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2018
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:555
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:1647
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:2072
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1907
static struct stasis_topic * manager_topic
A stasis_topic that all topics AMI cares about will be forwarded to.
Definition: manager.c:186
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7697
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.
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_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_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:3349
#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:3344
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:3066
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:3326
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3274
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: main/config.c:3769
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3842
@ CONFIG_FLAG_FILEUNCHANGED
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3796
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3750
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:869
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3879
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3726
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
#define AST_FRAME_DTMF
#define AST_OPTION_TONE_VERIFY
#define ast_frfree(fr)
@ AST_FRAME_CONTROL
@ AST_CONTROL_OFFHOOK
@ AST_CONTROL_BUSY
@ AST_CONTROL_REDIRECTING
@ AST_CONTROL_CONGESTION
@ AST_CONTROL_ANSWER
@ AST_CONTROL_RINGING
@ AST_CONTROL_HANGUP
@ AST_CONTROL_CONNECTED_LINE
@ AST_CONTROL_AOC
@ 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:962
#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.
Definition: linkedlists.h:291
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
#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.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
Asterisk locking-related definitions:
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
Definition: lock.h:608
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:761
size_t current
Definition: main/cli.c:113
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:192
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:10237
#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.
Definition: max_forwards.c:135
int ast_max_forwards_get(struct ast_channel *chan)
Get the current max forwards for a particular channel.
Definition: max_forwards.c:121
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:392
@ 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:7787
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7797
def info(msg)
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:8665
struct ast_context * ast_context_find(const char *name)
Find a context.
Definition: extconf.c:4172
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:4190
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
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:1559
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8796
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:4205
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:3871
@ 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:3838
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:3185
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: ael_main.c:211
static char url[512]
struct stasis_forward * sub
Definition: res_corosync.c:240
static void to_ami(struct ast_sip_subscription *sub, struct ast_str **buf)
#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:8261
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
#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:1575
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:1050
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:1104
#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:1201
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1161
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:1605
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1538
#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.
int stasis_message_router_set_default(struct stasis_message_router *router, stasis_subscription_cb callback, void *data)
Sets the default route of a router.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#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:2199
#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:2216
#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
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
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
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:1835
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::@286 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
const char *const type
Definition: channel.h:649
Main Channel structure associated with a channel.
const struct ast_channel_tech * tech
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.
Definition: devicestate.h:238
enum ast_device_state state
Definition: devicestate.h:248
const struct ast_eid * eid
The EID of the server where this message originated.
Definition: devicestate.h:246
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Data structure associated with a single frame of data.
union ast_frame::@228 data
struct ast_frame_subclass subclass
enum ast_frame_type frametype
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:503
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
struct ast_party_dialed::@210 number
Dialed/Called number.
char * str
Subscriber phone number (Malloced)
Definition: channel.h:388
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:1712
const char * name
Definition: app_queue.c:1713
unsigned int autopauseunavail
Definition: app_queue.c:2019
int talktime
Definition: app_queue.c:2031
const ast_string_field sound_thereare
Definition: app_queue.c:1999
unsigned int setinterfacevar
Definition: app_queue.c:2005
struct call_queue::@53 list
struct ast_str * sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS]
Definition: app_queue.c:2001
const ast_string_field sound_callerannounce
Definition: app_queue.c:1999
int announcefrequency
Definition: app_queue.c:2023
const ast_string_field sound_reporthold
Definition: app_queue.c:1999
unsigned int announceholdtime
Definition: app_queue.c:2011
const ast_string_field sound_holdtime
Definition: app_queue.c:1999
unsigned int dead
Definition: app_queue.c:2002
unsigned int reportholdtime
Definition: app_queue.c:2008
unsigned int setqueueentryvar
Definition: app_queue.c:2007
const ast_string_field sound_seconds
Definition: app_queue.c:1999
unsigned int timeoutrestart
Definition: app_queue.c:2010
struct ao2_container * members
Definition: app_queue.c:2057
int periodicannouncefrequency
Definition: app_queue.c:2026
const ast_string_field sound_thanks
Definition: app_queue.c:1999
unsigned int announceposition_only_up
Definition: app_queue.c:2013
unsigned int setqueuevar
Definition: app_queue.c:2006
int announcepositionlimit
Definition: app_queue.c:2022
unsigned int announce_to_first_user
Definition: app_queue.c:2004
int randomperiodicannounce
Definition: app_queue.c:2028
int autopause
Definition: app_queue.c:2046
int periodicannouncestartdelay
Definition: app_queue.c:2025
const ast_string_field defaultrule
Definition: app_queue.c:1999
int log_restricted_caller_id
Definition: app_queue.c:2055
struct queue_ent * head
Definition: app_queue.c:2058
struct call_queue::@54 rules
unsigned int realtime
Definition: app_queue.c:2015
const ast_string_field queue_quantity2
Definition: app_queue.c:1999
int servicelevel
Definition: app_queue.c:2035
const ast_string_field moh
Definition: app_queue.c:1999
int autofill
Definition: app_queue.c:2053
int minannouncefrequency
Definition: app_queue.c:2024
enum empty_conditions leavewhenempty
Definition: app_queue.c:2021
int penaltymemberslimit
Definition: app_queue.c:2041
unsigned int found
Definition: app_queue.c:2016
const ast_string_field context
Definition: app_queue.c:1999
const ast_string_field sound_calls
Definition: app_queue.c:1999
unsigned int ringinuse
Definition: app_queue.c:2003
int callsabandoned
Definition: app_queue.c:2033
int roundingseconds
Definition: app_queue.c:2029
int numperiodicannounce
Definition: app_queue.c:2027
int wrapuptime
Definition: app_queue.c:2040
const ast_string_field sound_minute
Definition: app_queue.c:1999
int callscompleted
Definition: app_queue.c:2032
int callsabandonedinsl
Definition: app_queue.c:2034
const ast_string_field sound_minutes
Definition: app_queue.c:1999
unsigned int announceposition
Definition: app_queue.c:2012
const ast_string_field queue_quantity1
Definition: app_queue.c:1999
const ast_string_field membergosub
Definition: app_queue.c:1999
char monfmt[8]
Definition: app_queue.c:2037
enum empty_conditions joinempty
Definition: app_queue.c:2020
unsigned int wrapped
Definition: app_queue.c:2009
int strategy
Definition: app_queue.c:2014
const ast_string_field name
Definition: app_queue.c:1999
int callscompletedinsl
Definition: app_queue.c:2036
int memberdelay
Definition: app_queue.c:2052
const ast_string_field sound_next
Definition: app_queue.c:1999
unsigned int autopausebusy
Definition: app_queue.c:2018
int holdtime
Definition: app_queue.c:2030
const ast_string_field announce
Definition: app_queue.c:1999
int autopausedelay
Definition: app_queue.c:2047
int timeoutpriority
Definition: app_queue.c:2048
unsigned int relativeperiodicannounce
Definition: app_queue.c:2017
We define a custom "local user" structure because we use it not only for keeping track of what is in ...
Definition: app_queue.c:1839
unsigned int dial_callerid_absent
Definition: app_queue.c:1853
unsigned int block_connected_update
Definition: app_queue.c:1851
struct ast_aoc_decoded * aoc_s_rate_list
Definition: app_queue.c:1856
struct ast_party_connected_line connected
Definition: app_queue.c:1847
char interface[256]
Definition: app_queue.c:1843
char * orig_chan_name
Definition: app_queue.c:1858
struct callattempt * call_next
Definition: app_queue.c:1841
struct ast_channel * chan
Definition: app_queue.c:1842
unsigned int stillgoing
Definition: app_queue.c:1855
struct callattempt * q_next
Definition: app_queue.c:1840
struct member * member
Definition: app_queue.c:1845
unsigned int pending_connected_update
Definition: app_queue.c:1849
Definition: astman.c:222
structure to hold extensions
Structure representing relevant data during a local channel optimization.
Definition: app_queue.c:6368
const char * source_chan_uniqueid
Definition: app_queue.c:6370
unsigned int id
Definition: app_queue.c:6374
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:327
Channel datastore data for max forwards.
Definition: max_forwards.c:29
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1897
unsigned int dead
Definition: app_queue.c:1918
unsigned int delme
Definition: app_queue.c:1919
int queuepos
Definition: app_queue.c:1910
time_t starttime
Definition: app_queue.c:1913
time_t lastcall
Definition: app_queue.c:1914
int dynamic
Definition: app_queue.c:1905
time_t logintime
Definition: app_queue.c:1916
char membername[80]
Definition: app_queue.c:1902
char rt_uniqueid[80]
Definition: app_queue.c:1920
int calls
Definition: app_queue.c:1904
int status
Definition: app_queue.c:1907
int penalty
Definition: app_queue.c:1903
int paused
Definition: app_queue.c:1908
unsigned int ringinuse
Definition: app_queue.c:1921
int wrapuptime
Definition: app_queue.c:1912
int callcompletedinsl
Definition: app_queue.c:1911
char state_exten[AST_MAX_EXTENSION]
Definition: app_queue.c:1898
char state_context[AST_MAX_CONTEXT]
Definition: app_queue.c:1899
int realtime
Definition: app_queue.c:1906
int state_id
Definition: app_queue.c:1901
time_t lastpause
Definition: app_queue.c:1915
char reason_paused[80]
Definition: app_queue.c:1909
struct call_queue * lastqueue
Definition: app_queue.c:1917
char state_interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1900
Number structure.
Definition: app_followme.c:157
int raise_relative
Definition: app_queue.c:1952
struct penalty_rule::@52 list
int min_relative
Definition: app_queue.c:1951
int max_relative
Definition: app_queue.c:1950
struct call_queue * q
Definition: app_queue.c:6993
struct ast_channel * chan
Definition: app_queue.c:6994
char digits[AST_MAX_EXTENSION]
Definition: app_queue.c:1867
int valid_digits
Definition: app_queue.c:1869
time_t last_pos
Definition: app_queue.c:1876
time_t last_periodic_announce_time
Definition: app_queue.c:1874
time_t expire
Definition: app_queue.c:1886
unsigned int withdraw
Definition: app_queue.c:1888
struct penalty_rule * pr
Definition: app_queue.c:1892
int linpos
Definition: app_queue.c:1883
int max_penalty
Definition: app_queue.c:1880
int pending
Definition: app_queue.c:1879
int raise_penalty
Definition: app_queue.c:1882
struct ast_channel * chan
Definition: app_queue.c:1890
int min_penalty
Definition: app_queue.c:1881
int ring_when_ringing
Definition: app_queue.c:1873
int cancel_answered_elsewhere
Definition: app_queue.c:1887
struct queue_ent::@51 qe_rules
char announce[PATH_MAX]
Definition: app_queue.c:1865
char * withdraw_info
Definition: app_queue.c:1889
int handled
Definition: app_queue.c:1878
int linwrapped
Definition: app_queue.c:1884
char moh[MAX_MUSICCLASS]
Definition: app_queue.c:1864
int last_pos_said
Definition: app_queue.c:1872
char context[AST_MAX_CONTEXT]
Definition: app_queue.c:1866
int last_periodic_announce_sound
Definition: app_queue.c:1875
const char * predial_callee
Definition: app_queue.c:1868
time_t start
Definition: app_queue.c:1885
struct call_queue * parent
Definition: app_queue.c:1863
struct queue_ent * next
Definition: app_queue.c:1893
User data for stasis subscriptions used for queue calls.
Definition: app_queue.c:6392
const ast_string_field caller_uniqueid
Definition: app_queue.c:6400
const ast_string_field member_uniqueid
Definition: app_queue.c:6400
struct local_optimization member_optimize
Definition: app_queue.c:6422
struct stasis_message_router * channel_router
Definition: app_queue.c:6418
struct call_queue * queue
Definition: app_queue.c:6402
const ast_string_field bridge_uniqueid
Definition: app_queue.c:6400
struct stasis_message_router * bridge_router
Definition: app_queue.c:6416
struct local_optimization caller_optimize
Definition: app_queue.c:6420
struct member * member
Definition: app_queue.c:6404
struct rule_list::@55 rules
char name[80]
Definition: app_queue.c:2064
struct rule_list::@56 list
Forwarding information.
Definition: stasis.c:1558
const char * name
Definition: app_queue.c:1698
int strategy
Definition: app_queue.c:1697
Definition: ast_expr2.c:325
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
const char * args
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:2281
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:63
#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:941
#define ast_assert(a)
Definition: utils.h:739
long int ast_random(void)
Definition: utils.c:2312
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition: utils.c:2343
#define AST_FLAGS_ALL
Definition: utils.h:196