Asterisk - The Open Source Telephony Project GIT-master-5963e62
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 raise_respect_min; /*!< A switch raise_penalty should respect min_penalty not just max_penalty */
1884 int linpos; /*!< If using linear strategy, what position are we at? */
1885 int linwrapped; /*!< Is the linpos wrapped? */
1886 time_t start; /*!< When we started holding */
1887 time_t expire; /*!< When this entry should expire (time out of queue) */
1888 int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
1889 unsigned int withdraw:1; /*!< Should this call exit the queue at its next iteration? Used for QueueWithdrawCaller */
1890 char *withdraw_info; /*!< Optional info passed by the caller of QueueWithdrawCaller */
1891 struct ast_channel *chan; /*!< Our channel */
1892 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
1893 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
1894 struct queue_ent *next; /*!< The next queue entry */
1895};
1896
1897struct member {
1898 char interface[AST_CHANNEL_NAME]; /*!< Technology/Location to dial to reach this member*/
1899 char state_exten[AST_MAX_EXTENSION]; /*!< Extension to get state from (if using hint) */
1900 char state_context[AST_MAX_CONTEXT]; /*!< Context to use when getting state (if using hint) */
1901 char state_interface[AST_CHANNEL_NAME]; /*!< Technology/Location from which to read devicestate changes */
1902 int state_id; /*!< Extension state callback id (if using hint) */
1903 char membername[80]; /*!< Member name to use in queue logs */
1904 int penalty; /*!< Are we a last resort? */
1905 int calls; /*!< Number of calls serviced by this member */
1906 int dynamic; /*!< Are we dynamically added? */
1907 int realtime; /*!< Is this member realtime? */
1908 int status; /*!< Status of queue member */
1909 int paused; /*!< Are we paused (not accepting calls)? */
1910 char reason_paused[80]; /*!< Reason of paused if member is paused */
1911 int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
1912 int callcompletedinsl; /*!< Whether the current call was completed within service level */
1913 int wrapuptime; /*!< Wrapup Time */
1914 time_t starttime; /*!< The time at which the member answered the current caller. */
1915 time_t lastcall; /*!< When last successful call was hungup */
1916 time_t lastpause; /*!< When started the last pause */
1917 time_t logintime; /*!< The time when started the login */
1918 struct call_queue *lastqueue; /*!< Last queue we received a call */
1919 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
1920 unsigned int delme:1; /*!< Flag to delete entry on reload */
1921 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
1922 unsigned int ringinuse:1; /*!< Flag to ring queue members even if their status is 'inuse' */
1923};
1924
1934};
1935
1939};
1940
1941/* values used in multi-bit flags in call_queue */
1942#define ANNOUNCEHOLDTIME_ALWAYS 1
1943#define ANNOUNCEHOLDTIME_ONCE 2
1944#define QUEUE_EVENT_VARIABLES 3
1945
1947 int time; /*!< Number of seconds that need to pass before applying this rule */
1948 int max_value; /*!< The amount specified in the penalty rule for max penalty */
1949 int min_value; /*!< The amount specified in the penalty rule for min penalty */
1950 int raise_value; /*!< The amount specified in the penalty rule for min penalty */
1951 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
1952 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1953 int raise_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1954 int raise_respect_min; /*!< A switch raise_penalty should respect min_penalty not just max_penalty */
1955 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
1956};
1957
1958#define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
1959#define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
1960#define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
1961#define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
1962
1965 /*! Queue name */
1967 /*! Music on Hold class */
1969 /*! Announcement to play when call is answered */
1971 /*! Exit context */
1973 /*! Gosub to run upon member connection */
1975 /*! Default rule to use if none specified in call to Queue() */
1977 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
1979 /*! Sound file: "There are currently" (def. queue-thereare) */
1981 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
1983 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
1985 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
1987 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
1989 /*! Sound file: "minutes." (def. queue-minutes) */
1991 /*! Sound file: "minute." (def. queue-minute) */
1993 /*! Sound file: "seconds." (def. queue-seconds) */
1995 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
1997 /*! Sound file: Custom announce for caller, no default */
1999 /*! Sound file: "Hold time" (def. queue-reporthold) */
2002 /*! Sound files: Custom announce, no default */
2004 unsigned int dead:1;
2005 unsigned int ringinuse:1;
2006 unsigned int announce_to_first_user:1; /*!< Whether or not we announce to the first user in a queue */
2007 unsigned int setinterfacevar:1;
2008 unsigned int setqueuevar:1;
2009 unsigned int setqueueentryvar:1;
2010 unsigned int reportholdtime:1;
2011 unsigned int wrapped:1;
2012 unsigned int timeoutrestart:1;
2013 unsigned int announceholdtime:2;
2014 unsigned int announceposition:3;
2015 unsigned int announceposition_only_up:1; /*!< Only announce position if it has improved */
2017 unsigned int realtime:1;
2018 unsigned int found:1;
2020 unsigned int autopausebusy:1;
2021 unsigned int autopauseunavail:1;
2024 int announcepositionlimit; /*!< How many positions we announce? */
2025 int announcefrequency; /*!< How often to announce their position */
2026 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
2027 int periodicannouncestartdelay; /*!< How long into the queue should the periodic accouncement start */
2028 int periodicannouncefrequency; /*!< How often to play periodic announcement */
2029 int numperiodicannounce; /*!< The number of periodic announcements configured */
2030 int randomperiodicannounce; /*!< Are periodic announcements randomly chosen */
2031 int roundingseconds; /*!< How many seconds do we round to? */
2032 int holdtime; /*!< Current avg holdtime, based on an exponential average */
2033 int talktime; /*!< Current avg talktime, based on the same exponential average */
2034 int callscompleted; /*!< Number of queue calls completed */
2035 int callsabandoned; /*!< Number of queue calls abandoned */
2036 int callsabandonedinsl; /*!< Number of queue calls abandoned in servicelevel */
2037 int servicelevel; /*!< seconds setting for servicelevel*/
2038 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
2039 char monfmt[8]; /*!< Format to use when recording calls */
2040 int count; /*!< How many entries */
2041 int maxlen; /*!< Max number of entries */
2042 int wrapuptime; /*!< Wrapup Time */
2043 int penaltymemberslimit; /*!< Disregard penalty when queue has fewer than this many members */
2044
2045 int retry; /*!< Retry calling everyone after this amount of time */
2046 int timeout; /*!< How long to wait for an answer */
2047 int weight; /*!< Respective weight */
2048 int autopause; /*!< Auto pause queue members if they fail to answer */
2049 int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
2050 int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
2051
2052 /* Queue strategy things */
2053 int rrpos; /*!< Round Robin - position */
2054 int memberdelay; /*!< Seconds to delay connecting member to caller */
2055 int autofill; /*!< Ignore the head call status and ring an available agent */
2056
2057 int log_restricted_caller_id:1; /*!< Whether log Restricted Caller ID */
2058
2059 struct ao2_container *members; /*!< Head of the list of members */
2060 struct queue_ent *head; /*!< Head of the list of callers */
2061 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
2062 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
2063};
2064
2066 char name[80];
2069};
2070
2072
2073static struct ao2_container *queues;
2074
2075static void update_realtime_members(struct call_queue *q);
2076static struct member *interface_exists(struct call_queue *q, const char *interface);
2077static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
2078static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime);
2079
2080static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface);
2081/*! \brief sets the QUEUESTATUS channel variable */
2082static void set_queue_result(struct ast_channel *chan, enum queue_result res)
2083{
2084 int i;
2085
2086 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
2087 if (queue_results[i].id == res) {
2088 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
2089 return;
2090 }
2091 }
2092}
2093
2094static const char *int2strat(int strategy)
2095{
2096 int x;
2097
2098 for (x = 0; x < ARRAY_LEN(strategies); x++) {
2099 if (strategy == strategies[x].strategy) {
2100 return strategies[x].name;
2101 }
2102 }
2103
2104 return "<unknown>";
2105}
2106
2107static int strat2int(const char *strategy)
2108{
2109 int x;
2110
2111 for (x = 0; x < ARRAY_LEN(strategies); x++) {
2112 if (!strcasecmp(strategy, strategies[x].name)) {
2113 return strategies[x].strategy;
2114 }
2115 }
2116
2117 return -1;
2118}
2119
2120static int autopause2int(const char *autopause)
2121{
2122 int x;
2123 /*This 'double check' that default value is OFF */
2125 return QUEUE_AUTOPAUSE_OFF;
2126 }
2127
2128 /*This 'double check' is to ensure old values works */
2129 if(ast_true(autopause)) {
2130 return QUEUE_AUTOPAUSE_ON;
2131 }
2132
2133 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
2134 if (!strcasecmp(autopause, autopausesmodes[x].name)) {
2135 return autopausesmodes[x].autopause;
2136 }
2137 }
2138
2139 /*This 'double check' that default value is OFF */
2140 return QUEUE_AUTOPAUSE_OFF;
2141}
2142
2143static int queue_hash_cb(const void *obj, const int flags)
2144{
2145 const struct call_queue *q = obj;
2146
2147 return ast_str_case_hash(q->name);
2148}
2149
2150static int queue_cmp_cb(void *obj, void *arg, int flags)
2151{
2152 struct call_queue *q = obj, *q2 = arg;
2153 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
2154}
2155
2156/*!
2157 * \brief Return wrapuptime
2158 *
2159 * This function checks if wrapuptime in member is set and return this value.
2160 * Otherwise return value the wrapuptime in the queue configuration
2161 * \return integer value
2162 */
2163static int get_wrapuptime(struct call_queue *q, struct member *member)
2164{
2165 if (member->wrapuptime) {
2166 return member->wrapuptime;
2167 }
2168 return q->wrapuptime;
2169}
2170
2171/*! \internal
2172 * \brief ao2_callback, Decreases queuepos of all followers with a queuepos greater than arg.
2173 * \param obj the member being acted on
2174 * \param arg pointer to an integer containing the position value that was removed and requires reduction for anything above
2175 * \param flag unused
2176 */
2177static int queue_member_decrement_followers(void *obj, void *arg, int flag)
2178{
2179 struct member *mem = obj;
2180 int *decrement_followers_after = arg;
2181
2182 if (mem->queuepos > *decrement_followers_after) {
2183 mem->queuepos--;
2184 }
2185
2186 return 0;
2187}
2188
2189/*! \internal
2190 * \brief ao2_callback, finds members in a queue marked for deletion and in a cascading fashion runs queue_member_decrement_followers
2191 * on them. This callback should always be ran before performing mass unlinking of delmarked members from queues.
2192 * \param obj member being acted on
2193 * \param arg pointer to the queue members are being removed from
2194 * \param flag unused
2195 */
2196static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
2197{
2198 struct member *mem = obj;
2199 struct call_queue *queue = arg;
2200 int rrpos = mem->queuepos;
2201
2202 if (mem->delme) {
2204 }
2205
2206 return 0;
2207}
2208
2209/*! \internal
2210 * \brief Use this to decrement followers during removal of a member
2211 * \param queue which queue the member is being removed from
2212 * \param mem which member is being removed from the queue
2213 */
2214static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
2215{
2216 int pos = mem->queuepos;
2217
2218 /* 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
2219 * who would have been next otherwise. */
2220 if (pos < queue->rrpos) {
2221 queue->rrpos--;
2222 }
2223
2225}
2226
2227#define queue_ref(q) ao2_bump(q)
2228#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2229#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2230#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2231#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2232#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2233
2234/*! \brief Set variables of queue */
2235static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
2236{
2237 char interfacevar[256]="";
2238 float sl = 0;
2239
2240 ao2_lock(q);
2241
2242 if (q->setqueuevar) {
2243 sl = 0;
2244 if (q->callscompleted > 0) {
2245 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
2246 }
2247
2248 snprintf(interfacevar, sizeof(interfacevar),
2249 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2251
2252 ao2_unlock(q);
2253
2254 pbx_builtin_setvar_multiple(chan, interfacevar);
2255 } else {
2256 ao2_unlock(q);
2257 }
2258}
2259
2260/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
2261static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
2262{
2263 struct queue_ent *cur;
2264
2265 if (!q || !new)
2266 return;
2267 if (prev) {
2268 cur = prev->next;
2269 prev->next = new;
2270 } else {
2271 cur = q->head;
2272 q->head = new;
2273 }
2274 new->next = cur;
2275
2276 /* every queue_ent must have a reference to it's parent call_queue, this
2277 * reference does not go away until the end of the queue_ent's life, meaning
2278 * that even when the queue_ent leaves the call_queue this ref must remain. */
2279 if (!new->parent) {
2280 queue_ref(q);
2281 new->parent = q;
2282 }
2283 new->pos = ++(*pos);
2284 new->opos = *pos;
2285}
2286
2288{
2290 RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
2291 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2292
2293 channel_string = ast_manager_build_channel_state_string(obj->snapshot);
2294 event_string = ast_manager_str_from_json_object(obj->blob, NULL);
2295 if (!channel_string || !event_string) {
2296 return NULL;
2297 }
2298
2300 "%s"
2301 "%s",
2302 ast_str_buffer(channel_string),
2303 ast_str_buffer(event_string));
2304}
2305
2307{
2308 return queue_channel_to_ami("QueueCallerJoin", message);
2309}
2310
2312{
2313 return queue_channel_to_ami("QueueCallerLeave", message);
2314}
2315
2317{
2318 return queue_channel_to_ami("QueueCallerAbandon", message);
2319}
2320
2321STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_join_type,
2323 );
2324STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_leave_type,
2326 );
2327STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_abandon_type,
2329 );
2330
2332{
2333 struct ast_json_payload *payload = stasis_message_data(message);
2334 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2335
2336 event_string = ast_manager_str_from_json_object(payload->json, NULL);
2337 if (!event_string) {
2338 return NULL;
2339 }
2340
2342 "%s",
2343 ast_str_buffer(event_string));
2344}
2345
2347{
2348 return queue_member_to_ami("QueueMemberStatus", message);
2349}
2350
2352{
2353 return queue_member_to_ami("QueueMemberAdded", message);
2354}
2355
2357{
2358 return queue_member_to_ami("QueueMemberRemoved", message);
2359}
2360
2362{
2363 return queue_member_to_ami("QueueMemberPause", message);
2364}
2365
2367{
2368 return queue_member_to_ami("QueueMemberPenalty", message);
2369}
2370
2372{
2373 return queue_member_to_ami("QueueMemberRinginuse", message);
2374}
2375
2376STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_status_type,
2378 );
2379STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_added_type,
2381 );
2382STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_removed_type,
2384 );
2385STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_pause_type,
2387 );
2388STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_penalty_type,
2390 );
2391STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_ringinuse_type,
2393 );
2394
2396{
2399 struct ast_channel_snapshot *agent;
2400 RAII_VAR(struct ast_str *, caller_event_string, NULL, ast_free);
2401 RAII_VAR(struct ast_str *, agent_event_string, NULL, ast_free);
2402 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2403
2405 if (caller) {
2406 caller_event_string = ast_manager_build_channel_state_string(caller);
2407 if (!caller_event_string) {
2408 ast_log(LOG_NOTICE, "No caller event string, bailing\n");
2409 return NULL;
2410 }
2411 }
2412
2413 agent = ast_multi_channel_blob_get_channel(obj, "agent");
2414 if (agent) {
2415 agent_event_string = ast_manager_build_channel_state_string_prefix(agent, "Dest");
2416 if (!agent_event_string) {
2417 ast_log(LOG_NOTICE, "No agent event string, bailing\n");
2418 return NULL;
2419 }
2420 }
2421
2423 if (!event_string) {
2424 return NULL;
2425 }
2426
2428 "%s"
2429 "%s"
2430 "%s",
2431 caller_event_string ? ast_str_buffer(caller_event_string) : "",
2432 agent_event_string ? ast_str_buffer(agent_event_string) : "",
2433 ast_str_buffer(event_string));
2434}
2435
2437{
2438 return queue_multi_channel_to_ami("AgentCalled", message);
2439}
2440
2442{
2443 return queue_multi_channel_to_ami("AgentConnect", message);
2444}
2445
2447{
2448 return queue_multi_channel_to_ami("AgentComplete", message);
2449}
2450
2452{
2453 return queue_multi_channel_to_ami("AgentDump", message);
2454}
2455
2457{
2458 return queue_multi_channel_to_ami("AgentRingNoAnswer", message);
2459}
2460
2461STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_called_type,
2463 );
2464STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_connect_type,
2466 );
2467STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_complete_type,
2469 );
2472 );
2473STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_ringnoanswer_type,
2475 );
2476
2478 struct ast_channel_snapshot *caller_snapshot,
2479 struct ast_channel_snapshot *agent_snapshot,
2480 struct stasis_message_type *type, struct ast_json *blob)
2481{
2482 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
2483 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2484
2485 if (!type) {
2486 return;
2487 }
2488
2489 payload = ast_multi_channel_blob_create(blob);
2490 if (!payload) {
2491 return;
2492 }
2493
2494 if (caller_snapshot) {
2495 ast_multi_channel_blob_add_channel(payload, "caller", caller_snapshot);
2496 } else {
2497 ast_debug(1, "Empty caller_snapshot; sending incomplete event\n");
2498 }
2499
2500 if (agent_snapshot) {
2501 ast_multi_channel_blob_add_channel(payload, "agent", agent_snapshot);
2502 }
2503
2504 msg = stasis_message_create(type, payload);
2505 if (!msg) {
2506 return;
2507 }
2508
2509 stasis_publish(topic, msg);
2510}
2511
2512static void queue_publish_multi_channel_blob(struct ast_channel *caller, struct ast_channel *agent,
2513 struct stasis_message_type *type, struct ast_json *blob)
2514{
2515 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
2516 RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
2517
2518 ast_channel_lock(caller);
2519 caller_snapshot = ast_channel_snapshot_create(caller);
2520 ast_channel_unlock(caller);
2521 ast_channel_lock(agent);
2522 agent_snapshot = ast_channel_snapshot_create(agent);
2523 ast_channel_unlock(agent);
2524
2525 if (!caller_snapshot || !agent_snapshot) {
2526 return;
2527 }
2528
2530 agent_snapshot, type, blob);
2531}
2532
2533/*!
2534 * \internal
2535 * \brief Publish the member blob.
2536 * \since 12.0.0
2537 *
2538 * \param type Stasis message type to publish.
2539 * \param blob The information being published.
2540 *
2541 * \note The json blob reference is passed to this function.
2542 */
2544{
2545 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
2546 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2547
2548 if (!blob || !type) {
2549 ast_json_unref(blob);
2550 return;
2551 }
2552
2553 payload = ast_json_payload_create(blob);
2554 ast_json_unref(blob);
2555 if (!payload) {
2556 return;
2557 }
2558
2559 msg = stasis_message_create(type, payload);
2560 if (!msg) {
2561 return;
2562 }
2563
2565}
2566
2567static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
2568{
2569 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}",
2570 "Queue", q->name,
2571 "MemberName", mem->membername,
2572 "Interface", mem->interface,
2573 "StateInterface", mem->state_interface,
2574 "Membership", (mem->dynamic ? "dynamic" : (mem->realtime ? "realtime" : "static")),
2575 "Penalty", mem->penalty,
2576 "CallsTaken", mem->calls,
2577 "LastCall", (int)mem->lastcall,
2578 "LastPause", (int)mem->lastpause,
2579 "LoginTime", (int)mem->logintime,
2580 "InCall", mem->starttime ? 1 : 0,
2581 "Status", mem->status,
2582 "Paused", mem->paused,
2583 "PausedReason", mem->reason_paused,
2584 "Ringinuse", mem->ringinuse,
2585 "Wrapuptime", mem->wrapuptime);
2586}
2587
2588/*! \brief Check if members are available
2589 *
2590 * This function checks to see if members are available to be called. If any member
2591 * is available, the function immediately returns 0. If no members are available,
2592 * then -1 is returned.
2593 */
2594static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate, int raise_respect_min)
2595{
2596 struct member *member;
2597 struct ao2_iterator mem_iter;
2598
2599 ao2_lock(q);
2600 mem_iter = ao2_iterator_init(q->members, 0);
2601 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
2602 int penalty = member->penalty;
2603 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2604 /* Check if we should respect minimum penalty threshold */
2605 if (raise_respect_min && penalty < min_penalty) {
2606 ast_debug(4, "%s penalty %d not raised (below min %d)\n", member->membername, penalty, min_penalty);
2607 } else {
2608 ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
2609 penalty = raise_penalty;
2610 }
2611 }
2612 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2613 if (conditions & QUEUE_EMPTY_PENALTY) {
2614 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
2615 continue;
2616 }
2617 }
2618
2619 switch (devstate ? ast_device_state(member->state_interface) : member->status) {
2620 case AST_DEVICE_INVALID:
2621 if (conditions & QUEUE_EMPTY_INVALID) {
2622 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
2623 break;
2624 }
2625 goto default_case;
2627 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2628 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
2629 break;
2630 }
2631 goto default_case;
2632 case AST_DEVICE_INUSE:
2633 if (conditions & QUEUE_EMPTY_INUSE) {
2634 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
2635 break;
2636 }
2637 goto default_case;
2638 case AST_DEVICE_RINGING:
2639 if (conditions & QUEUE_EMPTY_RINGING) {
2640 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
2641 break;
2642 }
2643 goto default_case;
2644 case AST_DEVICE_UNKNOWN:
2645 if (conditions & QUEUE_EMPTY_UNKNOWN) {
2646 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
2647 break;
2648 }
2649 /* Fall-through */
2650 default:
2651 default_case:
2652 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2653 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
2654 break;
2655 } else if ((conditions & QUEUE_EMPTY_WRAPUP)
2656 && member->lastcall
2657 && get_wrapuptime(q, member)
2658 && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
2659 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2660 member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
2661 break;
2662 } else {
2663 ao2_ref(member, -1);
2664 ao2_iterator_destroy(&mem_iter);
2665 ao2_unlock(q);
2666 ast_debug(4, "%s is available.\n", member->membername);
2667 return 0;
2668 }
2669 break;
2670 }
2671 }
2672 ao2_iterator_destroy(&mem_iter);
2673 ao2_unlock(q);
2674
2675 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2676 /* member state still may be RINGING due to lag in event message - check again with device state */
2677 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1, raise_respect_min);
2678 }
2679 return -1;
2680}
2681
2682/*
2683 * A "pool" of member objects that calls are currently pending on. If an
2684 * agent is a member of multiple queues it's possible for that agent to be
2685 * called by each of the queues at the same time. This happens because device
2686 * state is slow to notify the queue app of one of it's member's being rung.
2687 * This "pool" allows us to track which members are currently being rung while
2688 * we wait on the device state change.
2689 */
2691#define MAX_CALL_ATTEMPT_BUCKETS 353
2692
2693static int pending_members_hash(const void *obj, const int flags)
2694{
2695 const struct member *object;
2696 const char *key;
2697
2698 switch (flags & OBJ_SEARCH_MASK) {
2699 case OBJ_SEARCH_KEY:
2700 key = obj;
2701 break;
2702 case OBJ_SEARCH_OBJECT:
2703 object = obj;
2704 key = object->interface;
2705 break;
2706 default:
2707 ast_assert(0);
2708 return 0;
2709 }
2710 return ast_str_case_hash(key);
2711}
2712
2713static int pending_members_cmp(void *obj, void *arg, int flags)
2714{
2715 const struct member *object_left = obj;
2716 const struct member *object_right = arg;
2717 const char *right_key = arg;
2718 int cmp;
2719
2720 switch (flags & OBJ_SEARCH_MASK) {
2721 case OBJ_SEARCH_OBJECT:
2722 right_key = object_right->interface;
2723 /* Fall through */
2724 case OBJ_SEARCH_KEY:
2725 cmp = strcasecmp(object_left->interface, right_key);
2726 break;
2728 /* Not supported by container. */
2729 ast_assert(0);
2730 return 0;
2731 default:
2732 cmp = 0;
2733 break;
2734 }
2735 if (cmp) {
2736 return 0;
2737 }
2738 return CMP_MATCH;
2739}
2740
2741static void pending_members_remove(struct member *mem)
2742{
2743 ast_debug(3, "Removed %s from pending_members\n", mem->membername);
2745}
2746
2747/*! \brief set a member's status based on device state of that member's state_interface.
2748 *
2749 * Lock interface list find sc, iterate through each queues queue_member list for member to
2750 * update state inside queues
2751*/
2752static void update_status(struct call_queue *q, struct member *m, const int status)
2753{
2754 if (m->status != status) {
2755 /* If this member has transitioned to being available then update their queue
2756 * information. If they are currently in a call then the leg to the agent will be
2757 * considered done and the call finished.
2758 */
2761 }
2762
2763 m->status = status;
2764
2765 /* Remove the member from the pending members pool only when the status changes.
2766 * This is not done unconditionally because we can occasionally see multiple
2767 * device state notifications of not in use after a previous call has ended,
2768 * including after we have initiated a new call. This is more likely to
2769 * happen when there is latency in the connection to the member.
2770 */
2772
2773 queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2774 }
2775}
2776
2777/*!
2778 * \internal
2779 * \brief Determine if a queue member is available
2780 * \retval 1 if the member is available
2781 * \retval 0 if the member is not available
2782 */
2783static int is_member_available(struct call_queue *q, struct member *mem)
2784{
2785 int available = 0;
2786 int wrapuptime;
2787
2788 switch (mem->status) {
2789 case AST_DEVICE_INVALID:
2791 break;
2792 case AST_DEVICE_INUSE:
2793 case AST_DEVICE_BUSY:
2794 case AST_DEVICE_RINGING:
2796 case AST_DEVICE_ONHOLD:
2797 if (!mem->ringinuse) {
2798 break;
2799 }
2800 /* else fall through */
2802 case AST_DEVICE_UNKNOWN:
2803 if (!mem->paused) {
2804 available = 1;
2805 }
2806 break;
2807 }
2808
2809 /* Let wrapuptimes override device state availability */
2810 wrapuptime = get_wrapuptime(q, mem);
2811 if (mem->lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->lastcall)) {
2812 available = 0;
2813 }
2814 return available;
2815}
2816
2817/*! \brief set a member's status based on device state of that member's interface*/
2818static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
2819{
2820 struct ao2_iterator miter, qiter;
2821 struct ast_device_state_message *dev_state;
2822 struct member *m;
2823 struct call_queue *q;
2824 char interface[80], *slash_pos;
2825 int found = 0; /* Found this member in any queue */
2826 int found_member; /* Found this member in this queue */
2827 int avail = 0; /* Found an available member in this queue */
2828
2830 return;
2831 }
2832
2833 dev_state = stasis_message_data(msg);
2834 if (dev_state->eid) {
2835 /* ignore non-aggregate states */
2836 return;
2837 }
2838
2839 qiter = ao2_iterator_init(queues, 0);
2840 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
2841 ao2_lock(q);
2842
2843 avail = 0;
2844 found_member = 0;
2845 miter = ao2_iterator_init(q->members, 0);
2846 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2847 if (!found_member) {
2848 ast_copy_string(interface, m->state_interface, sizeof(interface));
2849
2850 if ((slash_pos = strchr(interface, '/'))) {
2851 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) {
2852 *slash_pos = '\0';
2853 }
2854 }
2855
2856 if (!strcasecmp(interface, dev_state->device)) {
2857 found_member = 1;
2858 update_status(q, m, dev_state->state);
2859 }
2860 }
2861
2862 /* check every member until we find one NOT_INUSE */
2863 if (!avail) {
2864 avail = is_member_available(q, m);
2865 }
2866 if (avail && found_member) {
2867 /* early exit as we've found an available member and the member of interest */
2868 ao2_ref(m, -1);
2869 break;
2870 }
2871 }
2872
2873 if (found_member) {
2874 found = 1;
2875 if (avail) {
2877 } else {
2879 }
2880 }
2881
2882 ao2_iterator_destroy(&miter);
2883
2884 ao2_unlock(q);
2885 queue_t_unref(q, "Done with iterator");
2886 }
2887 ao2_iterator_destroy(&qiter);
2888
2889 if (found) {
2890 ast_debug(1, "Device '%s' changed to state '%u' (%s)\n",
2891 dev_state->device,
2892 dev_state->state,
2893 ast_devstate2str(dev_state->state));
2894 } else {
2895 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",
2896 dev_state->device,
2897 dev_state->state,
2898 ast_devstate2str(dev_state->state));
2899 }
2900
2901 return;
2902}
2903
2904/*! \brief Helper function which converts from extension state to device state values */
2906{
2907 switch (state) {
2910 break;
2913 break;
2914 case AST_EXTENSION_BUSY:
2916 break;
2919 break;
2922 break;
2925 break;
2928 break;
2931 break;
2934 default:
2936 break;
2937 }
2938
2939 return state;
2940}
2941
2942/*!
2943 * \brief Returns if one context includes another context
2944 *
2945 * \param parent Parent context to search for child
2946 * \param child Context to check for inclusion in parent
2947 *
2948 * This function recursively checks if the context child is included in the context parent.
2949 *
2950 * \retval 1 if child is included in parent
2951 * \retval 0 if not
2952 */
2953static int context_included(const char *parent, const char *child);
2954static int context_included(const char *parent, const char *child)
2955{
2956 struct ast_context *c = NULL;
2957
2958 c = ast_context_find(parent);
2959 if (!c) {
2960 /* well, if parent doesn't exist, how can the child be included in it? */
2961 return 0;
2962 }
2963 if (!strcmp(ast_get_context_name(c), parent)) {
2964 /* found the context of the hint app_queue is using. Now, see
2965 if that context includes the one that just changed state */
2966 struct ast_include *inc = NULL;
2967
2968 while ((inc = (struct ast_include*) ast_walk_context_includes(c, inc))) {
2969 const char *includename = ast_get_include_name(inc);
2970 if (!strcasecmp(child, includename)) {
2971 return 1;
2972 }
2973 /* recurse on this context, for nested includes. The
2974 PBX extension parser will prevent infinite recursion. */
2975 if (context_included(includename, child)) {
2976 return 1;
2977 }
2978 }
2979 }
2980 return 0;
2981}
2982
2983static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
2984{
2985 struct ao2_iterator miter, qiter;
2986 struct member *m;
2987 struct call_queue *q;
2988 int state = info->exten_state;
2989 int found = 0, device_state = extensionstate2devicestate(state);
2990
2991 /* only interested in extension state updates involving device states */
2992 if (info->reason != AST_HINT_UPDATE_DEVICE) {
2993 return 0;
2994 }
2995
2996 qiter = ao2_iterator_init(queues, 0);
2997 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
2998 ao2_lock(q);
2999
3000 miter = ao2_iterator_init(q->members, 0);
3001 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
3002 if (!strcmp(m->state_exten, exten) &&
3004 /* context could be included in m->state_context. We need to check. */
3005 found = 1;
3006 update_status(q, m, device_state);
3007 }
3008 }
3009 ao2_iterator_destroy(&miter);
3010
3011 ao2_unlock(q);
3012 queue_t_unref(q, "Done with iterator");
3013 }
3014 ao2_iterator_destroy(&qiter);
3015
3016 if (found) {
3017 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
3018 } else {
3019 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",
3020 exten, context, device_state, ast_devstate2str(device_state));
3021 }
3022
3023 return 0;
3024}
3025
3026/*! \brief Return the current state of a member */
3027static int get_queue_member_status(struct member *cur)
3028{
3030}
3031
3032static void destroy_queue_member_cb(void *obj)
3033{
3034 struct member *mem = obj;
3035
3036 if (mem->state_id != -1) {
3038 }
3039}
3040
3041/*! \brief allocate space for new queue member and set fields based on parameters passed */
3042static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
3043{
3044 struct member *cur;
3045
3046 if ((cur = ao2_alloc(sizeof(*cur), destroy_queue_member_cb))) {
3047 cur->ringinuse = ringinuse;
3048 cur->penalty = penalty;
3049 cur->paused = paused;
3050 cur->wrapuptime = wrapuptime;
3051 if (paused) {
3052 time(&cur->lastpause); /* Update time of last pause */
3053 }
3054 time(&cur->logintime);
3055 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
3058 } else {
3060 }
3062 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
3063 } else {
3064 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
3065 }
3066 if (!strchr(cur->interface, '/')) {
3067 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
3068 }
3069 if (!strncmp(cur->state_interface, "hint:", 5)) {
3070 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
3071 char *exten = strsep(&context, "@") + 5;
3072
3073 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
3074 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
3075
3077 } else {
3078 cur->state_id = -1;
3079 }
3080 cur->status = get_queue_member_status(cur);
3081 }
3082
3083 return cur;
3084}
3085
3086
3087static int compress_char(const char c)
3088{
3089 if (c < 32) {
3090 return 0;
3091 } else if (c > 96) {
3092 return c - 64;
3093 }
3094 return c - 32;
3095}
3096
3097static int member_hash_fn(const void *obj, const int flags)
3098{
3099 const struct member *mem = obj;
3100 const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
3101 const char *chname = strchr(interface, '/');
3102 int ret = 0, i;
3103
3104 if (!chname) {
3105 chname = interface;
3106 }
3107 for (i = 0; i < 5 && chname[i]; i++) {
3108 ret += compress_char(chname[i]) << (i * 6);
3109 }
3110 return ret;
3111}
3112
3113static int member_cmp_fn(void *obj1, void *obj2, int flags)
3114{
3115 struct member *mem1 = obj1;
3116 struct member *mem2 = obj2;
3117 const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;
3118
3119 return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
3120}
3121
3122/*!
3123 * \brief Initialize Queue default values.
3124 * \note the queue's lock must be held before executing this function
3125*/
3126static void init_queue(struct call_queue *q)
3127{
3128 int i;
3129 struct penalty_rule *pr_iter;
3130
3131 q->dead = 0;
3132 q->retry = DEFAULT_RETRY;
3134 q->maxlen = 0;
3135
3136 ast_string_field_set(q, announce, "");
3138 ast_string_field_set(q, membergosub, "");
3139 ast_string_field_set(q, defaultrule, "");
3140
3141 q->announcefrequency = 0;
3143 q->announceholdtime = 1;
3145 q->announcepositionlimit = 10; /* Default 10 positions */
3146 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
3147 q->roundingseconds = 0; /* Default - don't announce seconds */
3148 q->servicelevel = 0;
3149 q->ringinuse = 1;
3151 q->setinterfacevar = 0;
3152 q->setqueuevar = 0;
3153 q->setqueueentryvar = 0;
3155 q->monfmt[0] = '\0';
3156 q->reportholdtime = 0;
3157 q->wrapuptime = 0;
3158 q->penaltymemberslimit = 0;
3159 q->joinempty = 0;
3160 q->leavewhenempty = 0;
3161 q->memberdelay = 0;
3162 q->weight = 0;
3163 q->timeoutrestart = 0;
3167 q->numperiodicannounce = 0;
3170 q->autopausebusy = 0;
3171 q->autopauseunavail = 0;
3173 q->autopausedelay = 0;
3175 if (!q->members) {
3177 /* linear strategy depends on order, so we have to place all members in a list */
3179 } else {
3182 }
3183 }
3184 q->found = 1;
3185
3186 ast_string_field_set(q, moh, "");
3187 ast_string_field_set(q, sound_next, "queue-youarenext");
3188 ast_string_field_set(q, sound_thereare, "queue-thereare");
3189 ast_string_field_set(q, sound_calls, "queue-callswaiting");
3190 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
3191 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
3192 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
3193 ast_string_field_set(q, sound_minutes, "queue-minutes");
3194 ast_string_field_set(q, sound_minute, "queue-minute");
3195 ast_string_field_set(q, sound_seconds, "queue-seconds");
3196 ast_string_field_set(q, sound_thanks, "queue-thankyou");
3197 ast_string_field_set(q, sound_callerannounce, "");
3198 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
3199
3200 if (!q->sound_periodicannounce[0]) {
3202 }
3203
3204 if (q->sound_periodicannounce[0]) {
3205 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
3206 }
3207
3208 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3209 if (q->sound_periodicannounce[i]) {
3210 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
3211 }
3212 }
3213
3214 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
3215 ast_free(pr_iter);
3216 }
3217
3218 /* On restart assume no members are available.
3219 * The queue_avail hint is a boolean state to indicate whether a member is available or not.
3220 *
3221 * This seems counter intuitive, but is required to light a BLF
3222 * AST_DEVICE_INUSE indicates no members are available.
3223 * AST_DEVICE_NOT_INUSE indicates a member is available.
3224 */
3226}
3227
3228static void clear_queue(struct call_queue *q)
3229{
3230 q->holdtime = 0;
3231 q->callscompleted = 0;
3232 q->callsabandoned = 0;
3233 q->callscompletedinsl = 0;
3234 q->callsabandonedinsl = 0;
3235 q->talktime = 0;
3236
3237 if (q->members) {
3238 struct member *mem;
3239 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3240 while ((mem = ao2_iterator_next(&mem_iter))) {
3241 mem->calls = 0;
3242 mem->callcompletedinsl = 0;
3243 mem->lastcall = 0;
3244 mem->starttime = 0;
3245 ao2_ref(mem, -1);
3246 }
3247 ao2_iterator_destroy(&mem_iter);
3248 }
3249}
3250
3251/*!
3252 * \brief Change queue penalty by adding rule.
3253 *
3254 * Check rule for errors with time or formatting, see if rule is relative to rest
3255 * of queue, iterate list of rules to find correct insertion point, insert and return.
3256 * \retval -1 on failure
3257 * \retval 0 on success
3258 * \note Call this with the rule_lists locked
3259*/
3260static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
3261{
3262 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3263 struct penalty_rule *rule = NULL, *rule_iter;
3264 struct rule_list *rl_iter;
3265 int penaltychangetime, inserted = 0;
3266
3267 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
3268 return -1;
3269 }
3270
3271 contentdup = ast_strdupa(content);
3272
3273 if (!(maxstr = strchr(contentdup, ','))) {
3274 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3275 ast_free(rule);
3276 return -1;
3277 }
3278
3279 *maxstr++ = '\0';
3280 if ((minstr = strchr(maxstr,','))) {
3281 *minstr++ = '\0';
3282 if ((raisestr = strchr(minstr,','))) {
3283 *raisestr++ = '\0';
3284 }
3285 } else {
3286 raisestr = NULL;
3287 }
3288
3289 timestr = contentdup;
3290 if ((penaltychangetime = atoi(timestr)) < 0) {
3291 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3292 ast_free(rule);
3293 return -1;
3294 }
3295
3296 rule->time = penaltychangetime;
3297
3298 /* The last check will evaluate true if either no penalty change is indicated for a given rule
3299 * OR if a min penalty change is indicated but no max penalty change is */
3300 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
3301 rule->max_relative = 1;
3302 }
3303
3304 rule->max_value = atoi(maxstr);
3305
3306 if (!ast_strlen_zero(minstr)) {
3307 if (*minstr == '+' || *minstr == '-') {
3308 rule->min_relative = 1;
3309 }
3310 rule->min_value = atoi(minstr);
3311 } else { /*there was no minimum specified, so assume this means no change*/
3312 rule->min_relative = 1;
3313 }
3314
3315 if (!ast_strlen_zero(raisestr)) {
3316 rule->raise_respect_min = 0; /* Initialize to 0 */
3317 if (*raisestr == 'r') {
3318 rule->raise_respect_min = 1; /* Set the flag */
3319 raisestr++;
3320 }
3321 if (*raisestr == '+' || *raisestr == '-') {
3322 rule->raise_relative = 1;
3323 }
3324 rule->raise_value = atoi(raisestr);
3325 } else { /*there was no raise specified, so assume this means no change*/
3326 rule->raise_relative = 1;
3327 }
3328
3329 /*We have the rule made, now we need to insert it where it belongs*/
3330 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
3331 if (strcasecmp(rl_iter->name, list_name)) {
3332 continue;
3333 }
3334
3335 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
3336 if (rule->time < rule_iter->time) {
3338 inserted = 1;
3339 break;
3340 }
3341 }
3343
3344 if (!inserted) {
3345 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
3346 inserted = 1;
3347 }
3348
3349 break;
3350 }
3351
3352 if (!inserted) {
3353 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
3354 ast_free(rule);
3355 return -1;
3356 }
3357 return 0;
3358}
3359
3360/*!
3361 * \brief Load queue rules from realtime.
3362 *
3363 * Check rule for errors with time or formatting, see if rule is relative to rest
3364 * of queue, iterate list of rules to find correct insertion point, insert and return.
3365 * \retval -1 on failure
3366 * \retval 0 on success
3367 * \note Call this with the rule_lists locked
3368*/
3369static int load_realtime_rules(void)
3370{
3371 struct ast_config *cfg;
3372 struct rule_list *rl_iter, *new_rl;
3373 struct penalty_rule *pr_iter;
3374 char *rulecat = NULL;
3375
3376 if (!ast_check_realtime("queue_rules")) {
3377 ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
3378 return 0;
3379 }
3380 if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
3381 ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
3382 return 0;
3383 }
3384 while ((rulecat = ast_category_browse(cfg, rulecat))) {
3385 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3386 int penaltychangetime, rule_exists = 0, inserted = 0;
3387 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3388 int min_relative = 0, max_relative = 0, raise_relative = 0;
3389 struct penalty_rule *new_penalty_rule = NULL;
3390
3391 rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
3392 if (ast_strlen_zero(rule_name)) {
3393 continue;
3394 }
3395
3396 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
3397 if (!(strcasecmp(rl_iter->name, rule_name))) {
3398 rule_exists = 1;
3399 new_rl = rl_iter;
3400 break;
3401 }
3402 }
3403 if (!rule_exists) {
3404 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
3405 ast_config_destroy(cfg);
3406 return -1;
3407 }
3408 ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
3410 }
3411 timestr = ast_variable_retrieve(cfg, rulecat, "time");
3412 if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
3413 ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
3414 (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
3415 continue;
3416 }
3417 if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
3418 ast_config_destroy(cfg);
3419 return -1;
3420 }
3421 if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
3422 ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
3423 max_penalty = 0;
3424 max_relative = 1;
3425 } else {
3426 if (*maxstr == '+' || *maxstr == '-') {
3427 max_relative = 1;
3428 }
3429 }
3430 if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
3431 ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
3432 min_penalty = 0;
3433 min_relative = 1;
3434 } else {
3435 if (*minstr == '+' || *minstr == '-') {
3436 min_relative = 1;
3437 }
3438 }
3439 if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
3440 ast_strlen_zero(raisestr) ) {
3441 raise_penalty = 0;
3442 raise_relative = 1;
3443 } else {
3444 if (*raisestr == 'r') {
3445 new_penalty_rule->raise_respect_min = 1;
3446 raisestr++;
3447 } else {
3448 new_penalty_rule->raise_respect_min = 0;
3449 }
3450 if (*raisestr == '+' || *raisestr == '-') {
3451 raise_relative = 1;
3452 }
3453 if (sscanf(raisestr, "%30d", &raise_penalty) != 1) {
3454 raise_penalty = 0;
3455 raise_relative = 1;
3456 }
3457 }
3458 new_penalty_rule->time = penaltychangetime;
3459 new_penalty_rule->max_relative = max_relative;
3460 new_penalty_rule->max_value = max_penalty;
3461 new_penalty_rule->min_relative = min_relative;
3462 new_penalty_rule->min_value = min_penalty;
3463 new_penalty_rule->raise_relative = raise_relative;
3464 new_penalty_rule->raise_value = raise_penalty;
3465 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
3466 if (new_penalty_rule->time < pr_iter->time) {
3467 AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
3468 inserted = 1;
3469 }
3470 }
3472 if (!inserted) {
3473 AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
3474 }
3475 }
3476
3477 ast_config_destroy(cfg);
3478 return 0;
3479}
3480
3481static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
3482{
3483 char *value_copy = ast_strdupa(value);
3484 char *option = NULL;
3485 while ((option = strsep(&value_copy, ","))) {
3486 if (!strcasecmp(option, "paused")) {
3487 *empty |= QUEUE_EMPTY_PAUSED;
3488 } else if (!strcasecmp(option, "penalty")) {
3489 *empty |= QUEUE_EMPTY_PENALTY;
3490 } else if (!strcasecmp(option, "inuse")) {
3491 *empty |= QUEUE_EMPTY_INUSE;
3492 } else if (!strcasecmp(option, "ringing")) {
3493 *empty |= QUEUE_EMPTY_RINGING;
3494 } else if (!strcasecmp(option, "invalid")) {
3495 *empty |= QUEUE_EMPTY_INVALID;
3496 } else if (!strcasecmp(option, "wrapup")) {
3497 *empty |= QUEUE_EMPTY_WRAPUP;
3498 } else if (!strcasecmp(option, "unavailable")) {
3499 *empty |= QUEUE_EMPTY_UNAVAILABLE;
3500 } else if (!strcasecmp(option, "unknown")) {
3501 *empty |= QUEUE_EMPTY_UNKNOWN;
3502 } else if (!strcasecmp(option, "loose")) {
3504 } else if (!strcasecmp(option, "strict")) {
3506 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
3508 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
3509 *empty = 0;
3510 } else {
3511 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
3512 }
3513 }
3514}
3515
3516/*! \brief Configure a queue parameter.
3517 *
3518 * The failunknown flag is set for config files (and static realtime) to show
3519 * errors for unknown parameters. It is cleared for dynamic realtime to allow
3520 * extra fields in the tables.
3521 * \note For error reporting, line number is passed for .conf static configuration,
3522 * for Realtime queues, linenum is -1.
3523*/
3524static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
3525{
3526 if (!strcasecmp(param, "musicclass") ||
3527 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
3528 ast_string_field_set(q, moh, val);
3529 } else if (!strcasecmp(param, "announce")) {
3530 ast_string_field_set(q, announce, val);
3531 } else if (!strcasecmp(param, "context")) {
3533 } else if (!strcasecmp(param, "timeout")) {
3534 q->timeout = atoi(val);
3535 if (q->timeout < 0) {
3537 }
3538 } else if (!strcasecmp(param, "ringinuse")) {
3539 q->ringinuse = ast_true(val);
3540 } else if (!strcasecmp(param, "setinterfacevar")) {
3542 } else if (!strcasecmp(param, "setqueuevar")) {
3543 q->setqueuevar = ast_true(val);
3544 } else if (!strcasecmp(param, "setqueueentryvar")) {
3546 } else if (!strcasecmp(param, "monitor-format")) {
3547 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
3548 } else if (!strcasecmp(param, "membergosub")) {
3549 ast_string_field_set(q, membergosub, val);
3550 } else if (!strcasecmp(param, "queue-youarenext")) {
3551 ast_string_field_set(q, sound_next, val);
3552 } else if (!strcasecmp(param, "queue-thereare")) {
3553 ast_string_field_set(q, sound_thereare, val);
3554 } else if (!strcasecmp(param, "queue-callswaiting")) {
3555 ast_string_field_set(q, sound_calls, val);
3556 } else if (!strcasecmp(param, "queue-quantity1")) {
3557 ast_string_field_set(q, queue_quantity1, val);
3558 } else if (!strcasecmp(param, "queue-quantity2")) {
3559 ast_string_field_set(q, queue_quantity2, val);
3560 } else if (!strcasecmp(param, "queue-holdtime")) {
3561 ast_string_field_set(q, sound_holdtime, val);
3562 } else if (!strcasecmp(param, "queue-minutes")) {
3563 ast_string_field_set(q, sound_minutes, val);
3564 } else if (!strcasecmp(param, "queue-minute")) {
3565 ast_string_field_set(q, sound_minute, val);
3566 } else if (!strcasecmp(param, "queue-seconds")) {
3567 ast_string_field_set(q, sound_seconds, val);
3568 } else if (!strcasecmp(param, "queue-thankyou")) {
3569 ast_string_field_set(q, sound_thanks, val);
3570 } else if (!strcasecmp(param, "queue-callerannounce")) {
3571 ast_string_field_set(q, sound_callerannounce, val);
3572 } else if (!strcasecmp(param, "queue-reporthold")) {
3573 ast_string_field_set(q, sound_reporthold, val);
3574 } else if (!strcasecmp(param, "announce-frequency")) {
3575 q->announcefrequency = atoi(val);
3576 } else if (!strcasecmp(param, "announce-to-first-user")) {
3578 } else if (!strcasecmp(param, "min-announce-frequency")) {
3579 q->minannouncefrequency = atoi(val);
3580 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
3581 } else if (!strcasecmp(param, "announce-round-seconds")) {
3582 q->roundingseconds = atoi(val);
3583 /* Rounding to any other values just doesn't make sense... */
3584 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
3585 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
3586 if (linenum >= 0) {
3587 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3588 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3589 val, param, q->name, linenum);
3590 } else {
3591 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3592 "using 0 instead for queue '%s'\n", val, param, q->name);
3593 }
3594 q->roundingseconds=0;
3595 }
3596 } else if (!strcasecmp(param, "announce-holdtime")) {
3597 if (!strcasecmp(val, "once")) {
3599 } else if (ast_true(val)) {
3601 } else {
3602 q->announceholdtime = 0;
3603 }
3604 } else if (!strcasecmp(param, "announce-position")) {
3605 if (!strcasecmp(val, "limit")) {
3607 } else if (!strcasecmp(val, "more")) {
3609 } else if (ast_true(val)) {
3611 } else {
3613 }
3614 } else if (!strcasecmp(param, "announce-position-only-up")) {
3616 } else if (!strcasecmp(param, "announce-position-limit")) {
3617 q->announcepositionlimit = atoi(val);
3618 } else if (!strcasecmp(param, "periodic-announce")) {
3619 if (strchr(val, ',')) {
3620 char *s, *buf = ast_strdupa(val);
3621 unsigned int i = 0;
3622
3623 while ((s = strsep(&buf, ",|"))) {
3624 if (!q->sound_periodicannounce[i]) {
3626 }
3627 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
3628 i++;
3629 if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
3630 break;
3631 }
3632 }
3633 q->numperiodicannounce = i;
3634 } else {
3635 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
3636 q->numperiodicannounce = 1;
3637 }
3638 } else if (!strcasecmp(param, "periodic-announce-startdelay")) {
3640 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
3641 q->periodicannouncefrequency = atoi(val);
3642 } else if (!strcasecmp(param, "relative-periodic-announce")) {
3644 } else if (!strcasecmp(param, "random-periodic-announce")) {
3646 } else if (!strcasecmp(param, "retry")) {
3647 q->retry = atoi(val);
3648 if (q->retry <= 0) {
3649 q->retry = DEFAULT_RETRY;
3650 }
3651 } else if (!strcasecmp(param, "wrapuptime")) {
3652 q->wrapuptime = atoi(val);
3653 } else if (!strcasecmp(param, "penaltymemberslimit")) {
3654 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
3655 q->penaltymemberslimit = 0;
3656 }
3657 } else if (!strcasecmp(param, "autofill")) {
3658 q->autofill = ast_true(val);
3659 } else if (!strcasecmp(param, "autopause")) {
3661 } else if (!strcasecmp(param, "autopausedelay")) {
3662 q->autopausedelay = atoi(val);
3663 } else if (!strcasecmp(param, "autopausebusy")) {
3665 } else if (!strcasecmp(param, "autopauseunavail")) {
3667 } else if (!strcasecmp(param, "maxlen")) {
3668 q->maxlen = atoi(val);
3669 if (q->maxlen < 0) {
3670 q->maxlen = 0;
3671 }
3672 } else if (!strcasecmp(param, "servicelevel")) {
3673 q->servicelevel= atoi(val);
3674 } else if (!strcasecmp(param, "strategy")) {
3675 int strategy;
3676
3677 /* We are a static queue and already have set this, no need to do it again */
3678 if (failunknown) {
3679 return;
3680 }
3682 if (strategy < 0) {
3683 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3684 val, q->name);
3686 }
3687 if (strategy == q->strategy) {
3688 return;
3689 }
3691 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
3692 return;
3693 }
3694 q->strategy = strategy;
3695 } else if (!strcasecmp(param, "joinempty")) {
3697 } else if (!strcasecmp(param, "leavewhenempty")) {
3699 } else if (!strcasecmp(param, "reportholdtime")) {
3701 } else if (!strcasecmp(param, "memberdelay")) {
3702 q->memberdelay = atoi(val);
3703 } else if (!strcasecmp(param, "weight")) {
3704 q->weight = atoi(val);
3705 } else if (!strcasecmp(param, "timeoutrestart")) {
3707 } else if (!strcasecmp(param, "defaultrule")) {
3708 ast_string_field_set(q, defaultrule, val);
3709 } else if (!strcasecmp(param, "timeoutpriority")) {
3710 if (!strcasecmp(val, "conf")) {
3712 } else {
3714 }
3715 } else if (!strcasecmp(param, "log-restricted-caller-id")) {
3717 } else if (failunknown) {
3718 if (linenum >= 0) {
3719 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3720 q->name, param, linenum);
3721 } else {
3722 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
3723 }
3724 }
3725}
3726
3727
3728#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3729#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3730#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3731
3732/*! \internal
3733 * \brief If adding a single new member to a queue, use this function instead of ao2_linking.
3734 * This adds round robin queue position data for a fresh member as well as links it.
3735 * \param queue Which queue the member is being added to
3736 * \param mem Which member is being added to the queue
3737 */
3738static void member_add_to_queue(struct call_queue *queue, struct member *mem)
3739{
3740 ao2_lock(queue->members);
3741 mem->queuepos = ao2_container_count(queue->members);
3742 ao2_link(queue->members, mem);
3744 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", queue->name, mem->interface);
3745 ao2_unlock(queue->members);
3746}
3747
3748/*! \internal
3749 * \brief If removing a single member from a queue, use this function instead of ao2_unlinking.
3750 * This will perform round robin queue position reordering for the remaining members.
3751 * \param queue Which queue the member is being removed from
3752 * \param mem Which member is being removed from the queue
3753 */
3754static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
3755{
3757 ao2_lock(queue->members);
3760 ao2_unlink(queue->members, mem);
3761 ao2_unlock(queue->members);
3762}
3763
3764/*!
3765 * \brief Find rt member record to update otherwise create one.
3766 *
3767 * Search for member in queue, if found update penalty/paused state,
3768 * if no member exists create one flag it as a RT member and add to queue member list.
3769*/
3770static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
3771{
3772 struct member *m;
3773 struct ao2_iterator mem_iter;
3774 int penalty = 0;
3775 int paused = 0;
3776 int found = 0;
3777 int wrapuptime = 0;
3778 int ringinuse = q->ringinuse;
3779
3780 const char *config_val;
3781 const char *interface = ast_variable_retrieve(member_config, category, "interface");
3782 const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
3783 const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
3784 const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
3785 const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
3786 const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
3787 const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
3788 const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
3789
3790 if (ast_strlen_zero(rt_uniqueid)) {
3791 ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
3792 S_OR(membername, "NULL"));
3793 return;
3794 }
3795
3796 if (ast_strlen_zero(interface)) {
3797 ast_log(LOG_WARNING, "Realtime field 'interface' is empty for member %s\n",
3798 S_OR(membername, "NULL"));
3799 return;
3800 }
3801
3802 if (penalty_str) {
3803 penalty = atoi(penalty_str);
3804 if ((penalty < 0) && negative_penalty_invalid) {
3805 return;
3806 } else if (penalty < 0) {
3807 penalty = 0;
3808 }
3809 }
3810
3811 if (paused_str) {
3812 paused = atoi(paused_str);
3813 if (paused < 0) {
3814 paused = 0;
3815 }
3816 }
3817
3818 if (wrapuptime_str) {
3819 wrapuptime = atoi(wrapuptime_str);
3820 if (wrapuptime < 0) {
3821 wrapuptime = 0;
3822 }
3823 }
3824
3825 if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3826 if (ast_true(config_val)) {
3827 ringinuse = 1;
3828 } else if (ast_false(config_val)) {
3829 ringinuse = 0;
3830 } else {
3831 ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
3832 }
3833 }
3834
3835 /* Find member by realtime uniqueid and update */
3836 mem_iter = ao2_iterator_init(q->members, 0);
3837 while ((m = ao2_iterator_next(&mem_iter))) {
3838 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
3839 m->dead = 0; /* Do not delete this one. */
3840 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3841 if (paused_str) {
3842 m->paused = paused;
3843 if (paused && m->lastpause == 0) {
3844 time(&m->lastpause); /* XXX: Should this come from realtime? */
3845 }
3847 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, m->interface);
3848 }
3849 if (strcasecmp(state_interface, m->state_interface)) {
3850 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
3851 }
3852 m->penalty = penalty;
3853 m->ringinuse = ringinuse;
3854 m->wrapuptime = wrapuptime;
3856 ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
3857 }
3858 found = 1;
3859 ao2_ref(m, -1);
3860 break;
3861 }
3862 ao2_ref(m, -1);
3863 }
3864 ao2_iterator_destroy(&mem_iter);
3865
3866 /* Create a new member */
3867 if (!found) {
3868 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3869 m->dead = 0;
3870 m->realtime = 1;
3871 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3872 if (!ast_strlen_zero(reason_paused)) {
3873 ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
3874 }
3876 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3877 } else {
3878 ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3879 }
3880 member_add_to_queue(q, m);
3881 ao2_ref(m, -1);
3882 m = NULL;
3883 }
3884 }
3885}
3886
3887/*! \brief Iterate through queue's member list and delete them */
3888static void free_members(struct call_queue *q, int all)
3889{
3890 /* Free non-dynamic members */
3891 struct member *cur;
3892 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3893
3894 while ((cur = ao2_iterator_next(&mem_iter))) {
3895 if (all || !cur->dynamic) {
3897 }
3898 ao2_ref(cur, -1);
3899 }
3900 ao2_iterator_destroy(&mem_iter);
3901}
3902
3903/*! \brief Free queue's member list then its string fields */
3904static void destroy_queue(void *obj)
3905{
3906 struct call_queue *q = obj;
3907 int i;
3908
3909 free_members(q, 1);
3911 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3912 if (q->sound_periodicannounce[i]) {
3914 }
3915 }
3916 ao2_ref(q->members, -1);
3917}
3918
3919static struct call_queue *alloc_queue(const char *queuename)
3920{
3921 struct call_queue *q;
3922
3923 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
3924 if (ast_string_field_init(q, 64)) {
3925 queue_t_unref(q, "String field allocation failed");
3926 return NULL;
3927 }
3928 ast_string_field_set(q, name, queuename);
3929 }
3930 return q;
3931}
3932
3933/*!
3934 * \brief Reload a single queue via realtime.
3935 *
3936 * Check for statically defined queue first, check if deleted RT queue,
3937 * check for new RT queue, if queue vars are not defined init them with defaults.
3938 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
3939 * \retval the queue,
3940 * \retval NULL if it doesn't exist.
3941 * \note Should be called with the "queues" container locked.
3942*/
3943static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
3944{
3945 struct ast_variable *v;
3946 struct call_queue *q, tmpq = {
3947 .name = queuename,
3948 };
3949 struct member *m;
3950 struct ao2_iterator mem_iter;
3951 char *category = NULL;
3952 const char *tmp_name;
3953 char *tmp;
3954 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
3955
3956 /* Static queues override realtime. */
3957 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
3958 ao2_lock(q);
3959 if (!q->realtime) {
3960 if (q->dead) {
3961 ao2_unlock(q);
3962 queue_t_unref(q, "Queue is dead; can't return it");
3963 return NULL;
3964 }
3965 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
3966 ao2_unlock(q);
3967 return q;
3968 }
3969 } else if (!member_config) {
3970 /* Not found in the list, and it's not realtime ... */
3971 return NULL;
3972 }
3973 /* Check if queue is defined in realtime. */
3974 if (!queue_vars) {
3975 /* Delete queue from in-core list if it has been deleted in realtime. */
3976 if (q) {
3977 /*! \note Hmm, can't seem to distinguish a DB failure from a not
3978 found condition... So we might delete an in-core queue
3979 in case of DB failure. */
3980 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
3981
3982 q->dead = 1;
3983 /* Delete if unused (else will be deleted when last caller leaves). */
3984 queues_t_unlink(queues, q, "Unused; removing from container");
3985 ao2_unlock(q);
3986 queue_t_unref(q, "Queue is dead; can't return it");
3987 }
3988 return NULL;
3989 }
3990
3991 /* Create a new queue if an in-core entry does not exist yet. */
3992 if (!q) {
3993 struct ast_variable *tmpvar = NULL;
3994 if (!(q = alloc_queue(queuename))) {
3995 return NULL;
3996 }
3997 ao2_lock(q);
3998 clear_queue(q);
3999 q->realtime = 1;
4000 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
4001 * will allocate the members properly
4002 */
4003 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
4004 if (!strcasecmp(tmpvar->name, "strategy")) {
4005 q->strategy = strat2int(tmpvar->value);
4006 if (q->strategy < 0) {
4007 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
4008 tmpvar->value, q->name);
4010 }
4011 break;
4012 }
4013 }
4014 /* We traversed all variables and didn't find a strategy */
4015 if (!tmpvar) {
4017 }
4018 queues_t_link(queues, q, "Add queue to container");
4019 }
4020 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
4021
4022 memset(tmpbuf, 0, sizeof(tmpbuf));
4023 for (v = queue_vars; v; v = v->next) {
4024 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
4025 if (strchr(v->name, '_')) {
4026 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
4027 tmp_name = tmpbuf;
4028 tmp = tmpbuf;
4029 while ((tmp = strchr(tmp, '_'))) {
4030 *tmp++ = '-';
4031 }
4032 } else {
4033 tmp_name = v->name;
4034 }
4035
4036 /* NULL values don't get returned from realtime; blank values should
4037 * still get set. If someone doesn't want a value to be set, they
4038 * should set the realtime column to NULL, not blank. */
4039 queue_set_param(q, tmp_name, v->value, -1, 0);
4040 }
4041
4042 /* Temporarily set realtime members dead so we can detect deleted ones. */
4043 mem_iter = ao2_iterator_init(q->members, 0);
4044 while ((m = ao2_iterator_next(&mem_iter))) {
4045 if (m->realtime) {
4046 m->dead = 1;
4047 }
4048 ao2_ref(m, -1);
4049 }
4050 ao2_iterator_destroy(&mem_iter);
4051
4052 while ((category = ast_category_browse(member_config, category))) {
4053 rt_handle_member_record(q, category, member_config);
4054 }
4055
4056 /* Delete all realtime members that have been deleted in DB. */
4057 mem_iter = ao2_iterator_init(q->members, 0);
4058 while ((m = ao2_iterator_next(&mem_iter))) {
4059 if (m->dead) {
4061 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4062 } else {
4063 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4064 }
4066 }
4067 ao2_ref(m, -1);
4068 }
4069 ao2_iterator_destroy(&mem_iter);
4070
4071 ao2_unlock(q);
4072
4073 return q;
4074}
4075
4076/*!
4077 * note */
4078
4079/*!
4080 * \internal
4081 * \brief Returns reference to the named queue. If the queue is realtime, it will load the queue as well.
4082 * \param queuename - name of the desired queue
4083 *
4084 * \retval the queue
4085 * \retval NULL if it doesn't exist
4086 */
4087static struct call_queue *find_load_queue_rt_friendly(const char *queuename)
4088{
4089 struct ast_variable *queue_vars;
4090 struct ast_config *member_config = NULL;
4091 struct call_queue *q = NULL, tmpq = {
4092 .name = queuename,
4093 };
4094 int prev_weight = 0;
4095
4096 /* Find the queue in the in-core list first. */
4097 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
4098
4099 if (!q || q->realtime) {
4100 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
4101 queue operations while waiting for the DB.
4102
4103 This will be two separate database transactions, so we might
4104 see queue parameters as they were before another process
4105 changed the queue and member list as it was after the change.
4106 Thus we might see an empty member list when a queue is
4107 deleted. In practise, this is unlikely to cause a problem. */
4108
4109 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
4110 if (queue_vars) {
4111 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
4112 if (!member_config) {
4113 ast_debug(1, "No queue_members defined in config extconfig.conf\n");
4114 member_config = ast_config_new();
4115 }
4116 }
4117 if (q) {
4118 prev_weight = q->weight ? 1 : 0;
4119 queue_t_unref(q, "Need to find realtime queue");
4120 }
4121
4122 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
4123 ast_config_destroy(member_config);
4124 ast_variables_destroy(queue_vars);
4125
4126 /* update the use_weight value if the queue's has gained or lost a weight */
4127 if (q) {
4128 if (!q->weight && prev_weight) {
4130 }
4131 if (q->weight && !prev_weight) {
4133 }
4134 }
4135 /* Other cases will end up with the proper value for use_weight */
4136 } else {
4138 }
4139 return q;
4140}
4141
4142/*!
4143 * \internal
4144 * \brief Load queues and members from realtime.
4145 *
4146 * \param queuename - name of the desired queue to load or empty if need to load all queues
4147*/
4148static void load_realtime_queues(const char *queuename)
4149{
4150 struct ast_config *cfg = NULL;
4151 char *category = NULL;
4152 const char *name = NULL;
4153 struct call_queue *q = NULL;
4154
4155 if (!ast_check_realtime("queues")) {
4156 return;
4157 }
4158
4159 if (ast_strlen_zero(queuename)) {
4160 if ((cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
4161 while ((category = ast_category_browse(cfg, category))) {
4162 name = ast_variable_retrieve(cfg, category, "name");
4164 queue_unref(q);
4165 }
4166 }
4167 ast_config_destroy(cfg);
4168 }
4169 } else {
4170 if ((q = find_load_queue_rt_friendly(queuename))) {
4171 queue_unref(q);
4172 }
4173 }
4174}
4175
4176static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
4177{
4178 int ret = -1;
4179
4180 if (ast_strlen_zero(mem->rt_uniqueid)) {
4181 return ret;
4182 }
4183
4184 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) >= 0) {
4185 ret = 0;
4186 }
4187
4188 return ret;
4189}
4190
4191
4193{
4194 struct ast_config *member_config = NULL;
4195 struct member *m;
4196 char *category = NULL;
4197 struct ao2_iterator mem_iter;
4198
4199 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
4200 /* This queue doesn't have realtime members. If the queue still has any realtime
4201 * members in memory, they need to be removed.
4202 */
4203 ao2_lock(q);
4204 mem_iter = ao2_iterator_init(q->members, 0);
4205 while ((m = ao2_iterator_next(&mem_iter))) {
4206 if (m->realtime) {
4208 }
4209 ao2_ref(m, -1);
4210 }
4211 ao2_iterator_destroy(&mem_iter);
4212 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
4213 ao2_unlock(q);
4214 return;
4215 }
4216
4217 ao2_lock(q);
4218
4219 /* Temporarily set realtime members dead so we can detect deleted ones.*/
4220 mem_iter = ao2_iterator_init(q->members, 0);
4221 while ((m = ao2_iterator_next(&mem_iter))) {
4222 if (m->realtime) {
4223 m->dead = 1;
4224 }
4225 ao2_ref(m, -1);
4226 }
4227 ao2_iterator_destroy(&mem_iter);
4228
4229 while ((category = ast_category_browse(member_config, category))) {
4230 rt_handle_member_record(q, category, member_config);
4231 }
4232
4233 /* Delete all realtime members that have been deleted in DB. */
4234 mem_iter = ao2_iterator_init(q->members, 0);
4235 while ((m = ao2_iterator_next(&mem_iter))) {
4236 if (m->dead) {
4238 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4239 } else {
4240 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4241 }
4243 }
4244 ao2_ref(m, -1);
4245 }
4246 ao2_iterator_destroy(&mem_iter);
4247 ao2_unlock(q);
4248 ast_config_destroy(member_config);
4249}
4250
4251static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
4252{
4253 struct call_queue *q;
4254 struct queue_ent *cur, *prev = NULL;
4255 int res = -1;
4256 int pos = 0;
4257 int inserted = 0;
4258
4259 if (!(q = find_load_queue_rt_friendly(queuename))) {
4260 return res;
4261 }
4262 ao2_lock(q);
4263
4264 /* This is our one */
4265 if (q->joinempty) {
4266 int status = 0;
4268 *reason = QUEUE_JOINEMPTY;
4269 ao2_unlock(q);
4270 queue_t_unref(q, "Done with realtime queue");
4271 return res;
4272 }
4273 }
4274 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) {
4275 *reason = QUEUE_FULL;
4276 } else if (*reason == QUEUE_UNKNOWN) {
4277 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4278
4279 /* There's space for us, put us at the right position inside
4280 * the queue.
4281 * Take into account the priority of the calling user */
4282 inserted = 0;
4283 prev = NULL;
4284 cur = q->head;
4285 while (cur) {
4286 /* We have higher priority than the current user, enter
4287 * before him, after all the other users with priority
4288 * higher or equal to our priority. */
4289 if ((!inserted) && (qe->prio > cur->prio)) {
4290 insert_entry(q, prev, qe, &pos);
4291 inserted = 1;
4292 }
4293 /* <= is necessary for the position comparison because it may not be possible to enter
4294 * at our desired position since higher-priority callers may have taken the position we want
4295 */
4296 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
4297 insert_entry(q, prev, qe, &pos);
4298 inserted = 1;
4299 /*pos is incremented inside insert_entry, so don't need to add 1 here*/
4300 if (position < pos) {
4301 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
4302 }
4303 }
4304 cur->pos = ++pos;
4305 prev = cur;
4306 cur = cur->next;
4307 }
4308 /* No luck, join at the end of the queue */
4309 if (!inserted) {
4310 insert_entry(q, prev, qe, &pos);
4311 }
4312 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
4313 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
4314 ast_copy_string(qe->context, q->context, sizeof(qe->context));
4315 q->count++;
4316 if (q->count == 1) {
4318 }
4319
4320 res = 0;
4321
4322 blob = ast_json_pack("{s: s, s: i, s: i}",
4323 "Queue", q->name,
4324 "Position", qe->pos,
4325 "Count", q->count);
4326 ast_channel_publish_cached_blob(qe->chan, queue_caller_join_type(), blob);
4327 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, ast_channel_name(qe->chan), qe->pos );
4328 }
4329 ao2_unlock(q);
4330 queue_t_unref(q, "Done with realtime queue");
4331
4332 return res;
4333}
4334
4335static int play_file(struct ast_channel *chan, const char *filename)
4336{
4337 int res;
4338
4339 if (ast_strlen_zero(filename)) {
4340 return 0;
4341 }
4342
4343 if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
4344 return 0;
4345 }
4346
4348
4349 res = ast_streamfile(chan, filename, ast_channel_language(chan));
4350 if (!res) {
4352 }
4353
4355
4356 return res;
4357}
4358
4359/*!
4360 * \brief Check for valid exit from queue via goto
4361 * \retval 0 if failure
4362 * \retval 1 if successful
4363*/
4364static int valid_exit(struct queue_ent *qe, char digit)
4365{
4366 int digitlen = strlen(qe->digits);
4367
4368 /* Prevent possible buffer overflow */
4369 if (digitlen < sizeof(qe->digits) - 2) {
4370 qe->digits[digitlen] = digit;
4371 qe->digits[digitlen + 1] = '\0';
4372 } else {
4373 qe->digits[0] = '\0';
4374 return 0;
4375 }
4376
4377 /* If there's no context to goto, short-circuit */
4378 if (ast_strlen_zero(qe->context)) {
4379 return 0;
4380 }
4381
4382 /* If the extension is bad, then reset the digits to blank */
4383 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
4385 qe->digits[0] = '\0';
4386 return 0;
4387 }
4388
4389 /* We have an exact match */
4390 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
4391 qe->valid_digits = 1;
4392 /* Return 1 on a successful goto */
4393 return 1;
4394 }
4395
4396 return 0;
4397}
4398
4399static int say_position(struct queue_ent *qe, int ringing)
4400{
4401 int res = 0, say_thanks = 0;
4402 long avgholdmins, avgholdsecs;
4403 time_t now;
4404
4405 /* Let minannouncefrequency seconds pass between the start of each position announcement */
4406 time(&now);
4407 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) {
4408 return 0;
4409 }
4410
4411 /* If either our position has changed, or we are over the freq timer, say position */
4412 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) {
4413 return 0;
4414 }
4415
4416 /* Only announce if the caller's queue position has improved since last time */
4417 if (qe->parent->announceposition_only_up && qe->last_pos_said <= qe->pos) {
4418 return 0;
4419 }
4420
4421 if (ringing) {
4422 ast_indicate(qe->chan,-1);
4423 } else {
4424 ast_moh_stop(qe->chan);
4425 }
4426
4430 qe->pos <= qe->parent->announcepositionlimit)) {
4431 say_thanks = 1;
4432 /* Say we're next, if we are */
4433 if (qe->pos == 1) {
4434 res = play_file(qe->chan, qe->parent->sound_next);
4435 if (!res) {
4436 goto posout;
4437 }
4438 /* Say there are more than N callers */
4440 res = (
4441 play_file(qe->chan, qe->parent->queue_quantity1) ||
4443 ast_channel_language(qe->chan), NULL) || /* Needs gender */
4445 /* Say there are currently N callers waiting */
4446 } else {
4447 res = (
4448 play_file(qe->chan, qe->parent->sound_thereare) ||
4450 ast_channel_language(qe->chan), "n") || /* Needs gender */
4451 play_file(qe->chan, qe->parent->sound_calls));
4452 }
4453 if (res) {
4454 goto playout;
4455 }
4456 }
4457 /* Round hold time to nearest minute */
4458 avgholdmins = labs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
4459
4460 /* If they have specified a rounding then round the seconds as well */
4461 if (qe->parent->roundingseconds) {
4462 avgholdsecs = (labs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
4463 avgholdsecs *= qe->parent->roundingseconds;
4464 } else {
4465 avgholdsecs = 0;
4466 }
4467
4468 ast_verb(3, "Hold time for %s is %ld minute(s) %ld seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
4469
4470 /* If the hold time is >1 min, if it's enabled, and if it's not
4471 supposed to be only once and we have already said it, say it */
4472 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
4475 say_thanks = 1;
4476 res = play_file(qe->chan, qe->parent->sound_holdtime);
4477 if (res) {
4478 goto playout;
4479 }
4480
4481 if (avgholdmins >= 1) {
4482 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4483 if (res) {
4484 goto playout;
4485 }
4486
4487 if (avgholdmins == 1) {
4488 res = play_file(qe->chan, qe->parent->sound_minute);
4489 if (res) {
4490 goto playout;
4491 }
4492 } else {
4493 res = play_file(qe->chan, qe->parent->sound_minutes);
4494 if (res) {
4495 goto playout;
4496 }
4497 }
4498 }
4499 if (avgholdsecs >= 1) {
4500 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4501 if (res) {
4502 goto playout;
4503 }
4504
4505 res = play_file(qe->chan, qe->parent->sound_seconds);
4506 if (res) {
4507 goto playout;
4508 }
4509 }
4510 }
4511
4512posout:
4513 if (qe->parent->announceposition) {
4514 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
4515 ast_channel_name(qe->chan), qe->parent->name, qe->pos);
4516 }
4517 if (say_thanks) {
4518 res = play_file(qe->chan, qe->parent->sound_thanks);
4519 }
4520playout:
4521
4522 if ((res > 0 && !valid_exit(qe, res))) {
4523 res = 0;
4524 }
4525
4526 /* Set our last_pos indicators */
4527 qe->last_pos = now;
4528 qe->last_pos_said = qe->pos;
4529
4530 /* Don't restart music on hold if we're about to exit the caller from the queue */
4531 if (!res) {
4532 if (ringing) {
4534 } else {
4535 ast_moh_start(qe->chan, qe->moh, NULL);
4536 }
4537 }
4538 return res;
4539}
4540
4541static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
4542{
4543 int oldvalue;
4544
4545 /* Calculate holdtime using an exponential average */
4546 /* Thanks to SRT for this contribution */
4547 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
4548
4549 ao2_lock(qe->parent);
4550 if ((qe->parent->callscompleted + qe->parent->callsabandoned) == 0) {
4551 qe->parent->holdtime = newholdtime;
4552 } else {
4553 oldvalue = qe->parent->holdtime;
4554 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4555 }
4556 ao2_unlock(qe->parent);
4557}
4558
4559/*! \brief Caller leaving queue.
4560 *
4561 * Search the queue to find the leaving client, if found remove from queue
4562 * create manager event, move others up the queue.
4563*/
4564static void leave_queue(struct queue_ent *qe)
4565{
4566 struct call_queue *q;
4567 struct queue_ent *current, *prev = NULL;
4568 struct penalty_rule *pr_iter;
4569 int pos = 0;
4570
4571 if (!(q = qe->parent)) {
4572 return;
4573 }
4574 queue_t_ref(q, "Copy queue pointer from queue entry");
4575 ao2_lock(q);
4576
4577 prev = NULL;
4578 for (current = q->head; current; current = current->next) {
4579 if (current == qe) {
4580 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4581 char posstr[20];
4582 q->count--;
4583 if (!q->count) {
4585 }
4586
4587 blob = ast_json_pack("{s: s, s: i, s: i}",
4588 "Queue", q->name,
4589 "Position", qe->pos,
4590 "Count", q->count);
4591 ast_channel_publish_cached_blob(qe->chan, queue_caller_leave_type(), blob);
4592 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
4593 /* Take us out of the queue */
4594 if (prev) {
4595 prev->next = current->next;
4596 } else {
4597 q->head = current->next;
4598 }
4599 /* Free penalty rules */
4600 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
4601 ast_free(pr_iter);
4602 }
4603 qe->pr = NULL;
4604 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
4605 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
4606 } else {
4607 /* Renumber the people after us in the queue based on a new count */
4608 current->pos = ++pos;
4609 prev = current;
4610 }
4611 }
4612 ao2_unlock(q);
4613
4614 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
4615 if (q->realtime) {
4616 struct ast_variable *var;
4617 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
4618 q->dead = 1;
4619 } else {
4621 }
4622 }
4623
4624 if (q->dead) {
4625 /* It's dead and nobody is in it, so kill it */
4626 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
4627 }
4628 /* unref the explicit ref earlier in the function */
4629 queue_t_unref(q, "Expire copied reference");
4630}
4631
4632/*!
4633 * \internal
4634 * \brief Destroy the given callattempt structure and free it.
4635 * \since 1.8
4636 *
4637 * \param doomed callattempt structure to destroy.
4638 */
4639static void callattempt_free(struct callattempt *doomed)
4640{
4641 if (doomed->member) {
4642 ao2_ref(doomed->member, -1);
4643 }
4645 ast_free(doomed->orig_chan_name);
4646 ast_free(doomed);
4647}
4648
4649static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
4650{
4651 struct callattempt *cur;
4652
4653 for (cur = outgoing; cur; cur = cur->q_next) {
4654 if (cur->chan && cur->chan != exception) {
4656 }
4657 }
4658}
4659
4660/*! \brief Hang up a list of outgoing calls */
4661static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
4662{
4663 struct callattempt *oo;
4664
4665 while (outgoing) {
4666 /* If someone else answered the call we should indicate this in the CANCEL */
4667 /* Hangup any existing lines we have open */
4668 if (outgoing->chan && (outgoing->chan != exception)) {
4669 if (exception || cancel_answered_elsewhere) {
4671 }
4672 ast_channel_publish_dial(qe->chan, outgoing->chan, outgoing->interface, "CANCEL");
4673
4674 /* When dialing channels it is possible that they may not ever
4675 * leave the not in use state (Local channels in particular) by
4676 * the time we cancel them. If this occurs but we know they were
4677 * dialed we explicitly remove them from the pending members
4678 * container so that subsequent call attempts occur.
4679 */
4680 if (outgoing->member->status == AST_DEVICE_NOT_INUSE) {
4682 }
4683
4684 ast_hangup(outgoing->chan);
4685 }
4686 oo = outgoing;
4687 outgoing = outgoing->q_next;
4689 callattempt_free(oo);
4690 }
4691}
4692
4693/*!
4694 * \brief Get the number of members available to accept a call.
4695 *
4696 * \note The queue passed in should be locked prior to this function call
4697 *
4698 * \param[in] q The queue for which we are counting the number of available members
4699 * \return Return the number of available members in queue q
4700 */
4702{
4703 struct member *mem;
4704 int avl = 0;
4705 struct ao2_iterator mem_iter;
4706
4707 mem_iter = ao2_iterator_init(q->members, 0);
4708 while ((mem = ao2_iterator_next(&mem_iter))) {
4709
4710 avl += is_member_available(q, mem);
4711 ao2_ref(mem, -1);
4712
4713 /* If autofill is not enabled or if the queue's strategy is ringall, then
4714 * we really don't care about the number of available members so much as we
4715 * do that there is at least one available.
4716 *
4717 * In fact, we purposely will return from this function stating that only
4718 * one member is available if either of those conditions hold. That way,
4719 * functions which determine what action to take based on the number of available
4720 * members will operate properly. The reasoning is that even if multiple
4721 * members are available, only the head caller can actually be serviced.
4722 */
4723 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4724 break;
4725 }
4726 }
4727 ao2_iterator_destroy(&mem_iter);
4728
4729 return avl;
4730}
4731
4732/* traverse all defined queues which have calls waiting and contain this member
4733 return 0 if no other queue has precedence (higher weight) or 1 if found */
4734static int compare_weight(struct call_queue *rq, struct member *member)
4735{
4736 struct call_queue *q;
4737 struct member *mem;
4738 int found = 0;
4739 struct ao2_iterator queue_iter;
4740
4741 queue_iter = ao2_iterator_init(queues, 0);
4742 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4743 if (q == rq) { /* don't check myself, could deadlock */
4744 queue_t_unref(q, "Done with iterator");
4745 continue;
4746 }
4747 ao2_lock(q);
4748 if (q->count && q->members) {
4749 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4750 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4751 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
4752 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);
4753 found = 1;
4754 }
4755 ao2_ref(mem, -1);
4756 }
4757 }
4758 ao2_unlock(q);
4759 queue_t_unref(q, "Done with iterator");
4760 if (found) {
4761 break;
4762 }
4763 }
4764 ao2_iterator_destroy(&queue_iter);
4765 return found;
4766}
4767
4768static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
4769{
4770 struct call_queue *q;
4771 struct member *mem;
4772 int is_longest_waiting = 1;
4773 struct ao2_iterator queue_iter;
4774 struct queue_ent *ch;
4775
4776 queue_iter = ao2_iterator_init(queues, 0);
4777 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4778 if (q == caller->parent) { /* don't check myself, could deadlock */
4779 queue_t_unref(q, "Done with iterator");
4780 continue;
4781 }
4782 ao2_lock(q);
4783 /*
4784 * If the other queue has equal weight, see if we should let that handle
4785 * their call first. If weights are not equal, compare_weights will step in.
4786 */
4787 if (q->weight == caller->parent->weight && q->count && q->members) {
4788 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4789 ast_debug(2, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4790
4791 /* Does this queue have a caller that's been waiting longer? */
4792 ch = q->head;
4793 while (ch) {
4794 /* If ch->pending, the other call (which may be waiting for a longer period of time),
4795 * is already ringing at another agent. Ignore such callers; otherwise, all agents
4796 * will be unused until the first caller is picked up.
4797 */
4798 if (ch->start < caller->start && !ch->pending) {
4799 ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",
4800 q->name, ch->pos, ch->start, caller->start);
4801 is_longest_waiting = 0;
4802 break;
4803 }
4804 ch = ch->next;
4805 }
4806 }
4807 }
4808 ao2_unlock(q);
4809 queue_t_unref(q, "Done with iterator");
4810 if (!is_longest_waiting) {
4811 break;
4812 }
4813 }
4814 ao2_iterator_destroy(&queue_iter);
4815 return is_longest_waiting;
4816}
4817
4818/*! \brief common hangup actions */
4819static void do_hang(struct callattempt *o)
4820{
4821 o->stillgoing = 0;
4822 ast_hangup(o->chan);
4824 o->chan = NULL;
4825}
4826
4827/*!
4828 * \internal
4829 * \brief Check if the member status is available.
4830 *
4831 * \param status Member status to check if available.
4832 *
4833 * \retval non-zero if the member status is available.
4834 */
4836{
4838}
4839
4840/*!
4841 * \internal
4842 * \brief Determine if can ring a queue entry.
4843 *
4844 * \param qe Queue entry to check.
4845 * \param call Member call attempt.
4846 *
4847 * \retval non-zero if an entry can be called.
4848 */
4849static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
4850{
4851 struct member *memberp = call->member;
4852 int wrapuptime;
4853
4854 if (memberp->paused) {
4855 ast_debug(1, "%s paused, can't receive call\n", call->interface);
4856 return 0;
4857 }
4858
4859 if (!memberp->ringinuse && !member_status_available(memberp->status)) {
4860 ast_debug(1, "%s not available, can't receive call\n", call->interface);
4861 return 0;
4862 }
4863
4864 if (memberp->lastqueue) {
4865 wrapuptime = get_wrapuptime(memberp->lastqueue, memberp);
4866 } else {
4867 wrapuptime = get_wrapuptime(qe->parent, memberp);
4868 }
4869 if (wrapuptime && (time(NULL) - memberp->lastcall) < wrapuptime) {
4870 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
4871 (memberp->lastqueue ? memberp->lastqueue->name : qe->parent->name),
4872 call->interface);
4873 return 0;
4874 }
4875
4876 if (use_weight && compare_weight(qe->parent, memberp)) {
4877 ast_debug(1, "Priority queue delaying call to %s:%s\n",
4878 qe->parent->name, call->interface);
4879 return 0;
4880 }
4881
4883 ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n",
4884 qe->parent->name, call->interface);
4885 return 0;
4886 }
4887
4888 if (!memberp->ringinuse) {
4889 struct member *mem;
4890
4892
4893 mem = ao2_find(pending_members, memberp,
4895 if (mem) {
4896 /*
4897 * If found that means this member is currently being attempted
4898 * from another calling thread, so stop trying from this thread
4899 */
4900 ast_debug(1, "%s has another call trying, can't receive call\n",
4901 call->interface);
4902 ao2_ref(mem, -1);
4904 return 0;
4905 }
4906
4907 /*
4908 * If not found add it to the container so another queue
4909 * won't attempt to call this member at the same time.
4910 */
4911 ast_debug(3, "Add %s to pending_members\n", memberp->membername);
4912 ao2_link(pending_members, memberp);
4914
4915 /*
4916 * The queue member is available. Get current status to be sure
4917 * because the device state and extension state callbacks may
4918 * not have updated the status yet.
4919 */
4921 ast_debug(1, "%s actually not available, can't receive call\n",
4922 call->interface);
4923 pending_members_remove(memberp);
4924 return 0;
4925 }
4926 }
4927
4928 return 1;
4929}
4930
4931/*!
4932 * \brief Part 2 of ring_one
4933 *
4934 * Does error checking before attempting to request a channel and call a member.
4935 * This function is only called from ring_one().
4936 * Failure can occur if:
4937 * - Agent on call
4938 * - Agent is paused
4939 * - Wrapup time not expired
4940 * - Priority by another queue
4941 *
4942 * \retval 1 on success to reach a free agent
4943 * \retval 0 on failure to get agent.
4944 */
4945static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
4946{
4947 int res;
4948 int status;
4949 char tech[256];
4950 char *location;
4951 struct ast_format_cap *nativeformats;
4952 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4953
4954 /* on entry here, we know that tmp->chan == NULL */
4955 if (!can_ring_entry(qe, tmp)) {
4956 tmp->stillgoing = 0;
4957 ++*busies;
4958 return 0;
4959 }
4960
4961 ast_copy_string(tech, tmp->interface, sizeof(tech));
4962 if ((location = strchr(tech, '/'))) {
4963 *location++ = '\0';
4964 } else {
4965 location = "";
4966 }
4967
4969 nativeformats = ao2_bump(ast_channel_nativeformats(qe->chan));
4971
4972 /* Request the peer */
4973 tmp->chan = ast_request(tech, nativeformats, NULL, qe->chan, location, &status);
4974 ao2_cleanup(nativeformats);
4975 if (!tmp->chan) { /* If we can't, just go on to the next call */
4976 ao2_lock(qe->parent);
4977 qe->parent->rrpos++;
4978 qe->linpos++;
4979 ao2_unlock(qe->parent);
4980
4982
4983 publish_dial_end_event(qe->chan, tmp, NULL, "BUSY");
4984 tmp->stillgoing = 0;
4985 ++*busies;
4986 return 0;
4987 }
4988
4989 ast_channel_lock_both(tmp->chan, qe->chan);
4990
4993 if (qe->cancel_answered_elsewhere) {
4995 }
4996 ast_channel_appl_set(tmp->chan, "AppQueue");
4997 ast_channel_data_set(tmp->chan, "(Outgoing Line)");
4998 memset(ast_channel_whentohangup(tmp->chan), 0, sizeof(*ast_channel_whentohangup(tmp->chan)));
4999
5000 /* If the new channel has no callerid, try to guess what it should be */
5001 if (!ast_channel_caller(tmp->chan)->id.number.valid) {
5003 struct ast_party_caller caller;
5004
5006 caller.id = ast_channel_connected(qe->chan)->id;
5007 caller.ani = ast_channel_connected(qe->chan)->ani;
5008 ast_channel_set_caller_event(tmp->chan, &caller, NULL);
5009 } else if (!ast_strlen_zero(ast_channel_dialed(qe->chan)->number.str)) {
5011 } else if (!ast_strlen_zero(ast_channel_exten(qe->chan))) {
5013 }
5014 tmp->dial_callerid_absent = 1;
5015 }
5016
5018
5020
5022
5023 /* Inherit specially named variables from parent channel */
5027
5028 /* Presense of ADSI CPE on outgoing channel follows ours */
5030
5031 /* Inherit context and extension */
5032 ast_channel_dialcontext_set(tmp->chan, ast_channel_context(qe->chan));
5034
5035 /* Save the original channel name to detect call pickup masquerading in. */
5037
5040
5041 /* location is tmp->interface where tech/ has been stripped, so it follow the same syntax as DIALEDPEERNUMBER in app_dial.c */
5042 pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", strlen(location) ? location : tmp->interface);
5043
5044 /* PREDIAL: Run gosub on the callee's channel */
5045 if (qe->predial_callee) {
5046 ast_pre_call(tmp->chan, qe->predial_callee);
5047 }
5048
5049 /* Place the call, but don't wait on the answer */
5050 if ((res = ast_call(tmp->chan, location, 0))) {
5051 /* Again, keep going even if there's an error */
5052 ast_verb(3, "Couldn't call %s\n", tmp->interface);
5053 do_hang(tmp);
5054 ++*busies;
5055 return 0;
5056 }
5057
5058 ast_channel_lock_both(tmp->chan, qe->chan);
5059
5060 blob = ast_json_pack("{s: s, s: s, s: s}",
5061 "Queue", qe->parent->name,
5062 "Interface", tmp->interface,
5063 "MemberName", tmp->member->membername);
5064 queue_publish_multi_channel_blob(qe->chan, tmp->chan, queue_agent_called_type(), blob);
5065
5067
5070
5071 ast_verb(3, "Called %s\n", tmp->interface);
5072
5073 return 1;
5074}
5075
5076/*! \brief find the entry with the best metric, or NULL */
5078{
5079 struct callattempt *best = NULL, *cur;
5080
5081 for (cur = outgoing; cur; cur = cur->q_next) {
5082 if (cur->stillgoing && /* Not already done */
5083 !cur->chan && /* Isn't already going */
5084 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
5085 best = cur;
5086 }
5087 }
5088
5089 return best;
5090}
5091
5092/*!
5093 * \brief Place a call to a queue member.
5094 *
5095 * Once metrics have been calculated for each member, this function is used
5096 * to place a call to the appropriate member (or members). The low-level
5097 * channel-handling and error detection is handled in ring_entry
5098 *
5099 * \retval 1 if a member was called successfully
5100 * \retval 0 otherwise
5101 */
5102static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
5103{
5104 int ret = 0;
5105 struct callattempt *cur;
5106
5107 if (qe->predial_callee) {
5109 for (cur = outgoing; cur; cur = cur->q_next) {
5110 if (cur->stillgoing && cur->chan) {
5112 }
5113 }
5114 }
5115
5116 while (ret == 0) {
5117 struct callattempt *best = find_best(outgoing);
5118 if (!best) {
5119 ast_debug(1, "Nobody left to try ringing in queue\n");
5120 break;
5121 }
5123 /* Ring everyone who shares this best metric (for ringall) */
5124 for (cur = outgoing; cur; cur = cur->q_next) {
5125 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
5126 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
5127 ret |= ring_entry(qe, cur, busies);
5128 if (qe->predial_callee && cur->chan) {
5130 }
5131 }
5132 }
5133 } else {
5134 /* Ring just the best channel */
5135 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
5136 ret = ring_entry(qe, best, busies);
5137 if (qe->predial_callee && best->chan) {
5139 }
5140 }
5141
5142 /* If we have timed out, break out */
5143 if (qe->expire && (time(NULL) >= qe->expire)) {
5144 ast_debug(1, "Queue timed out while ringing members.\n");
5145 ret = 0;
5146 break;
5147 }
5148 }
5149 if (qe->predial_callee) {
5150 for (cur = outgoing; cur; cur = cur->q_next) {
5151 if (cur->stillgoing && cur->chan) {
5153 }
5154 }
5156 }
5157
5158 return ret;
5159}
5160
5161/*! \brief Search for best metric and add to Round Robbin queue */
5162static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
5163{
5164 struct callattempt *best = find_best(outgoing);
5165
5166 if (best) {
5167 /* Ring just the best channel */
5168 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
5169 qe->parent->rrpos = best->metric % 1000;
5170 } else {
5171 /* Just increment rrpos */
5172 if (qe->parent->wrapped) {
5173 /* No more channels, start over */
5174 qe->parent->rrpos = 0;
5175 } else {
5176 /* Prioritize next entry */
5177 qe->parent->rrpos++;
5178 }
5179 }
5180 qe->parent->wrapped = 0;
5181
5182 return 0;
5183}
5184
5185/*! \brief Search for best metric and add to Linear queue */
5186static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
5187{
5188 struct callattempt *best = find_best(outgoing);
5189
5190 if (best) {
5191 /* Ring just the best channel */
5192 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
5193 qe->linpos = best->metric % 1000;
5194 } else {
5195 /* Just increment rrpos */
5196 if (qe->linwrapped) {
5197 /* No more channels, start over */
5198 qe->linpos = 0;
5199 } else {
5200 /* Prioritize next entry */
5201 qe->linpos++;
5202 }
5203 }
5204 qe->linwrapped = 0;
5205
5206 return 0;
5207}
5208
5209/*! \brief Playback announcement to queued members if period has elapsed */
5211{
5212 int res = 0;
5213 time_t now;
5214
5215 /* Get the current time */
5216 time(&now);
5217
5218 /* Check to see if it is time to announce */
5220 return 0;
5221 }
5222
5223 /* Stop the music on hold so we can play our own file */
5224 if (ringing) {
5225 ast_indicate(qe->chan,-1);
5226 } else {
5227 ast_moh_stop(qe->chan);
5228 }
5229
5230 ast_verb(3, "Playing periodic announcement\n");
5231
5233 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
5237 }
5238
5239 /* play the announcement */
5241
5242 if (res > 0 && !valid_exit(qe, res)) {
5243 res = 0;
5244 }
5245
5246 /* Resume Music on Hold if the caller is going to stay in the queue */
5247 if (!res) {
5248 if (ringing) {
5250 } else {
5251 ast_moh_start(qe->chan, qe->moh, NULL);
5252 }
5253 }
5254
5255 /* update last_periodic_announce_time */
5257 time(&qe->last_periodic_announce_time);
5258 } else {
5260 }
5261
5262 /* Update the current periodic announcement to the next announcement */
5263 if (!qe->parent->randomperiodicannounce) {
5265 }
5266
5267 return res;
5268}
5269
5270/*! \brief Record that a caller gave up on waiting in queue */
5271static void record_abandoned(struct queue_ent *qe)
5272{
5273 int callabandonedinsl = 0;
5274 time_t now;
5275
5276 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5277
5278 pbx_builtin_setvar_helper(qe->chan, "ABANDONED", "TRUE");
5279
5281 ao2_lock(qe->parent);
5282 blob = ast_json_pack("{s: s, s: i, s: i, s: i}",
5283 "Queue", qe->parent->name,
5284 "Position", qe->pos,
5285 "OriginalPosition", qe->opos,
5286 "HoldTime", (int)(time(NULL) - qe->start));
5287
5288
5289 time(&now);
5290 callabandonedinsl = ((now - qe->start) <= qe->parent->servicelevel);
5291 if (callabandonedinsl) {
5293 }
5294
5295 qe->parent->callsabandoned++;
5296 ao2_unlock(qe->parent);
5297
5298 ast_channel_publish_cached_blob(qe->chan, queue_caller_abandon_type(), blob);
5299}
5300
5301/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
5302static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, char *interface, char *membername, int autopause)
5303{
5304 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5305
5306 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
5307
5308 /* Stop ringing, and resume MOH if specified */
5309 if (qe->ring_when_ringing) {
5310 ast_indicate(qe->chan, -1);
5311 ast_moh_start(qe->chan, qe->moh, NULL);
5312 }
5313
5314 blob = ast_json_pack("{s: s, s: s, s: s, s: i}",
5315 "Queue", qe->parent->name,
5316 "Interface", interface,
5317 "MemberName", membername,
5318 "RingTime", rnatime);
5319 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_ringnoanswer_type(), blob);
5320
5321 ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), membername, "RINGNOANSWER", "%d", rnatime);
5323 if (qe->parent->autopausedelay > 0) {
5324 struct member *mem;
5325 ao2_lock(qe->parent);
5326 if ((mem = interface_exists(qe->parent, interface))) {
5327 time_t idletime = time(&idletime)-mem->lastcall;
5328 if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
5329 ao2_unlock(qe->parent);
5330 ao2_ref(mem, -1);
5331 return;
5332 }
5333 ao2_ref(mem, -1);
5334 }
5335 ao2_unlock(qe->parent);
5336 }
5337 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
5338 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
5339 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
5340 interface, qe->parent->name);
5341 } else {
5342 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
5343 }
5344 } else {
5345 /* If queue autopause is mode all, just don't send any queue to stop.
5346 * the function will stop in all queues */
5347 if (!set_member_paused("", interface, "Auto-Pause", 1)) {
5348 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
5349 interface, qe->parent->name);
5350 } else {
5351 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
5352 }
5353 }
5354 }
5355 return;
5356}
5357
5358/*!
5359 * \internal
5360 * \brief Update connected line on chan from peer.
5361 * \since 13.6.0
5362 *
5363 * \param chan Channel to get connected line updated.
5364 * \param peer Channel providing connected line information.
5365 * \param is_caller Non-zero if chan is the calling channel.
5366 */
5367static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
5368{
5369 struct ast_party_connected_line connected_caller;
5370
5371 ast_party_connected_line_init(&connected_caller);
5372
5373 ast_channel_lock(peer);
5375 ast_channel_unlock(peer);
5377 if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)) {
5378 ast_channel_update_connected_line(chan, &connected_caller, NULL);
5379 }
5380 ast_party_connected_line_free(&connected_caller);
5381}
5382
5383#define AST_MAX_WATCHERS 256
5384/*!
5385 * \brief Wait for a member to answer the call
5386 *
5387 * \param[in] qe the queue_ent corresponding to the caller in the queue
5388 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
5389 * \param[in] to the amount of time (in milliseconds) to wait for a response
5390 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
5391 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
5392 * \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
5393 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
5394 *
5395 * \todo eventually all call forward logic should be integrated into and replaced by ast_call_forward()
5396 */
5397static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
5398{
5399 const char *queue = qe->parent->name;
5400 struct callattempt *o, *start = NULL, *prev = NULL;
5401 int status;
5402 int numbusies = prebusies;
5403 int numnochan = 0;
5404 int stillgoing = 0;
5405 int orig = *to;
5406 struct ast_frame *f;
5407 struct callattempt *peer = NULL;
5408 struct ast_channel *winner;
5409 struct ast_channel *in = qe->chan;
5410 char on[80] = "";
5411 char membername[80] = "";
5412 long starttime = 0;
5413 long endtime = 0;
5414 char *inchan_name;
5415 struct timeval start_time_tv = ast_tvnow();
5416 int canceled_by_caller = 0; /* 1 when caller hangs up or press digit or press * */
5417
5419 inchan_name = ast_strdupa(ast_channel_name(qe->chan));
5421
5422 starttime = (long) time(NULL);
5423
5424 while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
5425 int numlines, retry, pos = 1;
5426 struct ast_channel *watchers[AST_MAX_WATCHERS];
5427 watchers[0] = in;
5428 start = NULL;
5429
5430 for (retry = 0; retry < 2; retry++) {
5431 numlines = 0;
5432 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
5433 if (o->stillgoing) { /* Keep track of important channels */
5434 stillgoing = 1;
5435 if (o->chan) {
5436 if (pos < AST_MAX_WATCHERS) {
5437 watchers[pos++] = o->chan;
5438 }
5439 if (!start) {
5440 start = o;
5441 } else {
5442 prev->call_next = o;
5443 }
5444 prev = o;
5445 }
5446 } else if (prev) {
5447 prev->call_next = NULL;
5448 }
5449 numlines++;
5450 }
5451 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
5452 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) {
5453 break;
5454 }
5455 /* On "ringall" strategy we only move to the next penalty level
5456 when *all* ringing phones are done in the current penalty level */
5457 ring_one(qe, outgoing, &numbusies);
5458 /* and retry... */
5459 }
5460 if (pos == 1 /* not found */) {
5461 if (numlines == (numbusies + numnochan)) {
5462 ast_debug(1, "Everyone is busy at this time\n");
5463 } else {
5464 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
5465 }
5466 *to = 0;
5467 return NULL;
5468 }
5469
5470 /* Poll for events from both the incoming channel as well as any outgoing channels */
5471 winner = ast_waitfor_n(watchers, pos, to);
5472
5473 /* Service all of the outgoing channels */
5474 for (o = start; o; o = o->call_next) {
5475 /* We go with a fixed buffer here instead of using ast_strdupa. Using
5476 * ast_strdupa in a loop like this one can cause a stack overflow
5477 */
5478 char ochan_name[AST_CHANNEL_NAME];
5479
5480 if (o->chan) {
5482 ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
5484 }
5485 if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
5486 if (!peer) {
5487 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5488 if (o->orig_chan_name
5489 && strcmp(o->orig_chan_name, ochan_name)) {
5490 /*
5491 * The channel name changed so we must generate COLP update.
5492 * Likely because a call pickup channel masqueraded in.
5493 */
5495 } else if (!o->block_connected_update) {
5496 if (o->pending_connected_update) {
5499 }
5500 } else if (!o->dial_callerid_absent) {
5502 }
5503 }
5504 if (o->aoc_s_rate_list) {
5505 size_t encoded_size;
5506 struct ast_aoc_encoded *encoded;
5507 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5508 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5509 ast_aoc_destroy_encoded(encoded);
5510 }
5511 }
5512 peer = o;
5513 }
5514 } else if (o->chan && (o->chan == winner)) {
5515
5516 ast_copy_string(on, o->member->interface, sizeof(on));
5517 ast_copy_string(membername, o->member->membername, sizeof(membername));
5518
5519 /* Before processing channel, go ahead and check for forwarding */
5520 if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
5521 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
5523 "CANCEL", ast_channel_call_forward(o->chan));
5524 numnochan++;
5525 do_hang(o);
5526 winner = NULL;
5527 continue;
5529 struct ast_channel *original = o->chan;
5530 char forwarder[AST_CHANNEL_NAME];
5531 char tmpchan[256];
5532 char *stuff;
5533 char *tech;
5534 int failed = 0;
5535
5536 ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
5537 ast_copy_string(forwarder, ast_channel_name(o->chan), sizeof(forwarder));
5538 if ((stuff = strchr(tmpchan, '/'))) {
5539 *stuff++ = '\0';
5540 tech = tmpchan;
5541 } else {
5542 const char *forward_context;
5544 forward_context = pbx_builtin_getvar_helper(o->chan, "FORWARD_CONTEXT");
5545 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), forward_context ? forward_context : ast_channel_context(o->chan));
5547 stuff = tmpchan;
5548 tech = "Local";
5549 }
5550 if (!strcasecmp(tech, "Local")) {
5551 /*
5552 * Drop the connected line update block for local channels since
5553 * this is going to run dialplan and the user can change his
5554 * mind about what connected line information he wants to send.
5555 */
5557 }
5558
5559 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
5560 /* Setup parameters */
5562 if (!o->chan) {
5564 "Forwarding failed to create channel to dial '%s/%s'\n",
5565 tech, stuff);
5566 o->stillgoing = 0;
5567 numnochan++;
5568 } else {
5569 ast_channel_lock_both(o->chan, original);
5571 ast_channel_redirecting(original));
5573 ast_channel_unlock(original);
5574
5578 pbx_builtin_setvar_helper(o->chan, "FORWARDERNAME", forwarder);
5580
5581 if (o->pending_connected_update) {
5582 /*
5583 * Re-seed the callattempt's connected line information with
5584 * previously acquired connected line info from the queued
5585 * channel. The previously acquired connected line info could
5586 * have been set through the CONNECTED_LINE dialplan function.
5587 */
5590 }
5591
5594
5596
5599 /*
5600 * The call was not previously redirected so it is
5601 * now redirected from this number.
5602 */
5608 }
5609
5611
5616
5619 && !o->block_connected_update) {
5620 struct ast_party_redirecting redirecting;
5621
5622 /*
5623 * Redirecting updates to the caller make sense only on single
5624 * call at a time strategies.
5625 *
5626 * Need to re-evaluate if calling unlock is still required as we no longer
5627 * use macro.
5628 */
5629 ast_party_redirecting_init(&redirecting);
5632 if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0)) {
5633 ast_channel_update_redirecting(in, &redirecting, NULL);
5634 }
5635 ast_party_redirecting_free(&redirecting);
5636 } else {
5638 }
5639
5640 if (ast_call(o->chan, stuff, 0)) {
5641 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
5642 tech, stuff);
5643 failed = 1;
5644 }
5645 }
5646
5648 "CANCEL", ast_channel_call_forward(original));
5649 if (o->chan) {
5650 ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
5651 }
5652
5653 if (failed) {
5654 do_hang(o);
5655 numnochan++;
5656 }
5657
5658 /* Hangup the original channel now, in case we needed it */
5659 ast_hangup(winner);
5660 continue;
5661 }
5662 f = ast_read(winner);
5663 if (f) {
5664 if (f->frametype == AST_FRAME_CONTROL) {
5665 switch (f->subclass.integer) {
5666 case AST_CONTROL_ANSWER:
5667 /* This is our guy if someone answered. */
5668 if (!peer) {
5669 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5670 ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
5671 publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
5672 if (o->orig_chan_name
5673 && strcmp(o->orig_chan_name, ochan_name)) {
5674 /*
5675 * The channel name changed so we must generate COLP update.
5676 * Likely because a call pickup channel masqueraded in.
5677 */
5679 } else if (!o->block_connected_update) {
5680 if (o->pending_connected_update) {
5683 }
5684 } else if (!o->dial_callerid_absent) {
5686 }
5687 }
5688 if (o->aoc_s_rate_list) {
5689 size_t encoded_size;
5690 struct ast_aoc_encoded *encoded;
5691 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5692 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5693 ast_aoc_destroy_encoded(encoded);
5694 }
5695 }
5696 peer = o;
5697 }
5698 break;
5699 case AST_CONTROL_BUSY:
5700 ast_verb(3, "%s is busy\n", ochan_name);
5701 ast_channel_publish_dial(qe->chan, o->chan, on, "BUSY");
5702 endtime = (long) time(NULL);
5703 endtime -= starttime;
5704 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopausebusy);
5705 do_hang(o);
5707 if (qe->parent->timeoutrestart) {
5708 start_time_tv = ast_tvnow();
5709 }
5710 /* Have enough time for a queue member to answer? */
5711 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5712 ring_one(qe, outgoing, &numbusies);
5713 starttime = (long) time(NULL);
5714 }
5715 }
5716 numbusies++;
5717 break;
5719 ast_verb(3, "%s is circuit-busy\n", ochan_name);
5720 ast_channel_publish_dial(qe->chan, o->chan, on, "CONGESTION");
5721 endtime = (long) time(NULL);
5722 endtime -= starttime;
5723 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopauseunavail);
5724 do_hang(o);
5726 if (qe->parent->timeoutrestart) {
5727 start_time_tv = ast_tvnow();
5728 }
5729 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5730 ring_one(qe, outgoing, &numbusies);
5731 starttime = (long) time(NULL);
5732 }
5733 }
5734 numbusies++;
5735 break;
5737 ast_verb(3, "%s is ringing\n", ochan_name);
5738
5739 ast_channel_publish_dial(qe->chan, o->chan, on, "RINGING");
5740
5741 /* Start ring indication when the channel is ringing, if specified */
5742 if (qe->ring_when_ringing) {
5743 ast_moh_stop(qe->chan);
5745 }
5746 break;
5748 /* Ignore going off hook */
5749 break;
5751 if (o->block_connected_update) {
5752 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
5753 break;
5754 }
5757
5758 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
5764 break;
5765 }
5766
5767 /*
5768 * Prevent using the CallerID from the outgoing channel since we
5769 * got a connected line update from it.
5770 */
5771 o->dial_callerid_absent = 1;
5772
5773 if (ast_channel_connected_line_sub(o->chan, in, f, 1)) {
5775 }
5776 break;
5777 case AST_CONTROL_AOC:
5778 {
5779 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
5780 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
5782 o->aoc_s_rate_list = decoded;
5783 } else {
5784 ast_aoc_destroy_decoded(decoded);
5785 }
5786 }
5787 break;
5790 /*
5791 * Redirecting updates to the caller make sense only on single
5792 * call at a time strategies.
5793 */
5794 break;
5795 }
5796 if (o->block_connected_update) {
5797 ast_verb(3, "Redirecting update to %s prevented\n",
5798 inchan_name);
5799 break;
5800 }
5801 ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
5802 ochan_name, inchan_name);
5803 if (ast_channel_redirecting_sub(o->chan, in, f, 1)) {
5805 }
5806 break;
5809 break;
5810 default:
5811 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
5812 break;
5813 }
5814 }
5815 ast_frfree(f);
5816 } else { /* ast_read() returned NULL */
5817 endtime = (long) time(NULL) - starttime;
5818 ast_channel_publish_dial(qe->chan, o->chan, on, "NOANSWER");
5819 rna(endtime * 1000, qe, o->chan, on, membername, 1);
5820 do_hang(o);
5822 if (qe->parent->timeoutrestart) {
5823 start_time_tv = ast_tvnow();
5824 }
5825 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5826 ring_one(qe, outgoing, &numbusies);
5827 starttime = (long) time(NULL);
5828 }
5829 }
5830 }
5831 }
5832 }
5833
5834 /* If we received an event from the caller, deal with it. */
5835 if (winner == in) {
5836 f = ast_read(in);
5837 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
5838 /* Got hung up */
5839 *to = -1;
5840 if (f) {
5841 if (f->data.uint32) {
5843 }
5844 ast_frfree(f);
5845 }
5846 canceled_by_caller = 1;
5847 } else if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
5848 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
5849 *to = 0;
5850 ast_frfree(f);
5851 canceled_by_caller = 1;
5852 } else if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
5853 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
5854 *to = 0;
5855 *digit = f->subclass.integer;
5856 ast_frfree(f);
5857 canceled_by_caller = 1;
5858 }
5859 /* When caller hung up or pressed * or digit. */
5860 if (canceled_by_caller) {
5862 for (o = start; o; o = o->call_next) {
5863 if (o->chan) {
5864 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));
5865 }
5866 }
5867 return NULL;
5868 }
5869
5870 /* Send the frame from the in channel to all outgoing channels. */
5871 for (o = start; o; o = o->call_next) {
5872 if (!o->stillgoing || !o->chan) {
5873 /* This outgoing channel has died so don't send the frame to it. */
5874 continue;
5875 }
5876 switch (f->frametype) {
5877 case AST_FRAME_CONTROL:
5878 switch (f->subclass.integer) {
5880 if (o->block_connected_update) {
5881 ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
5882 break;
5883 }
5884 if (ast_channel_connected_line_sub(in, o->chan, f, 1)) {
5886 }
5887 break;
5889 if (o->block_connected_update) {
5890 ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
5891 break;
5892 }
5893 if (ast_channel_redirecting_sub(in, o->chan, f, 1)) {
5895 }
5896 break;
5897 default:
5898 /* We are not going to do anything with this frame. */
5899 goto skip_frame;
5900 }
5901 break;
5902 default:
5903 /* We are not going to do anything with this frame. */
5904 goto skip_frame;
5905 }
5906 }
5907skip_frame:;
5908
5909 ast_frfree(f);
5910 }
5911 }
5912
5913 if (!*to) {
5914 for (o = start; o; o = o->call_next) {
5915 if (o->chan) {
5916 rna(orig, qe, o->chan, o->interface, o->member->membername, 1);
5917 }
5918 }
5919
5920 publish_dial_end_event(qe->chan, outgoing, NULL, "NOANSWER");
5921 }
5922
5923 return peer;
5924}
5925
5926/*!
5927 * \brief Check if we should start attempting to call queue members.
5928 *
5929 * A simple process, really. Count the number of members who are available
5930 * to take our call and then see if we are in a position in the queue at
5931 * which a member could accept our call.
5932 *
5933 * \param[in] qe The caller who wants to know if it is his turn
5934 * \retval 0 It is not our turn
5935 * \retval 1 It is our turn
5936 */
5937static int is_our_turn(struct queue_ent *qe)
5938{
5939 struct queue_ent *ch;
5940 int res;
5941 int avl;
5942 int idx = 0;
5943 /* This needs a lock. How many members are available to be served? */
5944 ao2_lock(qe->parent);
5945
5946 avl = num_available_members(qe->parent);
5947
5948 ch = qe->parent->head;
5949
5950 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
5951
5952 while ((idx < avl) && (ch) && (ch != qe)) {
5953 if (!ch->pending) {
5954 idx++;
5955 }
5956 ch = ch->next;
5957 }
5958
5959 ao2_unlock(qe->parent);
5960 /* If the queue entry is within avl [the number of available members] calls from the top ...
5961 * Autofill and position check added to support autofill=no (as only calls
5962 * from the front of the queue are valid when autofill is disabled)
5963 */
5964 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
5965 ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
5966 res = 1;
5967 } else {
5968 ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
5969 res = 0;
5970 }
5971
5972 /* Update realtime members if this is the first call and number of avalable members is 0 */
5973 if (avl == 0 && qe->pos == 1) {
5975 }
5976
5977 return res;
5978}
5979
5980/*!
5981 * \brief update rules for queues
5982 *
5983 * Calculate min/max penalties making sure if relative they stay within bounds.
5984 * Update queues penalty and set dialplan vars, goto next list entry.
5985*/
5986static void update_qe_rule(struct queue_ent *qe)
5987{
5988 int max_penalty = INT_MAX;
5989
5990 if (qe->max_penalty != INT_MAX) {
5991 char max_penalty_str[20];
5992
5993 if (qe->pr->max_relative) {
5994 max_penalty = qe->max_penalty + qe->pr->max_value;
5995 } else {
5996 max_penalty = qe->pr->max_value;
5997 }
5998
5999 /* a relative change to the penalty could put it below 0 */
6000 if (max_penalty < 0) {
6001 max_penalty = 0;
6002 }
6003
6004 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
6005 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
6007 ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
6008 qe->max_penalty, ast_channel_name(qe->chan), qe->pr->time);
6009 }
6010
6011 if (qe->min_penalty != INT_MAX) {
6012 char min_penalty_str[20];
6013 int min_penalty;
6014
6015 if (qe->pr->min_relative) {
6016 min_penalty = qe->min_penalty + qe->pr->min_value;
6017 } else {
6018 min_penalty = qe->pr->min_value;
6019 }
6020
6021 /* a relative change to the penalty could put it below 0 */
6022 if (min_penalty < 0) {
6023 min_penalty = 0;
6024 }
6025
6026 if (max_penalty != INT_MAX && min_penalty > max_penalty) {
6028 }
6029
6030 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
6031 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
6033 ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
6034 qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
6035 }
6036
6037 if (qe->raise_penalty != INT_MAX) {
6038 char raise_penalty_str[20];
6039 int raise_penalty;
6040
6041 if (qe->pr->raise_relative) {
6043 } else {
6045 }
6046
6047 /* a relative change to the penalty could put it below 0 */
6048 if (raise_penalty < 0) {
6049 raise_penalty = 0;
6050 }
6051
6052 if (max_penalty != INT_MAX && raise_penalty > max_penalty) {
6054 }
6055
6056 snprintf(raise_penalty_str, sizeof(raise_penalty_str), "%d", raise_penalty);
6057 pbx_builtin_setvar_helper(qe->chan, "QUEUE_RAISE_PENALTY", raise_penalty_str);
6059 ast_debug(3, "Setting raised penalty to %d for caller %s since %d seconds have elapsed\n",
6060 qe->raise_penalty, ast_channel_name(qe->chan), qe->pr->time);
6061 }
6062
6063 qe->pr = AST_LIST_NEXT(qe->pr, list);
6064}
6065
6066/*! \brief The waiting areas for callers who are not actively calling members
6067 *
6068 * This function is one large loop. This function will return if a caller
6069 * either exits the queue or it becomes that caller's turn to attempt calling
6070 * queue members. Inside the loop, we service the caller with periodic announcements,
6071 * holdtime announcements, etc. as configured in queues.conf
6072 *
6073 * \retval 0 if the caller's turn has arrived
6074 * \retval -1 if the caller should exit the queue.
6075 */
6076static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
6077{
6078 int res = 0;
6079
6080 /* This is the holding pen for callers 2 through maxlen */
6081 for (;;) {
6082
6083 /* A request to withdraw this call from the queue arrived */
6084 if (qe->withdraw) {
6085 *reason = QUEUE_WITHDRAW;
6086 res = 1;
6087 break;
6088 }
6089
6090 if (is_our_turn(qe)) {
6091 break;
6092 }
6093
6094 /* If we have timed out, break out */
6095 if (qe->expire && (time(NULL) >= qe->expire)) {
6096 *reason = QUEUE_TIMEOUT;
6097 break;
6098 }
6099
6100 if (qe->parent->leavewhenempty) {
6101 int status = 0;
6102
6104 record_abandoned(qe);
6105 *reason = QUEUE_LEAVEEMPTY;
6106 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));
6107 res = -1;
6108 qe->handled = -1;
6109 break;
6110 }
6111 }
6112
6113 /* Make a position announcement, if enabled */
6114 if (qe->parent->announcefrequency &&
6115 (res = say_position(qe,ringing))) {
6116 break;
6117 }
6118
6119 /* If we have timed out, break out */
6120 if (qe->expire && (time(NULL) >= qe->expire)) {
6121 *reason = QUEUE_TIMEOUT;
6122 break;
6123 }
6124
6125 /* Make a periodic announcement, if enabled */
6128 break;
6129
6130 /* see if we need to move to the next penalty level for this queue */
6131 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
6132 update_qe_rule(qe);
6133 }
6134
6135 /* If we have timed out, break out */
6136 if (qe->expire && (time(NULL) >= qe->expire)) {
6137 *reason = QUEUE_TIMEOUT;
6138 break;
6139 }
6140
6141 /* Wait a second before checking again */
6142 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
6143 if (res > 0 && !valid_exit(qe, res)) {
6144 res = 0;
6145 } else {
6146 break;
6147 }
6148 }
6149
6150 /* If we have timed out, break out */
6151 if (qe->expire && (time(NULL) >= qe->expire)) {
6152 *reason = QUEUE_TIMEOUT;
6153 break;
6154 }
6155 }
6156
6157 return res;
6158}
6159
6160/*!
6161 * \brief update the queue status
6162 * \retval 0 always
6163*/
6164static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
6165{
6166 int oldtalktime;
6167 int newtalktime = time(NULL) - starttime;
6168 struct member *mem;
6169 struct call_queue *qtmp;
6170 struct ao2_iterator queue_iter;
6171
6172 /* It is possible for us to be called when a call has already been considered terminated
6173 * and data updated, so to ensure we only act on the call that the agent is currently in
6174 * we check when the call was bridged.
6175 */
6176 if (!starttime || (member->starttime != starttime)) {
6177 return 0;
6178 }
6179
6180 if (shared_lastcall) {
6181 queue_iter = ao2_iterator_init(queues, 0);
6182 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
6183 ao2_lock(qtmp);
6184 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
6185 time(&mem->lastcall);
6186 mem->calls++;
6187 mem->callcompletedinsl = 0;
6188 mem->starttime = 0;
6189 mem->lastqueue = q;
6190 ao2_ref(mem, -1);
6191 }
6192 ao2_unlock(qtmp);
6193 queue_t_unref(qtmp, "Done with iterator");
6194 }
6195 ao2_iterator_destroy(&queue_iter);
6196 } else {
6197 ao2_lock(q);
6198 time(&member->lastcall);
6200 member->calls++;
6201 member->starttime = 0;
6202 member->lastqueue = q;
6203 ao2_unlock(q);
6204 }
6205 /* Member might never experience any direct status change (local
6206 * channel with forwarding in particular). If that's the case,
6207 * this is the last chance to remove it from pending or subsequent
6208 * calls will not occur.
6209 */
6211
6212 ao2_lock(q);
6213 q->callscompleted++;
6214 if (callcompletedinsl) {
6215 q->callscompletedinsl++;
6216 }
6217 if (q->callscompleted == 1) {
6218 q->talktime = newtalktime;
6219 } else {
6220 /* Calculate talktime using the same exponential average as holdtime code */
6221 oldtalktime = q->talktime;
6222 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
6223 }
6224 ao2_unlock(q);
6225 return 0;
6226}
6227
6228/*! \brief Calculate the metric of each member in the outgoing callattempts
6229 *
6230 * A numeric metric is given to each member depending on the ring strategy used
6231 * by the queue. Members with lower metrics will be called before members with
6232 * higher metrics
6233 * \retval -1 if penalties are exceeded
6234 * \retval 0 otherwise
6235 */
6236static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
6237{
6238 /* disregarding penalty on too few members? */
6239 int membercount = ao2_container_count(q->members);
6240 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
6241 int penalty = mem->penalty;
6242
6243 if (usepenalty) {
6244 if (qe->raise_penalty != INT_MAX && penalty < qe->raise_penalty) {
6245 /* Low penalty is raised up to the current minimum */
6246 penalty = qe->raise_penalty;
6247 }
6248 if ((qe->max_penalty != INT_MAX && penalty > qe->max_penalty) ||
6249 (qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) {
6250 return -1;
6251 }
6252 } else {
6253 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
6254 membercount, q->penaltymemberslimit);
6255 }
6256
6257 switch (q->strategy) {
6259 /* Everyone equal, except for penalty */
6260 tmp->metric = penalty * 1000000 * usepenalty;
6261 break;
6263 if (pos < qe->linpos) {
6264 tmp->metric = 1000 + pos;
6265 } else {
6266 if (pos > qe->linpos) {
6267 /* Indicate there is another priority */
6268 qe->linwrapped = 1;
6269 }
6270 tmp->metric = pos;
6271 }
6272 tmp->metric += penalty * 1000000 * usepenalty;
6273 break;
6276 pos = mem->queuepos;
6277 if (pos < q->rrpos) {
6278 tmp->metric = 1000 + pos;
6279 } else {
6280 if (pos > q->rrpos) {
6281 /* Indicate there is another priority */
6282 q->wrapped = 1;
6283 }
6284 tmp->metric = pos;
6285 }
6286 tmp->metric += penalty * 1000000 * usepenalty;
6287 break;
6289 tmp->metric = ast_random() % 1000;
6290 tmp->metric += penalty * 1000000 * usepenalty;
6291 break;
6293 tmp->metric = ast_random() % ((1 + penalty) * 1000);
6294 break;
6296 tmp->metric = mem->calls;
6297 tmp->metric += penalty * 1000000 * usepenalty;
6298 break;
6300 if (!mem->lastcall) {
6301 tmp->metric = 0;
6302 } else {
6303 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
6304 }
6305 tmp->metric += penalty * 1000000 * usepenalty;
6306 break;
6307 default:
6308 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
6309 break;
6310 }
6311 return 0;
6312}
6313
6317 TRANSFER
6319
6320/*! \brief Send out AMI message with member call completion status information */
6321static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller,
6322 struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart,
6323 time_t callstart, enum agent_complete_reason rsn)
6324{
6325 const char *reason = NULL; /* silence dumb compilers */
6326 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
6327
6328 switch (rsn) {
6329 case CALLER:
6330 reason = "caller";
6331 break;
6332 case AGENT:
6333 reason = "agent";
6334 break;
6335 case TRANSFER:
6336 reason = "transfer";
6337 break;
6338 }
6339
6340 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I, s: s}",
6341 "Queue", queuename,
6342 "Interface", member->interface,
6343 "MemberName", member->membername,
6344 "HoldTime", (ast_json_int_t)(callstart - holdstart),
6345 "TalkTime", (ast_json_int_t)(time(NULL) - callstart),
6346 "Reason", reason ?: "");
6347
6349 queue_agent_complete_type(), blob);
6350}
6351
6352static void queue_agent_cb(void *userdata, struct stasis_subscription *sub,
6353 struct stasis_message *msg)
6354{
6355 struct ast_channel_blob *agent_blob;
6356
6357 agent_blob = stasis_message_data(msg);
6358
6360 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6361 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6362 "AGENTLOGIN", "%s", agent_blob->snapshot->base->name);
6364 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6365 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6366 "AGENTLOGOFF", "%s|%ld", agent_blob->snapshot->base->name,
6367 (long) ast_json_integer_get(ast_json_object_get(agent_blob->blob, "logintime")));
6368 }
6369}
6370
6371/*!
6372 * \brief Structure representing relevant data during a local channel optimization
6373 *
6374 * The reason we care about local channel optimizations is that we want to be able
6375 * to accurately report when the caller and queue member have stopped talking to
6376 * each other. A local channel optimization can cause it to appear that the conversation
6377 * has stopped immediately after it has begun. By tracking that the relevant channels
6378 * to monitor have changed due to a local channel optimization, we can give accurate
6379 * reports.
6380 *
6381 * Local channel optimizations for queues are restricted from their normal operation.
6382 * Bridges created by queues can only be the destination of local channel optimizations,
6383 * not the source. In addition, move-swap local channel optimizations are the only
6384 * permitted types of local channel optimization.
6385 *
6386 * This data is populated when we are told that a local channel optimization begin
6387 * is occurring. When we get told the optimization has ended successfully, we then
6388 * apply the data here into the queue_stasis_data.
6389 */
6391 /*! The uniqueid of the channel that will be taking the place of the caller or member */
6393 /*! Indication of whether we think there is a local channel optimization in progress */
6395 /*! The identifier for this local channel optimization */
6396 unsigned int id;
6397};
6398
6399/*!
6400 * \brief User data for stasis subscriptions used for queue calls.
6401 *
6402 * app_queue subscribes to channel and bridge events for all bridged calls.
6403 * app_queue cares about the following events:
6404 *
6405 * \li bridge enter: To determine the unique ID of the bridge created for the call.
6406 * \li blind transfer: To send an appropriate agent complete event.
6407 * \li attended transfer: To send an appropriate agent complete event.
6408 * \li local optimization: To update caller and member unique IDs for the call.
6409 * \li hangup: To send an appropriate agent complete event.
6410 *
6411 * The stasis subscriptions last until we determine that the caller and the member
6412 * are no longer bridged with each other.
6413 */
6416 /*! The unique ID of the caller's channel. */
6418 /*! The unique ID of the queue member's channel */
6420 /*! The unique ID of the bridge created by the queue */
6423 /*! The relevant queue */
6425 /*! The queue member that has answered the call */
6427 /*! The time at which the caller entered the queue. Start of the caller's hold time */
6429 /*! The time at which the member answered the call. */
6431 /*! The original position of the caller when he entered the queue */
6433 /*! Indication if the call was answered within the configured service level of the queue */
6435 /*! Indicates if the stasis subscriptions are shutting down */
6437 /*! The stasis message router for bridge events */
6439 /*! The stasis message router for channel events */
6441 /*! Local channel optimization details for the caller */
6443 /*! Local channel optimization details for the member */
6445};
6446
6447/*!
6448 * \internal
6449 * \brief Free memory for a queue_stasis_data
6450 */
6451static void queue_stasis_data_destructor(void *obj)
6452{
6453 struct queue_stasis_data *queue_data = obj;
6454
6455 /* This can only happen if refcounts for this object have got severely messed up */
6456 ast_assert(queue_data->bridge_router == NULL);
6457 ast_assert(queue_data->channel_router == NULL);
6458
6459 ao2_cleanup(queue_data->member);
6460 queue_unref(queue_data->queue);
6461 ast_string_field_free_memory(queue_data);
6462}
6463
6464/*!
6465 * \internal
6466 * \brief End all stasis subscriptions on a queue_stasis_data
6467 */
6468static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
6469{
6470 SCOPED_AO2LOCK(lock, queue_data);
6471
6472 queue_data->dying = 1;
6474 queue_data->bridge_router = NULL;
6476 queue_data->channel_router = NULL;
6477}
6478
6479/*!
6480 * \internal
6481 * \brief Allocate a queue_stasis_data and initialize its data.
6482 */
6484 struct ast_channel *peer, struct member *mem, time_t holdstart,
6485 time_t starttime, int callcompletedinsl)
6486{
6487 struct queue_stasis_data *queue_data;
6488
6489 queue_data = ao2_alloc(sizeof(*queue_data), queue_stasis_data_destructor);
6490 if (!queue_data) {
6491 return NULL;
6492 }
6493
6494 if (ast_string_field_init(queue_data, 64)) {
6495 ao2_cleanup(queue_data);
6496 return NULL;
6497 }
6498
6501 queue_data->queue = queue_ref(qe->parent);
6502 queue_data->starttime = starttime;
6503 queue_data->holdstart = holdstart;
6505 queue_data->caller_pos = qe->opos;
6506 ao2_ref(mem, +1);
6507 queue_data->member = mem;
6508
6509 return queue_data;
6510}
6511
6512/*!
6513 * \internal
6514 * \brief Log an attended transfer in the queue log.
6515 *
6516 * Attended transfer queue log messages vary based on the method by which the
6517 * attended transfer was completed.
6518 *
6519 * \param queue_data Data pertaining to the particular call in the queue.
6520 * \param atxfer_msg The stasis attended transfer message data.
6521 */
6522static void log_attended_transfer(struct queue_stasis_data *queue_data,
6523 struct ast_attended_transfer_message *atxfer_msg)
6524{
6525 RAII_VAR(struct ast_str *, transfer_str, ast_str_create(32), ast_free);
6526
6527 if (!transfer_str) {
6528 ast_log(LOG_WARNING, "Unable to log attended transfer to queue log\n");
6529 return;
6530 }
6531
6532 switch (atxfer_msg->dest_type) {
6534 ast_str_set(&transfer_str, 0, "BRIDGE|%s", atxfer_msg->dest.bridge);
6535 break;
6538 ast_str_set(&transfer_str, 0, "APP|%s", atxfer_msg->dest.app);
6539 break;
6541 ast_str_set(&transfer_str, 0, "LINK|%s|%s", atxfer_msg->dest.links[0]->base->name,
6542 atxfer_msg->dest.links[1]->base->name);
6543 break;
6546 /* Threeways are headed off and should not be logged here */
6547 ast_assert(0);
6548 return;
6549 }
6550
6551 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername, "ATTENDEDTRANSFER", "%s|%ld|%ld|%d",
6552 ast_str_buffer(transfer_str),
6553 (long) (queue_data->starttime - queue_data->holdstart),
6554 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6555}
6556
6557/*!
6558 * \internal
6559 * \brief Handle a stasis bridge enter event.
6560 *
6561 * We track this particular event in order to learn what bridge
6562 * was created for the queue call.
6563 *
6564 * \param userdata Data pertaining to the particular call in the queue.
6565 * \param sub The stasis subscription on which the message occurred.
6566 * \param msg The stasis message for the bridge enter event
6567 */
6568static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub,
6569 struct stasis_message *msg)
6570{
6571 struct queue_stasis_data *queue_data = userdata;
6572 struct ast_bridge_blob *enter_blob = stasis_message_data(msg);
6573 SCOPED_AO2LOCK(lock, queue_data);
6574
6575 if (queue_data->dying) {
6576 return;
6577 }
6578
6579 if (!ast_strlen_zero(queue_data->bridge_uniqueid)) {
6580 return;
6581 }
6582
6583 if (!strcmp(enter_blob->channel->base->uniqueid, queue_data->caller_uniqueid)) {
6584 ast_string_field_set(queue_data, bridge_uniqueid,
6585 enter_blob->bridge->uniqueid);
6586 ast_debug(3, "Detected entry of caller channel %s into bridge %s\n",
6587 enter_blob->channel->base->name, queue_data->bridge_uniqueid);
6588 }
6589}
6590
6591/*!
6592 * \brief Handle a blind transfer event
6593 *
6594 * This event is important in order to be able to log the end of the
6595 * call to the queue log and to stasis.
6596 *
6597 * \param userdata Data pertaining to the particular call in the queue.
6598 * \param sub The stasis subscription on which the message occurred.
6599 * \param msg The stasis message for the blind transfer event
6600 */
6601static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub,
6602 struct stasis_message *msg)
6603{
6604 struct queue_stasis_data *queue_data = userdata;
6605 struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
6606 const char *exten;
6607 const char *context;
6608 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6609 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6610
6611 if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
6612 return;
6613 }
6614
6615 ao2_lock(queue_data);
6616
6617 if (queue_data->dying) {
6618 ao2_unlock(queue_data);
6619 return;
6620 }
6621
6622 if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
6623 strcmp(queue_data->bridge_uniqueid, transfer_msg->bridge->uniqueid)) {
6624 ao2_unlock(queue_data);
6625 return;
6626 }
6627
6628 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6629 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6630
6631 ao2_unlock(queue_data);
6632
6633 exten = transfer_msg->exten;
6634 context = transfer_msg->context;
6635
6636 ast_debug(3, "Detected blind transfer in queue %s\n", queue_data->queue->name);
6637 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6638 "BLINDTRANSFER", "%s|%s|%ld|%ld|%d",
6639 exten, context,
6640 (long) (queue_data->starttime - queue_data->holdstart),
6641 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6642
6643 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6644 queue_data->holdstart, queue_data->starttime, TRANSFER);
6645 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6646 queue_data->starttime);
6647 remove_stasis_subscriptions(queue_data);
6648}
6649
6650/*!
6651 * \brief Handle an attended transfer event
6652 *
6653 * This event is important in order to be able to log the end of the
6654 * call to the queue log and to stasis.
6655 *
6656 * \param userdata Data pertaining to the particular call in the queue.
6657 * \param sub The stasis subscription on which the message occurred.
6658 * \param msg The stasis message for the attended transfer event.
6659 */
6660static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub,
6661 struct stasis_message *msg)
6662{
6663 struct queue_stasis_data *queue_data = userdata;
6664 struct ast_attended_transfer_message *atxfer_msg = stasis_message_data(msg);
6665 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6666 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6667
6668 if (atxfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS ||
6670 return;
6671 }
6672
6673 ao2_lock(queue_data);
6674
6675 if (queue_data->dying) {
6676 ao2_unlock(queue_data);
6677 return;
6678 }
6679
6680 if (ast_strlen_zero(queue_data->bridge_uniqueid)) {
6681 ao2_unlock(queue_data);
6682 return;
6683 }
6684
6685 if ((!atxfer_msg->to_transferee.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6686 atxfer_msg->to_transferee.bridge_snapshot->uniqueid)) &&
6687 (!atxfer_msg->to_transfer_target.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6689 ao2_unlock(queue_data);
6690 return;
6691 }
6692
6693 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6694 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6695
6696 ao2_unlock(queue_data);
6697
6698 ast_debug(3, "Detected attended transfer in queue %s\n", queue_data->queue->name);
6699 log_attended_transfer(queue_data, atxfer_msg);
6700
6701 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6702 queue_data->holdstart, queue_data->starttime, TRANSFER);
6703 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6704 queue_data->starttime);
6705 remove_stasis_subscriptions(queue_data);
6706}
6707
6708/*!
6709 * \internal
6710 * \brief Callback for all stasis bridge events
6711 *
6712 * Based on the event and what bridge it is on, the task is farmed out to relevant
6713 * subroutines for further processing.
6714 */
6715static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub,
6716 struct stasis_message *msg)
6717{
6719 ao2_cleanup(userdata);
6720 }
6721}
6722
6723/*!
6724 * \internal
6725 * \brief Handler for the beginning of a local channel optimization
6726 *
6727 * This method gathers data relevant to the local channel optimization and stores
6728 * it to be used once the local optimization completes.
6729 *
6730 * \param userdata Data pertaining to the particular call in the queue.
6731 * \param sub The stasis subscription on which the message occurred.
6732 * \param msg The stasis message for the local optimization begin event
6733 */
6734static void handle_local_optimization_begin(void *userdata, struct stasis_subscription *sub,
6735 struct stasis_message *msg)
6736{
6737 struct queue_stasis_data *queue_data = userdata;
6738 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6739 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6740 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6741 struct ast_channel_snapshot *source = ast_multi_channel_blob_get_channel(optimization_blob, "source");
6742 struct local_optimization *optimization;
6743 unsigned int id;
6744 SCOPED_AO2LOCK(lock, queue_data);
6745
6746 if (queue_data->dying) {
6747 return;
6748 }
6749
6750 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6751 optimization = &queue_data->member_optimize;
6752 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6753 optimization = &queue_data->caller_optimize;
6754 } else {
6755 return;
6756 }
6757
6758 /* We only allow move-swap optimizations, so there had BETTER be a source */
6759 ast_assert(source != NULL);
6760
6761 optimization->source_chan_uniqueid = ast_strdup(source->base->uniqueid);
6762 if (!optimization->source_chan_uniqueid) {
6763 ast_log(LOG_ERROR, "Unable to track local channel optimization for channel %s. Expect further errors\n", local_one->base->name);
6764 return;
6765 }
6767
6768 optimization->id = id;
6769 optimization->in_progress = 1;
6770}
6771
6772/*!
6773 * \internal
6774 * \brief Handler for the end of a local channel optimization
6775 *
6776 * This method takes the data gathered during the local channel optimization begin
6777 * event and applies it to the queue stasis data appropriately. This generally involves
6778 * updating the caller or member unique ID with the channel that is taking the place of
6779 * the previous caller or member.
6780 *
6781 * \param userdata Data pertaining to the particular call in the queue.
6782 * \param sub The stasis subscription on which the message occurred.
6783 * \param msg The stasis message for the local optimization end event
6784 */
6785static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub,
6786 struct stasis_message *msg)
6787{
6788 struct queue_stasis_data *queue_data = userdata;
6789 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6790 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6791 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6792 struct local_optimization *optimization;
6793 int is_caller;
6794 unsigned int id;
6795 SCOPED_AO2LOCK(lock, queue_data);
6796
6797 if (queue_data->dying) {
6798 return;
6799 }
6800
6801 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6802 optimization = &queue_data->member_optimize;
6803 is_caller = 0;
6804 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6805 optimization = &queue_data->caller_optimize;
6806 is_caller = 1;
6807 } else {
6808 return;
6809 }
6810
6812
6813 if (!optimization->in_progress) {
6814 ast_log(LOG_WARNING, "Told of a local optimization end when we had no previous begin\n");
6815 return;
6816 }
6817
6818 if (id != optimization->id) {
6819 ast_log(LOG_WARNING, "Local optimization end event ID does not match begin (%u != %u)\n",
6820 id, optimization->id);
6821 return;
6822 }
6823
6824 if (is_caller) {
6825 ast_debug(3, "Local optimization: Changing queue caller uniqueid from %s to %s\n",
6826 queue_data->caller_uniqueid, optimization->source_chan_uniqueid);
6827 ast_string_field_set(queue_data, caller_uniqueid, optimization->source_chan_uniqueid);
6828 } else {
6829 ast_debug(3, "Local optimization: Changing queue member uniqueid from %s to %s\n",
6830 queue_data->member_uniqueid, optimization->source_chan_uniqueid);
6831 ast_string_field_set(queue_data, member_uniqueid, optimization->source_chan_uniqueid);
6832 }
6833
6834 optimization->in_progress = 0;
6835}
6836
6837/*!
6838 * \internal
6839 * \brief Handler for hangup stasis event
6840 *
6841 * This is how we determine that the caller or member has hung up and the call
6842 * has ended. An appropriate queue log and stasis message are raised in this
6843 * callback.
6844 *
6845 * \param userdata Data pertaining to the particular call in the queue.
6846 * \param sub The stasis subscription on which the message occurred.
6847 * \param msg The stasis message for the hangup event.
6848 */
6849static void handle_hangup(void *userdata, struct stasis_subscription *sub,
6850 struct stasis_message *msg)
6851{
6852 struct queue_stasis_data *queue_data = userdata;
6853 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6854 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6855 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6856 RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
6857 enum agent_complete_reason reason;
6858
6859 ao2_lock(queue_data);
6860
6861 if (queue_data->dying) {
6862 ao2_unlock(queue_data);
6863 return;
6864 }
6865
6866 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6867 reason = CALLER;
6868 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6869 reason = AGENT;
6870 } else {
6871 ao2_unlock(queue_data);
6872 return;
6873 }
6874
6875 chan = ast_channel_get_by_name(channel_blob->snapshot->base->name);
6876 if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
6877 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
6878 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
6879 /* Channel that is hanging up is doing it as part of a transfer.
6880 * We'll get a transfer event later
6881 */
6882 ao2_unlock(queue_data);
6883 return;
6884 }
6885
6886 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6887 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6888
6889 ao2_unlock(queue_data);
6890
6891 ast_debug(3, "Detected hangup of queue %s channel %s\n", reason == CALLER ? "caller" : "member",
6892 channel_blob->snapshot->base->name);
6893
6894 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6895 reason == CALLER ? "COMPLETECALLER" : "COMPLETEAGENT", "%ld|%ld|%d",
6896 (long) (queue_data->starttime - queue_data->holdstart),
6897 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6898
6899 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6900 queue_data->holdstart, queue_data->starttime, reason);
6901 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6902 queue_data->starttime);
6903 remove_stasis_subscriptions(queue_data);
6904}
6905
6906static void handle_masquerade(void *userdata, struct stasis_subscription *sub,
6907 struct stasis_message *msg)
6908{
6909 struct queue_stasis_data *queue_data = userdata;
6910 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6911 const char *new_channel_id;
6912
6913 new_channel_id = ast_json_string_get(ast_json_object_get(channel_blob->blob, "newchanneluniqueid"));
6914
6915 ao2_lock(queue_data);
6916
6917 if (queue_data->dying) {
6918 ao2_unlock(queue_data);
6919 return;
6920 }
6921
6922 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6923 ast_debug(1, "Replacing caller channel %s with %s due to masquerade\n", queue_data->caller_uniqueid, new_channel_id);
6924 ast_string_field_set(queue_data, caller_uniqueid, new_channel_id);
6925 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6926 ast_debug(1, "Replacing member channel %s with %s due to masquerade\n", queue_data->member_uniqueid, new_channel_id);
6927 ast_string_field_set(queue_data, member_uniqueid, new_channel_id);
6928 }
6929
6930 ao2_unlock(queue_data);
6931}
6932
6933/*!
6934 * \internal
6935 * \brief Callback for all stasis channel events
6936 *
6937 * Based on the event and the channels involved, the work is farmed out into
6938 * subroutines for further processing.
6939 */
6940static void queue_channel_cb(void *userdata, struct stasis_subscription *sub,
6941 struct stasis_message *msg)
6942{
6944 ao2_cleanup(userdata);
6945 }
6946}
6947
6948/*!
6949 * \internal
6950 * \brief Create stasis subscriptions for a particular call in the queue.
6951 *
6952 * These subscriptions are created once the call has been answered. The subscriptions
6953 * are put in place so that call progress may be tracked. Once the call can be determined
6954 * to have ended, then messages are logged to the queue log and stasis events are emitted.
6955 *
6956 * \param qe The queue entry representing the caller
6957 * \param peer The channel that has answered the call
6958 * \param mem The queue member that answered the call
6959 * \param holdstart The time at which the caller entered the queue
6960 * \param starttime The time at which the call was answered
6961 * \param callcompletedinsl Indicates if the call was answered within the configured service level of the queue.
6962 * \retval 0 Success
6963 * \retval non-zero Failure
6964 */
6965static int setup_stasis_subs(struct queue_ent *qe, struct ast_channel *peer, struct member *mem,
6966 time_t holdstart, time_t starttime, int callcompletedinsl)
6967{
6968 struct queue_stasis_data *queue_data = queue_stasis_data_alloc(qe, peer, mem, holdstart, starttime, callcompletedinsl);
6969
6970 if (!queue_data) {
6971 return -1;
6972 }
6973
6975 if (!queue_data->bridge_router) {
6976 ao2_ref(queue_data, -1);
6977 return -1;
6978 }
6979
6981 handle_bridge_enter, queue_data);
6983 handle_blind_transfer, queue_data);
6985 handle_attended_transfer, queue_data);
6987 queue_bridge_cb, queue_data);
6988
6990 if (!queue_data->channel_router) {
6991 /* Unsubscribing from the bridge router will remove the only ref of queue_data,
6992 * thus beginning the destruction process
6993 */
6995 queue_data->bridge_router = NULL;
6996 return -1;
6997 }
6998
6999 ao2_ref(queue_data, +1);
7003 handle_local_optimization_end, queue_data);
7005 handle_hangup, queue_data);
7007 handle_masquerade, queue_data);
7009 queue_channel_cb, queue_data);
7010
7011 return 0;
7012}
7013
7015 struct call_queue *q;
7017};
7018
7019static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
7020{
7021 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
7022 ao2_ref(qeb, +1);
7023 qeb->chan = originator;
7024}
7025
7026static void end_bridge_callback(void *data)
7027{
7028 struct queue_end_bridge *qeb = data;
7029 struct call_queue *q = qeb->q;
7030 struct ast_channel *chan = qeb->chan;
7031
7032 if (ao2_ref(qeb, -1) == 1) {
7033 set_queue_variables(q, chan);
7034 /* This unrefs the reference we made in try_calling when we allocated qeb */
7035 queue_t_unref(q, "Expire bridge_config reference");
7036 }
7037}
7038
7039/*!
7040 * \internal
7041 * \brief Setup the after bridge goto location on the peer.
7042 * \since 12.0.0
7043 *
7044 * \param chan Calling channel for bridge.
7045 * \param peer Peer channel for bridge.
7046 * \param opts Dialing option flags.
7047 * \param opt_args Dialing option argument strings.
7048 */
7049static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
7050{
7051 const char *context;
7052 const char *extension;
7053 int priority;
7054
7055 if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
7056 ast_channel_lock(chan);
7060 ast_channel_unlock(chan);
7062 opt_args[OPT_ARG_CALLEE_GO_ON]);
7063 }
7064}
7065
7066static void escape_and_substitute(struct ast_channel *chan, const char *input,
7067 char *output, size_t size)
7068{
7069 const char *m = input;
7070 char escaped[size];
7071 char *p;
7072
7073 for (p = escaped; p < escaped + size - 1; p++, m++) {
7074 switch (*m) {
7075 case '^':
7076 if (*(m + 1) == '{') {
7077 *p = '$';
7078 }
7079 break;
7080 case ',':
7081 *p++ = '\\';
7082 /* Fall through */
7083 default:
7084 *p = *m;
7085 }
7086 if (*m == '\0')
7087 break;
7088 }
7089
7090 if (p == escaped + size) {
7091 escaped[size - 1] = '\0';
7092 }
7093
7094 pbx_substitute_variables_helper(chan, escaped, output, size - 1);
7095}
7096
7097static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
7098{
7099 char escaped_filename[256];
7100 char file_with_ext[sizeof(escaped_filename) + sizeof(qe->parent->monfmt)];
7101 char mixmonargs[1512];
7102 char escaped_monitor_exec[1024];
7103 const char *monitor_options;
7104 const char *monitor_exec;
7105
7106 escaped_monitor_exec[0] = '\0';
7107
7108 if (filename) {
7109 escape_and_substitute(qe->chan, filename, escaped_filename, sizeof(escaped_filename));
7110 } else {
7111 ast_copy_string(escaped_filename, ast_channel_uniqueid(qe->chan), sizeof(escaped_filename));
7112 }
7113
7115 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
7116 monitor_exec = ast_strdupa(monitor_exec);
7117 }
7118 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
7119 monitor_options = ast_strdupa(monitor_options);
7120 } else {
7121 monitor_options = "";
7122 }
7124
7125 if (monitor_exec) {
7126 escape_and_substitute(qe->chan, monitor_exec, escaped_monitor_exec, sizeof(escaped_monitor_exec));
7127 }
7128
7129 snprintf(file_with_ext, sizeof(file_with_ext), "%s.%s", escaped_filename, qe->parent->monfmt);
7130
7131 if (!ast_strlen_zero(escaped_monitor_exec)) {
7132 snprintf(mixmonargs, sizeof(mixmonargs), "b%s,%s", monitor_options, escaped_monitor_exec);
7133 } else {
7134 snprintf(mixmonargs, sizeof(mixmonargs), "b%s", monitor_options);
7135 }
7136
7137 ast_debug(1, "Arguments being passed to MixMonitor: %s,%s\n", file_with_ext, mixmonargs);
7138
7139 if (ast_start_mixmonitor(qe->chan, file_with_ext, mixmonargs)) {
7140 ast_log(LOG_WARNING, "Unable to start mixmonitor. Is the MixMonitor app loaded?\n");
7141 }
7142}
7143
7144/*!
7145 * \internal
7146 * \brief A large function which calls members, updates statistics, and bridges the caller and a member
7147 *
7148 * Here is the process of this function
7149 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
7150 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member.
7151 * 3. Call ring_one to place a call to the appropriate member(s)
7152 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
7153 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
7154 * 6. Start the monitor or mixmonitor if the option is set
7155 * 7. Remove the caller from the queue to allow other callers to advance
7156 * 8. Bridge the call.
7157 * 9. Do any post processing after the call has disconnected.
7158 *
7159 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
7160 * \param[in] opts the options passed as the third parameter to the Queue() application
7161 * \param[in] opt_args the options passed as the third parameter to the Queue() application
7162 * \param[in] announceoverride filename to play to user when waiting
7163 * \param[in] url the url passed as the fourth parameter to the Queue() application
7164 * \param[in,out] tries the number of times we have tried calling queue members
7165 * \param[out] noption set if the call to Queue() has the 'n' option set.
7166 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
7167 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
7168 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
7169 */
7170static 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)
7171{
7172 struct member *cur;
7173 struct callattempt *outgoing = NULL; /* the list of calls we are building */
7174 int to, orig;
7175 char oldexten[AST_MAX_EXTENSION]="";
7176 char oldcontext[AST_MAX_CONTEXT]="";
7177 char queuename[256]="";
7178 struct ast_channel *peer;
7179 struct callattempt *lpeer;
7180 struct member *member;
7181 struct ast_app *application;
7182 int res = 0, bridge = 0;
7183 int numbusies = 0;
7184 int x=0;
7185 char *announce = NULL;
7186 char digit = 0;
7187 time_t now = time(NULL);
7188 struct ast_bridge_config bridge_config;
7189 char nondataquality = 1;
7190 char *agiexec = NULL;
7191 char *gosubexec = NULL;
7192 const char *monitorfilename;
7193 int forwardsallowed = 1;
7194 int block_connected_line = 0;
7195 struct ao2_iterator memi;
7197 int callcompletedinsl;
7198 time_t starttime;
7199
7200 memset(&bridge_config, 0, sizeof(bridge_config));
7201 time(&now);
7202
7203 /* If we've already exceeded our timeout, then just stop
7204 * This should be extremely rare. queue_exec will take care
7205 * of removing the caller and reporting the timeout as the reason.
7206 */
7207 if (qe->expire && now >= qe->expire) {
7208 res = 0;
7209 goto out;
7210 }
7211
7212 if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) {
7214 }
7215 if (ast_test_flag(&opts, OPT_CALLER_TRANSFER)) {
7217 }
7218 if (ast_test_flag(&opts, OPT_CALLEE_AUTOMON)) {
7220 }
7221 if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
7223 }
7224 if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
7225 nondataquality = 0;
7226 }
7227 if (ast_test_flag(&opts, OPT_CALLEE_HANGUP)) {
7229 }
7230 if (ast_test_flag(&opts, OPT_CALLER_HANGUP)) {
7232 }
7233 if (ast_test_flag(&opts, OPT_CALLEE_PARK)) {
7235 }
7236 if (ast_test_flag(&opts, OPT_CALLER_PARK)) {
7238 }
7239 if (ast_test_flag(&opts, OPT_NO_RETRY)) {
7242 (*tries)++;
7243 } else {
7244 *tries = ao2_container_count(qe->parent->members);
7245 }
7246 *noption = 1;
7247 }
7248 if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
7249 forwardsallowed = 0;
7250 }
7252 block_connected_line = 1;
7253 }
7256 }
7259 }
7260 if (ast_test_flag(&opts, OPT_MARK_AS_ANSWERED)) {
7262 }
7263
7264 /* if the calling channel has AST_CAUSE_ANSWERED_ELSEWHERE set, make sure this is inherited.
7265 (this is mainly to support unreal/local channels)
7266 */
7269 }
7270
7271 ao2_lock(qe->parent);
7272 ast_debug(1, "%s is trying to call a queue member.\n",
7273 ast_channel_name(qe->chan));
7274 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
7275 if (!ast_strlen_zero(qe->announce)) {
7276 announce = qe->announce;
7277 }
7278 if (!ast_strlen_zero(announceoverride)) {
7279 announce = announceoverride;
7280 }
7281
7282 memi = ao2_iterator_init(qe->parent->members, 0);
7283 while ((cur = ao2_iterator_next(&memi))) {
7284 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
7285 if (!tmp) {
7286 ao2_ref(cur, -1);
7287 ao2_iterator_destroy(&memi);
7288 ao2_unlock(qe->parent);
7289 goto out;
7290 }
7291
7292 /*
7293 * Seed the callattempt's connected line information with previously
7294 * acquired connected line info from the queued channel. The
7295 * previously acquired connected line info could have been set
7296 * through the CONNECTED_LINE dialplan function.
7297 */
7301
7302 tmp->block_connected_update = block_connected_line;
7303 tmp->stillgoing = 1;
7304 tmp->member = cur; /* Place the reference for cur into callattempt. */
7305 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
7306 /* Calculate the metric for the appropriate strategy. */
7307 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
7308 /* Put them in the list of outgoing thingies... We're ready now.
7309 XXX If we're forcibly removed, these outgoing calls won't get
7310 hung up XXX */
7311 tmp->q_next = outgoing;
7312 outgoing = tmp;
7313 } else {
7314 callattempt_free(tmp);
7315 }
7316 }
7317 ao2_iterator_destroy(&memi);
7318
7320 /* Application arguments have higher timeout priority (behaviour for <=1.6) */
7321 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) {
7322 to = (qe->expire - now) * 1000;
7323 } else {
7324 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
7325 }
7326 } else {
7327 /* Config timeout is higher priority thatn application timeout */
7328 if (qe->expire && qe->expire<=now) {
7329 to = 0;
7330 } else if (qe->parent->timeout) {
7331 to = qe->parent->timeout * 1000;
7332 } else {
7333 to = -1;
7334 }
7335 }
7336 orig = to;
7337 ++qe->pending;
7338 ao2_unlock(qe->parent);
7339 /* Call the queue members with the best metric now. */
7340 ring_one(qe, outgoing, &numbusies);
7341 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
7343 forwardsallowed);
7344
7345 ao2_lock(qe->parent);
7348
7349 }
7352 }
7353 ao2_unlock(qe->parent);
7354 peer = lpeer ? lpeer->chan : NULL;
7355 if (!peer) {
7356 qe->pending = 0;
7357 if (to) {
7358 /* Must gotten hung up */
7359 res = -1;
7360 } else {
7361 /* User exited by pressing a digit */
7362 res = digit;
7363 }
7364 if (res == -1) {
7365 ast_debug(1, "%s: Nobody answered.\n", ast_channel_name(qe->chan));
7366 }
7367 } else { /* peer is valid */
7368 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
7369 RAII_VAR(struct ast_str *, interfacevar, ast_str_create(325), ast_free);
7370 /* Ah ha! Someone answered within the desired timeframe. Of course after this
7371 we will always return with -1 so that it is hung up properly after the
7372 conversation. */
7373 if (!strcmp(ast_channel_tech(qe->chan)->type, "DAHDI")) {
7374 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7375 }
7376 if (!strcmp(ast_channel_tech(peer)->type, "DAHDI")) {
7377 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7378 }
7379 /* Update parameters for the queue */
7380 time(&now);
7381 recalc_holdtime(qe, (now - qe->start));
7382 member = lpeer->member;
7383 ao2_lock(qe->parent);
7384 callcompletedinsl = member->callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
7385 ao2_unlock(qe->parent);
7386 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
7387 ao2_ref(member, 1);
7389 outgoing = NULL;
7390 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
7391 int res2;
7392
7393 res2 = ast_autoservice_start(qe->chan);
7394 if (!res2) {
7395 if (qe->parent->memberdelay) {
7396 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
7397 res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
7398 }
7399 if (!res2 && announce) {
7400 char *front;
7401 char *announcefiles = ast_strdupa(announce);
7402 while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
7403 if (play_file(peer, front) < 0) {
7404 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
7405 }
7406 }
7407 }
7408 if (!res2 && qe->parent->reportholdtime) {
7409 if (!play_file(peer, qe->parent->sound_reporthold)) {
7410 long holdtime, holdtimesecs;
7411
7412 time(&now);
7413 holdtime = labs((now - qe->start) / 60);
7414 holdtimesecs = labs((now - qe->start) % 60);
7415 if (holdtime > 0) {
7416 ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7417 if (play_file(peer, qe->parent->sound_minutes) < 0) {
7418 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, ast_channel_name(peer));
7419 }
7420 }
7421 if (holdtimesecs > 1) {
7422 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7423 if (play_file(peer, qe->parent->sound_seconds) < 0) {
7424 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, ast_channel_name(peer));
7425 }
7426 }
7427 }
7428 }
7430 }
7431 if (ast_check_hangup(peer)) {
7432 /* Agent must have hung up */
7433 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", ast_channel_name(peer));
7434 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "AGENTDUMP", "%s", "");
7435
7436 blob = ast_json_pack("{s: s, s: s, s: s}",
7437 "Queue", queuename,
7438 "Interface", member->interface,
7439 "MemberName", member->membername);
7440 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_dump_type(), blob);
7441
7445 ao2_ref(member, -1);
7446 goto out;
7447 } else if (ast_check_hangup(qe->chan)) {
7448 /* Caller must have hung up just before being connected */
7449 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
7450 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
7451 record_abandoned(qe);
7452 qe->handled = -1;
7456 ao2_ref(member, -1);
7457 return -1;
7458 }
7459 }
7460 /* Stop music on hold */
7461 if (ringing) {
7462 ast_indicate(qe->chan,-1);
7463 } else {
7464 ast_moh_stop(qe->chan);
7465 }
7466
7467 /* Make sure channels are compatible */
7468 res = ast_channel_make_compatible(qe->chan, peer);
7469 if (res < 0) {
7470 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "SYSCOMPAT", "%s", "");
7471 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));
7472 record_abandoned(qe);
7476 ao2_ref(member, -1);
7477 return -1;
7478 }
7479
7480 /* Play announcement to the caller telling it's his turn if defined */
7482 if (play_file(qe->chan, qe->parent->sound_callerannounce)) {
7483 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
7484 }
7485 }
7486
7487 ao2_lock(qe->parent);
7488 /* if setinterfacevar is defined, make member variables available to the channel */
7489 /* use pbx_builtin_setvar to set a load of variables with one call */
7490 if (qe->parent->setinterfacevar && interfacevar) {
7491 ast_str_set(&interfacevar, 0, "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
7494 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7495 }
7496
7497 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
7498 /* use pbx_builtin_setvar to set a load of variables with one call */
7499 if (qe->parent->setqueueentryvar && interfacevar) {
7500 ast_str_set(&interfacevar, 0, "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
7501 (long) (time(NULL) - qe->start), qe->opos);
7503 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7504 }
7505
7506 ao2_unlock(qe->parent);
7507
7508 /* try to set queue variables if configured to do so*/
7510 set_queue_variables(qe->parent, peer);
7511
7512 setup_peer_after_bridge_goto(qe->chan, peer, &opts, opt_args);
7514 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
7515 monitorfilename = ast_strdupa(monitorfilename);
7516 }
7518
7519 /* Begin Monitoring */
7520 if (*qe->parent->monfmt) {
7521 setup_mixmonitor(qe, monitorfilename);
7522 }
7523 /* Drop out of the queue at this point, to prepare for next caller */
7524 leave_queue(qe);
7526 ast_debug(1, "app_queue: sendurl=%s.\n", url);
7527 ast_channel_sendurl(peer, url);
7528 }
7529
7530 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
7531 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
7532 if (!ast_strlen_zero(gosub)) {
7533 gosubexec = ast_strdupa(gosub);
7534 } else {
7535 if (qe->parent->membergosub) {
7536 gosubexec = ast_strdupa(qe->parent->membergosub);
7537 }
7538 }
7539
7540 if (!ast_strlen_zero(gosubexec)) {
7541 char *gosub_args = NULL;
7542 char *gosub_argstart;
7543
7544 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
7545
7546 gosub_argstart = strchr(gosubexec, ',');
7547 if (gosub_argstart) {
7548 const char *what_is_s = "s";
7549 *gosub_argstart = 0;
7550 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)) &&
7551 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7552 what_is_s = "~~s~~";
7553 }
7554 if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
7555 gosub_args = NULL;
7556 }
7557 *gosub_argstart = ',';
7558 } else {
7559 const char *what_is_s = "s";
7560 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)) &&
7561 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7562 what_is_s = "~~s~~";
7563 }
7564 if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
7565 gosub_args = NULL;
7566 }
7567 }
7568 if (gosub_args) {
7569 ast_app_exec_sub(qe->chan, peer, gosub_args, 0);
7570 ast_free(gosub_args);
7571 } else {
7572 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
7573 }
7574 }
7575
7576 if (!ast_strlen_zero(agi)) {
7577 ast_debug(1, "app_queue: agi=%s.\n", agi);
7578 application = pbx_findapp("agi");
7579 if (application) {
7580 agiexec = ast_strdupa(agi);
7581 pbx_exec(qe->chan, application, agiexec);
7582 } else {
7583 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
7584 }
7585 }
7586 qe->handled++;
7587
7588 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer),
7589 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
7590
7591 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I}",
7592 "Queue", queuename,
7593 "Interface", member->interface,
7594 "MemberName", member->membername,
7595 "HoldTime", (ast_json_int_t)(time(NULL) - qe->start),
7596 "RingTime", (ast_json_int_t)(orig - to > 0 ? (orig - to) / 1000 : 0));
7597 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_connect_type(), blob);
7598
7599 ast_copy_string(oldcontext, ast_channel_context(qe->chan), sizeof(oldcontext));
7600 ast_copy_string(oldexten, ast_channel_exten(qe->chan), sizeof(oldexten));
7601
7602 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
7603 queue_end_bridge->q = qe->parent;
7604 queue_end_bridge->chan = qe->chan;
7608 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
7609 * to make sure to increase the refcount of this queue so it cannot be freed until we
7610 * are done with it. We remove this reference in end_bridge_callback.
7611 */
7612 queue_t_ref(qe->parent, "For bridge_config reference");
7613 }
7614
7615 ao2_lock(qe->parent);
7616 time(&member->starttime);
7617 starttime = member->starttime;
7618 ao2_unlock(qe->parent);
7619 /* As a queue member may end up in multiple calls at once if a transfer occurs with
7620 * a Local channel in the mix we pass the current call information (starttime) to the
7621 * Stasis subscriptions so when they update the queue member data it becomes a noop
7622 * if this call is no longer between the caller and the queue member.
7623 */
7624 setup_stasis_subs(qe, peer, member, qe->start, starttime, callcompletedinsl);
7625 bridge = ast_bridge_call_with_flags(qe->chan, peer, &bridge_config,
7627
7628 res = bridge ? bridge : 1;
7629 ao2_ref(member, -1);
7630 }
7631out:
7633
7634 return res;
7635}
7636
7637static int wait_a_bit(struct queue_ent *qe)
7638{
7639 /* Don't need to hold the lock while we setup the outgoing calls */
7640 int retrywait = qe->parent->retry * 1000;
7641
7642 int res = ast_waitfordigit(qe->chan, retrywait);
7643 if (res > 0 && !valid_exit(qe, res)) {
7644 res = 0;
7645 }
7646
7647 return res;
7648}
7649
7650static struct member *interface_exists(struct call_queue *q, const char *interface)
7651{
7652 struct member *mem;
7653 struct ao2_iterator mem_iter;
7654
7655 if (!q) {
7656 return NULL;
7657 }
7658 mem_iter = ao2_iterator_init(q->members, 0);
7659 while ((mem = ao2_iterator_next(&mem_iter))) {
7660 if (!strcasecmp(interface, mem->interface)) {
7661 ao2_iterator_destroy(&mem_iter);
7662 return mem;
7663 }
7664 ao2_ref(mem, -1);
7665 }
7666 ao2_iterator_destroy(&mem_iter);
7667
7668 return NULL;
7669}
7670
7671
7672/*! \brief Dump all members in a specific queue to the database
7673 * \code
7674 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
7675 * \endcode
7676 */
7677static void dump_queue_members(struct call_queue *pm_queue)
7678{
7679 struct member *cur_member;
7680 struct ast_str *value;
7681 struct ao2_iterator mem_iter;
7682
7683 if (!pm_queue) {
7684 return;
7685 }
7686
7687 /* 4K is a reasonable default for most applications, but we grow to
7688 * accommodate more if necessary. */
7689 if (!(value = ast_str_create(4096))) {
7690 return;
7691 }
7692
7693 mem_iter = ao2_iterator_init(pm_queue->members, 0);
7694 while ((cur_member = ao2_iterator_next(&mem_iter))) {
7695 if (!cur_member->dynamic) {
7696 ao2_ref(cur_member, -1);
7697 continue;
7698 }
7699
7700 ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s;%s;%d",
7701 ast_str_strlen(value) ? "|" : "",
7702 cur_member->interface,
7703 cur_member->penalty,
7704 cur_member->paused,
7705 cur_member->membername,
7706 cur_member->state_interface,
7707 cur_member->reason_paused,
7708 cur_member->wrapuptime);
7709
7710 ao2_ref(cur_member, -1);
7711 }
7712 ao2_iterator_destroy(&mem_iter);
7713
7714 if (ast_str_strlen(value) && !cur_member) {
7715 if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) {
7716 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
7717 }
7718 } else {
7719 /* Delete the entry if the queue is empty or there is an error */
7720 ast_db_del(pm_family, pm_queue->name);
7721 }
7722
7723 ast_free(value);
7724}
7725
7726/*! \brief Remove member from queue
7727 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7728 * \retval RES_NOSUCHQUEUE queue does not exist
7729 * \retval RES_OKAY removed member from queue
7730 * \retval RES_EXISTS queue exists but no members
7731*/
7732static int remove_from_queue(const char *queuename, const char *interface)
7733{
7734 struct call_queue *q, tmpq = {
7735 .name = queuename,
7736 };
7737 struct member *mem, tmpmem;
7738 int res = RES_NOSUCHQUEUE;
7739
7740 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
7741 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
7742 ao2_lock(q);
7743 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
7744 /* XXX future changes should beware of this assumption!! */
7745 /*Change Penalty on realtime users*/
7747 update_realtime_member_field(mem, q->name, "penalty", "-1");
7748 } else if (!mem->dynamic) {
7749 ao2_ref(mem, -1);
7750 ao2_unlock(q);
7751 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
7752 return RES_NOT_DYNAMIC;
7753 }
7754 queue_publish_member_blob(queue_member_removed_type(), queue_member_blob_create(q, mem));
7755
7757 ao2_ref(mem, -1);
7758
7761 }
7762
7763 if (!num_available_members(q)) {
7765 }
7766
7767 res = RES_OKAY;
7768 } else {
7769 res = RES_EXISTS;
7770 }
7771 ao2_unlock(q);
7772 queue_t_unref(q, "Expiring temporary reference");
7773 }
7774
7775 return res;
7776}
7777
7778/*! \brief Add member to queue
7779 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7780 * \retval RES_NOSUCHQUEUE queue does not exist
7781 * \retval RES_OKAY added member from queue
7782 * \retval RES_EXISTS queue exists but no members
7783 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
7784*/
7785static 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)
7786{
7787 struct call_queue *q;
7788 struct member *new_member, *old_member;
7789 int res = RES_NOSUCHQUEUE;
7790
7791 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7792 * short-circuits if the queue is already in memory. */
7793 if (!(q = find_load_queue_rt_friendly(queuename))) {
7794 return res;
7795 }
7796
7797 ao2_lock(q);
7798 if ((old_member = interface_exists(q, interface)) == NULL) {
7800 new_member->dynamic = 1;
7801 if (reason_paused) {
7802 ast_copy_string(new_member->reason_paused, reason_paused, sizeof(new_member->reason_paused));
7803 }
7804 member_add_to_queue(q, new_member);
7805 queue_publish_member_blob(queue_member_added_type(), queue_member_blob_create(q, new_member));
7806
7807 if (is_member_available(q, new_member)) {
7809 }
7810
7811 ao2_ref(new_member, -1);
7812 new_member = NULL;
7813
7814 if (dump) {
7816 }
7817
7818 res = RES_OKAY;
7819 } else {
7820 res = RES_OUTOFMEMORY;
7821 }
7822 } else {
7823 ao2_ref(old_member, -1);
7824 res = RES_EXISTS;
7825 }
7826 ao2_unlock(q);
7827 queue_t_unref(q, "Expiring temporary reference");
7828
7829 return res;
7830}
7831
7832
7833/*! \brief Change priority caller into a queue
7834 * \retval RES_NOSUCHQUEUE queue does not exist
7835 * \retval RES_OKAY change priority
7836 * \retval RES_NOT_CALLER queue exists but no caller
7837*/
7838static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
7839{
7840 struct call_queue *q;
7841 struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
7842 int res = RES_NOSUCHQUEUE;
7843
7844 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7845 * short-circuits if the queue is already in memory. */
7846 if (!(q = find_load_queue_rt_friendly(queuename))) {
7847 return res;
7848 }
7849
7850 ao2_lock(q);
7851 res = RES_NOT_CALLER;
7852 for (current = q->head; current; current = current->next) {
7853 if (strcmp(ast_channel_name(current->chan), caller) == 0) {
7854 ast_debug(1, "%s Caller new priority %d in queue %s\n",
7855 caller, priority, queuename);
7856 current->prio = priority;
7857 if (immediate) {
7858 /* This caller is being immediately moved in the queue so remove them */
7859 if (prev) {
7860 prev->next = current->next;
7861 } else {
7862 q->head = current->next;
7863 }
7864 caller_qe = current;
7865 /* The position for all callers is not recalculated in here as it will
7866 * be updated when the moved caller is inserted back into the queue
7867 */
7868 }
7869 res = RES_OKAY;
7870 break;
7871 } else if (immediate) {
7872 prev = current;
7873 }
7874 }
7875
7876 if (caller_qe) {
7877 int inserted = 0, pos = 0;
7878
7879 /* If a caller queue entry exists, we are applying their priority immediately
7880 * and have to reinsert them at the correct position.
7881 */
7882 prev = NULL;
7883 current = q->head;
7884 while (current) {
7885 if (!inserted && (caller_qe->prio > current->prio)) {
7886 insert_entry(q, prev, caller_qe, &pos);
7887 inserted = 1;
7888 }
7889
7890 /* We always update the position as it may have changed */
7891 current->pos = ++pos;
7892
7893 /* Move to the next caller in the queue */
7894 prev = current;
7895 current = current->next;
7896 }
7897
7898 if (!inserted) {
7899 insert_entry(q, prev, caller_qe, &pos);
7900 }
7901 }
7902
7903 ao2_unlock(q);
7904 return res;
7905}
7906
7907
7908/*! \brief Request to withdraw a caller from a queue
7909 * \retval RES_NOSUCHQUEUE queue does not exist
7910 * \retval RES_OKAY withdraw request sent
7911 * \retval RES_NOT_CALLER queue exists but no caller
7912 * \retval RES_EXISTS a withdraw request was already sent for this caller (channel) and queue
7913*/
7914static int request_withdraw_caller_from_queue(const char *queuename, const char *caller, const char *withdraw_info)
7915{
7916 struct call_queue *q;
7917 struct queue_ent *qe;
7918 int res = RES_NOSUCHQUEUE;
7919
7920 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7921 * short-circuits if the queue is already in memory. */
7922 if (!(q = find_load_queue_rt_friendly(queuename))) {
7923 return res;
7924 }
7925
7926 ao2_lock(q);
7927 res = RES_NOT_CALLER;
7928 for (qe = q->head; qe; qe = qe->next) {
7929 if (!strcmp(ast_channel_name(qe->chan), caller)) {
7930 if (qe->withdraw) {
7931 ast_debug(1, "Ignoring duplicate withdraw request of caller %s from queue %s\n", caller, queuename);
7932 res = RES_EXISTS;
7933 } else {
7934 ast_debug(1, "Requested withdraw of caller %s from queue %s\n", caller, queuename);
7935 /* It is not possible to change the withdraw info by further withdraw requests for this caller (channel)
7936 in this queue, so we do not need to worry about a memory leak here. */
7937 if (withdraw_info) {
7939 }
7940 qe->withdraw = 1;
7941 res = RES_OKAY;
7942 }
7943 break;
7944 }
7945 }
7946 ao2_unlock(q);
7947 queue_unref(q);
7948
7949 return res;
7950}
7951
7952
7954{
7955 struct ast_json *json_blob = queue_member_blob_create(q, member);
7956
7957 if (!json_blob) {
7958 return -1;
7959 }
7960
7961 queue_publish_member_blob(queue_member_pause_type(), json_blob);
7962
7963 return 0;
7964}
7965
7966/*!
7967 * \internal
7968 * \brief Set the pause status of the specific queue member.
7969 *
7970 * \param q Which queue the member belongs.
7971 * \param mem Queue member being paused/unpaused.
7972 * \param reason Why is this happening (Can be NULL/empty for no reason given.)
7973 * \param paused Set to 1 if the member is being paused or 0 to unpause.
7974 *
7975 * \pre The q is locked on entry.
7976 */
7977static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
7978{
7979 if (mem->paused == paused) {
7980 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n",
7981 (paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
7982 }
7983
7984 if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid)) {
7986 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "reason_paused", S_OR(reason, ""), "paused", paused ? "1" : "0", SENTINEL) < 0) {
7987 ast_log(LOG_WARNING, "Failed update of realtime queue member %s:%s %spause and reason '%s'\n",
7988 q->name, mem->interface, (paused ? "" : "un"), S_OR(reason, ""));
7989 }
7990 } else {
7991 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "paused", paused ? "1" : "0", SENTINEL) < 0) {
7992 ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
7993 (paused ? "" : "un"), q->name, mem->interface);
7994 }
7995 }
7996 }
7997
7998 mem->paused = paused;
7999 if (paused) {
8000 time(&mem->lastpause); /* update last pause field */
8001 }
8002 if (paused && !ast_strlen_zero(reason)) {
8003 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
8004 } else {
8005 /* We end up filling this in again later (temporarily) but we need it
8006 * empty for now so that the intervening code - specifically
8007 * dump_queue_members() - has the correct view of things. */
8008 mem->reason_paused[0] = '\0';
8009 }
8010
8012 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
8013
8016 }
8017
8018 if (is_member_available(q, mem)) {
8020 "Queue:%s_avail", q->name);
8021 } else if (!num_available_members(q)) {
8023 "Queue:%s_avail", q->name);
8024 }
8025
8026 if (!paused && !ast_strlen_zero(reason)) {
8027 /* Because we've been unpaused with a 'reason' we need to ensure that
8028 * that reason is emitted when the subsequent PauseQueueMember event
8029 * is raised. So temporarily set it on the member and clear it out
8030 * again right after. */
8031 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
8032 }
8033
8034 ast_queue_log(q->name, "NONE", mem->membername, paused ? "PAUSE" : "UNPAUSE",
8035 "%s", mem->reason_paused);
8036
8038
8039 if (!paused) {
8040 mem->reason_paused[0] = '\0';
8041 }
8042}
8043
8044static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
8045{
8046 int found = 0;
8047 struct call_queue *q;
8048 struct ao2_iterator queue_iter;
8049
8050 if (ast_check_realtime("queues")) {
8051 load_realtime_queues(queuename);
8052 }
8053
8054 queue_iter = ao2_iterator_init(queues, 0);
8055 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
8056 ao2_lock(q);
8057 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
8058 struct member *mem;
8059
8060 if ((mem = interface_exists(q, interface))) {
8061 /*
8062 * Before we do the PAUSE/UNPAUSE, log if this was a
8063 * PAUSEALL/UNPAUSEALL but only on the first found entry.
8064 */
8065 ++found;
8066 if (found == 1
8067 && ast_strlen_zero(queuename)) {
8068 /*
8069 * XXX In all other cases, we use the queue name,
8070 * but since this affects all queues, we cannot.
8071 */
8072 ast_queue_log("NONE", "NONE", mem->membername,
8073 (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", S_OR(reason, ""));
8074 }
8075
8076 set_queue_member_pause(q, mem, reason, paused);
8077 ao2_ref(mem, -1);
8078 }
8079
8080 if (!ast_strlen_zero(queuename)) {
8081 ao2_unlock(q);
8082 queue_t_unref(q, "Done with iterator");
8083 break;
8084 }
8085 }
8086
8087 ao2_unlock(q);
8088 queue_t_unref(q, "Done with iterator");
8089 }
8090 ao2_iterator_destroy(&queue_iter);
8091
8092 return found ? RESULT_SUCCESS : RESULT_FAILURE;
8093}
8094
8095/*!
8096 * \internal
8097 * \brief helper function for set_member_penalty - given a queue, sets all member penalties with the interface
8098 * \param[in] q queue which is having its member's penalty changed - must be unlocked prior to calling
8099 * \param[in] interface String of interface used to search for queue members being changed
8100 * \param[in] penalty Value penalty is being changed to for the member.
8101 * \retval 0 if the there is no member with interface belonging to q and no change is made
8102 * \retval 1 if the there is a member with interface belonging to q and changes are made
8103 */
8104static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
8105{
8106 struct member *mem;
8107 int foundinterface = 0;
8108
8109 ao2_lock(q);
8110 if ((mem = interface_exists(q, interface))) {
8111 foundinterface++;
8112 if (mem->realtime) {
8113 char rtpenalty[80];
8114
8115 sprintf(rtpenalty, "%i", penalty);
8116 update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
8117 }
8118
8119 mem->penalty = penalty;
8120
8121 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
8122 queue_publish_member_blob(queue_member_penalty_type(), queue_member_blob_create(q, mem));
8123 ao2_ref(mem, -1);
8124 }
8125 ao2_unlock(q);
8126
8127 return foundinterface;
8128}
8129
8130/*!
8131 * \internal
8132 * \brief Set the ringinuse value of the specific queue member.
8133 *
8134 * \param q Which queue the member belongs.
8135 * \param mem Queue member being set.
8136 * \param ringinuse Set to 1 if the member is called when inuse.
8137 *
8138 * \pre The q is locked on entry.
8139 */
8140static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
8141{
8142 if (mem->realtime) {
8144 ringinuse ? "1" : "0");
8145 }
8146
8147 mem->ringinuse = ringinuse;
8148
8149 ast_queue_log(q->name, "NONE", mem->interface, "RINGINUSE", "%d", ringinuse);
8150 queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
8151}
8152
8154{
8155 struct member *mem;
8156 int foundinterface = 0;
8157
8158 ao2_lock(q);
8159 if ((mem = interface_exists(q, interface))) {
8160 foundinterface++;
8162 ao2_ref(mem, -1);
8163 }
8164 ao2_unlock(q);
8165
8166 return foundinterface;
8167}
8168
8169static int set_member_value_help_members(struct call_queue *q, const char *interface, int property, int value)
8170{
8171 switch(property) {
8172 case MEMBER_PENALTY:
8174
8175 case MEMBER_RINGINUSE:
8177
8178 default:
8179 ast_log(LOG_ERROR, "Attempted to set invalid property\n");
8180 return 0;
8181 }
8182}
8183
8184/*!
8185 * \internal
8186 * \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues.
8187 * \param[in] queuename If specified, only act on a member if it belongs to this queue
8188 * \param[in] interface Interface of queue member(s) having priority set.
8189 * \param[in] property Which queue property is being set
8190 * \param[in] value Value penalty is being changed to for each member
8191 */
8192static int set_member_value(const char *queuename, const char *interface, int property, int value)
8193{
8194 int foundinterface = 0, foundqueue = 0;
8195 struct call_queue *q;
8196 struct ast_config *queue_config = NULL;
8197 struct ao2_iterator queue_iter;
8198
8199 /* property dependent restrictions on values should be checked in this switch */
8200 switch (property) {
8201 case MEMBER_PENALTY:
8202 if (value < 0 && !negative_penalty_invalid) {
8203 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", value);
8204 return RESULT_FAILURE;
8205 }
8206 }
8207
8208 if (ast_strlen_zero(queuename)) { /* This means we need to iterate through all the queues. */
8209 if (ast_check_realtime("queues")) {
8210 queue_config = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
8211 if (queue_config) {
8212 char *category = NULL;
8213 while ((category = ast_category_browse(queue_config, category))) {
8214 const char *name = ast_variable_retrieve(queue_config, category, "name");
8215 if (ast_strlen_zero(name)) {
8216 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
8217 continue;
8218 }
8219 if ((q = find_load_queue_rt_friendly(name))) {
8220 foundqueue++;
8221 foundinterface += set_member_value_help_members(q, interface, property, value);
8222 queue_unref(q);
8223 }
8224 }
8225
8226 ast_config_destroy(queue_config);
8227 }
8228 }
8229
8230 /* After hitting realtime queues, go back and get the regular ones. */
8231 queue_iter = ao2_iterator_init(queues, 0);
8232 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
8233 foundqueue++;
8234 foundinterface += set_member_value_help_members(q, interface, property, value);
8235 queue_unref(q);
8236 }
8237 ao2_iterator_destroy(&queue_iter);
8238 } else { /* We actually have a queuename, so we can just act on the single queue. */
8239 if ((q = find_load_queue_rt_friendly(queuename))) {
8240 foundqueue++;
8241 foundinterface += set_member_value_help_members(q, interface, property, value);
8242 queue_unref(q);
8243 }
8244 }
8245
8246 if (foundinterface) {
8247 return RESULT_SUCCESS;
8248 } else if (!foundqueue) {
8249 ast_log (LOG_ERROR, "Invalid queuename\n");
8250 } else {
8251 ast_log (LOG_ERROR, "Invalid interface\n");
8252 }
8253
8254 return RESULT_FAILURE;
8255}
8256
8257/*!
8258 * \brief Gets members penalty.
8259 * \return Return the members penalty or RESULT_FAILURE on error.
8260 */
8261static int get_member_penalty(char *queuename, char *interface)
8262{
8263 int foundqueue = 0, penalty;
8264 struct call_queue *q;
8265 struct member *mem;
8266
8267 if ((q = find_load_queue_rt_friendly(queuename))) {
8268 foundqueue = 1;
8269 ao2_lock(q);
8270 if ((mem = interface_exists(q, interface))) {
8271 penalty = mem->penalty;
8272 ao2_ref(mem, -1);
8273 ao2_unlock(q);
8274 queue_t_unref(q, "Search complete");
8275 return penalty;
8276 }
8277 ao2_unlock(q);
8278 queue_t_unref(q, "Search complete");
8279 }
8280
8281 /* some useful debugging */
8282 if (foundqueue) {
8283 ast_log (LOG_ERROR, "Invalid queuename\n");
8284 } else {
8285 ast_log (LOG_ERROR, "Invalid interface\n");
8286 }
8287
8288 return RESULT_FAILURE;
8289}
8290
8291/*! \brief Reload dynamic queue members persisted into the astdb */
8292static void reload_queue_members(void)
8293{
8294 char *cur_ptr;
8295 const char *queue_name;
8296 char *member;
8297 char *interface;
8298 char *membername = NULL;
8299 char *state_interface;
8300 char *penalty_tok;
8301 int penalty = 0;
8302 char *paused_tok;
8303 int paused = 0;
8304 char *wrapuptime_tok;
8305 int wrapuptime = 0;
8306 char *reason_paused;
8307 struct ast_db_entry *db_tree;
8308 struct ast_db_entry *entry;
8309 struct call_queue *cur_queue;
8310 char *queue_data;
8311
8312 /* Each key in 'pm_family' is the name of a queue */
8313 db_tree = ast_db_gettree(pm_family, NULL);
8314 for (entry = db_tree; entry; entry = entry->next) {
8315
8316 queue_name = entry->key + strlen(pm_family) + 2;
8317
8318 {
8319 struct call_queue tmpq = {
8320 .name = queue_name,
8321 };
8322 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
8323 }
8324
8325 if (!cur_queue) {
8326 cur_queue = find_load_queue_rt_friendly(queue_name);
8327 }
8328
8329 if (!cur_queue) {
8330 /* If the queue no longer exists, remove it from the
8331 * database */
8332 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
8333 ast_db_del(pm_family, queue_name);
8334 continue;
8335 }
8336
8337 if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
8338 queue_t_unref(cur_queue, "Expire reload reference");
8339 continue;
8340 }
8341
8342 cur_ptr = queue_data;
8343 while ((member = strsep(&cur_ptr, ",|"))) {
8344 if (ast_strlen_zero(member)) {
8345 continue;
8346 }
8347
8348 interface = strsep(&member, ";");
8349 penalty_tok = strsep(&member, ";");
8350 paused_tok = strsep(&member, ";");
8351 membername = strsep(&member, ";");
8352 state_interface = strsep(&member, ";");
8353 reason_paused = strsep(&member, ";");
8354 wrapuptime_tok = strsep(&member, ";");
8355
8356 if (!penalty_tok) {
8357 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
8358 break;
8359 }
8360 penalty = strtol(penalty_tok, NULL, 10);
8361 if (errno == ERANGE) {
8362 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
8363 break;
8364 }
8365
8366 if (!paused_tok) {
8367 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
8368 break;
8369 }
8370 paused = strtol(paused_tok, NULL, 10);
8371 if ((errno == ERANGE) || paused < 0 || paused > 1) {
8372 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
8373 break;
8374 }
8375
8376 if (!ast_strlen_zero(wrapuptime_tok)) {
8377 wrapuptime = strtol(wrapuptime_tok, NULL, 10);
8378 if (errno == ERANGE) {
8379 ast_log(LOG_WARNING, "Error converting wrapuptime: %s: Out of range.\n", wrapuptime_tok);
8380 break;
8381 }
8382 }
8383
8384 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d ReasonPause: %s Wrapuptime: %d\n",
8385 queue_name, interface, membername, penalty, paused, reason_paused, wrapuptime);
8386
8387 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface, reason_paused, wrapuptime) == RES_OUTOFMEMORY) {
8388 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
8389 break;
8390 }
8391 }
8392 queue_t_unref(cur_queue, "Expire reload reference");
8393 ast_free(queue_data);
8394 }
8395
8396 if (db_tree) {
8397 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
8398 ast_db_freetree(db_tree);
8399 }
8400}
8401
8402/*! \brief PauseQueueMember application */
8403static int pqm_exec(struct ast_channel *chan, const char *data)
8404{
8405 char *parse;
8407 AST_APP_ARG(queuename);
8408 AST_APP_ARG(interface);
8410 AST_APP_ARG(reason);
8411 );
8412
8413 if (ast_strlen_zero(data)) {
8414 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
8415 return -1;
8416 }
8417
8418 parse = ast_strdupa(data);
8419
8421
8422 if (ast_strlen_zero(args.interface)) {
8423 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
8424 return -1;
8425 }
8426
8427 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
8428 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
8429 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
8430 return 0;
8431 }
8432
8433 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
8434
8435 return 0;
8436}
8437
8438/*! \brief UnpauseQueueMember application */
8439static int upqm_exec(struct ast_channel *chan, const char *data)
8440{
8441 char *parse;
8443 AST_APP_ARG(queuename);
8444 AST_APP_ARG(interface);
8446 AST_APP_ARG(reason);
8447 );
8448
8449 if (ast_strlen_zero(data)) {
8450 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
8451 return -1;
8452 }
8453
8454 parse = ast_strdupa(data);
8455
8457
8458 if (ast_strlen_zero(args.interface)) {
8459 ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
8460 return -1;
8461 }
8462
8463 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
8464 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
8465 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
8466 return 0;
8467 }
8468
8469 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
8470
8471 return 0;
8472}
8473
8474/*! \brief RemoveQueueMember application */
8475static int rqm_exec(struct ast_channel *chan, const char *data)
8476{
8477 int res=-1;
8478 char *parse, *temppos = NULL;
8479 struct member *mem = NULL;
8480
8482 AST_APP_ARG(queuename);
8484 );
8485
8486
8487 if (ast_strlen_zero(data)) {
8488 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
8489 return -1;
8490 }
8491
8492 parse = ast_strdupa(data);
8493
8495
8496 if (ast_strlen_zero(args.interface)) {
8497 args.interface = ast_strdupa(ast_channel_name(chan));
8498 temppos = strrchr(args.interface, '-');
8499 if (temppos) {
8500 *temppos = '\0';
8501 }
8502 }
8503
8504 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
8505
8507 mem = find_member_by_queuename_and_interface(args.queuename, args.interface);
8508 }
8509
8510 switch (remove_from_queue(args.queuename, args.interface)) {
8511 case RES_OKAY:
8512 if (!mem || ast_strlen_zero(mem->membername)) {
8513 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "REMOVEMEMBER", "%s", "");
8514 } else {
8515 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), mem->membername, "REMOVEMEMBER", "%s", "");
8516 }
8517 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
8518 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
8519 res = 0;
8520 break;
8521 case RES_EXISTS:
8522 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
8523 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
8524 res = 0;
8525 break;
8526 case RES_NOSUCHQUEUE:
8527 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
8528 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
8529 res = 0;
8530 break;
8531 case RES_NOT_DYNAMIC:
8532 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
8533 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
8534 res = 0;
8535 break;
8536 }
8537
8538 if (mem) {
8539 ao2_ref(mem, -1);
8540 }
8541
8542 return res;
8543}
8544
8545/*! \brief AddQueueMember application */
8546static int aqm_exec(struct ast_channel *chan, const char *data)
8547{
8548 int res=-1;
8549 char *parse, *tmp, *temppos = NULL, *reason = NULL;
8551 AST_APP_ARG(queuename);
8558 );
8559 int penalty = 0;
8560 int paused = 0;
8561 int wrapuptime;
8562 struct ast_flags flags = { 0 };
8563
8564 if (ast_strlen_zero(data)) {
8565 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface][,wrapuptime]]]]])\n");
8566 return -1;
8567 }
8568
8569 parse = ast_strdupa(data);
8570
8572
8573 if (args.options) {
8574 char *opts[AQM_OPT_ARG_ARRAY_SIZE] = { NULL, };
8575 ast_app_parse_options(aqm_opts, &flags, opts, args.options);
8577 paused = 1;
8579 reason = ast_strdupa(opts[AQM_OPT_ARG_PAUSE_REASON]);
8580 }
8581 }
8582 }
8583
8584 if (ast_strlen_zero(args.interface)) {
8585 args.interface = ast_strdupa(ast_channel_name(chan));
8586 temppos = strrchr(args.interface, '-');
8587 if (temppos) {
8588 *temppos = '\0';
8589 }
8590 }
8591
8592 if (!ast_strlen_zero(args.penalty)) {
8593 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
8594 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
8595 penalty = 0;
8596 }
8597 }
8598
8599 if (!ast_strlen_zero(args.wrapuptime)) {
8600 tmp = args.wrapuptime;
8601 ast_strip(tmp);
8602 wrapuptime = atoi(tmp);
8603 if (wrapuptime < 0) {
8604 wrapuptime = 0;
8605 }
8606 } else {
8607 wrapuptime = 0;
8608 }
8609
8610 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, paused, queue_persistent_members, args.state_interface, reason, wrapuptime)) {
8611 case RES_OKAY:
8612 if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
8613 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
8614 } else {
8615 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
8616 }
8617 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
8618 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
8619 res = 0;
8620 break;
8621 case RES_EXISTS:
8622 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
8623 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
8624 res = 0;
8625 break;
8626 case RES_NOSUCHQUEUE:
8627 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
8628 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
8629 res = 0;
8630 break;
8631 case RES_OUTOFMEMORY:
8632 ast_log(LOG_ERROR, "Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
8633 break;
8634 }
8635
8636 return res;
8637}
8638
8639/*! \brief QueueLog application */
8640static int ql_exec(struct ast_channel *chan, const char *data)
8641{
8642 char *parse;
8643
8645 AST_APP_ARG(queuename);
8646 AST_APP_ARG(uniqueid);
8647 AST_APP_ARG(membername);
8649 AST_APP_ARG(params);
8650 );
8651
8652 if (ast_strlen_zero(data)) {
8653 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
8654 return -1;
8655 }
8656
8657 parse = ast_strdupa(data);
8658
8660
8661 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
8662 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
8663 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
8664 return -1;
8665 }
8666
8667 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
8668 "%s", args.params ? args.params : "");
8669
8670 return 0;
8671}
8672
8673/*! \brief Copy rule from global list into specified queue */
8674static void copy_rules(struct queue_ent *qe, const char *rulename)
8675{
8676 struct penalty_rule *pr_iter;
8677 struct rule_list *rl_iter;
8678 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
8680 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
8681 if (!strcasecmp(rl_iter->name, tmp)) {
8682 break;
8683 }
8684 }
8685 if (rl_iter) {
8686 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
8687 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
8688 if (!new_pr) {
8689 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
8690 break;
8691 }
8692 new_pr->time = pr_iter->time;
8693 new_pr->max_value = pr_iter->max_value;
8694 new_pr->min_value = pr_iter->min_value;
8695 new_pr->raise_value = pr_iter->raise_value;
8696 new_pr->max_relative = pr_iter->max_relative;
8697 new_pr->min_relative = pr_iter->min_relative;
8698 new_pr->raise_relative = pr_iter->raise_relative;
8699 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
8700 }
8701 }
8703}
8704
8705/*!\brief The starting point for all queue calls
8706 *
8707 * The process involved here is to
8708 * 1. Parse the options specified in the call to Queue()
8709 * 2. Join the queue
8710 * 3. Wait in a loop until it is our turn to try calling a queue member
8711 * 4. Attempt to call a queue member
8712 * 5. If 4. did not result in a bridged call, then check for between
8713 * call options such as periodic announcements etc.
8714 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
8715 * exit the queue.
8716 */
8717static int queue_exec(struct ast_channel *chan, const char *data)
8718{
8719 int res=-1;
8720 int ringing=0;
8721 const char *user_priority;
8722 const char *max_penalty_str;
8723 const char *min_penalty_str;
8724 const char *raise_penalty_str;
8725 int prio;
8726 int qcontinue = 0;
8727 int max_penalty, min_penalty, raise_penalty;
8728 enum queue_result reason = QUEUE_UNKNOWN;
8729 /* whether to exit Queue application after the timeout hits */
8730 int tries = 0;
8731 int noption = 0;
8732 char *parse;
8733 int makeannouncement = 0;
8734 int position = 0;
8736 AST_APP_ARG(queuename);
8739 AST_APP_ARG(announceoverride);
8740 AST_APP_ARG(queuetimeoutstr);
8741 AST_APP_ARG(agi);
8742 AST_APP_ARG(gosub);
8744 AST_APP_ARG(position);
8745 );
8746 /* Our queue entry */
8747 struct queue_ent qe = { 0 };
8748 struct ast_flags opts = { 0, };
8749 char *opt_args[OPT_ARG_ARRAY_SIZE];
8750 int max_forwards;
8751 int cid_allow;
8752
8753 if (ast_strlen_zero(data)) {
8754 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,gosub[,rule[,position]]]]]]]]\n");
8755 return -1;
8756 }
8757
8758 ast_channel_lock(chan);
8760 ast_channel_unlock(chan);
8761
8762 if (max_forwards <= 0) {
8763 ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
8764 return -1;
8765 }
8766
8767 parse = ast_strdupa(data);
8769
8770 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, gosub: %s, rule: %s, position: %s\n",
8771 args.queuename,
8772 S_OR(args.options, ""),
8773 S_OR(args.url, ""),
8774 S_OR(args.announceoverride, ""),
8775 S_OR(args.queuetimeoutstr, ""),
8776 S_OR(args.agi, ""),
8777 S_OR(args.gosub, ""),
8778 S_OR(args.rule, ""),
8779 S_OR(args.position, ""));
8780
8781 if (!ast_strlen_zero(args.options)) {
8782 ast_app_parse_options(queue_exec_options, &opts, opt_args, args.options);
8783 }
8784
8785 /* Setup our queue entry */
8786 qe.start = time(NULL);
8787
8788 pbx_builtin_setvar_helper(chan, "ABANDONED", NULL);
8789
8790 /* set the expire time based on the supplied timeout; */
8791 if (!ast_strlen_zero(args.queuetimeoutstr)) {
8792 qe.expire = qe.start + atoi(args.queuetimeoutstr);
8793 } else {
8794 qe.expire = 0;
8795 }
8796
8797 /* Get the priority from the variable ${QUEUE_PRIO} */
8798 ast_channel_lock(chan);
8799 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
8800 if (user_priority) {
8801 if (sscanf(user_priority, "%30d", &prio) == 1) {
8802 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
8803 } else {
8804 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
8805 user_priority, ast_channel_name(chan));
8806 prio = 0;
8807 }
8808 } else {
8809 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
8810 prio = 0;
8811 }
8812
8813 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
8814
8815 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
8816 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
8817 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
8818 } else {
8819 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
8820 max_penalty_str, ast_channel_name(chan));
8821 max_penalty = INT_MAX;
8822 }
8823 } else {
8824 max_penalty = INT_MAX;
8825 }
8826
8827 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
8828 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
8829 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
8830 } else {
8831 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
8832 min_penalty_str, ast_channel_name(chan));
8833 min_penalty = INT_MAX;
8834 }
8835 } else {
8836 min_penalty = INT_MAX;
8837 }
8838
8839 if ((raise_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_RAISE_PENALTY"))) {
8840 if (*raise_penalty_str == 'r') {
8841 qe.raise_respect_min = 1;
8842 raise_penalty_str++;
8843 } else {
8844 qe.raise_respect_min = 0;
8845 }
8846 if (sscanf(raise_penalty_str, "%30d", &raise_penalty) == 1) {
8847 ast_debug(1, "%s: Got raise penalty %s%d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), qe.raise_respect_min ? "r" : "", raise_penalty);
8848 } else {
8849 ast_log(LOG_WARNING, "${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n",
8850 raise_penalty_str, ast_channel_name(chan));
8851 raise_penalty = INT_MAX;
8852 }
8853 } else {
8854 raise_penalty = INT_MAX;
8855 }
8856 ast_channel_unlock(chan);
8857
8858 if (ast_test_flag(&opts, OPT_RINGING)) {
8859 ringing = 1;
8860 }
8861
8862 if (ringing != 1 && ast_test_flag(&opts, OPT_RING_WHEN_RINGING)) {
8863 qe.ring_when_ringing = 1;
8864 }
8865
8866 if (ast_test_flag(&opts, OPT_GO_ON)) {
8867 qcontinue = 1;
8868 }
8869
8870 if (args.position) {
8871 position = atoi(args.position);
8872 if (position < 0) {
8873 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
8874 position = 0;
8875 }
8876 }
8877
8878 ast_debug(1, "queue: %s, expires: %ld, priority: %d\n",
8879 args.queuename, (long)qe.expire, prio);
8880
8881 qe.chan = chan;
8882 qe.prio = prio;
8883 qe.max_penalty = max_penalty;
8884 qe.min_penalty = min_penalty;
8885 qe.raise_penalty = raise_penalty;
8886 qe.last_pos_said = 0;
8887 qe.last_pos = 0;
8890 qe.valid_digits = 0;
8891 if (join_queue(args.queuename, &qe, &reason, position)) {
8892 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
8893 set_queue_result(chan, reason);
8894 return 0;
8895 }
8896 ast_assert(qe.parent != NULL);
8897
8898 if (qe.parent->periodicannouncestartdelay >= 0) {
8901 }
8902
8904
8905 if (log_caller_id_name) {
8906 char *escaped_cidname = NULL;
8907 /* Ensure caller ID name is valid and not NULL before processing */
8908 if (cid_allow && ast_channel_caller(chan)->id.name.valid && ast_channel_caller(chan)->id.name.str) {
8909 escaped_cidname = ast_strdupa(ast_channel_caller(chan)->id.name.str);
8910 /* Only iterate if '|' is found */
8911 if (strchr(escaped_cidname, '|')) {
8912 for (char *p = escaped_cidname; *p; p++) {
8913 if (*p == '|') {
8914 *p = '_';
8915 }
8916 }
8917 }
8918 }
8919
8920 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d|%s",
8921 S_OR(args.url, ""),
8922 S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8923 qe.opos,
8924 S_OR(escaped_cidname, ""));
8925 } else {
8926 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
8927 S_OR(args.url, ""),
8928 S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8929 qe.opos);
8930 }
8931
8932 /* PREDIAL: Preprocess any callee gosub arguments. */
8934 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
8937 }
8938
8939 /* PREDIAL: Run gosub on the caller's channel */
8941 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
8943 ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
8944 }
8945
8946 /* Music on hold class override */
8949 ast_copy_string(qe.moh, opt_args[OPT_ARG_MUSICONHOLD_CLASS], sizeof(qe.moh));
8950 }
8951
8952 copy_rules(&qe, args.rule);
8953 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
8954check_turns:
8955 if (ringing) {
8957 } else {
8958 ast_moh_start(chan, qe.moh, NULL);
8959 }
8960
8961 /* This is the wait loop for callers 2 through maxlen */
8962 res = wait_our_turn(&qe, ringing, &reason);
8963 if (res) {
8964 goto stop;
8965 }
8966
8967 makeannouncement = qe.parent->announce_to_first_user;
8968
8969 for (;;) {
8970 /* This is the wait loop for the head caller*/
8971 /* To exit, they may get their call answered; */
8972 /* they may dial a digit from the queue context; */
8973 /* or, they may timeout. */
8974
8975 /* A request to withdraw this call from the queue arrived */
8976 if (qe.withdraw) {
8977 reason = QUEUE_WITHDRAW;
8978 res = 1;
8979 break;
8980 }
8981
8982 /* Leave if we have exceeded our queuetimeout */
8983 if (qe.expire && (time(NULL) >= qe.expire)) {
8984 record_abandoned(&qe);
8985 reason = QUEUE_TIMEOUT;
8986 res = 0;
8987 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
8988 qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8989 break;
8990 }
8991
8992 if (makeannouncement) {
8993 /* Make a position announcement, if enabled */
8994 if (qe.parent->announcefrequency) {
8995 if ((res = say_position(&qe, ringing))) {
8996 goto stop;
8997 }
8998 }
8999 }
9000 makeannouncement = 1;
9001
9002 /* Make a periodic announcement, if enabled */
9004 if ((res = say_periodic_announcement(&qe, ringing))) {
9005 goto stop;
9006 }
9007 }
9008
9009 /* A request to withdraw this call from the queue arrived */
9010 if (qe.withdraw) {
9011 reason = QUEUE_WITHDRAW;
9012 res = 1;
9013 break;
9014 }
9015
9016 /* Leave if we have exceeded our queuetimeout */
9017 if (qe.expire && (time(NULL) >= qe.expire)) {
9018 record_abandoned(&qe);
9019 reason = QUEUE_TIMEOUT;
9020 res = 0;
9021 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
9022 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9023 break;
9024 }
9025
9026 /* see if we need to move to the next penalty level for this queue */
9027 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
9028 update_qe_rule(&qe);
9029 }
9030
9031 /* Try calling all queue members for 'timeout' seconds */
9032 res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.gosub, ringing);
9033 if (res) {
9034 goto stop;
9035 }
9036
9037 if (qe.parent->leavewhenempty) {
9038 int status = 0;
9040 record_abandoned(&qe);
9041 reason = QUEUE_LEAVEEMPTY;
9042 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
9043 res = 0;
9044 break;
9045 }
9046 }
9047
9048 /* exit after 'timeout' cycle if 'n' option enabled */
9049 if (noption && tries >= ao2_container_count(qe.parent->members)) {
9050 ast_verb(3, "Exiting on time-out cycle\n");
9051 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
9052 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9053 record_abandoned(&qe);
9054 reason = QUEUE_TIMEOUT;
9055 res = 0;
9056 break;
9057 }
9058
9059
9060 /* Leave if we have exceeded our queuetimeout */
9061 if (qe.expire && (time(NULL) >= qe.expire)) {
9062 record_abandoned(&qe);
9063 reason = QUEUE_TIMEOUT;
9064 res = 0;
9065 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));
9066 break;
9067 }
9068
9069 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
9070 res = wait_a_bit(&qe);
9071 if (res) {
9072 goto stop;
9073 }
9074
9075 /* If using dynamic realtime members, we should regenerate the member list for this queue */
9077
9078 /* Since this is a priority queue and
9079 * it is not sure that we are still at the head
9080 * of the queue, go and check for our turn again.
9081 */
9082 if (!is_our_turn(&qe)) {
9083 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.chan));
9084 goto check_turns;
9085 }
9086 }
9087
9088stop:
9089 if (res) {
9090 if (reason == QUEUE_WITHDRAW) {
9091 record_abandoned(&qe);
9092 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 : "");
9093 if (qe.withdraw_info) {
9094 pbx_builtin_setvar_helper(qe.chan, "QUEUE_WITHDRAW_INFO", qe.withdraw_info);
9095 }
9096 res = 0;
9097 } else if (res < 0) {
9098 if (!qe.handled) {
9099 record_abandoned(&qe);
9100 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
9101 "%d|%d|%ld", qe.pos, qe.opos,
9102 (long) (time(NULL) - qe.start));
9103 res = -1;
9104 } else if (reason == QUEUE_LEAVEEMPTY) {
9105 /* Return back to dialplan, don't hang up */
9106 res = 0;
9107 } else if (qcontinue) {
9108 reason = QUEUE_CONTINUE;
9109 res = 0;
9110 }
9111 } else if (qe.valid_digits) {
9112 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
9113 "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) (time(NULL) - qe.start));
9114 }
9115 }
9116
9117 /* Free the optional withdraw info if present */
9118 /* This is done here to catch all cases. e.g. if the call eventually wasn't withdrawn, e.g. answered */
9119 if (qe.withdraw_info) {
9121 qe.withdraw_info = NULL;
9122 }
9123
9124 /* Don't allow return code > 0 */
9125 if (res >= 0) {
9126 res = 0;
9127 if (ringing) {
9128 ast_indicate(chan, -1);
9129 } else {
9130 ast_moh_stop(chan);
9131 }
9132 ast_stopstream(chan);
9133 }
9134
9136
9137 leave_queue(&qe);
9138 if (reason != QUEUE_UNKNOWN)
9139 set_queue_result(chan, reason);
9140
9141 /*
9142 * every queue_ent is given a reference to it's parent
9143 * call_queue when it joins the queue. This ref must be taken
9144 * away right before the queue_ent is destroyed. In this case
9145 * the queue_ent is about to be returned on the stack
9146 */
9147 qe.parent = queue_unref(qe.parent);
9148
9149 return res;
9150}
9151
9152/*!
9153 * \brief create interface var with all queue details.
9154 * \retval 0 on success
9155 * \retval -1 on error
9156*/
9157static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9158{
9159 int res = -1;
9160 struct call_queue *q;
9161 char interfacevar[256] = "";
9162 float sl = 0;
9163
9164 if (ast_strlen_zero(data)) {
9165 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9166 return -1;
9167 }
9168
9169 if ((q = find_load_queue_rt_friendly(data))) {
9170 ao2_lock(q);
9171 if (q->setqueuevar) {
9172 sl = 0;
9173 res = 0;
9174
9175 if (q->callscompleted > 0) {
9176 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
9177 }
9178
9179 snprintf(interfacevar, sizeof(interfacevar),
9180 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
9182
9183 pbx_builtin_setvar_multiple(chan, interfacevar);
9184 }
9185
9186 ao2_unlock(q);
9187 queue_t_unref(q, "Done with QUEUE() function");
9188 } else {
9189 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9190 }
9191
9192 snprintf(buf, len, "%d", res);
9193
9194 return 0;
9195}
9196
9197/*!
9198 * \brief Check if a given queue exists
9199 *
9200 */
9201static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9202{
9203 struct call_queue *q;
9204
9205 buf[0] = '\0';
9206
9207 if (ast_strlen_zero(data)) {
9208 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9209 return -1;
9210 }
9212 snprintf(buf, len, "%d", q != NULL? 1 : 0);
9213 if (q) {
9214 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
9215 }
9216
9217 return 0;
9218}
9219
9220static struct member *get_interface_helper(struct call_queue *q, const char *interface)
9221{
9222 struct member *m;
9223
9225 ast_log(LOG_ERROR, "QUEUE_MEMBER: Missing required interface argument.\n");
9226 return NULL;
9227 }
9228
9230 if (!m) {
9231 ast_log(LOG_ERROR, "Queue member interface '%s' not in queue '%s'.\n",
9232 interface, q->name);
9233 }
9234 return m;
9235}
9236
9237/*!
9238 * \brief Get number either busy / free / ready or total members of a specific queue
9239 * \brief Get or set member properties penalty / paused / ringinuse
9240 * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ringinuse)
9241 * \retval -1 on error
9242 */
9243static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9244{
9245 int count = 0;
9246 struct member *m;
9247 struct ao2_iterator mem_iter;
9248 struct call_queue *q;
9249
9251 AST_APP_ARG(queuename);
9252 AST_APP_ARG(option);
9253 AST_APP_ARG(interface);
9254 );
9255 /* Make sure the returned value on error is zero length string. */
9256 buf[0] = '\0';
9257
9258 if (ast_strlen_zero(data)) {
9260 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9261 cmd);
9262 return -1;
9263 }
9264
9266
9267 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
9269 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9270 cmd);
9271 return -1;
9272 }
9273
9274 if ((q = find_load_queue_rt_friendly(args.queuename))) {
9275 ao2_lock(q);
9276 if (!strcasecmp(args.option, "logged")) {
9277 mem_iter = ao2_iterator_init(q->members, 0);
9278 while ((m = ao2_iterator_next(&mem_iter))) {
9279 /* Count the agents who are logged in and presently answering calls */
9280 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9281 count++;
9282 }
9283 ao2_ref(m, -1);
9284 }
9285 ao2_iterator_destroy(&mem_iter);
9286 } else if (!strcasecmp(args.option, "free")) {
9287 mem_iter = ao2_iterator_init(q->members, 0);
9288 while ((m = ao2_iterator_next(&mem_iter))) {
9289 /* Count the agents who are logged in and presently answering calls */
9290 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
9291 count++;
9292 }
9293 ao2_ref(m, -1);
9294 }
9295 ao2_iterator_destroy(&mem_iter);
9296 } else if (!strcasecmp(args.option, "ready")) {
9297 time_t now;
9298 time(&now);
9299 mem_iter = ao2_iterator_init(q->members, 0);
9300 while ((m = ao2_iterator_next(&mem_iter))) {
9301 /* Count the agents who are logged in, not paused and not wrapping up */
9302 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
9303 !(m->lastcall && get_wrapuptime(q, m) && ((now - get_wrapuptime(q, m)) < m->lastcall))) {
9304 count++;
9305 }
9306 ao2_ref(m, -1);
9307 }
9308 ao2_iterator_destroy(&mem_iter);
9309 } else if (!strcasecmp(args.option, "count")) {
9311 } else if (!strcasecmp(args.option, "penalty")) {
9312 m = get_interface_helper(q, args.interface);
9313 if (m) {
9314 count = m->penalty;
9315 ao2_ref(m, -1);
9316 }
9317 } else if (!strcasecmp(args.option, "paused")) {
9318 m = get_interface_helper(q, args.interface);
9319 if (m) {
9320 count = m->paused;
9321 ao2_ref(m, -1);
9322 }
9323 } else if ((!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9324 || !strcasecmp(args.option, "ringinuse"))) {
9325 m = get_interface_helper(q, args.interface);
9326 if (m) {
9327 count = m->ringinuse;
9328 ao2_ref(m, -1);
9329 }
9330 } else {
9331 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9332 }
9333 ao2_unlock(q);
9334 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
9335 } else {
9336 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9337 }
9338
9339 snprintf(buf, len, "%d", count);
9340
9341 return 0;
9342}
9343
9344/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse. */
9345static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9346{
9347 int memvalue;
9348
9350 AST_APP_ARG(queuename);
9351 AST_APP_ARG(option);
9352 AST_APP_ARG(interface);
9353 );
9354
9355 if (ast_strlen_zero(data)) {
9357 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9358 cmd);
9359 return -1;
9360 }
9361
9363
9364 if (ast_strlen_zero(args.option)
9365 || ast_strlen_zero(args.interface)) {
9367 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9368 cmd);
9369 return -1;
9370 }
9371
9372 /*
9373 * If queuename is empty then the option will be
9374 * set for the interface in all queues.
9375 */
9376
9377 memvalue = atoi(value);
9378 if (!strcasecmp(args.option, "penalty")) {
9379 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
9380 ast_log(LOG_ERROR, "Invalid interface, queue, or penalty\n");
9381 return -1;
9382 }
9383 } else if (!strcasecmp(args.option, "paused")) {
9384 memvalue = (memvalue <= 0) ? 0 : 1;
9385 if (set_member_paused(args.queuename, args.interface, NULL, memvalue)) {
9386 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9387 return -1;
9388 }
9389 } else if (!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9390 || !strcasecmp(args.option, "ringinuse")) {
9391 memvalue = (memvalue <= 0) ? 0 : 1;
9392 if (set_member_value(args.queuename, args.interface, MEMBER_RINGINUSE, memvalue)) {
9393 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9394 return -1;
9395 }
9396 } else {
9397 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9398 return -1;
9399 }
9400 return 0;
9401}
9402
9403/*!
9404 * \brief Get the total number of members in a specific queue (Deprecated)
9405 * \retval number of members
9406 * \retval -1 on error
9407*/
9408static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9409{
9410 int count = 0;
9411 struct member *m;
9412 struct call_queue *q;
9413 struct ao2_iterator mem_iter;
9414 static int depflag = 1;
9415
9416 if (depflag) {
9417 depflag = 0;
9418 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");
9419 }
9420
9421 if (ast_strlen_zero(data)) {
9422 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9423 return -1;
9424 }
9425
9426 if ((q = find_load_queue_rt_friendly(data))) {
9427 ao2_lock(q);
9428 mem_iter = ao2_iterator_init(q->members, 0);
9429 while ((m = ao2_iterator_next(&mem_iter))) {
9430 /* Count the agents who are logged in and presently answering calls */
9431 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9432 count++;
9433 }
9434 ao2_ref(m, -1);
9435 }
9436 ao2_iterator_destroy(&mem_iter);
9437 ao2_unlock(q);
9438 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
9439 } else {
9440 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9441 }
9442
9443 snprintf(buf, len, "%d", count);
9444
9445 return 0;
9446}
9447
9448/*! \brief Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue */
9449static int queue_function_queuegetchannel(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9450{
9451 int position;
9452 char *parse;
9453 struct call_queue *q;
9454 struct ast_variable *var;
9455
9457 AST_APP_ARG(queuename);
9458 AST_APP_ARG(position);
9459 );
9460
9461 buf[0] = '\0';
9462
9463 if (ast_strlen_zero(data)) {
9464 ast_log(LOG_ERROR, "Missing argument. QUEUE_GET_CHANNEL(<queuename>,<position>)\n");
9465 return -1;
9466 }
9467
9468 parse = ast_strdupa(data);
9470
9471 if (ast_strlen_zero(args.queuename)) {
9472 ast_log (LOG_ERROR, "The <queuename> parameter is required.\n");
9473 return -1;
9474 }
9475
9476 if (ast_strlen_zero(args.position)) {
9477 position = 1;
9478 } else {
9479 if (sscanf(args.position, "%30d", &position) != 1) {
9480 ast_log (LOG_ERROR, "<position> parameter must be an integer.\n");
9481 return -1;
9482 }
9483 if (position < 1) {
9484 ast_log (LOG_ERROR, "<position> parameter must be an integer greater than zero.\n");
9485 return -1;
9486 }
9487 }
9488
9489 {
9490 struct call_queue tmpq = {
9491 .name = args.queuename,
9492 };
9493
9494 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_GET_CHANNEL()");
9495 }
9496 if (q) {
9497 ao2_lock(q);
9498 if (q->count >= position) {
9499 struct queue_ent *qe;
9500
9501 for (qe = q->head; qe; qe = qe->next) {
9502 if (qe->pos == position) {
9504 break;
9505 }
9506 }
9507 }
9508 ao2_unlock(q);
9509 queue_t_unref(q, "Done with reference in QUEUE_GET_CHANNEL()");
9510 return 0;
9511 }
9512
9513 var = ast_load_realtime("queues", "name", args.queuename, SENTINEL);
9514 if (var) {
9515 /* if the queue is realtime but was not found in memory, this
9516 * means that the queue had been deleted from memory since it was
9517 * "dead."
9518 */
9520 return 0;
9521 }
9522
9523 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9524 return 0;
9525}
9526
9527/*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
9528static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9529{
9530 int count = 0;
9531 struct call_queue *q, tmpq = {
9532 .name = data,
9533 };
9534 struct ast_variable *var = NULL;
9535
9536 buf[0] = '\0';
9537
9538 if (ast_strlen_zero(data)) {
9539 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
9540 return -1;
9541 }
9542
9543 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
9544 ao2_lock(q);
9545 count = q->count;
9546 ao2_unlock(q);
9547 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
9548 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
9549 /* if the queue is realtime but was not found in memory, this
9550 * means that the queue had been deleted from memory since it was
9551 * "dead." This means it has a 0 waiting count
9552 */
9553 count = 0;
9555 } else {
9556 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9557 }
9558
9559 snprintf(buf, len, "%d", count);
9560
9561 return 0;
9562}
9563
9564/*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
9565static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9566{
9567 struct call_queue *q;
9568 struct member *m;
9569
9570 /* Ensure an otherwise empty list doesn't return garbage */
9571 buf[0] = '\0';
9572
9573 if (ast_strlen_zero(data)) {
9574 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
9575 return -1;
9576 }
9577
9578 if ((q = find_load_queue_rt_friendly(data))) {
9579 int buflen = 0, count = 0;
9580 struct ao2_iterator mem_iter;
9581
9582 ao2_lock(q);
9583 mem_iter = ao2_iterator_init(q->members, 0);
9584 while ((m = ao2_iterator_next(&mem_iter))) {
9585 /* strcat() is always faster than printf() */
9586 if (count++) {
9587 strncat(buf + buflen, ",", len - buflen - 1);
9588 buflen++;
9589 }
9590 strncat(buf + buflen, m->interface, len - buflen - 1);
9591 buflen += strlen(m->interface);
9592 /* Safeguard against overflow (negative length) */
9593 if (buflen >= len - 2) {
9594 ao2_ref(m, -1);
9595 ast_log(LOG_WARNING, "Truncating list\n");
9596 break;
9597 }
9598 ao2_ref(m, -1);
9599 }
9600 ao2_iterator_destroy(&mem_iter);
9601 ao2_unlock(q);
9602 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
9603 } else
9604 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9605
9606 /* We should already be terminated, but let's make sure. */
9607 buf[len - 1] = '\0';
9608
9609 return 0;
9610}
9611
9612/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
9613static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9614{
9615 int penalty;
9617 AST_APP_ARG(queuename);
9618 AST_APP_ARG(interface);
9619 );
9620 /* Make sure the returned value on error is NULL. */
9621 buf[0] = '\0';
9622
9623 if (ast_strlen_zero(data)) {
9624 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9625 return -1;
9626 }
9627
9629
9630 if (args.argc < 2) {
9631 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9632 return -1;
9633 }
9634
9635 penalty = get_member_penalty (args.queuename, args.interface);
9636
9637 if (penalty >= 0) { /* remember that buf is already '\0' */
9638 snprintf (buf, len, "%d", penalty);
9639 }
9640
9641 return 0;
9642}
9643
9644/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
9645static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9646{
9647 int penalty;
9649 AST_APP_ARG(queuename);
9650 AST_APP_ARG(interface);
9651 );
9652
9653 if (ast_strlen_zero(data)) {
9654 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9655 return -1;
9656 }
9657
9659
9660 if (args.argc < 2) {
9661 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9662 return -1;
9663 }
9664
9665 penalty = atoi(value);
9666
9667 if (ast_strlen_zero(args.interface)) {
9668 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
9669 return -1;
9670 }
9671
9672 /* if queuename = NULL then penalty will be set for interface in all the queues. */
9673 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, penalty)) {
9674 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
9675 return -1;
9676 }
9677
9678 return 0;
9679}
9680
9682 .name = "QUEUE_EXISTS",
9683 .read = queue_function_exists,
9684};
9685
9687 .name = "QUEUE_VARIABLES",
9688 .read = queue_function_var,
9689};
9690
9692 .name = "QUEUE_MEMBER",
9694 .write = queue_function_mem_write,
9695};
9696
9698 .name = "QUEUE_MEMBER_COUNT",
9699 .read = queue_function_qac_dep,
9700};
9701
9703 .name = "QUEUE_GET_CHANNEL",
9705};
9706
9708 .name = "QUEUE_WAITING_COUNT",
9710};
9711
9713 .name = "QUEUE_MEMBER_LIST",
9715};
9716
9718 .name = "QUEUE_MEMBER_PENALTY",
9721};
9722
9723/*! Reset the global queue rules parameters even if there is no "general" section of queuerules.conf */
9725{
9726 realtime_rules = 0;
9727}
9728
9729/*! Set the global queue rules parameters as defined in the "general" section of queuerules.conf */
9731{
9732 const char *general_val = NULL;
9733 if ((general_val = ast_variable_retrieve(cfg, "general", "realtime_rules"))) {
9734 realtime_rules = ast_true(general_val);
9735 }
9736}
9737
9738/*! \brief Reload the rules defined in queuerules.conf
9739 *
9740 * \param reload If 1, then only process queuerules.conf if the file
9741 * has changed since the last time we inspected it.
9742 * \return Always returns AST_MODULE_LOAD_SUCCESS
9743 */
9745{
9746 struct ast_config *cfg;
9747 struct rule_list *rl_iter, *new_rl;
9748 struct penalty_rule *pr_iter;
9749 char *rulecat = NULL;
9750 struct ast_variable *rulevar = NULL;
9751 struct ast_flags config_flags = { (reload && !realtime_rules) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9752
9753 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
9754 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
9756 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9757 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
9759 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
9760 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n");
9762 }
9763
9765 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
9766 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
9767 ast_free(pr_iter);
9768 ast_free(rl_iter);
9769 }
9771 while ((rulecat = ast_category_browse(cfg, rulecat))) {
9772 if (!strcasecmp(rulecat, "general")) {
9774 continue;
9775 }
9776 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
9778 ast_config_destroy(cfg);
9780 } else {
9781 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
9782 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
9783 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
9784 if(!strcasecmp(rulevar->name, "penaltychange"))
9785 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
9786 else
9787 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
9788 }
9789 }
9790
9791 ast_config_destroy(cfg);
9792
9796 }
9797
9800}
9801
9802/*! Always set the global queue defaults, even if there is no "general" section in queues.conf */
9804{
9806 autofill_default = 0;
9807 montype_default = 0;
9808 shared_lastcall = 0;
9812}
9813
9814/*! Set the global queue parameters as defined in the "general" section of queues.conf */
9815static void queue_set_global_params(struct ast_config *cfg)
9816{
9817 const char *general_val = NULL;
9818 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
9819 queue_persistent_members = ast_true(general_val);
9820 }
9821 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) {
9822 autofill_default = ast_true(general_val);
9823 }
9824 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
9825 if (!strcasecmp(general_val, "mixmonitor"))
9826 montype_default = 1;
9827 }
9828 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) {
9829 shared_lastcall = ast_true(general_val);
9830 }
9831 if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid"))) {
9832 negative_penalty_invalid = ast_true(general_val);
9833 }
9834 if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
9835 log_membername_as_agent = ast_true(general_val);
9836 }
9837 if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) {
9839 }
9840 /* Apply log-caller-id-name in the same place as other global settings */
9841 if ((general_val = ast_variable_retrieve(cfg, "general", "log-caller-id-name"))) {
9842 log_caller_id_name = ast_true(general_val);
9843 }
9844}
9845
9846/*! \brief reload information pertaining to a single member
9847 *
9848 * This function is called when a member = line is encountered in
9849 * queues.conf.
9850 *
9851 * \param memberdata The part after member = in the config file
9852 * \param q The queue to which this member belongs
9853 */
9854static void reload_single_member(const char *memberdata, struct call_queue *q)
9855{
9856 char *membername, *interface, *state_interface, *tmp;
9857 char *parse;
9858 struct member *cur, *newm;
9859 struct member tmpmem;
9860 int penalty;
9861 int ringinuse;
9862 int wrapuptime;
9863 int paused;
9872 );
9873
9874 if (ast_strlen_zero(memberdata)) {
9875 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
9876 return;
9877 }
9878
9879 /* Add a new member */
9880 parse = ast_strdupa(memberdata);
9881
9883
9884 interface = args.interface;
9885 if (!ast_strlen_zero(args.penalty)) {
9886 tmp = args.penalty;
9887 ast_strip(tmp);
9888 penalty = atoi(tmp);
9889 if (penalty < 0) {
9890 penalty = 0;
9891 }
9892 } else {
9893 penalty = 0;
9894 }
9895
9896 if (!ast_strlen_zero(args.membername)) {
9897 membername = args.membername;
9898 ast_strip(membername);
9899 } else {
9900 membername = interface;
9901 }
9902
9903 if (!ast_strlen_zero(args.state_interface)) {
9904 state_interface = args.state_interface;
9905 ast_strip(state_interface);
9906 } else {
9907 state_interface = interface;
9908 }
9909
9910 if (!ast_strlen_zero(args.ringinuse)) {
9911 tmp = args.ringinuse;
9912 ast_strip(tmp);
9913 if (ast_true(tmp)) {
9914 ringinuse = 1;
9915 } else if (ast_false(tmp)) {
9916 ringinuse = 0;
9917 } else {
9918 ast_log(LOG_ERROR, "Member %s has an invalid ringinuse value. Using %s ringinuse value.\n",
9919 membername, q->name);
9920 ringinuse = q->ringinuse;
9921 }
9922 } else {
9923 ringinuse = q->ringinuse;
9924 }
9925
9926 if (!ast_strlen_zero(args.wrapuptime)) {
9927 tmp = args.wrapuptime;
9928 ast_strip(tmp);
9929 wrapuptime = atoi(tmp);
9930 if (wrapuptime < 0) {
9931 wrapuptime = 0;
9932 }
9933 } else {
9934 wrapuptime = 0;
9935 }
9936
9937 if (!ast_strlen_zero(args.paused)) {
9938 tmp = args.paused;
9939 ast_strip(tmp);
9940 if (ast_true(tmp)) {
9941 paused = 1;
9942 } else if (ast_false(tmp)) {
9943 paused = 0;
9944 } else {
9945 ast_log(LOG_ERROR, "Member %s has an invalid paused value.\n", membername);
9946 paused = 0;
9947 }
9948 } else {
9949 paused = 0;
9950 }
9951
9952 /* Find the old position in the list */
9953 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
9954 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
9955
9956 if (cur) {
9957 paused = cur->paused;
9958 }
9959
9960 if ((newm = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
9961 newm->wrapuptime = wrapuptime;
9962 if (cur) {
9963 ao2_lock(q->members);
9964 /* Round Robin Queue Position must be copied if this is replacing an existing member */
9965 newm->queuepos = cur->queuepos;
9966 /* Don't reset agent stats either */
9967 newm->calls = cur->calls;
9968 newm->lastcall = cur->lastcall;
9969
9970 ao2_link(q->members, newm);
9971 ao2_unlink(q->members, cur);
9972 ao2_unlock(q->members);
9973 } else {
9974 /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
9975 member_add_to_queue(q, newm);
9976 }
9977 ao2_ref(newm, -1);
9978 }
9979 newm = NULL;
9980
9981 if (cur) {
9982 ao2_ref(cur, -1);
9983 }
9984}
9985
9986static int mark_member_dead(void *obj, void *arg, int flags)
9987{
9988 struct member *member = obj;
9989 if (!member->dynamic && !member->realtime) {
9990 member->delme = 1;
9991 }
9992 return 0;
9993}
9994
9995static int kill_dead_members(void *obj, void *arg, int flags)
9996{
9997 struct member *member = obj;
9998
9999 if (!member->delme) {
10001 return 0;
10002 } else {
10003 return CMP_MATCH;
10004 }
10005}
10006
10007/*! \brief Reload information pertaining to a particular queue
10008 *
10009 * Once we have isolated a queue within reload_queues, we call this. This will either
10010 * reload information for the queue or if we're just reloading member information, we'll just
10011 * reload that without touching other settings within the queue
10012 *
10013 * \param cfg The configuration which we are reading
10014 * \param mask Tells us what information we need to reload
10015 * \param queuename The name of the queue we are reloading information from
10016 */
10017static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
10018{
10019 int new;
10020 struct call_queue *q = NULL;
10021 struct member *member;
10022 /*We're defining a queue*/
10023 struct call_queue tmpq = {
10024 .name = queuename,
10025 };
10026 const char *tmpvar;
10027 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
10028 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
10029 int prev_weight = 0;
10030 struct ast_variable *var;
10031 struct ao2_iterator mem_iter;
10032
10033 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
10034 if (queue_reload) {
10035 /* Make one then */
10036 if (!(q = alloc_queue(queuename))) {
10037 return;
10038 }
10039 } else {
10040 /* Since we're not reloading queues, this means that we found a queue
10041 * in the configuration file which we don't know about yet. Just return.
10042 */
10043 return;
10044 }
10045 new = 1;
10046 } else {
10047 new = 0;
10048 }
10049
10050 if (!new) {
10051 ao2_lock(q);
10052 prev_weight = q->weight ? 1 : 0;
10053 }
10054 /* Check if we already found a queue with this name in the config file */
10055 if (q->found) {
10056 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
10057 if (!new) {
10058 /* It should be impossible to *not* hit this case*/
10059 ao2_unlock(q);
10060 }
10061 queue_t_unref(q, "We exist! Expiring temporary pointer");
10062 return;
10063 }
10064 /* Due to the fact that the "linear" strategy will have a different allocation
10065 * scheme for queue members, we must devise the queue's strategy before other initializations.
10066 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
10067 * container used will have only a single bucket instead of the typical number.
10068 */
10069 if (queue_reload) {
10070 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
10071 q->strategy = strat2int(tmpvar);
10072 if (q->strategy < 0) {
10073 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
10074 tmpvar, q->name);
10076 }
10077 } else {
10079 }
10080 init_queue(q);
10081 }
10082 if (member_reload) {
10084 q->found = 1;
10085 }
10086
10087 /* On the first pass we just read the parameters of the queue */
10088 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
10089 if (queue_reload && strcasecmp(var->name, "member")) {
10090 queue_set_param(q, var->name, var->value, var->lineno, 1);
10091 }
10092 }
10093
10094 /* On the second pass, we read members */
10095 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
10096 if (member_reload && !strcasecmp(var->name, "member")) {
10097 reload_single_member(var->value, q);
10098 }
10099 }
10100
10101 /* Update ringinuse for dynamic members */
10102 if (member_reload) {
10103 ao2_lock(q->members);
10105 while ((member = ao2_iterator_next(&mem_iter))) {
10106 if (member->dynamic) {
10108 }
10109 ao2_ref(member, -1);
10110 }
10111 ao2_iterator_destroy(&mem_iter);
10112 ao2_unlock(q->members);
10113 }
10114
10115 /* At this point, we've determined if the queue has a weight, so update use_weight
10116 * as appropriate
10117 */
10118 if (!q->weight && prev_weight) {
10120 } else if (q->weight && !prev_weight) {
10122 }
10123
10124 /* Free remaining members marked as delme */
10125 if (member_reload) {
10126 ao2_lock(q->members);
10129 ao2_unlock(q->members);
10130 }
10131
10132 if (new) {
10133 queues_t_link(queues, q, "Add queue to container");
10134 } else {
10135 ao2_unlock(q);
10136 }
10137 queue_t_unref(q, "Expiring creation reference");
10138}
10139
10140static int mark_unfound(void *obj, void *arg, int flags)
10141{
10142 struct call_queue *q = obj;
10143 char *queuename = arg;
10144 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
10145 q->found = 0;
10146 }
10147 return 0;
10148}
10149
10150static int kill_if_unfound(void *obj, void *arg, int flags)
10151{
10152 struct call_queue *q = obj;
10153 char *queuename = arg;
10154 if (!q->realtime && !q->found && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
10155 q->dead = 1;
10156 return CMP_MATCH;
10157 } else {
10158 return 0;
10159 }
10160}
10161
10162/*! \brief reload the queues.conf file
10163 *
10164 * This function reloads the information in the general section of the queues.conf
10165 * file and potentially more, depending on the value of mask.
10166 *
10167 * \param reload 0 if we are calling this the first time, 1 every other time
10168 * \param mask Gives flags telling us what information to actually reload
10169 * \param queuename If set to a non-zero string, then only reload information from
10170 * that particular queue. Otherwise inspect all queues
10171 * \retval -1 Failure occurred
10172 * \retval 0 All clear!
10173 */
10174static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
10175{
10176 struct ast_config *cfg;
10177 char *cat;
10178 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10179 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
10180
10181 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
10182 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
10183 return -1;
10184 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
10185 return 0;
10186 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
10187 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n");
10188 return -1;
10189 }
10190
10191 /* We've made it here, so it looks like we're doing operations on all queues. */
10193
10194 /* Mark non-realtime queues not found at the beginning. */
10195 ao2_callback(queues, OBJ_NODATA, mark_unfound, (char *) queuename);
10196
10197 /* Chug through config file. */
10198 cat = NULL;
10200 while ((cat = ast_category_browse(cfg, cat)) ) {
10201 if (!strcasecmp(cat, "general") && queue_reload) {
10203 continue;
10204 }
10205 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
10206 reload_single_queue(cfg, mask, cat);
10207 }
10208
10209 ast_config_destroy(cfg);
10210 if (queue_reload) {
10211 /* Unlink and mark dead all non-realtime queues that were not found in the configuration file. */
10213 }
10215 return 0;
10216}
10217
10218/*! \brief Facilitates resetting statistics for a queue
10219 *
10220 * This function actually does not reset any statistics, but
10221 * rather finds a call_queue struct which corresponds to the
10222 * passed-in queue name and passes that structure to the
10223 * clear_queue function. If no queuename is passed in, then
10224 * all queues will have their statistics reset.
10225 *
10226 * \param queuename The name of the queue to reset the statistics
10227 * for. If this is NULL or zero-length, then this means to reset
10228 * the statistics for all queues
10229 * \retval 0 always
10230 */
10231static int clear_stats(const char *queuename)
10232{
10233 struct call_queue *q;
10234 struct ao2_iterator queue_iter;
10235
10236 queue_iter = ao2_iterator_init(queues, 0);
10237 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10238 ao2_lock(q);
10239 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
10240 clear_queue(q);
10241 ao2_unlock(q);
10242 queue_t_unref(q, "Done with iterator");
10243 }
10244 ao2_iterator_destroy(&queue_iter);
10245 return 0;
10246}
10247
10248/*! \brief The command center for all reload operations
10249 *
10250 * Whenever any piece of queue information is to be reloaded, this function
10251 * is called. It interprets the flags set in the mask parameter and acts
10252 * based on how they are set.
10253 *
10254 * \param reload True if we are reloading information, false if we are loading
10255 * information for the first time.
10256 * \param mask A bitmask which tells the handler what actions to take
10257 * \param queuename The name of the queue on which we wish to take action
10258 * \retval 0 All reloads were successful
10259 * \retval non-zero There was a failure
10260 */
10261static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
10262{
10263 int res = 0;
10264
10265 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
10266 res |= reload_queue_rules(reload);
10267 }
10268 if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
10269 res |= clear_stats(queuename);
10270 }
10272 res |= reload_queues(reload, mask, queuename);
10273 }
10274 return res;
10275}
10276
10277/*! \brief direct output to manager or cli with proper terminator */
10278static void do_print(struct mansession *s, int fd, const char *str)
10279{
10280 if (s) {
10281 astman_append(s, "%s\r\n", str);
10282 } else {
10283 ast_cli(fd, "%s\n", str);
10284 }
10285}
10286
10287/*! \brief Print a single queue to AMI or the CLI */
10288static void print_queue(struct mansession *s, int fd, struct call_queue *q)
10289{
10290 float sl;
10291 float sl2;
10292 struct ao2_iterator mem_iter;
10293 struct ast_str *out = ast_str_alloca(512);
10294 time_t now = time(NULL);
10295
10296 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
10297 if (q->maxlen) {
10298 ast_str_append(&out, 0, "%d", q->maxlen);
10299 } else {
10300 ast_str_append(&out, 0, "unlimited");
10301 }
10302 sl = 0;
10303 sl2 = 0;
10304 if (q->callscompleted > 0) {
10305 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
10306 }
10307 if (q->callscompleted + q->callsabandoned > 0) {
10308 sl2 =100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted));
10309 }
10310
10311 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",
10313 do_print(s, fd, ast_str_buffer(out));
10314 if (!ao2_container_count(q->members)) {
10315 do_print(s, fd, " No Members");
10316 } else {
10317 struct member *mem;
10318
10319 do_print(s, fd, " Members: ");
10320 mem_iter = ao2_iterator_init(q->members, 0);
10321 while ((mem = ao2_iterator_next(&mem_iter))) {
10322 ast_str_set(&out, 0, " %s", mem->membername);
10323 if (strcasecmp(mem->membername, mem->interface)) {
10324 ast_str_append(&out, 0, " (%s", mem->interface);
10326 && strcmp(mem->state_interface, mem->interface)) {
10327 ast_str_append(&out, 0, " from %s", mem->state_interface);
10328 }
10329 ast_str_append(&out, 0, ")");
10330 }
10331 if (mem->penalty) {
10332 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
10333 }
10334
10335 ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
10336
10337 ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
10338 mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
10339 mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
10340 mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset());
10341
10342 if (mem->paused) {
10343 ast_str_append(&out, 0, " %s(paused%s%s was %ld secs ago)%s",
10345 ast_strlen_zero(mem->reason_paused) ? "" : ":",
10347 (long) (now - mem->lastpause),
10348 ast_term_reset());
10349 }
10350
10351 ast_str_append(&out, 0, " (%s%s%s)",
10356 if (mem->calls) {
10357 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
10358 mem->calls, (long) (now - mem->lastcall));
10359 } else {
10360 ast_str_append(&out, 0, " has taken no calls yet");
10361 }
10362 ast_str_append(&out, 0, " %s(login was %ld secs ago)%s",
10364 (long) (now - mem->logintime),
10365 ast_term_reset());
10366 do_print(s, fd, ast_str_buffer(out));
10367 ao2_ref(mem, -1);
10368 }
10369 ao2_iterator_destroy(&mem_iter);
10370 }
10371 if (!q->head) {
10372 do_print(s, fd, " No Callers");
10373 } else {
10374 struct queue_ent *qe;
10375 int pos = 1;
10376
10377 do_print(s, fd, " Callers: ");
10378 for (qe = q->head; qe; qe = qe->next) {
10379 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
10380 pos++, ast_channel_name(qe->chan), (long) (now - qe->start) / 60,
10381 (long) (now - qe->start) % 60, qe->prio);
10382 do_print(s, fd, ast_str_buffer(out));
10383 }
10384 }
10385 do_print(s, fd, ""); /* blank line between entries */
10386}
10387
10389
10390/*!
10391 * \brief Show queue(s) status and statistics
10392 *
10393 * List the queues strategy, calls processed, members logged in,
10394 * other queue statistics such as avg hold time.
10395*/
10396static char *__queues_show(struct mansession *s, int fd, int argc, const char * const *argv)
10397{
10398 struct call_queue *q;
10399 struct ast_str *out = ast_str_alloca(512);
10400 struct ao2_container *sorted_queues;
10401
10402 struct ao2_iterator queue_iter;
10403 int found = 0;
10404
10405 if (argc != 2 && argc != 3) {
10406 return CLI_SHOWUSAGE;
10407 }
10408
10409 if (argc == 3) { /* specific queue */
10410 if ((q = find_load_queue_rt_friendly(argv[2]))) {
10411 ao2_lock(q);
10412 print_queue(s, fd, q);
10413 ao2_unlock(q);
10414 queue_unref(q);
10415 } else {
10416 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
10417 do_print(s, fd, ast_str_buffer(out));
10418 }
10419 return CLI_SUCCESS;
10420 }
10421
10422 if (ast_check_realtime("queues")) {
10423 /* This block is to find any queues which are defined in realtime but
10424 * which have not yet been added to the in-core container
10425 */
10426 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
10427 if (cfg) {
10428 char *category = NULL;
10429 while ((category = ast_category_browse(cfg, category))) {
10430 const char *queuename = ast_variable_retrieve(cfg, category, "name");
10431 if (ast_strlen_zero(queuename)) {
10432 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
10433 continue;
10434 }
10435 if ((q = find_load_queue_rt_friendly(queuename))) {
10436 queue_t_unref(q, "Done with temporary pointer");
10437 }
10438 }
10439 ast_config_destroy(cfg);
10440 }
10441 }
10442
10443 /*
10444 * Snapping a copy of the container prevents having to lock both the queues container
10445 * and the queue itself at the same time. It also allows us to sort the entries.
10446 */
10447 sorted_queues = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, call_queue_sort_fn, NULL);
10448 if (!sorted_queues) {
10449 return CLI_SUCCESS;
10450 }
10451 if (ao2_container_dup(sorted_queues, queues, 0)) {
10452 ao2_ref(sorted_queues, -1);
10453 return CLI_SUCCESS;
10454 }
10455
10456 /*
10457 * No need to lock the container since it's temporary and static.
10458 * We also unlink the entries as we use them so the container is
10459 * empty when the iterator finishes. We can then just unref the container.
10460 */
10461 queue_iter = ao2_iterator_init(sorted_queues, AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK);
10462 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10463 struct call_queue *realtime_queue = NULL;
10464 ao2_lock(q);
10465 /* This check is to make sure we don't print information for realtime
10466 * queues which have been deleted from realtime but which have not yet
10467 * been deleted from the in-core container. Only do this if we're not
10468 * looking for a specific queue.
10469 */
10470 if (q->realtime) {
10471 realtime_queue = find_load_queue_rt_friendly(q->name);
10472 if (!realtime_queue) {
10473 ao2_unlock(q);
10474 queue_t_unref(q, "Done with iterator");
10475 continue;
10476 }
10477 queue_t_unref(realtime_queue, "Queue is already in memory");
10478 }
10479
10480 found = 1;
10481 print_queue(s, fd, q);
10482
10483 ao2_unlock(q);
10484 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
10485 }
10486 ao2_iterator_destroy(&queue_iter);
10487 ao2_ref(sorted_queues, -1);
10488 if (!found) {
10489 ast_str_set(&out, 0, "No queues.");
10490 do_print(s, fd, ast_str_buffer(out));
10491 }
10492 return CLI_SUCCESS;
10493}
10494
10495/*!
10496 * \brief Check if a given word is in a space-delimited list
10497 *
10498 * \param list Space delimited list of words
10499 * \param word The word used to search the list
10500 *
10501 * \note This function will not return 1 if the word is at the very end of the
10502 * list (followed immediately by a \0, not a space) since it is used for
10503 * checking tab-completion and a word at the end is still being tab-completed.
10504 *
10505 * \retval 1 if the word is found
10506 * \retval 0 if the word is not found
10507*/
10508static int word_in_list(const char *list, const char *word) {
10509 int list_len, word_len = strlen(word);
10510 const char *find, *end_find, *end_list;
10511
10512 /* strip whitespace from front */
10513 while(isspace(*list)) {
10514 list++;
10515 }
10516
10517 while((find = strstr(list, word))) {
10518 /* beginning of find starts inside another word? */
10519 if (find != list && *(find - 1) != ' ') {
10520 list = find;
10521 /* strip word from front */
10522 while(!isspace(*list) && *list != '\0') {
10523 list++;
10524 }
10525 /* strip whitespace from front */
10526 while(isspace(*list)) {
10527 list++;
10528 }
10529 continue;
10530 }
10531
10532 /* end of find ends inside another word or at very end of list? */
10533 list_len = strlen(list);
10534 end_find = find + word_len;
10535 end_list = list + list_len;
10536 if (end_find == end_list || *end_find != ' ') {
10537 list = find;
10538 /* strip word from front */
10539 while(!isspace(*list) && *list != '\0') {
10540 list++;
10541 }
10542 /* strip whitespace from front */
10543 while(isspace(*list)) {
10544 list++;
10545 }
10546 continue;
10547 }
10548
10549 /* terminating conditions satisfied, word at beginning or separated by ' ' */
10550 return 1;
10551 }
10552
10553 return 0;
10554}
10555
10556/*!
10557 * \brief Check if a given word is in a space-delimited list
10558 *
10559 * \param line The line as typed not including the current word being completed
10560 * \param word The word currently being completed
10561 * \param pos The number of completed words in line
10562 * \param state The nth desired completion option
10563 * \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.
10564 *
10565 * \return Returns the queue tab-completion for the given word and state
10566*/
10567static char *complete_queue(const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset)
10568{
10569 struct call_queue *q;
10570 char *ret = NULL;
10571 int which = 0;
10572 int wordlen = strlen(word);
10573 struct ao2_iterator queue_iter;
10574 const char *word_list = NULL;
10575
10576 /* for certain commands, already completed items should be left out of
10577 * the list */
10578 if (word_list_offset && strlen(line) >= word_list_offset) {
10579 word_list = line + word_list_offset;
10580 }
10581
10582 queue_iter = ao2_iterator_init(queues, 0);
10583 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10584 if (!strncasecmp(word, q->name, wordlen) && ++which > state
10585 && (!word_list_offset || !word_in_list(word_list, q->name))) {
10586 ret = ast_strdup(q->name);
10587 queue_t_unref(q, "Done with iterator");
10588 break;
10589 }
10590 queue_t_unref(q, "Done with iterator");
10591 }
10592 ao2_iterator_destroy(&queue_iter);
10593
10594 /* Pretend "rules" is at the end of the queues list in certain
10595 * circumstances since it is an alternate command that should be
10596 * tab-completable for "queue show" */
10597 if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
10598 ret = ast_strdup("rules");
10599 }
10600
10601 return ret;
10602}
10603
10604static char *complete_queue_show(const char *line, const char *word, int pos, int state)
10605{
10606 if (pos == 2) {
10607 return complete_queue(line, word, pos, state, 0);
10608 }
10609 return NULL;
10610}
10611
10612static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10613{
10614 switch ( cmd ) {
10615 case CLI_INIT:
10616 e->command = "queue show";
10617 e->usage =
10618 "Usage: queue show\n"
10619 " Provides summary information on a specified queue.\n";
10620 return NULL;
10621 case CLI_GENERATE:
10622 return complete_queue_show(a->line, a->word, a->pos, a->n);
10623 }
10624
10625 return __queues_show(NULL, a->fd, a->argc, a->argv);
10626}
10627
10628static int manager_queue_rule_show(struct mansession *s, const struct message *m)
10629{
10630 const char *rule = astman_get_header(m, "Rule");
10631 const char *id = astman_get_header(m, "ActionID");
10632 struct rule_list *rl_iter;
10633 struct penalty_rule *pr_iter;
10634
10635 astman_append(s, "Response: Success\r\n");
10636 if (!ast_strlen_zero(id)) {
10637 astman_append(s, "ActionID: %s\r\n", id);
10638 }
10639
10641 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
10642 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
10643 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
10644 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
10645 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 );
10646 }
10647 if (!ast_strlen_zero(rule)) {
10648 break;
10649 }
10650 }
10651 }
10653
10654 /*
10655 * Two blank lines instead of one because the Response and
10656 * ActionID headers used to not be present.
10657 */
10658 astman_append(s, "\r\n\r\n");
10659
10660 return RESULT_SUCCESS;
10661}
10662
10663/*! \brief Summary of queue info via the AMI */
10664static int manager_queues_summary(struct mansession *s, const struct message *m)
10665{
10666 time_t now;
10667 int qmemcount = 0;
10668 int qmemavail = 0;
10669 int qchancount = 0;
10670 int qlongestholdtime = 0;
10671 int qsummaries = 0;
10672 const char *id = astman_get_header(m, "ActionID");
10673 const char *queuefilter = astman_get_header(m, "Queue");
10674 char idText[256];
10675 struct call_queue *q;
10676 struct queue_ent *qe;
10677 struct member *mem;
10678 struct ao2_iterator queue_iter;
10679 struct ao2_iterator mem_iter;
10680
10681 if (ast_check_realtime("queues")) {
10682 load_realtime_queues(queuefilter);
10683 }
10684
10685 astman_send_listack(s, m, "Queue summary will follow", "start");
10686 time(&now);
10687 idText[0] = '\0';
10688 if (!ast_strlen_zero(id)) {
10689 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10690 }
10691 queue_iter = ao2_iterator_init(queues, 0);
10692 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10693 ao2_lock(q);
10694
10695 /* List queue properties */
10696 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10697 /* Reset the necessary local variables if no queuefilter is set*/
10698 qmemcount = 0;
10699 qmemavail = 0;
10700 qchancount = 0;
10701 qlongestholdtime = 0;
10702
10703 /* List Queue Members */
10704 mem_iter = ao2_iterator_init(q->members, 0);
10705 while ((mem = ao2_iterator_next(&mem_iter))) {
10706 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
10707 ++qmemcount;
10708 if (member_status_available(mem->status) && !mem->paused) {
10709 ++qmemavail;
10710 }
10711 }
10712 ao2_ref(mem, -1);
10713 }
10714 ao2_iterator_destroy(&mem_iter);
10715 for (qe = q->head; qe; qe = qe->next) {
10716 if ((now - qe->start) > qlongestholdtime) {
10717 qlongestholdtime = now - qe->start;
10718 }
10719 ++qchancount;
10720 }
10721 astman_append(s, "Event: QueueSummary\r\n"
10722 "Queue: %s\r\n"
10723 "LoggedIn: %d\r\n"
10724 "Available: %d\r\n"
10725 "Callers: %d\r\n"
10726 "HoldTime: %d\r\n"
10727 "TalkTime: %d\r\n"
10728 "LongestHoldTime: %d\r\n"
10729 "%s"
10730 "\r\n",
10731 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
10732 ++qsummaries;
10733 }
10734 ao2_unlock(q);
10735 queue_t_unref(q, "Done with iterator");
10736 }
10737 ao2_iterator_destroy(&queue_iter);
10738
10739 astman_send_list_complete_start(s, m, "QueueSummaryComplete", qsummaries);
10741
10742 return RESULT_SUCCESS;
10743}
10744
10745/*! \brief Queue status info via AMI */
10746static int manager_queues_status(struct mansession *s, const struct message *m)
10747{
10748 time_t now;
10749 int pos;
10750 int q_items = 0;
10751 const char *id = astman_get_header(m,"ActionID");
10752 const char *queuefilter = astman_get_header(m,"Queue");
10753 const char *memberfilter = astman_get_header(m,"Member");
10754 char idText[256];
10755 struct call_queue *q;
10756 struct queue_ent *qe;
10757 float sl = 0;
10758 float sl2 = 0;
10759 struct member *mem;
10760 struct ao2_iterator queue_iter;
10761 struct ao2_iterator mem_iter;
10762
10763 if (ast_check_realtime("queues")) {
10764 load_realtime_queues(queuefilter);
10765 }
10766
10767 astman_send_listack(s, m, "Queue status will follow", "start");
10768 time(&now);
10769 idText[0] = '\0';
10770 if (!ast_strlen_zero(id)) {
10771 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10772 }
10773
10774 queue_iter = ao2_iterator_init(queues, 0);
10775 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10776 ao2_lock(q);
10777
10778 /* List queue properties */
10779 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10780 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
10781 sl2 = (((q->callscompleted + q->callsabandoned) > 0) ? 100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted)) : 0);
10782
10783 astman_append(s, "Event: QueueParams\r\n"
10784 "Queue: %s\r\n"
10785 "Max: %d\r\n"
10786 "Strategy: %s\r\n"
10787 "Calls: %d\r\n"
10788 "Holdtime: %d\r\n"
10789 "TalkTime: %d\r\n"
10790 "Completed: %d\r\n"
10791 "Abandoned: %d\r\n"
10792 "ServiceLevel: %d\r\n"
10793 "ServicelevelPerf: %2.1f\r\n"
10794 "ServicelevelPerf2: %2.1f\r\n"
10795 "Weight: %d\r\n"
10796 "%s"
10797 "\r\n",
10798 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
10799 q->callsabandoned, q->servicelevel, sl, sl2, q->weight, idText);
10800 ++q_items;
10801
10802 /* List Queue Members */
10803 mem_iter = ao2_iterator_init(q->members, 0);
10804 while ((mem = ao2_iterator_next(&mem_iter))) {
10805 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
10806 astman_append(s, "Event: QueueMember\r\n"
10807 "Queue: %s\r\n"
10808 "Name: %s\r\n"
10809 "Location: %s\r\n"
10810 "StateInterface: %s\r\n"
10811 "Membership: %s\r\n"
10812 "Penalty: %d\r\n"
10813 "CallsTaken: %d\r\n"
10814 "LastCall: %d\r\n"
10815 "LastPause: %d\r\n"
10816 "LoginTime: %d\r\n"
10817 "InCall: %d\r\n"
10818 "Status: %d\r\n"
10819 "Paused: %d\r\n"
10820 "PausedReason: %s\r\n"
10821 "Wrapuptime: %d\r\n"
10822 "%s"
10823 "\r\n",
10824 q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
10825 mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, (int)mem->logintime, mem->starttime ? 1 : 0, mem->status,
10826 mem->paused, mem->reason_paused, mem->wrapuptime, idText);
10827 ++q_items;
10828 }
10829 ao2_ref(mem, -1);
10830 }
10831 ao2_iterator_destroy(&mem_iter);
10832
10833 /* List Queue Entries */
10834 pos = 1;
10835 for (qe = q->head; qe; qe = qe->next) {
10836 astman_append(s, "Event: QueueEntry\r\n"
10837 "Queue: %s\r\n"
10838 "Position: %d\r\n"
10839 "Channel: %s\r\n"
10840 "Uniqueid: %s\r\n"
10841 "CallerIDNum: %s\r\n"
10842 "CallerIDName: %s\r\n"
10843 "ConnectedLineNum: %s\r\n"
10844 "ConnectedLineName: %s\r\n"
10845 "Wait: %ld\r\n"
10846 "Priority: %d\r\n"
10847 "%s"
10848 "\r\n",
10849 q->name, pos++, ast_channel_name(qe->chan), ast_channel_uniqueid(qe->chan),
10854 (long) (now - qe->start), qe->prio, idText);
10855 ++q_items;
10856 }
10857 }
10858 ao2_unlock(q);
10859 queue_t_unref(q, "Done with iterator");
10860 }
10861 ao2_iterator_destroy(&queue_iter);
10862
10863 astman_send_list_complete_start(s, m, "QueueStatusComplete", q_items);
10865
10866 return RESULT_SUCCESS;
10867}
10868
10869static int manager_add_queue_member(struct mansession *s, const struct message *m)
10870{
10871 const char *queuename, *interface, *penalty_s, *paused_s, *reason, *membername, *state_interface, *wrapuptime_s;
10872 int paused, penalty, wrapuptime = 0;
10873
10874 queuename = astman_get_header(m, "Queue");
10875 interface = astman_get_header(m, "Interface");
10876 penalty_s = astman_get_header(m, "Penalty");
10877 paused_s = astman_get_header(m, "Paused");
10878 reason = astman_get_header(m, "Reason"); /* Optional */
10879 membername = astman_get_header(m, "MemberName");
10880 state_interface = astman_get_header(m, "StateInterface");
10881 wrapuptime_s = astman_get_header(m, "Wrapuptime");
10882
10883 if (ast_strlen_zero(queuename)) {
10884 astman_send_error(s, m, "'Queue' not specified.");
10885 return 0;
10886 }
10887
10888 if (ast_strlen_zero(interface)) {
10889 astman_send_error(s, m, "'Interface' not specified.");
10890 return 0;
10891 }
10892
10893 if (ast_strlen_zero(penalty_s)) {
10894 penalty = 0;
10895 } else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) {
10896 penalty = 0;
10897 }
10898
10899 if (ast_strlen_zero(wrapuptime_s)) {
10900 wrapuptime = 0;
10901 } else if (sscanf(wrapuptime_s, "%30d", &wrapuptime) != 1 || wrapuptime < 0) {
10902 wrapuptime = 0;
10903 }
10904
10905 if (ast_strlen_zero(paused_s)) {
10906 paused = 0;
10907 } else {
10908 paused = abs(ast_true(paused_s));
10909 }
10910
10911 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, reason, wrapuptime)) {
10912 case RES_OKAY:
10913 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
10914 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10915 } else {
10916 ast_queue_log(queuename, "MANAGER", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10917 }
10918 astman_send_ack(s, m, "Added interface to queue");
10919 break;
10920 case RES_EXISTS:
10921 astman_send_error(s, m, "Unable to add interface: Already there");
10922 break;
10923 case RES_NOSUCHQUEUE:
10924 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
10925 break;
10926 case RES_OUTOFMEMORY:
10927 astman_send_error(s, m, "Out of memory");
10928 break;
10929 }
10930
10931 return 0;
10932}
10933
10934static int manager_remove_queue_member(struct mansession *s, const struct message *m)
10935{
10936 const char *queuename, *interface;
10937 struct member *mem = NULL;
10938
10939 queuename = astman_get_header(m, "Queue");
10940 interface = astman_get_header(m, "Interface");
10941
10942 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
10943 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
10944 return 0;
10945 }
10946
10948 mem = find_member_by_queuename_and_interface(queuename, interface);
10949 }
10950
10951 switch (remove_from_queue(queuename, interface)) {
10952 case RES_OKAY:
10953 if (!mem || ast_strlen_zero(mem->membername)) {
10954 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
10955 } else {
10956 ast_queue_log(queuename, "MANAGER", mem->membername, "REMOVEMEMBER", "%s", "");
10957 }
10958 astman_send_ack(s, m, "Removed interface from queue");
10959 break;
10960 case RES_EXISTS:
10961 astman_send_error(s, m, "Unable to remove interface: Not there");
10962 break;
10963 case RES_NOSUCHQUEUE:
10964 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
10965 break;
10966 case RES_OUTOFMEMORY:
10967 astman_send_error(s, m, "Out of memory");
10968 break;
10969 case RES_NOT_DYNAMIC:
10970 astman_send_error(s, m, "Member not dynamic");
10971 break;
10972 }
10973
10974 if (mem) {
10975 ao2_ref(mem, -1);
10976 }
10977
10978 return 0;
10979}
10980
10981static int manager_pause_queue_member(struct mansession *s, const struct message *m)
10982{
10983 const char *queuename, *interface, *paused_s, *reason;
10984 int paused;
10985
10986 interface = astman_get_header(m, "Interface");
10987 paused_s = astman_get_header(m, "Paused");
10988 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
10989 reason = astman_get_header(m, "Reason"); /* Optional */
10990
10991 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
10992 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
10993 return 0;
10994 }
10995
10996 paused = abs(ast_true(paused_s));
10997
10998 if (set_member_paused(queuename, interface, reason, paused)) {
10999 astman_send_error(s, m, "Interface not found");
11000 } else {
11001 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
11002 }
11003 return 0;
11004}
11005
11006static int manager_queue_log_custom(struct mansession *s, const struct message *m)
11007{
11008 const char *queuename, *event, *message, *interface, *uniqueid;
11009
11010 queuename = astman_get_header(m, "Queue");
11011 uniqueid = astman_get_header(m, "UniqueId");
11012 interface = astman_get_header(m, "Interface");
11013 event = astman_get_header(m, "Event");
11014 message = astman_get_header(m, "Message");
11015
11016 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
11017 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
11018 return 0;
11019 }
11020
11021 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
11022 astman_send_ack(s, m, "Event added successfully");
11023
11024 return 0;
11025}
11026
11027static int manager_queue_reload(struct mansession *s, const struct message *m)
11028{
11029 struct ast_flags mask = {0,};
11030 const char *queuename = NULL;
11031 int header_found = 0;
11032
11033 queuename = astman_get_header(m, "Queue");
11034 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
11036 header_found = 1;
11037 }
11038 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
11040 header_found = 1;
11041 }
11042 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
11044 header_found = 1;
11045 }
11046
11047 if (!header_found) {
11049 }
11050
11051 if (!reload_handler(1, &mask, queuename)) {
11052 astman_send_ack(s, m, "Queue reloaded successfully");
11053 } else {
11054 astman_send_error(s, m, "Error encountered while reloading queue");
11055 }
11056 return 0;
11057}
11058
11059static int manager_queue_reset(struct mansession *s, const struct message *m)
11060{
11061 const char *queuename = NULL;
11062 struct ast_flags mask = {QUEUE_RESET_STATS,};
11063
11064 queuename = astman_get_header(m, "Queue");
11065
11066 if (!reload_handler(1, &mask, queuename)) {
11067 astman_send_ack(s, m, "Queue stats reset successfully");
11068 } else {
11069 astman_send_error(s, m, "Error encountered while resetting queue stats");
11070 }
11071 return 0;
11072}
11073
11074static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
11075{
11076 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
11077 switch (pos) {
11078 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
11079 return NULL;
11080 case 4: /* only one possible match, "to" */
11081 return state == 0 ? ast_strdup("to") : NULL;
11082 case 5: /* <queue> */
11083 return complete_queue(line, word, pos, state, 0);
11084 case 6: /* only one possible match, "penalty" */
11085 return state == 0 ? ast_strdup("penalty") : NULL;
11086 case 7:
11087 if (0 <= state && state < 100) { /* 0-99 */
11088 char *num;
11089 if ((num = ast_malloc(3))) {
11090 sprintf(num, "%d", state);
11091 }
11092 return num;
11093 } else {
11094 return NULL;
11095 }
11096 case 8: /* only one possible match, "as" */
11097 return state == 0 ? ast_strdup("as") : NULL;
11098 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
11099 return NULL;
11100 default:
11101 return NULL;
11102 }
11103}
11104
11105static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
11106{
11107 const char *queuename, *interface, *ringinuse_s;
11108 int ringinuse;
11109
11110 interface = astman_get_header(m, "Interface");
11111 ringinuse_s = astman_get_header(m, "RingInUse");
11112
11113 /* Optional - if not supplied, set the ringinuse value for the given Interface in all queues */
11114 queuename = astman_get_header(m, "Queue");
11115
11116 if (ast_strlen_zero(interface) || ast_strlen_zero(ringinuse_s)) {
11117 astman_send_error(s, m, "Need 'Interface' and 'RingInUse' parameters.");
11118 return 0;
11119 }
11120
11121 if (ast_true(ringinuse_s)) {
11122 ringinuse = 1;
11123 } else if (ast_false(ringinuse_s)) {
11124 ringinuse = 0;
11125 } else {
11126 astman_send_error(s, m, "'RingInUse' parameter must be a truth value (yes/no, on/off, 0/1, etc)");
11127 return 0;
11128 }
11129
11130 if (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11131 astman_send_error(s, m, "Invalid interface, queuename, or ringinuse value\n");
11132 } else {
11133 astman_send_ack(s, m, "Interface ringinuse set successfully");
11134 }
11135
11136 return 0;
11137}
11138
11139static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
11140{
11141 const char *queuename, *interface, *penalty_s;
11142 int penalty;
11143
11144 interface = astman_get_header(m, "Interface");
11145 penalty_s = astman_get_header(m, "Penalty");
11146 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
11147 queuename = astman_get_header(m, "Queue");
11148
11149 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
11150 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
11151 return 0;
11152 }
11153
11154 penalty = atoi(penalty_s);
11155
11156 if (set_member_value((char *)queuename, (char *)interface, MEMBER_PENALTY, penalty)) {
11157 astman_send_error(s, m, "Invalid interface, queuename or penalty");
11158 } else {
11159 astman_send_ack(s, m, "Interface penalty set successfully");
11160 }
11161
11162 return 0;
11163}
11164
11165static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
11166{
11167 const char *queuename, *caller, *priority_s, *immediate_s;
11168 int priority = 0, immediate = 0;
11169
11170 queuename = astman_get_header(m, "Queue");
11171 caller = astman_get_header(m, "Caller");
11172 priority_s = astman_get_header(m, "Priority");
11173 immediate_s = astman_get_header(m, "Immediate");
11174
11175 if (ast_strlen_zero(queuename)) {
11176 astman_send_error(s, m, "'Queue' not specified.");
11177 return 0;
11178 }
11179
11180 if (ast_strlen_zero(caller)) {
11181 astman_send_error(s, m, "'Caller' not specified.");
11182 return 0;
11183 }
11184
11185 if (ast_strlen_zero(priority_s)) {
11186 astman_send_error(s, m, "'Priority' not specified.");
11187 return 0;
11188 } else if (sscanf(priority_s, "%30d", &priority) != 1) {
11189 astman_send_error(s, m, "'Priority' need integer.");
11190 return 0;
11191 }
11192
11193 if (!ast_strlen_zero(immediate_s)) {
11194 immediate = ast_true(immediate_s);
11195 }
11196
11197 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
11198 case RES_OKAY:
11199 astman_send_ack(s, m, "Priority change for caller on queue");
11200 break;
11201 case RES_NOSUCHQUEUE:
11202 astman_send_error(s, m, "Unable to change priority caller on queue: No such queue");
11203 break;
11204 case RES_NOT_CALLER:
11205 astman_send_error(s, m, "Unable to change priority caller on queue: No such caller");
11206 break;
11207 }
11208
11209 return 0;
11210}
11211
11213{
11214 const char *queuename, *caller, *withdraw_info;
11215
11216 queuename = astman_get_header(m, "Queue");
11217 caller = astman_get_header(m, "Caller");
11218 withdraw_info = astman_get_header(m, "WithdrawInfo");
11219
11220 if (ast_strlen_zero(queuename)) {
11221 astman_send_error(s, m, "'Queue' not specified.");
11222 return 0;
11223 }
11224
11225 if (ast_strlen_zero(caller)) {
11226 astman_send_error(s, m, "'Caller' not specified.");
11227 return 0;
11228 }
11229
11230 switch (request_withdraw_caller_from_queue(queuename, caller, withdraw_info)) {
11231 case RES_OKAY:
11232 astman_send_ack(s, m, "Withdraw requested successfully");
11233 break;
11234 case RES_NOSUCHQUEUE:
11235 astman_send_error(s, m, "Unable to request withdraw from queue: No such queue");
11236 break;
11237 case RES_NOT_CALLER:
11238 astman_send_error(s, m, "Unable to request withdraw from queue: No such caller");
11239 break;
11240 case RES_EXISTS:
11241 astman_send_error(s, m, "Unable to request withdraw from queue: Already requested");
11242 break;
11243 }
11244
11245 return 0;
11246}
11247
11248
11249static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11250{
11251 const char *queuename, *interface, *membername = NULL, *state_interface = NULL, *reason = NULL;
11252 int penalty, paused = 0;
11253
11254 switch ( cmd ) {
11255 case CLI_INIT:
11256 e->command = "queue add member";
11257 e->usage =
11258 "Usage: queue add member <dial string> to <queue> [penalty <penalty> [as <membername> [state_interface <interface> [paused <reason>]]]]\n"
11259 " 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";
11260 return NULL;
11261 case CLI_GENERATE:
11262 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
11263 }
11264
11265 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12) && (a->argc != 14)) {
11266 return CLI_SHOWUSAGE;
11267 } else if (strcmp(a->argv[4], "to")) {
11268 return CLI_SHOWUSAGE;
11269 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
11270 return CLI_SHOWUSAGE;
11271 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
11272 return CLI_SHOWUSAGE;
11273 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
11274 return CLI_SHOWUSAGE;
11275 } else if ((a->argc == 14) && strcmp(a->argv[12], "paused")) {
11276 return CLI_SHOWUSAGE;
11277 }
11278
11279 queuename = a->argv[5];
11280 interface = a->argv[3];
11281 if (a->argc >= 8) {
11282 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
11283 if (penalty < 0) {
11284 ast_cli(a->fd, "Penalty must be >= 0\n");
11285 penalty = 0;
11286 }
11287 } else {
11288 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
11289 penalty = 0;
11290 }
11291 } else {
11292 penalty = 0;
11293 }
11294
11295 if (a->argc >= 10) {
11296 membername = a->argv[9];
11297 }
11298
11299 if (a->argc >= 12) {
11300 state_interface = a->argv[11];
11301 }
11302
11303 if (a->argc >= 14) {
11304 paused = 1;
11305 reason = a->argv[13];
11306 }
11307
11308 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, reason, 0)) {
11309 case RES_OKAY:
11310 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
11311 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
11312 } else {
11313 ast_queue_log(queuename, "CLI", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
11314 }
11315 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
11316 return CLI_SUCCESS;
11317 case RES_EXISTS:
11318 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
11319 return CLI_FAILURE;
11320 case RES_NOSUCHQUEUE:
11321 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
11322 return CLI_FAILURE;
11323 case RES_OUTOFMEMORY:
11324 ast_cli(a->fd, "Out of memory\n");
11325 return CLI_FAILURE;
11326 case RES_NOT_DYNAMIC:
11327 ast_cli(a->fd, "Member not dynamic\n");
11328 return CLI_FAILURE;
11329 default:
11330 return CLI_FAILURE;
11331 }
11332}
11333
11334static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
11335{
11336 int which = 0;
11337 struct call_queue *q;
11338 struct member *m;
11339 struct ao2_iterator queue_iter;
11340 struct ao2_iterator mem_iter;
11341 int wordlen = strlen(word);
11342
11343 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
11344 if (pos > 5 || pos < 3) {
11345 return NULL;
11346 }
11347 if (pos == 4) { /* only one possible match, 'from' */
11348 return (state == 0 ? ast_strdup("from") : NULL);
11349 }
11350
11351 if (pos == 5) { /* No need to duplicate code */
11352 return complete_queue(line, word, pos, state, 0);
11353 }
11354
11355 /* here is the case for 3, <member> */
11356 queue_iter = ao2_iterator_init(queues, 0);
11357 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
11358 ao2_lock(q);
11359 mem_iter = ao2_iterator_init(q->members, 0);
11360 while ((m = ao2_iterator_next(&mem_iter))) {
11361 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
11362 char *tmp;
11363 tmp = ast_strdup(m->interface);
11364 ao2_ref(m, -1);
11365 ao2_iterator_destroy(&mem_iter);
11366 ao2_unlock(q);
11367 queue_t_unref(q, "Done with iterator, returning interface name");
11368 ao2_iterator_destroy(&queue_iter);
11369 return tmp;
11370 }
11371 ao2_ref(m, -1);
11372 }
11373 ao2_iterator_destroy(&mem_iter);
11374 ao2_unlock(q);
11375 queue_t_unref(q, "Done with iterator");
11376 }
11377 ao2_iterator_destroy(&queue_iter);
11378
11379 return NULL;
11380}
11381
11382static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11383{
11384 const char *queuename, *interface;
11385 struct member *mem = NULL;
11386 char *res = CLI_FAILURE;
11387
11388 switch (cmd) {
11389 case CLI_INIT:
11390 e->command = "queue remove member";
11391 e->usage =
11392 "Usage: queue remove member <channel> from <queue>\n"
11393 " Remove a specific channel from a queue.\n";
11394 return NULL;
11395 case CLI_GENERATE:
11396 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
11397 }
11398
11399 if (a->argc != 6) {
11400 return CLI_SHOWUSAGE;
11401 } else if (strcmp(a->argv[4], "from")) {
11402 return CLI_SHOWUSAGE;
11403 }
11404
11405 queuename = a->argv[5];
11406 interface = a->argv[3];
11407
11409 mem = find_member_by_queuename_and_interface(queuename, interface);
11410 }
11411
11412 switch (remove_from_queue(queuename, interface)) {
11413 case RES_OKAY:
11414 if (!mem || ast_strlen_zero(mem->membername)) {
11415 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
11416 } else {
11417 ast_queue_log(queuename, "CLI", mem->membername, "REMOVEMEMBER", "%s", "");
11418 }
11419 ast_cli(a->fd, "Removed interface %s from queue '%s'\n", interface, queuename);
11420 res = CLI_SUCCESS;
11421 break;
11422 case RES_EXISTS:
11423 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
11424 break;
11425 case RES_NOSUCHQUEUE:
11426 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
11427 break;
11428 case RES_OUTOFMEMORY:
11429 ast_cli(a->fd, "Out of memory\n");
11430 break;
11431 case RES_NOT_DYNAMIC:
11432 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
11433 break;
11434 }
11435
11436 if (mem) {
11437 ao2_ref(mem, -1);
11438 }
11439
11440 return res;
11441}
11442
11443
11444
11445static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11446{
11447 const char *queuename, *caller;
11448 int priority, immediate = 0;
11449 char *res = CLI_FAILURE;
11450
11451 switch (cmd) {
11452 case CLI_INIT:
11453 e->command = "queue priority caller";
11454 e->usage =
11455 "Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
11456 " Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
11457 return NULL;
11458 case CLI_GENERATE:
11459 return NULL;
11460 }
11461
11462 if (a->argc < 8) {
11463 return CLI_SHOWUSAGE;
11464 } else if (strcmp(a->argv[4], "on")) {
11465 return CLI_SHOWUSAGE;
11466 } else if (strcmp(a->argv[6], "to")) {
11467 return CLI_SHOWUSAGE;
11468 } else if (sscanf(a->argv[7], "%30d", &priority) != 1) {
11469 ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
11470 return CLI_SHOWUSAGE;
11471 } else if (a->argc == 9) {
11472 if (strcmp(a->argv[8], "immediate")) {
11473 return CLI_SHOWUSAGE;
11474 }
11475 immediate = 1;
11476 }
11477
11478 caller = a->argv[3];
11479 queuename = a->argv[5];
11480
11481 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
11482 case RES_OKAY:
11483 res = CLI_SUCCESS;
11484 break;
11485 case RES_NOSUCHQUEUE:
11486 ast_cli(a->fd, "Unable change priority caller %s on queue '%s': No such queue\n", caller, queuename);
11487 break;
11488 case RES_NOT_CALLER:
11489 ast_cli(a->fd, "Unable to change priority caller '%s' on queue '%s': Not there\n", caller, queuename);
11490
11491 break;
11492 }
11493
11494 return res;
11495}
11496
11497
11498
11499static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
11500{
11501 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
11502 switch (pos) {
11503 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
11504 return NULL;
11505 case 4: /* only one possible match, "queue" */
11506 return state == 0 ? ast_strdup("queue") : NULL;
11507 case 5: /* <queue> */
11508 return complete_queue(line, word, pos, state, 0);
11509 case 6: /* "reason" */
11510 return state == 0 ? ast_strdup("reason") : NULL;
11511 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
11512 return NULL;
11513 default:
11514 return NULL;
11515 }
11516}
11517
11518static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11519{
11520 const char *queuename, *interface, *reason;
11521 int paused;
11522
11523 switch (cmd) {
11524 case CLI_INIT:
11525 e->command = "queue {pause|unpause} member";
11526 e->usage =
11527 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
11528 " Pause or unpause a queue member. Not specifying a particular queue\n"
11529 " will pause or unpause a member across all queues to which the member\n"
11530 " belongs.\n";
11531 return NULL;
11532 case CLI_GENERATE:
11533 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
11534 }
11535
11536 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
11537 return CLI_SHOWUSAGE;
11538 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
11539 return CLI_SHOWUSAGE;
11540 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
11541 return CLI_SHOWUSAGE;
11542 }
11543
11544
11545 interface = a->argv[3];
11546 queuename = a->argc >= 6 ? a->argv[5] : NULL;
11547 reason = a->argc == 8 ? a->argv[7] : NULL;
11548 paused = !strcasecmp(a->argv[1], "pause");
11549
11550 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
11551 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
11552 if (!ast_strlen_zero(queuename)) {
11553 ast_cli(a->fd, " in queue '%s'", queuename);
11554 }
11555 if (!ast_strlen_zero(reason)) {
11556 ast_cli(a->fd, " for reason '%s'", reason);
11557 }
11558 ast_cli(a->fd, "\n");
11559 return CLI_SUCCESS;
11560 } else {
11561 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
11562 if (!ast_strlen_zero(queuename)) {
11563 ast_cli(a->fd, " in queue '%s'", queuename);
11564 }
11565 if (!ast_strlen_zero(reason)) {
11566 ast_cli(a->fd, " for reason '%s'", reason);
11567 }
11568 ast_cli(a->fd, "\n");
11569 return CLI_FAILURE;
11570 }
11571}
11572
11573static char *complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
11574{
11575 /* 0 - queue; 1 - set; 2 - penalty/ringinuse; 3 - <value>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
11576 switch (pos) {
11577 case 4:
11578 if (state == 0) {
11579 return ast_strdup("on");
11580 } else {
11581 return NULL;
11582 }
11583 case 6:
11584 if (state == 0) {
11585 return ast_strdup("in");
11586 } else {
11587 return NULL;
11588 }
11589 case 7:
11590 return complete_queue(line, word, pos, state, 0);
11591 default:
11592 return NULL;
11593 }
11594}
11595
11596static char *handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11597{
11598 const char *queuename = NULL, *interface;
11599 int ringinuse;
11600
11601 switch (cmd) {
11602 case CLI_INIT:
11603 e->command = "queue set ringinuse";
11604 e->usage =
11605 "Usage: queue set ringinuse <yes/no> on <interface> [in <queue>]\n"
11606 " Set a member's ringinuse in the queue specified. If no queue is specified\n"
11607 " then that interface's penalty is set in all queues to which that interface is a member.\n";
11608 break;
11609 return NULL;
11610 case CLI_GENERATE:
11611 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11612 }
11613
11614 /* Sensible argument counts */
11615 if (a->argc != 6 && a->argc != 8) {
11616 return CLI_SHOWUSAGE;
11617 }
11618
11619 /* Uses proper indicational words */
11620 if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11621 return CLI_SHOWUSAGE;
11622 }
11623
11624 /* Set the queue name if applicable */
11625 if (a->argc == 8) {
11626 queuename = a->argv[7];
11627 }
11628
11629 /* Interface being set */
11630 interface = a->argv[5];
11631
11632 /* Check and set the ringinuse value */
11633 if (ast_true(a->argv[3])) {
11634 ringinuse = 1;
11635 } else if (ast_false(a->argv[3])) {
11636 ringinuse = 0;
11637 } else {
11638 return CLI_SHOWUSAGE;
11639 }
11640
11641 switch (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11642 case RESULT_SUCCESS:
11643 ast_cli(a->fd, "Set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11644 return CLI_SUCCESS;
11645 case RESULT_FAILURE:
11646 ast_cli(a->fd, "Failed to set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11647 return CLI_FAILURE;
11648 default:
11649 return CLI_FAILURE;
11650 }
11651}
11652
11653static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11654{
11655 const char *queuename = NULL, *interface;
11656 int penalty = 0;
11657
11658 switch (cmd) {
11659 case CLI_INIT:
11660 e->command = "queue set penalty";
11661 e->usage =
11662 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
11663 " Set a member's penalty in the queue specified. If no queue is specified\n"
11664 " then that interface's penalty is set in all queues to which that interface is a member\n";
11665 return NULL;
11666 case CLI_GENERATE:
11667 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11668 }
11669
11670 if (a->argc != 6 && a->argc != 8) {
11671 return CLI_SHOWUSAGE;
11672 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11673 return CLI_SHOWUSAGE;
11674 }
11675
11676 if (a->argc == 8) {
11677 queuename = a->argv[7];
11678 }
11679 interface = a->argv[5];
11680 penalty = atoi(a->argv[3]);
11681
11682 switch (set_member_value(queuename, interface, MEMBER_PENALTY, penalty)) {
11683 case RESULT_SUCCESS:
11684 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11685 return CLI_SUCCESS;
11686 case RESULT_FAILURE:
11687 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11688 return CLI_FAILURE;
11689 default:
11690 return CLI_FAILURE;
11691 }
11692}
11693
11694static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
11695{
11696 int which = 0;
11697 struct rule_list *rl_iter;
11698 int wordlen = strlen(word);
11699 char *ret = NULL;
11700 if (pos != 3) /* Wha? */ {
11701 return NULL;
11702 }
11703
11705 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11706 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
11707 ret = ast_strdup(rl_iter->name);
11708 break;
11709 }
11710 }
11712
11713 return ret;
11714}
11715
11716static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11717{
11718 const char *rule;
11719 struct rule_list *rl_iter;
11720 struct penalty_rule *pr_iter;
11721 switch (cmd) {
11722 case CLI_INIT:
11723 e->command = "queue show rules";
11724 e->usage =
11725 "Usage: queue show rules [rulename]\n"
11726 " Show the list of rules associated with rulename. If no\n"
11727 " rulename is specified, list all rules defined in queuerules.conf\n";
11728 return NULL;
11729 case CLI_GENERATE:
11730 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
11731 }
11732
11733 if (a->argc != 3 && a->argc != 4) {
11734 return CLI_SHOWUSAGE;
11735 }
11736
11737 rule = a->argc == 4 ? a->argv[3] : "";
11739 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11740 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
11741 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
11742 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
11743 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);
11744 }
11745 }
11746 }
11748 return CLI_SUCCESS;
11749}
11750
11751static char *handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11752{
11753 struct ast_flags mask = {QUEUE_RESET_STATS,};
11754 int i;
11755
11756 switch (cmd) {
11757 case CLI_INIT:
11758 e->command = "queue reset stats";
11759 e->usage =
11760 "Usage: queue reset stats [<queuenames>]\n"
11761 "\n"
11762 "Issuing this command will reset statistics for\n"
11763 "<queuenames>, or for all queues if no queue is\n"
11764 "specified.\n";
11765 return NULL;
11766 case CLI_GENERATE:
11767 if (a->pos >= 3) {
11768 return complete_queue(a->line, a->word, a->pos, a->n, 17);
11769 } else {
11770 return NULL;
11771 }
11772 }
11773
11774 if (a->argc < 3) {
11775 return CLI_SHOWUSAGE;
11776 }
11777
11778 if (a->argc == 3) {
11779 reload_handler(1, &mask, NULL);
11780 return CLI_SUCCESS;
11781 }
11782
11783 for (i = 3; i < a->argc; ++i) {
11784 reload_handler(1, &mask, a->argv[i]);
11785 }
11786
11787 return CLI_SUCCESS;
11788}
11789
11790static char *handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11791{
11792 struct ast_flags mask = {0,};
11793 int i;
11794
11795 switch (cmd) {
11796 case CLI_INIT:
11797 e->command = "queue reload {parameters|members|rules|all}";
11798 e->usage =
11799 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
11800 "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
11801 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
11802 "specified in order to know what information to reload. Below is an explanation\n"
11803 "of each of these qualifiers.\n"
11804 "\n"
11805 "\t'members' - reload queue members from queues.conf\n"
11806 "\t'parameters' - reload all queue options except for queue members\n"
11807 "\t'rules' - reload the queuerules.conf file\n"
11808 "\t'all' - reload queue rules, parameters, and members\n"
11809 "\n"
11810 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
11811 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
11812 "one queue is specified when using this command, reloading queue rules may cause\n"
11813 "other queues to be affected\n";
11814 return NULL;
11815 case CLI_GENERATE:
11816 if (a->pos >= 3) {
11817 /* find the point at which the list of queue names starts */
11818 const char *command_end = a->line + strlen("queue reload ");
11819 command_end = strchr(command_end, ' ');
11820 if (!command_end) {
11821 command_end = a->line + strlen(a->line);
11822 }
11823 return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
11824 } else {
11825 return NULL;
11826 }
11827 }
11828
11829 if (a->argc < 3)
11830 return CLI_SHOWUSAGE;
11831
11832 if (!strcasecmp(a->argv[2], "rules")) {
11834 } else if (!strcasecmp(a->argv[2], "members")) {
11836 } else if (!strcasecmp(a->argv[2], "parameters")) {
11838 } else if (!strcasecmp(a->argv[2], "all")) {
11840 }
11841
11842 if (a->argc == 3) {
11843 reload_handler(1, &mask, NULL);
11844 return CLI_SUCCESS;
11845 }
11846
11847 for (i = 3; i < a->argc; ++i) {
11848 reload_handler(1, &mask, a->argv[i]);
11849 }
11850
11851 return CLI_SUCCESS;
11852}
11853
11854/*!
11855 * \brief Update Queue with data of an outgoing call
11856*/
11857static int qupd_exec(struct ast_channel *chan, const char *data)
11858{
11859 int oldtalktime;
11860 char *parse;
11861 struct call_queue *q;
11862 struct member *mem;
11863 int newtalktime = 0;
11864
11866 AST_APP_ARG(queuename);
11867 AST_APP_ARG(uniqueid);
11868 AST_APP_ARG(agent);
11870 AST_APP_ARG(talktime);
11871 AST_APP_ARG(params););
11872
11873 if (ast_strlen_zero(data)) {
11874 ast_log(LOG_WARNING, "QueueUpdate requires arguments (queuename,uniqueid,agent,status,talktime,params[totaltime,callednumber])\n");
11875 return -1;
11876 }
11877
11878 parse = ast_strdupa(data);
11879
11881
11882 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) || ast_strlen_zero(args.agent) || ast_strlen_zero(args.status)) {
11883 ast_log(LOG_WARNING, "Missing argument to QueueUpdate (queuename,uniqueid,agent,status,talktime,params[totaltime|callednumber])\n");
11884 return -1;
11885 }
11886
11887 if (!ast_strlen_zero(args.talktime)) {
11888 newtalktime = atoi(args.talktime);
11889 }
11890
11891 q = find_load_queue_rt_friendly(args.queuename);
11892 if (!q) {
11893 ast_log(LOG_WARNING, "QueueUpdate could not find requested queue '%s'\n", args.queuename);
11894 return 0;
11895 }
11896
11897 ao2_lock(q);
11898 if (q->members) {
11899 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
11900 while ((mem = ao2_iterator_next(&mem_iter))) {
11901 if (!strcasecmp(mem->membername, args.agent)) {
11902 if (!strcasecmp(args.status, "ANSWER")) {
11903 oldtalktime = q->talktime;
11904 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
11905 time(&mem->lastcall);
11906 mem->calls++;
11907 mem->lastqueue = q;
11908 q->callscompleted++;
11909
11910 if (newtalktime <= q->servicelevel) {
11911 q->callscompletedinsl++;
11912 }
11913 } else {
11914
11915 time(&mem->lastcall);
11916 q->callsabandoned++;
11917 }
11918
11919 ast_queue_log(args.queuename, args.uniqueid, args.agent, "OUTCALL", "%s|%s|%s", args.status, args.talktime, args.params);
11920 }
11921
11922 ao2_ref(mem, -1);
11923 }
11924
11925 ao2_iterator_destroy(&mem_iter);
11926 }
11927
11928 ao2_unlock(q);
11929 queue_t_unref(q, "Done with temporary pointer");
11930
11931 return 0;
11932}
11933
11934static struct ast_cli_entry cli_queue[] = {
11935 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
11936 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
11937 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
11938 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
11939 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
11940 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
11941 AST_CLI_DEFINE(handle_queue_set_member_ringinuse, "Set ringinuse for a channel of a specified queue"),
11942 AST_CLI_DEFINE(handle_queue_reload, "Reload queues, members, queue rules, or parameters"),
11943 AST_CLI_DEFINE(handle_queue_reset, "Reset statistics for a queue"),
11944 AST_CLI_DEFINE(handle_queue_change_priority_caller, "Change priority caller on queue"),
11945};
11946
11949
11950static int unload_module(void)
11951{
11954
11956
11957 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_join_type);
11958 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_leave_type);
11959 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_abandon_type);
11960
11961 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_status_type);
11962 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_added_type);
11963 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_removed_type);
11964 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_pause_type);
11965 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_penalty_type);
11966 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_ringinuse_type);
11967
11968 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_called_type);
11969 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_connect_type);
11970 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_complete_type);
11971 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_dump_type);
11972 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_ringnoanswer_type);
11973
11975 ast_manager_unregister("QueueStatus");
11976 ast_manager_unregister("QueueRule");
11977 ast_manager_unregister("QueueSummary");
11978 ast_manager_unregister("QueueAdd");
11979 ast_manager_unregister("QueueRemove");
11980 ast_manager_unregister("QueuePause");
11981 ast_manager_unregister("QueueLog");
11982 ast_manager_unregister("QueueUpdate");
11983 ast_manager_unregister("QueuePenalty");
11984 ast_manager_unregister("QueueReload");
11985 ast_manager_unregister("QueueReset");
11986 ast_manager_unregister("QueueMemberRingInUse");
11987 ast_manager_unregister("QueueChangePriorityCaller");
11988 ast_manager_unregister("QueueWithdrawCaller");
12004
12006
12007 ast_unload_realtime("queue_members");
12010
12011 queues = NULL;
12012 return 0;
12013}
12014
12015/*!
12016 * \brief Load the module
12017 *
12018 * Module loading including tests for configuration or dependencies.
12019 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
12020 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
12021 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
12022 * configuration file or other non-critical problem return
12023 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
12024 */
12025static int load_module(void)
12026{
12027 int err = 0;
12028 struct ast_flags mask = {AST_FLAGS_ALL, };
12029 struct ast_config *member_config;
12030 struct stasis_topic *queue_topic;
12032
12035 if (!queues) {
12037 }
12038
12041 if (!pending_members) {
12042 unload_module();
12044 }
12045
12046 use_weight = 0;
12047
12048 if (reload_handler(0, &mask, NULL)) {
12049 unload_module();
12051 }
12052
12053 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, "reason_paused", RQ_CHAR, 80, SENTINEL);
12054
12055 /*
12056 * This section is used to determine which name for 'ringinuse' to use in realtime members
12057 * Necessary for supporting older setups.
12058 *
12059 * It also checks if 'reason_paused' exists in the realtime backend
12060 */
12061 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
12062 if (!member_config) {
12063 realtime_ringinuse_field = "ringinuse";
12064 } else {
12065 const char *config_val;
12066
12067 if ((config_val = ast_variable_retrieve(member_config, NULL, "ringinuse"))) {
12068 ast_log(LOG_NOTICE, "ringinuse field entries found in queue_members table. Using 'ringinuse'\n");
12069 realtime_ringinuse_field = "ringinuse";
12070 } else if ((config_val = ast_variable_retrieve(member_config, NULL, "ignorebusy"))) {
12071 ast_log(LOG_NOTICE, "ignorebusy field found in queue_members table with no ringinuse field. Using 'ignorebusy'\n");
12072 realtime_ringinuse_field = "ignorebusy";
12073 } else {
12074 ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
12075 realtime_ringinuse_field = "ringinuse";
12076 }
12077
12078 if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
12080 }
12081 }
12082 ast_config_destroy(member_config);
12083
12086 }
12087
12096 err |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
12097 err |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
12104 err |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
12105 err |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
12106 err |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
12107 err |= ast_manager_register_xml("QueueChangePriorityCaller", 0, manager_change_priority_caller_on_queue);
12117
12118 /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
12120 if (!device_state_sub) {
12121 err = -1;
12122 }
12125
12127 queue_topic = ast_queue_topic_all();
12128 if (!manager_topic || !queue_topic) {
12129 unload_module();
12131 }
12133 if (!topic_forwarder) {
12134 unload_module();
12136 }
12137
12140 unload_module();
12142 }
12144 if (!agent_router) {
12145 unload_module();
12147 }
12151 NULL);
12155 NULL);
12156
12157 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_join_type);
12158 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_leave_type);
12159 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_abandon_type);
12160
12161 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_status_type);
12162 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_added_type);
12163 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_removed_type);
12164 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_pause_type);
12165 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_penalty_type);
12166 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_ringinuse_type);
12167
12168 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_called_type);
12169 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_connect_type);
12170 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_complete_type);
12171 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_dump_type);
12172 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_ringnoanswer_type);
12173
12174 if (err) {
12175 unload_module();
12177 }
12179}
12180
12181static int reload(void)
12182{
12183 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
12184 ast_unload_realtime("queue_members");
12185 reload_handler(1, &mask, NULL);
12186 return 0;
12187}
12188
12189/*!
12190 * \brief Find a member by looking up queuename and interface.
12191 * \return member or NULL if member not found.
12192 */
12193static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface)
12194{
12195 struct member *mem = NULL;
12196 struct call_queue *q;
12197
12198 if ((q = find_load_queue_rt_friendly(queuename))) {
12199 ao2_lock(q);
12200 mem = ao2_find(q->members, interface, OBJ_KEY);
12201 ao2_unlock(q);
12202 queue_t_unref(q, "Expiring temporary reference.");
12203 }
12204 return mem;
12205}
12206
12208 .support_level = AST_MODULE_SUPPORT_CORE,
12209 .load = load_module,
12210 .unload = unload_module,
12211 .reload = reload,
12212 .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:9686
static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6568
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:3042
static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:3754
static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
Definition: app_queue.c:4768
static void load_realtime_queues(const char *queuename)
Definition: app_queue.c:4148
static struct member * interface_exists(struct call_queue *q, const char *interface)
Definition: app_queue.c:7650
static int is_our_turn(struct queue_ent *qe)
Check if we should start attempting to call queue members.
Definition: app_queue.c:5937
static void record_abandoned(struct queue_ent *qe)
Record that a caller gave up on waiting in queue.
Definition: app_queue.c:5271
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:9243
static char * queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:10612
static int get_wrapuptime(struct call_queue *q, struct member *member)
Return wrapuptime.
Definition: app_queue.c:2163
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:3770
static int context_included(const char *parent, const char *child)
Returns if one context includes another context.
Definition: app_queue.c:2954
static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
Set variables of queue.
Definition: app_queue.c:2235
static int manager_queue_reset(struct mansession *s, const struct message *m)
Definition: app_queue.c:11059
static struct ast_manager_event_blob * queue_member_ringinuse_to_ami(struct stasis_message *message)
Definition: app_queue.c:2371
static struct ast_manager_event_blob * queue_member_penalty_to_ami(struct stasis_message *message)
Definition: app_queue.c:2366
member_properties
Definition: app_queue.c:1936
@ MEMBER_RINGINUSE
Definition: app_queue.c:1938
@ MEMBER_PENALTY
Definition: app_queue.c:1937
#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:6965
#define ANNOUNCEPOSITION_MORE_THAN
Definition: app_queue.c:1960
static int pending_members_cmp(void *obj, void *arg, int flags)
Definition: app_queue.c:2713
static void queue_reset_global_params(void)
Definition: app_queue.c:9803
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:11694
static void dump_queue_members(struct call_queue *pm_queue)
Dump all members in a specific queue to the database.
Definition: app_queue.c:7677
#define QUEUE_UNPAUSED_DEVSTATE
Definition: app_queue.c:3729
static struct ast_custom_function queuemembercount_function
Definition: app_queue.c:9691
static struct ast_custom_function queuewaitingcount_function
Definition: app_queue.c:9707
static int play_file(struct ast_channel *chan, const char *filename)
Definition: app_queue.c:4335
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:9986
static char * app_pqm
Definition: app_queue.c:1745
static struct ast_custom_function queuememberlist_function
Definition: app_queue.c:9712
static struct ast_manager_event_blob * queue_channel_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2287
static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
Definition: app_queue.c:8140
static char * realtime_ringinuse_field
name of the ringinuse field in the realtime database
Definition: app_queue.c:1790
static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6715
static int remove_from_queue(const char *queuename, const char *interface)
Remove member from queue.
Definition: app_queue.c:7732
static int manager_add_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10869
#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:3481
static int aqm_exec(struct ast_channel *chan, const char *data)
AddQueueMember application.
Definition: app_queue.c:8546
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
sets the QUEUESTATUS channel variable
Definition: app_queue.c:2082
static struct ast_manager_event_blob * queue_member_pause_to_ami(struct stasis_message *message)
Definition: app_queue.c:2361
static void leave_queue(struct queue_ent *qe)
Caller leaving queue.
Definition: app_queue.c:4564
#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:10261
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:9201
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:9613
static char * complete_queue_pause_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11499
static void do_hang(struct callattempt *o)
common hangup actions
Definition: app_queue.c:4819
static int set_member_value(const char *queuename, const char *interface, int property, int value)
Definition: app_queue.c:8192
static void reload_single_member(const char *memberdata, struct call_queue *q)
reload information pertaining to a single member
Definition: app_queue.c:9854
static struct ast_manager_event_blob * queue_agent_ringnoanswer_to_ami(struct stasis_message *message)
Definition: app_queue.c:2456
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:6321
static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
reload the queues.conf file
Definition: app_queue.c:10174
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
Playback announcement to queued members if period has elapsed.
Definition: app_queue.c:5210
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:10017
static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
Definition: app_queue.c:7097
static struct ast_manager_event_blob * queue_member_removed_to_ami(struct stasis_message *message)
Definition: app_queue.c:2356
#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:8439
static void clear_queue(struct call_queue *q)
Definition: app_queue.c:3228
static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle an attended transfer event.
Definition: app_queue.c:6660
static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
Definition: app_queue.c:4541
#define RES_NOT_DYNAMIC
Definition: app_queue.c:1736
static int compare_weight(struct call_queue *rq, struct member *member)
Definition: app_queue.c:4734
static char * handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11518
static int set_member_ringinuse_help_members(struct call_queue *q, const char *interface, int ringinuse)
Definition: app_queue.c:8153
static void queue_agent_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6352
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:9565
static char * app_ql
Definition: app_queue.c:1749
static void queue_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9815
static void reload_queue_members(void)
Reload dynamic queue members persisted into the astdb.
Definition: app_queue.c:8292
static int rqm_exec(struct ast_channel *chan, const char *data)
RemoveQueueMember application.
Definition: app_queue.c:8475
static int valid_exit(struct queue_ent *qe, char digit)
Check for valid exit from queue via goto.
Definition: app_queue.c:4364
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:2783
#define QUEUE_PAUSED_DEVSTATE
Definition: app_queue.c:3728
#define ANNOUNCEPOSITION_NO
Definition: app_queue.c:1959
static char * handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11653
static void handle_masquerade(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6906
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:6076
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:10288
char * text
Definition: app_queue.c:1809
#define MAX_CALL_ATTEMPT_BUCKETS
Definition: app_queue.c:2691
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:8674
static int manager_queue_rule_show(struct mansession *s, const struct message *m)
Definition: app_queue.c:10628
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:7914
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
Definition: app_queue.c:8044
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:7838
static struct ao2_container * queues
Definition: app_queue.c:2073
static int negative_penalty_invalid
queues.conf [general] option
Definition: app_queue.c:1778
#define AST_MAX_WATCHERS
Definition: app_queue.c:5383
static int load_realtime_rules(void)
Load queue rules from realtime.
Definition: app_queue.c:3369
static void queue_rules_reset_global_params(void)
Definition: app_queue.c:9724
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
Definition: app_queue.c:6164
static void escape_and_substitute(struct ast_channel *chan, const char *input, char *output, size_t size)
Definition: app_queue.c:7066
#define RES_OUTOFMEMORY
Definition: app_queue.c:1734
static int member_hash_fn(const void *obj, const int flags)
Definition: app_queue.c:3097
static int member_cmp_fn(void *obj1, void *obj2, int flags)
Definition: app_queue.c:3113
#define queues_t_unlink(c, q, tag)
Definition: app_queue.c:2232
#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:4661
static void queue_stasis_data_destructor(void *obj)
Definition: app_queue.c:6451
static struct ast_manager_event_blob * queue_member_added_to_ami(struct stasis_message *message)
Definition: app_queue.c:2351
static struct ast_manager_event_blob * queue_agent_dump_to_ami(struct stasis_message *message)
Definition: app_queue.c:2451
static int extensionstate2devicestate(int state)
Helper function which converts from extension state to device state values.
Definition: app_queue.c:2905
#define queues_t_link(c, q, tag)
Definition: app_queue.c:2231
static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
Definition: app_queue.c:2983
static int manager_queue_reload(struct mansession *s, const struct message *m)
Definition: app_queue.c:11027
static int get_member_penalty(char *queuename, char *interface)
Gets members penalty.
Definition: app_queue.c:8261
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:6734
static int pqm_exec(struct ast_channel *chan, const char *data)
PauseQueueMember application.
Definition: app_queue.c:8403
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:10567
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate, int raise_respect_min)
Check if members are available.
Definition: app_queue.c:2594
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:6236
static char * handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11751
static int qupd_exec(struct ast_channel *chan, const char *data)
Update Queue with data of an outgoing call.
Definition: app_queue.c:11857
static char * complete_queue_show(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:10604
static char * handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11596
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:9157
static int reload_queue_rules(int reload)
Reload the rules defined in queuerules.conf.
Definition: app_queue.c:9744
static char * complete_queue_add_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11074
static char * handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11445
static char * handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11716
static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
Definition: app_queue.c:11105
static char * complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11573
@ 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:4849
static const char * int2strat(int strategy)
Definition: app_queue.c:2094
#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:3888
static int publish_queue_member_pause(struct call_queue *q, struct member *member)
Definition: app_queue.c:7953
static void callattempt_free(struct callattempt *doomed)
Definition: app_queue.c:4639
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:5186
#define queue_t_unref(q, tag)
Definition: app_queue.c:2230
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:5397
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:2752
static char * app_qupd
Definition: app_queue.c:1751
#define queue_unref(q)
Definition: app_queue.c:2228
#define ANNOUNCEHOLDTIME_ALWAYS
Definition: app_queue.c:1942
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:2512
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:11139
#define queue_t_ref(q, tag)
Definition: app_queue.c:2229
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:10396
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:3524
#define ANNOUNCEPOSITION_LIMIT
Definition: app_queue.c:1961
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:10231
static struct ast_custom_function queuememberpenalty_function
Definition: app_queue.c:9717
static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
Definition: app_queue.c:7977
static int queue_cmp_cb(void *obj, void *arg, int flags)
Definition: app_queue.c:2150
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:11212
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:8169
static int queue_hash_cb(const void *obj, const int flags)
Definition: app_queue.c:2143
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:3943
static const char *const pm_family
Persistent Members astdb family.
Definition: app_queue.c:1754
#define queue_ref(q)
Definition: app_queue.c:2227
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:9408
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:5986
static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
Definition: app_queue.c:5367
static struct ast_manager_event_blob * queue_agent_called_to_ami(struct stasis_message *message)
Definition: app_queue.c:2436
static int queue_exec(struct ast_channel *chan, const char *data)
The starting point for all queue calls.
Definition: app_queue.c:8717
static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
Definition: app_queue.c:4251
static char * handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11382
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:5302
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:3032
static void init_queue(struct call_queue *q)
Initialize Queue default values.
Definition: app_queue.c:3126
static struct ao2_container * pending_members
Definition: app_queue.c:2690
static struct ast_manager_event_blob * queue_multi_channel_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2395
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:12193
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:2346
static int kill_dead_members(void *obj, void *arg, int flags)
Definition: app_queue.c:9995
#define DEFAULT_TIMEOUT
Definition: app_queue.c:1721
static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
Definition: app_queue.c:6468
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:9528
static int pending_members_hash(const void *obj, const int flags)
Definition: app_queue.c:2693
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:10981
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:2261
#define ANNOUNCEHOLDTIME_ONCE
Definition: app_queue.c:1943
static struct ast_manager_event_blob * queue_caller_join_to_ami(struct stasis_message *message)
Definition: app_queue.c:2306
static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
Definition: app_queue.c:2196
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
Change queue penalty by adding rule.
Definition: app_queue.c:3260
static char * handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11790
static int num_available_members(struct call_queue *q)
Get the number of members available to accept a call.
Definition: app_queue.c:4701
static int ql_exec(struct ast_channel *chan, const char *data)
QueueLog application.
Definition: app_queue.c:8640
static int manager_remove_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10934
static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
Definition: app_queue.c:4176
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:5162
static struct stasis_message_router * agent_router
Definition: app_queue.c:11947
static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:2214
static struct member * get_interface_helper(struct call_queue *q, const char *interface)
Definition: app_queue.c:9220
static int mark_unfound(void *obj, void *arg, int flags)
Definition: app_queue.c:10140
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:2818
static int load_module(void)
Load the module.
Definition: app_queue.c:12025
static void member_add_to_queue(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:3738
#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:4945
static void pending_members_remove(struct member *mem)
Definition: app_queue.c:2741
static void end_bridge_callback(void *data)
Definition: app_queue.c:7026
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:10664
static struct call_queue * alloc_queue(const char *queuename)
Definition: app_queue.c:3919
static struct callattempt * find_best(struct callattempt *outgoing)
find the entry with the best metric, or NULL
Definition: app_queue.c:5077
static int get_queue_member_status(struct member *cur)
Return the current state of a member.
Definition: app_queue.c:3027
static int unload_module(void)
Definition: app_queue.c:11950
static int reload(void)
Definition: app_queue.c:12181
#define QUEUE_UNKNOWN_PAUSED_DEVSTATE
Definition: app_queue.c:3730
static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
Definition: app_queue.c:4649
static int manager_queues_status(struct mansession *s, const struct message *m)
Queue status info via AMI.
Definition: app_queue.c:10746
static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6785
static int say_position(struct queue_ent *qe, int ringing)
Definition: app_queue.c:4399
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:6483
static void update_realtime_members(struct call_queue *q)
Definition: app_queue.c:4192
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:9645
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:2477
static struct ast_custom_function queuemembercount_dep
Definition: app_queue.c:9697
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:7785
static struct ast_manager_event_blob * queue_agent_connect_to_ami(struct stasis_message *message)
Definition: app_queue.c:2441
static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
Definition: app_queue.c:7019
empty_conditions
Definition: app_queue.c:1925
@ QUEUE_EMPTY_INVALID
Definition: app_queue.c:1931
@ QUEUE_EMPTY_UNKNOWN
Definition: app_queue.c:1932
@ QUEUE_EMPTY_PENALTY
Definition: app_queue.c:1926
@ QUEUE_EMPTY_RINGING
Definition: app_queue.c:1929
@ QUEUE_EMPTY_INUSE
Definition: app_queue.c:1928
@ QUEUE_EMPTY_UNAVAILABLE
Definition: app_queue.c:1930
@ QUEUE_EMPTY_WRAPUP
Definition: app_queue.c:1933
@ QUEUE_EMPTY_PAUSED
Definition: app_queue.c:1927
static struct ast_json * queue_member_blob_create(struct call_queue *q, struct member *mem)
Definition: app_queue.c:2567
static void queue_channel_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6940
static int use_weight
Records that one or more queues use weight.
Definition: app_queue.c:1760
#define ANNOUNCEPOSITION_YES
Definition: app_queue.c:1958
static int wait_a_bit(struct queue_ent *qe)
Definition: app_queue.c:7637
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:3904
static int kill_if_unfound(void *obj, void *arg, int flags)
Definition: app_queue.c:10150
static void handle_hangup(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6849
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:10278
static char * complete_queue_remove_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11334
static char * handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11249
#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:9345
static void queue_rules_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9730
static int member_status_available(int status)
Definition: app_queue.c:4835
static int queue_member_decrement_followers(void *obj, void *arg, int flag)
Definition: app_queue.c:2177
static struct ast_cli_entry cli_queue[]
Definition: app_queue.c:11934
@ 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:4087
static struct ast_custom_function queuegetchannel_function
Definition: app_queue.c:9702
static struct ast_manager_event_blob * queue_caller_abandon_to_ami(struct stasis_message *message)
Definition: app_queue.c:2316
static struct stasis_forward * topic_forwarder
Definition: app_queue.c:11948
static struct ast_custom_function queueexists_function
Definition: app_queue.c:9681
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:2107
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:9449
static struct ast_manager_event_blob * queue_agent_complete_to_ami(struct stasis_message *message)
Definition: app_queue.c:2446
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:7049
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:10508
static void log_attended_transfer(struct queue_stasis_data *queue_data, struct ast_attended_transfer_message *atxfer_msg)
Definition: app_queue.c:6522
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
Place a call to a queue member.
Definition: app_queue.c:5102
@ 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:6601
static const struct autopause autopausesmodes[]
agent_complete_reason
Definition: app_queue.c:6314
@ AGENT
Definition: app_queue.c:6316
@ CALLER
Definition: app_queue.c:6315
@ TRANSFER
Definition: app_queue.c:6317
static struct ast_manager_event_blob * queue_member_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2331
@ 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:2120
static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
Definition: app_queue.c:11165
static int compress_char(const char c)
Definition: app_queue.c:3087
static void queue_publish_member_blob(struct stasis_message_type *type, struct ast_json *blob)
Definition: app_queue.c:2543
static int manager_queue_log_custom(struct mansession *s, const struct message *m)
Definition: app_queue.c:11006
static struct ast_manager_event_blob * queue_caller_leave_to_ami(struct stasis_message *message)
Definition: app_queue.c:2311
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:7170
static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
Definition: app_queue.c:8104
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:13574
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:3143
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
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:2089
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Definition: channel.c:6416
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:10339
void ast_party_number_init(struct ast_party_number *init)
Initialize the given number structure.
Definition: channel.c:1611
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2510
@ AST_CHANNEL_REQUESTOR_BRIDGE_PEER
Definition: channel.h:1525
int ast_party_id_presentation(const struct ast_party_id *id)
Determine the overall presentation value for the given party.
Definition: channel.c:1788
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2039
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:7341
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:3125
#define ast_channel_lock(chan)
Definition: channel.h:2972
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
Make the frame formats of two channels compatible.
Definition: channel.c:6675
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:1998
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:2021
int ast_channel_priority(const struct ast_channel *chan)
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2979
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:2337
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:6389
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:4210
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:9094
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:1966
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:7303
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:10384
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:6731
@ AST_FEATURE_AUTOMIXMON
Definition: channel.h:1089
@ AST_FEATURE_REDIRECT
Definition: channel.h:1084
@ AST_FEATURE_PARKCALL
Definition: channel.h:1088
@ AST_FEATURE_AUTOMON
Definition: channel.h:1087
@ AST_FEATURE_DISCONNECT
Definition: channel.h:1085
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition: channel.c:1397
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:8786
int ast_channel_supports_html(struct ast_channel *channel)
Checks for HTML support on a channel.
Definition: channel.c:6578
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:444
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:4608
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:10285
#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:1658
#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:1989
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:6590
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:2146
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:8294
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:6394
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:6399
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:7391
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:2012
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
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:4230
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1541
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:6309
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2973
#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:2102
#define MAX_MUSICCLASS
Definition: channel.h:175
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
size_t current
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:7699
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
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
@ CONFIG_FLAG_FILEUNCHANGED
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:611
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:764
int errno
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h: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:10239
#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:7735
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7745
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:8669
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:1562
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8800
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:8236
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
struct ast_channel_snapshot * links[2]
struct ast_bridge_channel_snapshot_pair to_transfer_target
union ast_attended_transfer_message::@289 dest
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.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@231 data
ast_include: include= support in extensions.conf
Definition: pbx_include.c:37
struct ast_json * json
Definition: json.h:1083
Abstract JSON element (object, array, string, int, ...).
Struct containing info for an AMI event to send out.
Definition: manager.h: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
char * str
Subscriber phone number (Malloced)
Definition: channel.h:388
struct ast_party_dialed::@213 number
Dialed/Called number.
int transit_network_select
Transit Network Select.
Definition: channel.h:399
struct ast_party_name name
Subscriber name.
Definition: channel.h:342
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:344
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:281
char * str
Subscriber name (Malloced)
Definition: channel.h:266
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:299
char * str
Subscriber phone number (Malloced)
Definition: channel.h:293
Redirecting Line information. RDNIS (Redirecting Directory Number Information Service) Where a call d...
Definition: channel.h:524
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
Definition: channel.h:529
Support for dynamic strings.
Definition: strings.h:623
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
int autopause
Definition: app_queue.c:1712
const char * name
Definition: app_queue.c:1713
unsigned int autopauseunavail
Definition: app_queue.c:2021
int talktime
Definition: app_queue.c:2033
const ast_string_field sound_thereare
Definition: app_queue.c:2001
unsigned int setinterfacevar
Definition: app_queue.c:2007
struct call_queue::@53 list
struct ast_str * sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS]
Definition: app_queue.c:2003
const ast_string_field sound_callerannounce
Definition: app_queue.c:2001
int announcefrequency
Definition: app_queue.c:2025
const ast_string_field sound_reporthold
Definition: app_queue.c:2001
unsigned int announceholdtime
Definition: app_queue.c:2013
const ast_string_field sound_holdtime
Definition: app_queue.c:2001
unsigned int dead
Definition: app_queue.c:2004
unsigned int reportholdtime
Definition: app_queue.c:2010
unsigned int setqueueentryvar
Definition: app_queue.c:2009
const ast_string_field sound_seconds
Definition: app_queue.c:2001
unsigned int timeoutrestart
Definition: app_queue.c:2012
struct ao2_container * members
Definition: app_queue.c:2059
int periodicannouncefrequency
Definition: app_queue.c:2028
const ast_string_field sound_thanks
Definition: app_queue.c:2001
unsigned int announceposition_only_up
Definition: app_queue.c:2015
unsigned int setqueuevar
Definition: app_queue.c:2008
int announcepositionlimit
Definition: app_queue.c:2024
unsigned int announce_to_first_user
Definition: app_queue.c:2006
int randomperiodicannounce
Definition: app_queue.c:2030
int autopause
Definition: app_queue.c:2048
int periodicannouncestartdelay
Definition: app_queue.c:2027
const ast_string_field defaultrule
Definition: app_queue.c:2001
int log_restricted_caller_id
Definition: app_queue.c:2057
struct queue_ent * head
Definition: app_queue.c:2060
struct call_queue::@54 rules
unsigned int realtime
Definition: app_queue.c:2017
const ast_string_field queue_quantity2
Definition: app_queue.c:2001
int servicelevel
Definition: app_queue.c:2037
const ast_string_field moh
Definition: app_queue.c:2001
int autofill
Definition: app_queue.c:2055
int minannouncefrequency
Definition: app_queue.c:2026
enum empty_conditions leavewhenempty
Definition: app_queue.c:2023
int penaltymemberslimit
Definition: app_queue.c:2043
unsigned int found
Definition: app_queue.c:2018
const ast_string_field context
Definition: app_queue.c:2001
const ast_string_field sound_calls
Definition: app_queue.c:2001
unsigned int ringinuse
Definition: app_queue.c:2005
int callsabandoned
Definition: app_queue.c:2035
int roundingseconds
Definition: app_queue.c:2031
int numperiodicannounce
Definition: app_queue.c:2029
int wrapuptime
Definition: app_queue.c:2042
const ast_string_field sound_minute
Definition: app_queue.c:2001
int callscompleted
Definition: app_queue.c:2034
int callsabandonedinsl
Definition: app_queue.c:2036
const ast_string_field sound_minutes
Definition: app_queue.c:2001
unsigned int announceposition
Definition: app_queue.c:2014
const ast_string_field queue_quantity1
Definition: app_queue.c:2001
const ast_string_field membergosub
Definition: app_queue.c:2001
char monfmt[8]
Definition: app_queue.c:2039
enum empty_conditions joinempty
Definition: app_queue.c:2022
unsigned int wrapped
Definition: app_queue.c:2011
int strategy
Definition: app_queue.c:2016
const ast_string_field name
Definition: app_queue.c:2001
int callscompletedinsl
Definition: app_queue.c:2038
int memberdelay
Definition: app_queue.c:2054
const ast_string_field sound_next
Definition: app_queue.c:2001
unsigned int autopausebusy
Definition: app_queue.c:2020
int holdtime
Definition: app_queue.c:2032
const ast_string_field announce
Definition: app_queue.c:2001
int autopausedelay
Definition: app_queue.c:2049
int timeoutpriority
Definition: app_queue.c:2050
unsigned int relativeperiodicannounce
Definition: app_queue.c:2019
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:6390
const char * source_chan_uniqueid
Definition: app_queue.c:6392
unsigned int id
Definition: app_queue.c:6396
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:1898
unsigned int dead
Definition: app_queue.c:1919
unsigned int delme
Definition: app_queue.c:1920
int queuepos
Definition: app_queue.c:1911
time_t starttime
Definition: app_queue.c:1914
time_t lastcall
Definition: app_queue.c:1915
int dynamic
Definition: app_queue.c:1906
time_t logintime
Definition: app_queue.c:1917
char membername[80]
Definition: app_queue.c:1903
char rt_uniqueid[80]
Definition: app_queue.c:1921
int calls
Definition: app_queue.c:1905
int status
Definition: app_queue.c:1908
int penalty
Definition: app_queue.c:1904
int paused
Definition: app_queue.c:1909
unsigned int ringinuse
Definition: app_queue.c:1922
int wrapuptime
Definition: app_queue.c:1913
int callcompletedinsl
Definition: app_queue.c:1912
char state_exten[AST_MAX_EXTENSION]
Definition: app_queue.c:1899
char state_context[AST_MAX_CONTEXT]
Definition: app_queue.c:1900
int realtime
Definition: app_queue.c:1907
int state_id
Definition: app_queue.c:1902
time_t lastpause
Definition: app_queue.c:1916
char reason_paused[80]
Definition: app_queue.c:1910
struct call_queue * lastqueue
Definition: app_queue.c:1918
char state_interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1901
Number structure.
Definition: app_followme.c:157
int raise_respect_min
Definition: app_queue.c:1954
int raise_relative
Definition: app_queue.c:1953
struct penalty_rule::@52 list
int min_relative
Definition: app_queue.c:1952
int max_relative
Definition: app_queue.c:1951
struct call_queue * q
Definition: app_queue.c:7015
struct ast_channel * chan
Definition: app_queue.c:7016
int raise_respect_min
Definition: app_queue.c:1883
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:1887
unsigned int withdraw
Definition: app_queue.c:1889
struct penalty_rule * pr
Definition: app_queue.c:1893
int linpos
Definition: app_queue.c:1884
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:1891
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:1888
struct queue_ent::@51 qe_rules
char announce[PATH_MAX]
Definition: app_queue.c:1865
char * withdraw_info
Definition: app_queue.c:1890
int handled
Definition: app_queue.c:1878
int linwrapped
Definition: app_queue.c:1885
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:1886
struct call_queue * parent
Definition: app_queue.c:1863
struct queue_ent * next
Definition: app_queue.c:1894
User data for stasis subscriptions used for queue calls.
Definition: app_queue.c:6414
const ast_string_field caller_uniqueid
Definition: app_queue.c:6422
const ast_string_field member_uniqueid
Definition: app_queue.c:6422
struct local_optimization member_optimize
Definition: app_queue.c:6444
struct stasis_message_router * channel_router
Definition: app_queue.c:6440
struct call_queue * queue
Definition: app_queue.c:6424
const ast_string_field bridge_uniqueid
Definition: app_queue.c:6422
struct stasis_message_router * bridge_router
Definition: app_queue.c:6438
struct local_optimization caller_optimize
Definition: app_queue.c:6442
struct member * member
Definition: app_queue.c:6426
struct rule_list::@55 rules
char name[80]
Definition: app_queue.c:2066
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:947
#define ast_assert(a)
Definition: utils.h:745
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:672
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition: utils.c:2343
#define AST_FLAGS_ALL
Definition: utils.h:196