Asterisk - The Open Source Telephony Project GIT-master-f45f878
app_queue.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2018, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief True call queues with optional send URL on answer
22 *
23 * \author Mark Spencer <markster@digium.com>
24 *
25 * \par Development notes
26 * \note 2004-11-25: Persistent Dynamic Members added by:
27 * NetNation Communications (www.netnation.com)
28 * Kevin Lindsay <kevinl@netnation.com>
29 *
30 * Each dynamic agent in each queue is now stored in the astdb.
31 * When asterisk is restarted, each agent will be automatically
32 * readded into their recorded queues. This feature can be
33 * configured with the 'persistent_members=<1|0>' setting in the
34 * '[general]' category in queues.conf. The default is on.
35 *
36 * \note 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
37 *
38 * \note These features added by David C. Troy <dave@toad.net>:
39 * - Per-queue holdtime calculation
40 * - Estimated holdtime announcement
41 * - Position announcement
42 * - Abandoned/completed call counters
43 * - Failout timer passed as optional app parameter
44 *
45 * Patch Version 1.07 2003-12-24 01
46 *
47 * Added servicelevel statistic by Michiel Betel <michiel@betel.nl>
48 * Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
49 *
50 * Fixed to work with CVS as of 2004-02-25 and released as 1.07a
51 * by Matthew Enger <m.enger@xi.com.au>
52 *
53 * \ingroup applications
54 */
55
56/*! \li \ref app_queues.c uses configuration file \ref queues.conf
57 * \addtogroup configuration_file
58 */
59
60/*! \page queues.conf queues.conf
61 * \verbinclude queues.conf.sample
62 */
63
64/*** MODULEINFO
65 <support_level>core</support_level>
66 ***/
67
68#include "asterisk.h"
69
70#include <sys/time.h>
71#include <signal.h>
72#include <netinet/in.h>
73#include <ctype.h>
74
75#include "asterisk/lock.h"
76#include "asterisk/file.h"
77#include "asterisk/channel.h"
78#include "asterisk/pbx.h"
79#include "asterisk/app.h"
81#include "asterisk/module.h"
82#include "asterisk/translate.h"
83#include "asterisk/say.h"
84#include "asterisk/features.h"
86#include "asterisk/cli.h"
87#include "asterisk/manager.h"
88#include "asterisk/config.h"
89#include "asterisk/utils.h"
90#include "asterisk/causes.h"
91#include "asterisk/astdb.h"
94#include "asterisk/astobj2.h"
95#include "asterisk/strings.h"
97#include "asterisk/aoc.h"
98#include "asterisk/callerid.h"
99#include "asterisk/term.h"
100#include "asterisk/dial.h"
105#include "asterisk/core_local.h"
106#include "asterisk/mixmonitor.h"
109
110/*!
111 * \par Please read before modifying this file.
112 * There are three locks which are regularly used
113 * throughout this file, the queue list lock, the lock
114 * for each individual queue, and the interface list lock.
115 * Please be extra careful to always lock in the following order
116 * 1) queue list lock
117 * 2) individual queue lock
118 * 3) interface list lock
119 * This order has sort of "evolved" over the lifetime of this
120 * application, but it is now in place this way, so please adhere
121 * to this order!
122 */
123
124/*** DOCUMENTATION
125 <application name="Queue" language="en_US">
126 <synopsis>
127 Queue a call for a call queue.
128 </synopsis>
129 <syntax>
130 <parameter name="queuename" required="true" />
131 <parameter name="options">
132 <optionlist>
133 <option name="b" argsep="^">
134 <para>Before initiating an outgoing call, <literal>Gosub</literal> to the specified
135 location using the newly created channel. The <literal>Gosub</literal> will be
136 executed for each destination channel.</para>
137 <argument name="context" required="false" />
138 <argument name="exten" required="false" />
139 <argument name="priority" required="true" hasparams="optional" argsep="^">
140 <argument name="arg1" multiple="true" required="true" />
141 <argument name="argN" />
142 </argument>
143 </option>
144 <option name="B" argsep="^">
145 <para>Before initiating the outgoing call(s), <literal>Gosub</literal> to the
146 specified location using the current channel.</para>
147 <argument name="context" required="false" />
148 <argument name="exten" required="false" />
149 <argument name="priority" required="true" hasparams="optional" argsep="^">
150 <argument name="arg1" multiple="true" required="true" />
151 <argument name="argN" />
152 </argument>
153 </option>
154 <option name="C">
155 <para>Mark all calls as "answered elsewhere" when cancelled.</para>
156 </option>
157 <option name="c">
158 <para>Continue in the dialplan if the callee hangs up.</para>
159 </option>
160 <option name="d">
161 <para>Data-quality (modem) call (minimum delay).</para>
162 <para>This option only applies to DAHDI channels. By default,
163 DTMF is verified by muting audio TX/RX to verify the tone
164 is still present. This option disables that behavior.</para>
165 </option>
166 <option name="F" argsep="^">
167 <argument name="context" required="false" />
168 <argument name="exten" required="false" />
169 <argument name="priority" required="true" />
170 <para>When the caller hangs up, transfer the <emphasis>called member</emphasis>
171 to the specified destination and <emphasis>start</emphasis> execution at that location.</para>
172 <para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
173 prefixed with one or two underbars ('_').</para>
174 <para>NOTE: Using this option from a GoSub() might not make sense as there would be no return points.</para>
175 </option>
176 <option name="h">
177 <para>Allow <emphasis>callee</emphasis> to hang up by pressing <literal>*</literal>.</para>
178 </option>
179 <option name="H">
180 <para>Allow <emphasis>caller</emphasis> to hang up by pressing <literal>*</literal>.</para>
181 </option>
182 <option name="i">
183 <para>Ignore call forward requests from queue members and do nothing
184 when they are requested.</para>
185 </option>
186 <option name="I">
187 <para>Asterisk will ignore any connected line update requests or any redirecting party
188 update requests it may receive on this dial attempt.</para>
189 </option>
190 <option name="k">
191 <para>Allow the <emphasis>called</emphasis> party to enable parking of the call by sending
192 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
193 </option>
194 <option name="K">
195 <para>Allow the <emphasis>calling</emphasis> party to enable parking of the call by sending
196 the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
197 </option>
198 <option name="m">
199 <para>Custom music on hold class to use, which will override the music on hold class configured
200 in <filename>queues.conf</filename>, if specified.</para>
201 <para>Note that CHANNEL(musicclass), if set, will still override this option.</para>
202 </option>
203 <option name="n">
204 <para>No retries on the timeout; will exit this application and
205 go to the next step.</para>
206 </option>
207 <option name="r">
208 <para>Ring instead of playing MOH. Periodic Announcements are still made, if applicable.</para>
209 </option>
210 <option name="R">
211 <para>Ring instead of playing MOH when a member channel is actually ringing.</para>
212 </option>
213 <option name="t">
214 <para>Allow the <emphasis>called</emphasis> user to transfer the calling user.</para>
215 </option>
216 <option name="T">
217 <para>Allow the <emphasis>calling</emphasis> user to transfer the call.</para>
218 </option>
219 <option name="x">
220 <para>Allow the <emphasis>called</emphasis> user to write the conversation
221 to disk via MixMonitor.</para>
222 </option>
223 <option name="X">
224 <para>Allow the <emphasis>calling</emphasis> user to write the conversation to
225 disk via MixMonitor.</para>
226 </option>
227 </optionlist>
228 </parameter>
229 <parameter name="URL">
230 <para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
231 </parameter>
232 <parameter name="announceoverride" argsep="&amp;">
233 <para>Announcement file(s) to play to agent before bridging
234 call, overriding the announcement(s) configured in
235 <filename>queues.conf</filename>, if any.</para>
236 <para>Ampersand separated list of filenames. If the filename
237 is a relative filename (it does not begin with a slash), it
238 will be searched for in the Asterisk sounds directory. If the
239 filename is able to be parsed as a URL, Asterisk will
240 download the file and then begin playback on it. To include a
241 literal <literal>&amp;</literal> in the URL you can enclose
242 the URL in single quotes.</para>
243 <argument name="announceoverride" required="true" />
244 <argument name="announceoverride2" multiple="true" />
245 </parameter>
246 <parameter name="timeout">
247 <para>Will cause the queue to fail out after a specified number of
248 seconds, checked between each <filename>queues.conf</filename> <replaceable>timeout</replaceable> and
249 <replaceable>retry</replaceable> cycle.</para>
250 </parameter>
251 <parameter name="AGI">
252 <para>Will setup an AGI script to be executed on the calling party's channel once they are
253 connected to a queue member.</para>
254 </parameter>
255 <parameter name="gosub">
256 <para>Will run a gosub on the called party's channel (the queue member)
257 once the parties are connected. The subroutine execution starts in the
258 named context at the s exten and priority 1.</para>
259 </parameter>
260 <parameter name="rule">
261 <para>Will cause the queue's defaultrule to be overridden by the rule specified.</para>
262 </parameter>
263 <parameter name="position">
264 <para>Attempt to enter the caller into the queue at the numerical position specified. <literal>1</literal>
265 would attempt to enter the caller at the head of the queue, and <literal>3</literal> would attempt to place
266 the caller third in the queue.</para>
267 </parameter>
268 </syntax>
269 <description>
270 <para>In addition to transferring the call, a call may be parked and then picked
271 up by another user.</para>
272 <para>This application will return to the dialplan if the queue does not exist, or
273 any of the join options cause the caller to not enter the queue.</para>
274 <para>This application does not automatically answer and should be preceeded
275 by an application such as Answer(), Progress(), or Ringing().</para>
276 <para>This application sets the following channel variables upon completion:</para>
277 <variablelist>
278 <variable name="QUEUESTATUS">
279 <para>The status of the call as a text string.</para>
280 <value name="TIMEOUT" />
281 <value name="FULL" />
282 <value name="JOINEMPTY" />
283 <value name="LEAVEEMPTY" />
284 <value name="JOINUNAVAIL" />
285 <value name="LEAVEUNAVAIL" />
286 <value name="CONTINUE" />
287 <value name="WITHDRAW" />
288 </variable>
289 <variable name="ABANDONED">
290 <para>If the call was not answered by an agent this variable will be TRUE.</para>
291 <value name="TRUE" />
292 </variable>
293 <variable name="DIALEDPEERNUMBER">
294 <para>Resource of the agent that was dialed set on the outbound channel.</para>
295 </variable>
296 <variable name="QUEUE_WITHDRAW_INFO">
297 <para>If the call was successfully withdrawn from the queue, and the withdraw request was provided with optional withdraw info, the withdraw info will be stored in this variable.</para>
298 </variable>
299 </variablelist>
300 </description>
301 <see-also>
302 <ref type="application">Queue</ref>
303 <ref type="application">QueueLog</ref>
304 <ref type="application">AddQueueMember</ref>
305 <ref type="application">RemoveQueueMember</ref>
306 <ref type="application">PauseQueueMember</ref>
307 <ref type="application">UnpauseQueueMember</ref>
308 <ref type="function">QUEUE_VARIABLES</ref>
309 <ref type="function">QUEUE_MEMBER</ref>
310 <ref type="function">QUEUE_MEMBER_COUNT</ref>
311 <ref type="function">QUEUE_EXISTS</ref>
312 <ref type="function">QUEUE_GET_CHANNEL</ref>
313 <ref type="function">QUEUE_WAITING_COUNT</ref>
314 <ref type="function">QUEUE_MEMBER_LIST</ref>
315 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
316 </see-also>
317 </application>
318 <application name="AddQueueMember" language="en_US">
319 <synopsis>
320 Dynamically adds queue members.
321 </synopsis>
322 <syntax>
323 <parameter name="queuename" required="true" />
324 <parameter name="interface" />
325 <parameter name="penalty" />
326 <parameter name="options" />
327 <parameter name="membername" />
328 <parameter name="stateinterface" />
329 <parameter name="wrapuptime" />
330 </syntax>
331 <description>
332 <para>Dynamically adds interface to an existing queue. If the interface is
333 already in the queue it will return an error.</para>
334 <para>This application sets the following channel variable upon completion:</para>
335 <variablelist>
336 <variable name="AQMSTATUS">
337 <para>The status of the attempt to add a queue member as a text string.</para>
338 <value name="ADDED" />
339 <value name="MEMBERALREADY" />
340 <value name="NOSUCHQUEUE" />
341 </variable>
342 </variablelist>
343 </description>
344 <see-also>
345 <ref type="application">Queue</ref>
346 <ref type="application">QueueLog</ref>
347 <ref type="application">AddQueueMember</ref>
348 <ref type="application">RemoveQueueMember</ref>
349 <ref type="application">PauseQueueMember</ref>
350 <ref type="application">UnpauseQueueMember</ref>
351 <ref type="function">QUEUE_VARIABLES</ref>
352 <ref type="function">QUEUE_MEMBER</ref>
353 <ref type="function">QUEUE_MEMBER_COUNT</ref>
354 <ref type="function">QUEUE_EXISTS</ref>
355 <ref type="function">QUEUE_GET_CHANNEL</ref>
356 <ref type="function">QUEUE_WAITING_COUNT</ref>
357 <ref type="function">QUEUE_MEMBER_LIST</ref>
358 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
359 </see-also>
360 </application>
361 <application name="RemoveQueueMember" language="en_US">
362 <synopsis>
363 Dynamically removes queue members.
364 </synopsis>
365 <syntax>
366 <parameter name="queuename" required="true" />
367 <parameter name="interface" />
368 </syntax>
369 <description>
370 <para>If the interface is <emphasis>NOT</emphasis> in the queue it will return an error.</para>
371 <para>This application sets the following channel variable upon completion:</para>
372 <variablelist>
373 <variable name="RQMSTATUS">
374 <value name="REMOVED" />
375 <value name="NOTINQUEUE" />
376 <value name="NOSUCHQUEUE" />
377 <value name="NOTDYNAMIC" />
378 </variable>
379 </variablelist>
380 <example title="Remove queue member">
381 same => n,RemoveQueueMember(techsupport,SIP/3000)
382 </example>
383 </description>
384 <see-also>
385 <ref type="application">Queue</ref>
386 <ref type="application">QueueLog</ref>
387 <ref type="application">AddQueueMember</ref>
388 <ref type="application">RemoveQueueMember</ref>
389 <ref type="application">PauseQueueMember</ref>
390 <ref type="application">UnpauseQueueMember</ref>
391 <ref type="function">QUEUE_VARIABLES</ref>
392 <ref type="function">QUEUE_MEMBER</ref>
393 <ref type="function">QUEUE_MEMBER_COUNT</ref>
394 <ref type="function">QUEUE_EXISTS</ref>
395 <ref type="function">QUEUE_GET_CHANNEL</ref>
396 <ref type="function">QUEUE_WAITING_COUNT</ref>
397 <ref type="function">QUEUE_MEMBER_LIST</ref>
398 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
399 </see-also>
400 </application>
401 <application name="PauseQueueMember" language="en_US">
402 <synopsis>
403 Pauses a queue member.
404 </synopsis>
405 <syntax>
406 <parameter name="queuename" />
407 <parameter name="interface" required="true" />
408 <parameter name="options" />
409 <parameter name="reason">
410 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
411 </parameter>
412 </syntax>
413 <description>
414 <para>Pauses (blocks calls for) a queue member. The given interface will be paused in the given queue.
415 This prevents any calls from being sent from the queue to the interface until it is
416 unpaused with UnpauseQueueMember or the manager interface. If no queuename is given,
417 the interface is paused in every queue it is a member of. The application will fail if the
418 interface is not found.</para>
419 <para>This application sets the following channel variable upon completion:</para>
420 <variablelist>
421 <variable name="PQMSTATUS">
422 <para>The status of the attempt to pause a queue member as a text string.</para>
423 <value name="PAUSED" />
424 <value name="NOTFOUND" />
425 </variable>
426 </variablelist>
427 <example title="Pause queue member">
428 same => n,PauseQueueMember(,SIP/3000)
429 </example>
430 </description>
431 <see-also>
432 <ref type="application">Queue</ref>
433 <ref type="application">QueueLog</ref>
434 <ref type="application">AddQueueMember</ref>
435 <ref type="application">RemoveQueueMember</ref>
436 <ref type="application">PauseQueueMember</ref>
437 <ref type="application">UnpauseQueueMember</ref>
438 <ref type="function">QUEUE_VARIABLES</ref>
439 <ref type="function">QUEUE_MEMBER</ref>
440 <ref type="function">QUEUE_MEMBER_COUNT</ref>
441 <ref type="function">QUEUE_EXISTS</ref>
442 <ref type="function">QUEUE_GET_CHANNEL</ref>
443 <ref type="function">QUEUE_WAITING_COUNT</ref>
444 <ref type="function">QUEUE_MEMBER_LIST</ref>
445 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
446 </see-also>
447 </application>
448 <application name="UnpauseQueueMember" language="en_US">
449 <synopsis>
450 Unpauses a queue member.
451 </synopsis>
452 <syntax>
453 <parameter name="queuename" />
454 <parameter name="interface" required="true" />
455 <parameter name="options" />
456 <parameter name="reason">
457 <para>Is used to add extra information to the appropriate queue_log entries and manager events.</para>
458 </parameter>
459 </syntax>
460 <description>
461 <para>Unpauses (resumes calls to) a queue member. This is the counterpart to <literal>PauseQueueMember()</literal>
462 and operates exactly the same way, except it unpauses instead of pausing the given interface.</para>
463 <para>This application sets the following channel variable upon completion:</para>
464 <variablelist>
465 <variable name="UPQMSTATUS">
466 <para>The status of the attempt to unpause a queue member as a text string.</para>
467 <value name="UNPAUSED" />
468 <value name="NOTFOUND" />
469 </variable>
470 </variablelist>
471 <example title="Unpause queue member">
472 same => n,UnpauseQueueMember(,SIP/3000)
473 </example>
474 </description>
475 <see-also>
476 <ref type="application">Queue</ref>
477 <ref type="application">QueueLog</ref>
478 <ref type="application">AddQueueMember</ref>
479 <ref type="application">RemoveQueueMember</ref>
480 <ref type="application">PauseQueueMember</ref>
481 <ref type="application">UnpauseQueueMember</ref>
482 <ref type="function">QUEUE_VARIABLES</ref>
483 <ref type="function">QUEUE_MEMBER</ref>
484 <ref type="function">QUEUE_MEMBER_COUNT</ref>
485 <ref type="function">QUEUE_EXISTS</ref>
486 <ref type="function">QUEUE_GET_CHANNEL</ref>
487 <ref type="function">QUEUE_WAITING_COUNT</ref>
488 <ref type="function">QUEUE_MEMBER_LIST</ref>
489 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
490 </see-also>
491 </application>
492 <application name="QueueLog" language="en_US">
493 <synopsis>
494 Writes to the queue_log file.
495 </synopsis>
496 <syntax>
497 <parameter name="queuename" required="true" />
498 <parameter name="uniqueid" required="true" />
499 <parameter name="agent" required="true" />
500 <parameter name="event" required="true" />
501 <parameter name="additionalinfo" />
502 </syntax>
503 <description>
504 <para>Allows you to write your own events into the queue log.</para>
505 <example title="Log custom queue event">
506 same => n,QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)
507 </example>
508 </description>
509 <see-also>
510 <ref type="application">Queue</ref>
511 <ref type="application">QueueLog</ref>
512 <ref type="application">AddQueueMember</ref>
513 <ref type="application">RemoveQueueMember</ref>
514 <ref type="application">PauseQueueMember</ref>
515 <ref type="application">UnpauseQueueMember</ref>
516 <ref type="function">QUEUE_VARIABLES</ref>
517 <ref type="function">QUEUE_MEMBER</ref>
518 <ref type="function">QUEUE_MEMBER_COUNT</ref>
519 <ref type="function">QUEUE_EXISTS</ref>
520 <ref type="function">QUEUE_GET_CHANNEL</ref>
521 <ref type="function">QUEUE_WAITING_COUNT</ref>
522 <ref type="function">QUEUE_MEMBER_LIST</ref>
523 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
524 </see-also>
525 </application>
526 <application name="QueueUpdate" language="en_US">
527 <synopsis>
528 Writes to the queue_log file for outbound calls and updates Realtime Data.
529 Is used at h extension to be able to have all the parameters.
530 </synopsis>
531 <syntax>
532 <parameter name="queuename" required="true" />
533 <parameter name="uniqueid" required="true" />
534 <parameter name="agent" required="true" />
535 <parameter name="status" required="true" />
536 <parameter name="talktime" required="true" />
537 <parameter name="params" required="false" />
538 </syntax>
539 <description>
540 <para>Allows you to write Outbound events into the queue log.</para>
541 <example title="Write outbound event into queue log">
542 exten => h,1,QueueUpdate(${QUEUE}, ${UNIQUEID}, ${AGENT}, ${DIALSTATUS}, ${ANSWEREDTIME}, ${DIALEDTIME} | ${DIALEDNUMBER})
543 </example>
544 </description>
545 </application>
546 <function name="QUEUE_VARIABLES" language="en_US">
547 <synopsis>
548 Return Queue information in variables.
549 </synopsis>
550 <syntax>
551 <parameter name="queuename" required="true">
552 <enumlist>
553 <enum name="QUEUEMAX">
554 <para>Maxmimum number of calls allowed.</para>
555 </enum>
556 <enum name="QUEUESTRATEGY">
557 <para>The strategy of the queue.</para>
558 </enum>
559 <enum name="QUEUECALLS">
560 <para>Number of calls currently in the queue.</para>
561 </enum>
562 <enum name="QUEUEHOLDTIME">
563 <para>Current average hold time.</para>
564 </enum>
565 <enum name="QUEUECOMPLETED">
566 <para>Number of completed calls for the queue.</para>
567 </enum>
568 <enum name="QUEUEABANDONED">
569 <para>Number of abandoned calls.</para>
570 </enum>
571 <enum name="QUEUESRVLEVEL">
572 <para>Queue service level.</para>
573 </enum>
574 <enum name="QUEUESRVLEVELPERF">
575 <para>Current service level performance.</para>
576 </enum>
577 </enumlist>
578 </parameter>
579 </syntax>
580 <description>
581 <para>Makes the following queue variables available.</para>
582 <para>Returns <literal>0</literal> if queue is found and setqueuevar is defined, <literal>-1</literal> otherwise.</para>
583 </description>
584 <see-also>
585 <ref type="application">Queue</ref>
586 <ref type="application">QueueLog</ref>
587 <ref type="application">AddQueueMember</ref>
588 <ref type="application">RemoveQueueMember</ref>
589 <ref type="application">PauseQueueMember</ref>
590 <ref type="application">UnpauseQueueMember</ref>
591 <ref type="function">QUEUE_VARIABLES</ref>
592 <ref type="function">QUEUE_MEMBER</ref>
593 <ref type="function">QUEUE_MEMBER_COUNT</ref>
594 <ref type="function">QUEUE_EXISTS</ref>
595 <ref type="function">QUEUE_GET_CHANNEL</ref>
596 <ref type="function">QUEUE_WAITING_COUNT</ref>
597 <ref type="function">QUEUE_MEMBER_LIST</ref>
598 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
599 </see-also>
600 </function>
601 <function name="QUEUE_MEMBER" language="en_US">
602 <synopsis>
603 Provides a count of queue members based on the provided criteria, or updates a
604 queue member's settings.
605 </synopsis>
606 <syntax>
607 <parameter name="queuename" required="false" />
608 <parameter name="option" required="true">
609 <enumlist>
610 <enum name="logged">
611 <para>Returns the number of logged-in members for the specified queue.</para>
612 </enum>
613 <enum name="free">
614 <para>Returns the number of logged-in members for the specified queue that either can take calls or are currently wrapping up after a previous call.</para>
615 </enum>
616 <enum name="ready">
617 <para>Returns the number of logged-in members for the specified queue that are immediately available to answer a call.</para>
618 </enum>
619 <enum name="count">
620 <para>Returns the total number of members for the specified queue.</para>
621 </enum>
622 <enum name="penalty">
623 <para>Gets or sets queue member penalty. If
624 <replaceable>queuename</replaceable> is not specified
625 when setting the penalty then the penalty is set in all queues
626 the interface is a member.</para>
627 </enum>
628 <enum name="paused">
629 <para>Gets or sets queue member paused status. If
630 <replaceable>queuename</replaceable> is not specified
631 when setting the paused status then the paused status is set
632 in all queues the interface is a member.</para>
633 </enum>
634 <enum name="ringinuse">
635 <para>Gets or sets queue member ringinuse. If
636 <replaceable>queuename</replaceable> is not specified
637 when setting ringinuse then ringinuse is set
638 in all queues the interface is a member.</para>
639 </enum>
640 </enumlist>
641 </parameter>
642 <parameter name="interface" required="false" />
643 </syntax>
644 <description>
645 <para>Allows access to queue counts [R] and member information [R/W].</para>
646 <para><replaceable>queuename</replaceable> is required for all read operations.</para>
647 <para><replaceable>interface</replaceable> is required for all member operations.</para>
648 </description>
649 <see-also>
650 <ref type="application">Queue</ref>
651 <ref type="application">QueueLog</ref>
652 <ref type="application">AddQueueMember</ref>
653 <ref type="application">RemoveQueueMember</ref>
654 <ref type="application">PauseQueueMember</ref>
655 <ref type="application">UnpauseQueueMember</ref>
656 <ref type="function">QUEUE_VARIABLES</ref>
657 <ref type="function">QUEUE_MEMBER</ref>
658 <ref type="function">QUEUE_MEMBER_COUNT</ref>
659 <ref type="function">QUEUE_EXISTS</ref>
660 <ref type="function">QUEUE_GET_CHANNEL</ref>
661 <ref type="function">QUEUE_WAITING_COUNT</ref>
662 <ref type="function">QUEUE_MEMBER_LIST</ref>
663 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
664 </see-also>
665 </function>
666 <function name="QUEUE_MEMBER_COUNT" language="en_US">
667 <synopsis>
668 Count number of members answering a queue.
669 </synopsis>
670 <syntax>
671 <parameter name="queuename" required="true" />
672 </syntax>
673 <description>
674 <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
675 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
676 </description>
677 <see-also>
678 <ref type="application">Queue</ref>
679 <ref type="application">QueueLog</ref>
680 <ref type="application">AddQueueMember</ref>
681 <ref type="application">RemoveQueueMember</ref>
682 <ref type="application">PauseQueueMember</ref>
683 <ref type="application">UnpauseQueueMember</ref>
684 <ref type="function">QUEUE_VARIABLES</ref>
685 <ref type="function">QUEUE_MEMBER</ref>
686 <ref type="function">QUEUE_MEMBER_COUNT</ref>
687 <ref type="function">QUEUE_EXISTS</ref>
688 <ref type="function">QUEUE_GET_CHANNEL</ref>
689 <ref type="function">QUEUE_WAITING_COUNT</ref>
690 <ref type="function">QUEUE_MEMBER_LIST</ref>
691 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
692 </see-also>
693 </function>
694 <function name="QUEUE_EXISTS" language="en_US">
695 <synopsis>
696 Check if a named queue exists on this server
697 </synopsis>
698 <syntax>
699 <parameter name="queuename" />
700 </syntax>
701 <description>
702 <para>Returns 1 if the specified queue exists, 0 if it does not</para>
703 </description>
704 <see-also>
705 <ref type="application">Queue</ref>
706 <ref type="application">QueueLog</ref>
707 <ref type="application">AddQueueMember</ref>
708 <ref type="application">RemoveQueueMember</ref>
709 <ref type="application">PauseQueueMember</ref>
710 <ref type="application">UnpauseQueueMember</ref>
711 <ref type="function">QUEUE_VARIABLES</ref>
712 <ref type="function">QUEUE_MEMBER</ref>
713 <ref type="function">QUEUE_MEMBER_COUNT</ref>
714 <ref type="function">QUEUE_EXISTS</ref>
715 <ref type="function">QUEUE_GET_CHANNEL</ref>
716 <ref type="function">QUEUE_WAITING_COUNT</ref>
717 <ref type="function">QUEUE_MEMBER_LIST</ref>
718 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
719 </see-also>
720 </function>
721 <function name="QUEUE_GET_CHANNEL" language="en_US">
722 <synopsis>
723 Return caller at the specified position in a queue.
724 </synopsis>
725 <syntax>
726 <parameter name="queuename" required="true" />
727 <parameter name="position" />
728 </syntax>
729 <description>
730 <para>Returns the caller channel at <replaceable>position</replaceable> in the specified <replaceable>queuename</replaceable>.</para>
731 <para>If <replaceable>position</replaceable> is unspecified the first channel is returned.</para>
732 </description>
733 <see-also>
734 <ref type="application">Queue</ref>
735 <ref type="application">QueueLog</ref>
736 <ref type="application">AddQueueMember</ref>
737 <ref type="application">RemoveQueueMember</ref>
738 <ref type="application">PauseQueueMember</ref>
739 <ref type="application">UnpauseQueueMember</ref>
740 <ref type="function">QUEUE_VARIABLES</ref>
741 <ref type="function">QUEUE_MEMBER</ref>
742 <ref type="function">QUEUE_MEMBER_COUNT</ref>
743 <ref type="function">QUEUE_EXISTS</ref>
744 <ref type="function">QUEUE_WAITING_COUNT</ref>
745 <ref type="function">QUEUE_MEMBER_LIST</ref>
746 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
747 </see-also>
748 </function>
749 <function name="QUEUE_WAITING_COUNT" language="en_US">
750 <synopsis>
751 Count number of calls currently waiting in a queue.
752 </synopsis>
753 <syntax>
754 <parameter name="queuename" />
755 </syntax>
756 <description>
757 <para>Returns the number of callers currently waiting in the specified <replaceable>queuename</replaceable>.</para>
758 </description>
759 <see-also>
760 <ref type="application">Queue</ref>
761 <ref type="application">QueueLog</ref>
762 <ref type="application">AddQueueMember</ref>
763 <ref type="application">RemoveQueueMember</ref>
764 <ref type="application">PauseQueueMember</ref>
765 <ref type="application">UnpauseQueueMember</ref>
766 <ref type="function">QUEUE_VARIABLES</ref>
767 <ref type="function">QUEUE_MEMBER</ref>
768 <ref type="function">QUEUE_MEMBER_COUNT</ref>
769 <ref type="function">QUEUE_EXISTS</ref>
770 <ref type="function">QUEUE_GET_CHANNEL</ref>
771 <ref type="function">QUEUE_WAITING_COUNT</ref>
772 <ref type="function">QUEUE_MEMBER_LIST</ref>
773 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
774 </see-also>
775 </function>
776 <function name="QUEUE_MEMBER_LIST" language="en_US">
777 <synopsis>
778 Returns a list of interfaces on a queue.
779 </synopsis>
780 <syntax>
781 <parameter name="queuename" required="true" />
782 </syntax>
783 <description>
784 <para>Returns a comma-separated list of members associated with the specified <replaceable>queuename</replaceable>.</para>
785 </description>
786 <see-also>
787 <ref type="application">Queue</ref>
788 <ref type="application">QueueLog</ref>
789 <ref type="application">AddQueueMember</ref>
790 <ref type="application">RemoveQueueMember</ref>
791 <ref type="application">PauseQueueMember</ref>
792 <ref type="application">UnpauseQueueMember</ref>
793 <ref type="function">QUEUE_VARIABLES</ref>
794 <ref type="function">QUEUE_MEMBER</ref>
795 <ref type="function">QUEUE_MEMBER_COUNT</ref>
796 <ref type="function">QUEUE_EXISTS</ref>
797 <ref type="function">QUEUE_GET_CHANNEL</ref>
798 <ref type="function">QUEUE_WAITING_COUNT</ref>
799 <ref type="function">QUEUE_MEMBER_LIST</ref>
800 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
801 </see-also>
802 </function>
803 <function name="QUEUE_MEMBER_PENALTY" language="en_US">
804 <synopsis>
805 Gets or sets queue members penalty.
806 </synopsis>
807 <syntax>
808 <parameter name="queuename" required="true" />
809 <parameter name="interface" required="true" />
810 </syntax>
811 <description>
812 <para>Gets or sets queue members penalty.</para>
813 <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
814 </description>
815 <see-also>
816 <ref type="application">Queue</ref>
817 <ref type="application">QueueLog</ref>
818 <ref type="application">AddQueueMember</ref>
819 <ref type="application">RemoveQueueMember</ref>
820 <ref type="application">PauseQueueMember</ref>
821 <ref type="application">UnpauseQueueMember</ref>
822 <ref type="function">QUEUE_VARIABLES</ref>
823 <ref type="function">QUEUE_MEMBER</ref>
824 <ref type="function">QUEUE_MEMBER_COUNT</ref>
825 <ref type="function">QUEUE_EXISTS</ref>
826 <ref type="function">QUEUE_GET_CHANNEL</ref>
827 <ref type="function">QUEUE_WAITING_COUNT</ref>
828 <ref type="function">QUEUE_MEMBER_LIST</ref>
829 <ref type="function">QUEUE_MEMBER_PENALTY</ref>
830 </see-also>
831 </function>
832 <manager name="QueueStatus" language="en_US">
833 <synopsis>
834 Show queue status.
835 </synopsis>
836 <syntax>
837 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
838 <parameter name="Queue">
839 <para>Limit the response to the status of the specified queue.</para>
840 </parameter>
841 <parameter name="Member">
842 <para>Limit the response to the status of the specified member.</para>
843 </parameter>
844 </syntax>
845 <description>
846 <para>Check the status of one or more queues.</para>
847 </description>
848 </manager>
849 <manager name="QueueSummary" language="en_US">
850 <synopsis>
851 Show queue summary.
852 </synopsis>
853 <syntax>
854 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
855 <parameter name="Queue">
856 <para>Queue for which the summary is requested.</para>
857 </parameter>
858 </syntax>
859 <description>
860 <para>Request the manager to send a QueueSummary event.</para>
861 </description>
862 </manager>
863 <manager name="QueueAdd" language="en_US">
864 <synopsis>
865 Add interface to queue.
866 </synopsis>
867 <syntax>
868 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
869 <parameter name="Queue" required="true">
870 <para>Queue's name.</para>
871 </parameter>
872 <parameter name="Interface" required="true">
873 <para>The name of the interface (tech/name) to add to the queue.</para>
874 </parameter>
875 <parameter name="Penalty">
876 <para>A penalty (number) to apply to this member. Asterisk will distribute calls to members with higher penalties only after attempting to distribute calls to those with lower penalty.</para>
877 </parameter>
878 <parameter name="Paused">
879 <para>To pause or not the member initially (true/false or 1/0).</para>
880 </parameter>
881 <parameter name="MemberName">
882 <para>Text alias for the interface.</para>
883 </parameter>
884 <parameter name="StateInterface" />
885 </syntax>
886 <description>
887 </description>
888 </manager>
889 <manager name="QueueRemove" language="en_US">
890 <synopsis>
891 Remove interface from queue.
892 </synopsis>
893 <syntax>
894 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
895 <parameter name="Queue" required="true">
896 <para>The name of the queue to take action on.</para>
897 </parameter>
898 <parameter name="Interface" required="true">
899 <para>The interface (tech/name) to remove from queue.</para>
900 </parameter>
901 </syntax>
902 <description>
903 </description>
904 </manager>
905 <manager name="QueuePause" language="en_US">
906 <synopsis>
907 Makes a queue member temporarily unavailable.
908 </synopsis>
909 <syntax>
910 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
911 <parameter name="Interface" required="true">
912 <para>The name of the interface (tech/name) to pause or unpause.</para>
913 </parameter>
914 <parameter name="Paused" required="true">
915 <para>Pause or unpause the interface. Set to 'true' to pause the member or 'false' to unpause.</para>
916 </parameter>
917 <parameter name="Queue">
918 <para>The name of the queue in which to pause or unpause this member. If not specified, the member will be paused or unpaused in all the queues it is a member of.</para>
919 </parameter>
920 <parameter name="Reason">
921 <para>Text description, returned in the event QueueMemberPaused.</para>
922 </parameter>
923 </syntax>
924 <description>
925 <para>Pause or unpause a member in a queue.</para>
926 </description>
927 </manager>
928 <manager name="QueueLog" language="en_US">
929 <synopsis>
930 Adds custom entry in queue_log.
931 </synopsis>
932 <syntax>
933 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
934 <parameter name="Queue" required="true" />
935 <parameter name="Event" required="true" />
936 <parameter name="Uniqueid" />
937 <parameter name="Interface" />
938 <parameter name="Message" />
939 </syntax>
940 <description>
941 </description>
942 </manager>
943 <manager name="QueuePenalty" language="en_US">
944 <synopsis>
945 Set the penalty for a queue member.
946 </synopsis>
947 <syntax>
948 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
949 <parameter name="Interface" required="true">
950 <para>The interface (tech/name) of the member whose penalty to change.</para>
951 </parameter>
952 <parameter name="Penalty" required="true">
953 <para>The new penalty (number) for the member. Must be nonnegative.</para>
954 </parameter>
955 <parameter name="Queue">
956 <para>If specified, only set the penalty for the member of this queue. Otherwise, set the penalty for the member in all queues to which the member belongs.</para>
957 </parameter>
958 </syntax>
959 <description>
960 <para>Change the penalty of a queue member</para>
961 </description>
962 </manager>
963 <manager name="QueueMemberRingInUse" language="en_US">
964 <synopsis>
965 Set the ringinuse value for a queue member.
966 </synopsis>
967 <syntax>
968 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
969 <parameter name="Interface" required="true" />
970 <parameter name="RingInUse" required="true" />
971 <parameter name="Queue" />
972 </syntax>
973 <description>
974 </description>
975 </manager>
976 <manager name="QueueRule" language="en_US">
977 <synopsis>
978 Queue Rules.
979 </synopsis>
980 <syntax>
981 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
982 <parameter name="Rule">
983 <para>The name of the rule in queuerules.conf whose contents to list.</para>
984 </parameter>
985 </syntax>
986 <description>
987 <para>List queue rules defined in queuerules.conf</para>
988 </description>
989 </manager>
990 <manager name="QueueReload" language="en_US">
991 <synopsis>
992 Reload a queue, queues, or any sub-section of a queue or queues.
993 </synopsis>
994 <syntax>
995 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
996 <parameter name="Queue">
997 <para>The name of the queue to take action on. If no queue name is specified, then all queues are affected.</para>
998 </parameter>
999 <parameter name="Members">
1000 <para>Whether to reload the queue's members.</para>
1001 <enumlist>
1002 <enum name="yes" />
1003 <enum name="no" />
1004 </enumlist>
1005 </parameter>
1006 <parameter name="Rules">
1007 <para>Whether to reload queuerules.conf</para>
1008 <enumlist>
1009 <enum name="yes" />
1010 <enum name="no" />
1011 </enumlist>
1012 </parameter>
1013 <parameter name="Parameters">
1014 <para>Whether to reload the other queue options.</para>
1015 <enumlist>
1016 <enum name="yes" />
1017 <enum name="no" />
1018 </enumlist>
1019 </parameter>
1020 </syntax>
1021 <description>
1022 </description>
1023 </manager>
1024 <manager name="QueueReset" language="en_US">
1025 <synopsis>
1026 Reset queue statistics.
1027 </synopsis>
1028 <syntax>
1029 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1030 <parameter name="Queue">
1031 <para>The name of the queue on which to reset statistics.</para>
1032 </parameter>
1033 </syntax>
1034 <description>
1035 <para>Reset the statistics for a queue.</para>
1036 </description>
1037 </manager>
1038 <manager name="QueueChangePriorityCaller" language="en_US">
1039 <synopsis>
1040 Change priority of a caller on queue.
1041 </synopsis>
1042 <syntax>
1043 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1044 <parameter name="Queue" required="true">
1045 <para>The name of the queue to take action on.</para>
1046 </parameter>
1047 <parameter name="Caller" required="true">
1048 <para>The caller (channel) to change priority on queue.</para>
1049 </parameter>
1050
1051 <parameter name="Priority" required="true">
1052 <para>Priority value for change for caller on queue.</para>
1053 </parameter>
1054 <parameter name="Immediate">
1055 <para>When set to yes will cause the priority change to be reflected immediately, causing the channel to change position within the queue.</para>
1056 </parameter>
1057 </syntax>
1058 <description>
1059 </description>
1060 </manager>
1061 <manager name="QueueWithdrawCaller" language="en_US">
1062 <synopsis>
1063 Request to withdraw a caller from the queue back to the dialplan.
1064 </synopsis>
1065 <syntax>
1066 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1067 <parameter name="Queue" required="true">
1068 <para>The name of the queue to take action on.</para>
1069 </parameter>
1070 <parameter name="Caller" required="true">
1071 <para>The caller (channel) to withdraw from the queue.</para>
1072 </parameter>
1073 <parameter name="WithdrawInfo" required="false">
1074 <para>Optional info to store. If the call is successfully withdrawn from the queue, this information will be available in the QUEUE_WITHDRAW_INFO variable.</para>
1075 </parameter>
1076 </syntax>
1077 <description>
1078 </description>
1079 </manager>
1080
1081 <managerEvent language="en_US" name="QueueParams">
1082 <managerEventInstance class="EVENT_FLAG_AGENT">
1083 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1084 <syntax>
1085 <parameter name="Max">
1086 <para>The name of the queue.</para>
1087 </parameter>
1088 <parameter name="Strategy">
1089 <para>The strategy of the queue.</para>
1090 </parameter>
1091 <parameter name="Calls">
1092 <para>The queue member's channel technology or location.</para>
1093 </parameter>
1094 <parameter name="Holdtime">
1095 <para>The queue's hold time.</para>
1096 </parameter>
1097 <parameter name="TalkTime">
1098 <para>The queue's talk time.</para>
1099 </parameter>
1100 <parameter name="Completed">
1101 <para>The queue's completion time.</para>
1102 </parameter>
1103 <parameter name="Abandoned">
1104 <para>The queue's call abandonment metric.</para>
1105 </parameter>
1106 <parameter name="ServiceLevelPerf">
1107 <para>Primary service level performance metric.</para>
1108 </parameter>
1109 <parameter name="ServiceLevelPerf2">
1110 <para>Secondary service level performance metric.</para>
1111 </parameter>
1112 </syntax>
1113 <see-also>
1114 <ref type="managerEvent">QueueMember</ref>
1115 <ref type="managerEvent">QueueEntry</ref>
1116 </see-also>
1117 </managerEventInstance>
1118 </managerEvent>
1119 <managerEvent language="en_US" name="QueueEntry">
1120 <managerEventInstance class="EVENT_FLAG_AGENT">
1121 <synopsis>Raised in response to the QueueStatus action.</synopsis>
1122 <syntax>
1123 <parameter name="Queue">
1124 <para>The name of the queue.</para>
1125 </parameter>
1126 <parameter name="Position">
1127 <para>The caller's position within the queue.</para>
1128 </parameter>
1129 <parameter name="Channel">
1130 <para>The name of the caller's channel.</para>
1131 </parameter>
1132 <parameter name="Uniqueid">
1133 <para>The unique ID of the channel.</para>
1134 </parameter>
1135 <parameter name="CallerIDNum">
1136 <para>The Caller ID number.</para>
1137 </parameter>
1138 <parameter name="CallerIDName">
1139 <para>The Caller ID name.</para>
1140 </parameter>
1141 <parameter name="ConnectedLineNum">
1142 <para>The bridged party's number.</para>
1143 </parameter>
1144 <parameter name="ConnectedLineName">
1145 <para>The bridged party's name.</para>
1146 </parameter>
1147 <parameter name="Wait">
1148 <para>The caller's wait time.</para>
1149 </parameter>
1150 <parameter name="Priority">
1151 <para>The caller's priority within the queue.</para>
1152 </parameter>
1153 </syntax>
1154 <see-also>
1155 <ref type="managerEvent">QueueParams</ref>
1156 <ref type="managerEvent">QueueMember</ref>
1157 </see-also>
1158 </managerEventInstance>
1159 </managerEvent>
1160 <managerEvent language="en_US" name="QueueMemberStatus">
1161 <managerEventInstance class="EVENT_FLAG_AGENT">
1162 <synopsis>Raised when a Queue member's status has changed.</synopsis>
1163 <syntax>
1164 <parameter name="Queue">
1165 <para>The name of the queue.</para>
1166 </parameter>
1167 <parameter name="MemberName">
1168 <para>The name of the queue member.</para>
1169 </parameter>
1170 <parameter name="Interface">
1171 <para>The queue member's channel technology or location.</para>
1172 </parameter>
1173 <parameter name="StateInterface">
1174 <para>Channel technology or location from which to read device state changes.</para>
1175 </parameter>
1176 <parameter name="Membership">
1177 <enumlist>
1178 <enum name="dynamic"/>
1179 <enum name="realtime"/>
1180 <enum name="static"/>
1181 </enumlist>
1182 </parameter>
1183 <parameter name="Penalty">
1184 <para>The penalty associated with the queue member.</para>
1185 </parameter>
1186 <parameter name="CallsTaken">
1187 <para>The number of calls this queue member has serviced.</para>
1188 </parameter>
1189 <parameter name="LastCall">
1190 <para>The time this member last took a call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1191 </parameter>
1192 <parameter name="LastPause">
1193 <para>The time when started last paused the queue member.</para>
1194 </parameter>
1195 <parameter name="LoginTime">
1196 <para>The time this member logged in to the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1197 </parameter>
1198 <parameter name="InCall">
1199 <para>Set to 1 if member is in call. Set to 0 after LastCall time is updated.</para>
1200 <enumlist>
1201 <enum name="0"/>
1202 <enum name="1"/>
1203 </enumlist>
1204 </parameter>
1205 <parameter name="Status">
1206 <para>The numeric device state status of the queue member.</para>
1207 <enumlist>
1208 <enum name="0"><para>AST_DEVICE_UNKNOWN</para></enum>
1209 <enum name="1"><para>AST_DEVICE_NOT_INUSE</para></enum>
1210 <enum name="2"><para>AST_DEVICE_INUSE</para></enum>
1211 <enum name="3"><para>AST_DEVICE_BUSY</para></enum>
1212 <enum name="4"><para>AST_DEVICE_INVALID</para></enum>
1213 <enum name="5"><para>AST_DEVICE_UNAVAILABLE</para></enum>
1214 <enum name="6"><para>AST_DEVICE_RINGING</para></enum>
1215 <enum name="7"><para>AST_DEVICE_RINGINUSE</para></enum>
1216 <enum name="8"><para>AST_DEVICE_ONHOLD</para></enum>
1217 </enumlist>
1218 </parameter>
1219 <parameter name="Paused">
1220 <enumlist>
1221 <enum name="0"/>
1222 <enum name="1"/>
1223 </enumlist>
1224 </parameter>
1225 <parameter name="PausedReason">
1226 <para>If set when paused, the reason the queue member was paused.</para>
1227 </parameter>
1228 <parameter name="Ringinuse">
1229 <enumlist>
1230 <enum name="0"/>
1231 <enum name="1"/>
1232 </enumlist>
1233 </parameter>
1234 <parameter name="Wrapuptime">
1235 <para>The Wrapup Time of the queue member. If this value is set will override the wrapup time of queue.</para>
1236 </parameter>
1237 </syntax>
1238 </managerEventInstance>
1239 </managerEvent>
1240 <managerEvent language="en_US" name="QueueMemberAdded">
1241 <managerEventInstance class="EVENT_FLAG_AGENT">
1242 <synopsis>Raised when a member is added to the queue.</synopsis>
1243 <syntax>
1244 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1245 </syntax>
1246 <see-also>
1247 <ref type="managerEvent">QueueMemberRemoved</ref>
1248 <ref type="application">AddQueueMember</ref>
1249 </see-also>
1250 </managerEventInstance>
1251 </managerEvent>
1252 <managerEvent language="en_US" name="QueueMemberRemoved">
1253 <managerEventInstance class="EVENT_FLAG_AGENT">
1254 <synopsis>Raised when a member is removed from the queue.</synopsis>
1255 <syntax>
1256 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1257 </syntax>
1258 <see-also>
1259 <ref type="managerEvent">QueueMemberAdded</ref>
1260 <ref type="application">RemoveQueueMember</ref>
1261 </see-also>
1262 </managerEventInstance>
1263 </managerEvent>
1264 <managerEvent language="en_US" name="QueueMemberPause">
1265 <managerEventInstance class="EVENT_FLAG_AGENT">
1266 <synopsis>Raised when a member is paused/unpaused in the queue.</synopsis>
1267 <syntax>
1268 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1269 </syntax>
1270 <see-also>
1271 <ref type="application">PauseQueueMember</ref>
1272 <ref type="application">UnpauseQueueMember</ref>
1273 </see-also>
1274 </managerEventInstance>
1275 </managerEvent>
1276 <managerEvent language="en_US" name="QueueMemberPenalty">
1277 <managerEventInstance class="EVENT_FLAG_AGENT">
1278 <synopsis>Raised when a member's penalty is changed.</synopsis>
1279 <syntax>
1280 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1281 </syntax>
1282 <see-also>
1283 <ref type="function">QUEUE_MEMBER</ref>
1284 </see-also>
1285 </managerEventInstance>
1286 </managerEvent>
1287 <managerEvent language="en_US" name="QueueMemberRinginuse">
1288 <managerEventInstance class="EVENT_FLAG_AGENT">
1289 <synopsis>Raised when a member's ringinuse setting is changed.</synopsis>
1290 <syntax>
1291 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter)" />
1292 </syntax>
1293 <see-also>
1294 <ref type="function">QUEUE_MEMBER</ref>
1295 </see-also>
1296 </managerEventInstance>
1297 </managerEvent>
1298 <managerEvent language="en_US" name="QueueCallerJoin">
1299 <managerEventInstance class="EVENT_FLAG_AGENT">
1300 <synopsis>Raised when a caller joins a Queue.</synopsis>
1301 <syntax>
1302 <channel_snapshot/>
1303 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1304 <parameter name="Position">
1305 <para>This channel's current position in the queue.</para>
1306 </parameter>
1307 <parameter name="Count">
1308 <para>The total number of channels in the queue.</para>
1309 </parameter>
1310 </syntax>
1311 <see-also>
1312 <ref type="managerEvent">QueueCallerLeave</ref>
1313 <ref type="application">Queue</ref>
1314 </see-also>
1315 </managerEventInstance>
1316 </managerEvent>
1317 <managerEvent language="en_US" name="QueueCallerLeave">
1318 <managerEventInstance class="EVENT_FLAG_AGENT">
1319 <synopsis>Raised when a caller leaves a Queue.</synopsis>
1320 <syntax>
1321 <channel_snapshot/>
1322 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1323 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Count'])" />
1324 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1325 </syntax>
1326 <see-also>
1327 <ref type="managerEvent">QueueCallerJoin</ref>
1328 </see-also>
1329 </managerEventInstance>
1330 </managerEvent>
1331 <managerEvent language="en_US" name="QueueCallerAbandon">
1332 <managerEventInstance class="EVENT_FLAG_AGENT">
1333 <synopsis>Raised when a caller abandons the queue.</synopsis>
1334 <syntax>
1335 <channel_snapshot/>
1336 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1337 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerJoin']/managerEventInstance/syntax/parameter[@name='Position'])" />
1338 <parameter name="OriginalPosition">
1339 <para>The channel's original position in the queue.</para>
1340 </parameter>
1341 <parameter name="HoldTime">
1342 <para>The time the channel was in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1343 </parameter>
1344 </syntax>
1345 </managerEventInstance>
1346 </managerEvent>
1347 <managerEvent language="en_US" name="AgentCalled">
1348 <managerEventInstance class="EVENT_FLAG_AGENT">
1349 <synopsis>Raised when an queue member is notified of a caller in the queue.</synopsis>
1350 <syntax>
1351 <channel_snapshot/>
1352 <channel_snapshot prefix="Dest"/>
1353 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1354 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1355 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1356 </syntax>
1357 <see-also>
1358 <ref type="managerEvent">AgentRingNoAnswer</ref>
1359 <ref type="managerEvent">AgentComplete</ref>
1360 <ref type="managerEvent">AgentConnect</ref>
1361 </see-also>
1362 </managerEventInstance>
1363 </managerEvent>
1364 <managerEvent language="en_US" name="AgentRingNoAnswer">
1365 <managerEventInstance class="EVENT_FLAG_AGENT">
1366 <synopsis>Raised when a queue member is notified of a caller in the queue and fails to answer.</synopsis>
1367 <syntax>
1368 <channel_snapshot/>
1369 <channel_snapshot prefix="Dest"/>
1370 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1371 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1372 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1373 <parameter name="RingTime">
1374 <para>The time the queue member was rung, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1375 </parameter>
1376 </syntax>
1377 <see-also>
1378 <ref type="managerEvent">AgentCalled</ref>
1379 </see-also>
1380 </managerEventInstance>
1381 </managerEvent>
1382 <managerEvent language="en_US" name="AgentComplete">
1383 <managerEventInstance class="EVENT_FLAG_AGENT">
1384 <synopsis>Raised when a queue member has finished servicing a caller in the queue.</synopsis>
1385 <syntax>
1386 <channel_snapshot/>
1387 <channel_snapshot prefix="Dest"/>
1388 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1389 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1390 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1391 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1392 <parameter name="TalkTime">
1393 <para>The time the queue member talked with the caller in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
1394 </parameter>
1395 <parameter name="Reason">
1396 <enumlist>
1397 <enum name="caller"/>
1398 <enum name="agent"/>
1399 <enum name="transfer"/>
1400 </enumlist>
1401 </parameter>
1402 </syntax>
1403 <see-also>
1404 <ref type="managerEvent">AgentCalled</ref>
1405 <ref type="managerEvent">AgentConnect</ref>
1406 </see-also>
1407 </managerEventInstance>
1408 </managerEvent>
1409 <managerEvent language="en_US" name="AgentDump">
1410 <managerEventInstance class="EVENT_FLAG_AGENT">
1411 <synopsis>Raised when a queue member hangs up on a caller in the queue.</synopsis>
1412 <syntax>
1413 <channel_snapshot/>
1414 <channel_snapshot prefix="Dest"/>
1415 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1416 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1417 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1418 </syntax>
1419 <see-also>
1420 <ref type="managerEvent">AgentCalled</ref>
1421 <ref type="managerEvent">AgentConnect</ref>
1422 </see-also>
1423 </managerEventInstance>
1424 </managerEvent>
1425 <managerEvent language="en_US" name="AgentConnect">
1426 <managerEventInstance class="EVENT_FLAG_AGENT">
1427 <synopsis>Raised when a queue member answers and is bridged to a caller in the queue.</synopsis>
1428 <syntax>
1429 <channel_snapshot/>
1430 <channel_snapshot prefix="Dest"/>
1431 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
1432 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
1433 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Interface'])" />
1434 <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='RingTime'])" />
1435 <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
1436 </syntax>
1437 <see-also>
1438 <ref type="managerEvent">AgentCalled</ref>
1439 <ref type="managerEvent">AgentComplete</ref>
1440 <ref type="managerEvent">AgentDump</ref>
1441 </see-also>
1442 </managerEventInstance>
1443 </managerEvent>
1444 ***/
1445
1446enum {
1448 OPT_GO_ON = (1 << 1),
1457 OPT_NO_RETRY = (1 << 10),
1458 OPT_RINGING = (1 << 11),
1469};
1470
1471enum {
1476 /* note: this entry _MUST_ be the last one in the enum */
1479
1504
1505enum {
1514};
1515
1516enum {
1521
1527};
1528
1529static const struct strategy {
1531 const char *name;
1532} strategies[] = {
1533 { QUEUE_STRATEGY_RINGALL, "ringall" },
1534 { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" },
1535 { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" },
1536 { QUEUE_STRATEGY_RANDOM, "random" },
1537 { QUEUE_STRATEGY_RRMEMORY, "rrmemory" },
1538 { QUEUE_STRATEGY_RRMEMORY, "roundrobin" },
1539 { QUEUE_STRATEGY_LINEAR, "linear" },
1540 { QUEUE_STRATEGY_WRANDOM, "wrandom"},
1541 { QUEUE_STRATEGY_RRORDERED, "rrordered"},
1543
1544static const struct autopause {
1546 const char *name;
1547} autopausesmodes [] = {
1548 { QUEUE_AUTOPAUSE_OFF,"no" },
1549 { QUEUE_AUTOPAUSE_ON, "yes" },
1550 { QUEUE_AUTOPAUSE_ALL,"all" },
1552
1553#define DEFAULT_RETRY 5
1554#define DEFAULT_TIMEOUT 15
1555#define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
1556#define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
1557/*!
1558 * \brief The minimum number of seconds between position announcements.
1559 * \note The default value of 15 provides backwards compatibility.
1560 */
1561#define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15
1562
1563#define MAX_QUEUE_BUCKETS 53
1564
1565#define RES_OKAY 0 /*!< Action completed */
1566#define RES_EXISTS (-1) /*!< Entry already exists */
1567#define RES_OUTOFMEMORY (-2) /*!< Out of memory */
1568#define RES_NOSUCHQUEUE (-3) /*!< No such queue */
1569#define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
1570#define RES_NOT_CALLER (-5) /*!< Caller not found */
1571
1572static char *app = "Queue";
1573
1574static char *app_aqm = "AddQueueMember" ;
1575
1576static char *app_rqm = "RemoveQueueMember" ;
1577
1578static char *app_pqm = "PauseQueueMember" ;
1579
1580static char *app_upqm = "UnpauseQueueMember" ;
1581
1582static char *app_ql = "QueueLog" ;
1583
1584static char *app_qupd = "QueueUpdate";
1585
1586/*! \brief Persistent Members astdb family */
1587static const char * const pm_family = "Queue/PersistentMembers";
1588
1589/*! \brief queues.conf [general] option */
1591
1592/*! \brief Records that one or more queues use weight */
1593static int use_weight;
1594
1595/*! \brief queues.conf [general] option */
1597
1598/*! \brief queues.conf [general] option */
1600
1601/*! \brief queues.conf [general] option */
1603
1604/*! \brief queuerules.conf [general] option */
1606
1607/*! \brief Subscription to device state change messages */
1609
1610/*! \brief queues.conf [general] option */
1612
1613/*! \brief queues.conf [general] option */
1615
1616/*! \brief queues.conf [general] option */
1618
1619/*! \brief name of the ringinuse field in the realtime database */
1621
1622/*! \brief does realtime backend support reason_paused */
1624
1635};
1636
1637static const struct {
1639 char *text;
1640} queue_results[] = {
1641 { QUEUE_UNKNOWN, "UNKNOWN" },
1642 { QUEUE_TIMEOUT, "TIMEOUT" },
1643 { QUEUE_JOINEMPTY,"JOINEMPTY" },
1644 { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" },
1645 { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" },
1646 { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" },
1647 { QUEUE_FULL, "FULL" },
1648 { QUEUE_CONTINUE, "CONTINUE" },
1649 { QUEUE_WITHDRAW, "WITHDRAW" },
1651
1655};
1656
1657/*! \brief We define a custom "local user" structure because we
1658 * use it not only for keeping track of what is in use but
1659 * also for keeping track of who we're dialing.
1660 *
1661 * There are two "links" defined in this structure, q_next and call_next.
1662 * q_next links ALL defined callattempt structures into a linked list. call_next is
1663 * a link which allows for a subset of the callattempts to be traversed. This subset
1664 * is used in wait_for_answer so that irrelevant callattempts are not traversed. This
1665 * also is helpful so that queue logs are always accurate in the case where a call to
1666 * a member times out, especially if using the ringall strategy.
1667*/
1668
1673 char interface[256]; /*!< An Asterisk dial string (not a channel name) */
1676 /*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
1678 /*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
1680 /*! TRUE if the connected line update is blocked. */
1682 /*! TRUE if caller id is not available for connected line */
1683 unsigned int dial_callerid_absent:1;
1684 /*! TRUE if the call is still active */
1685 unsigned int stillgoing:1;
1687 /*! Original channel name. Must be freed. Could be NULL if allocation failed. */
1689};
1690
1691
1693 struct call_queue *parent; /*!< What queue is our parent */
1694 char moh[MAX_MUSICCLASS]; /*!< Name of musiconhold to be used */
1695 char announce[PATH_MAX]; /*!< Announcement to play for member when call is answered */
1696 char context[AST_MAX_CONTEXT]; /*!< Context when user exits queue */
1697 char digits[AST_MAX_EXTENSION]; /*!< Digits entered while in queue */
1698 const char *predial_callee; /*!< Gosub app arguments for outgoing calls. NULL if not supplied. */
1699 int valid_digits; /*!< Digits entered correspond to valid extension. Exited */
1700 int pos; /*!< Where we are in the queue */
1701 int prio; /*!< Our priority */
1702 int last_pos_said; /*!< Last position we told the user */
1703 int ring_when_ringing; /*!< Should we only use ring indication when a channel is ringing? */
1704 time_t last_periodic_announce_time; /*!< The last time we played a periodic announcement */
1705 int last_periodic_announce_sound; /*!< The last periodic announcement we made */
1706 time_t last_pos; /*!< Last time we told the user their position */
1707 int opos; /*!< Where we started in the queue */
1708 int handled; /*!< Whether our call was handled */
1709 int pending; /*!< Non-zero if we are attempting to call a member */
1710 int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
1711 int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
1712 int raise_penalty; /*!< Float lower penalty members to a minimum penalty */
1713 int linpos; /*!< If using linear strategy, what position are we at? */
1714 int linwrapped; /*!< Is the linpos wrapped? */
1715 time_t start; /*!< When we started holding */
1716 time_t expire; /*!< When this entry should expire (time out of queue) */
1717 int cancel_answered_elsewhere; /*!< Whether we should force the CAE flag on this call (C) option*/
1718 unsigned int withdraw:1; /*!< Should this call exit the queue at its next iteration? Used for QueueWithdrawCaller */
1719 char *withdraw_info; /*!< Optional info passed by the caller of QueueWithdrawCaller */
1720 struct ast_channel *chan; /*!< Our channel */
1721 AST_LIST_HEAD_NOLOCK(,penalty_rule) qe_rules; /*!< Local copy of the queue's penalty rules */
1722 struct penalty_rule *pr; /*!< Pointer to the next penalty rule to implement */
1723 struct queue_ent *next; /*!< The next queue entry */
1724};
1725
1726struct member {
1727 char interface[AST_CHANNEL_NAME]; /*!< Technology/Location to dial to reach this member*/
1728 char state_exten[AST_MAX_EXTENSION]; /*!< Extension to get state from (if using hint) */
1729 char state_context[AST_MAX_CONTEXT]; /*!< Context to use when getting state (if using hint) */
1730 char state_interface[AST_CHANNEL_NAME]; /*!< Technology/Location from which to read devicestate changes */
1731 int state_id; /*!< Extension state callback id (if using hint) */
1732 char membername[80]; /*!< Member name to use in queue logs */
1733 int penalty; /*!< Are we a last resort? */
1734 int calls; /*!< Number of calls serviced by this member */
1735 int dynamic; /*!< Are we dynamically added? */
1736 int realtime; /*!< Is this member realtime? */
1737 int status; /*!< Status of queue member */
1738 int paused; /*!< Are we paused (not accepting calls)? */
1739 char reason_paused[80]; /*!< Reason of paused if member is paused */
1740 int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
1741 int callcompletedinsl; /*!< Whether the current call was completed within service level */
1742 int wrapuptime; /*!< Wrapup Time */
1743 time_t starttime; /*!< The time at which the member answered the current caller. */
1744 time_t lastcall; /*!< When last successful call was hungup */
1745 time_t lastpause; /*!< When started the last pause */
1746 time_t logintime; /*!< The time when started the login */
1747 struct call_queue *lastqueue; /*!< Last queue we received a call */
1748 unsigned int dead:1; /*!< Used to detect members deleted in realtime */
1749 unsigned int delme:1; /*!< Flag to delete entry on reload */
1750 char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
1751 unsigned int ringinuse:1; /*!< Flag to ring queue members even if their status is 'inuse' */
1752};
1753
1763};
1764
1768};
1769
1770/* values used in multi-bit flags in call_queue */
1771#define ANNOUNCEHOLDTIME_ALWAYS 1
1772#define ANNOUNCEHOLDTIME_ONCE 2
1773#define QUEUE_EVENT_VARIABLES 3
1774
1776 int time; /*!< Number of seconds that need to pass before applying this rule */
1777 int max_value; /*!< The amount specified in the penalty rule for max penalty */
1778 int min_value; /*!< The amount specified in the penalty rule for min penalty */
1779 int raise_value; /*!< The amount specified in the penalty rule for min penalty */
1780 int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
1781 int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1782 int raise_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
1783 AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
1784};
1785
1786#define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
1787#define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
1788#define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
1789#define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
1790
1793 /*! Queue name */
1795 /*! Music on Hold class */
1797 /*! Announcement to play when call is answered */
1799 /*! Exit context */
1801 /*! Gosub to run upon member connection */
1803 /*! Default rule to use if none specified in call to Queue() */
1805 /*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
1807 /*! Sound file: "There are currently" (def. queue-thereare) */
1809 /*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
1811 /*! Sound file: "Currently there are more than" (def. queue-quantity1) */
1813 /*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
1815 /*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
1817 /*! Sound file: "minutes." (def. queue-minutes) */
1819 /*! Sound file: "minute." (def. queue-minute) */
1821 /*! Sound file: "seconds." (def. queue-seconds) */
1823 /*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
1825 /*! Sound file: Custom announce for caller, no default */
1827 /*! Sound file: "Hold time" (def. queue-reporthold) */
1830 /*! Sound files: Custom announce, no default */
1832 unsigned int dead:1;
1833 unsigned int ringinuse:1;
1834 unsigned int announce_to_first_user:1; /*!< Whether or not we announce to the first user in a queue */
1835 unsigned int setinterfacevar:1;
1836 unsigned int setqueuevar:1;
1837 unsigned int setqueueentryvar:1;
1838 unsigned int reportholdtime:1;
1839 unsigned int wrapped:1;
1840 unsigned int timeoutrestart:1;
1841 unsigned int announceholdtime:2;
1842 unsigned int announceposition:3;
1843 unsigned int announceposition_only_up:1; /*!< Only announce position if it has improved */
1845 unsigned int realtime:1;
1846 unsigned int found:1;
1848 unsigned int autopausebusy:1;
1849 unsigned int autopauseunavail:1;
1852 int announcepositionlimit; /*!< How many positions we announce? */
1853 int announcefrequency; /*!< How often to announce their position */
1854 int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
1855 int periodicannouncestartdelay; /*!< How long into the queue should the periodic accouncement start */
1856 int periodicannouncefrequency; /*!< How often to play periodic announcement */
1857 int numperiodicannounce; /*!< The number of periodic announcements configured */
1858 int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
1859 int roundingseconds; /*!< How many seconds do we round to? */
1860 int holdtime; /*!< Current avg holdtime, based on an exponential average */
1861 int talktime; /*!< Current avg talktime, based on the same exponential average */
1862 int callscompleted; /*!< Number of queue calls completed */
1863 int callsabandoned; /*!< Number of queue calls abandoned */
1864 int callsabandonedinsl; /*!< Number of queue calls abandoned in servicelevel */
1865 int servicelevel; /*!< seconds setting for servicelevel*/
1866 int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
1867 char monfmt[8]; /*!< Format to use when recording calls */
1868 int count; /*!< How many entries */
1869 int maxlen; /*!< Max number of entries */
1870 int wrapuptime; /*!< Wrapup Time */
1871 int penaltymemberslimit; /*!< Disregard penalty when queue has fewer than this many members */
1872
1873 int retry; /*!< Retry calling everyone after this amount of time */
1874 int timeout; /*!< How long to wait for an answer */
1875 int weight; /*!< Respective weight */
1876 int autopause; /*!< Auto pause queue members if they fail to answer */
1877 int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
1878 int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
1879
1880 /* Queue strategy things */
1881 int rrpos; /*!< Round Robin - position */
1882 int memberdelay; /*!< Seconds to delay connecting member to caller */
1883 int autofill; /*!< Ignore the head call status and ring an available agent */
1884
1885 int log_restricted_caller_id:1; /*!< Whether log Restricted Caller ID */
1886
1887 struct ao2_container *members; /*!< Head of the list of members */
1888 struct queue_ent *head; /*!< Head of the list of callers */
1889 AST_LIST_ENTRY(call_queue) list; /*!< Next call queue */
1890 AST_LIST_HEAD_NOLOCK(, penalty_rule) rules; /*!< The list of penalty rules to invoke */
1891};
1892
1894 char name[80];
1897};
1898
1900
1901static struct ao2_container *queues;
1902
1903static void update_realtime_members(struct call_queue *q);
1904static struct member *interface_exists(struct call_queue *q, const char *interface);
1905static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
1906static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime);
1907
1908static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface);
1909/*! \brief sets the QUEUESTATUS channel variable */
1910static void set_queue_result(struct ast_channel *chan, enum queue_result res)
1911{
1912 int i;
1913
1914 for (i = 0; i < ARRAY_LEN(queue_results); i++) {
1915 if (queue_results[i].id == res) {
1916 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
1917 return;
1918 }
1919 }
1920}
1921
1922static const char *int2strat(int strategy)
1923{
1924 int x;
1925
1926 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1927 if (strategy == strategies[x].strategy) {
1928 return strategies[x].name;
1929 }
1930 }
1931
1932 return "<unknown>";
1933}
1934
1935static int strat2int(const char *strategy)
1936{
1937 int x;
1938
1939 for (x = 0; x < ARRAY_LEN(strategies); x++) {
1940 if (!strcasecmp(strategy, strategies[x].name)) {
1941 return strategies[x].strategy;
1942 }
1943 }
1944
1945 return -1;
1946}
1947
1948static int autopause2int(const char *autopause)
1949{
1950 int x;
1951 /*This 'double check' that default value is OFF */
1953 return QUEUE_AUTOPAUSE_OFF;
1954 }
1955
1956 /*This 'double check' is to ensure old values works */
1957 if(ast_true(autopause)) {
1958 return QUEUE_AUTOPAUSE_ON;
1959 }
1960
1961 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
1962 if (!strcasecmp(autopause, autopausesmodes[x].name)) {
1963 return autopausesmodes[x].autopause;
1964 }
1965 }
1966
1967 /*This 'double check' that default value is OFF */
1968 return QUEUE_AUTOPAUSE_OFF;
1969}
1970
1971static int queue_hash_cb(const void *obj, const int flags)
1972{
1973 const struct call_queue *q = obj;
1974
1975 return ast_str_case_hash(q->name);
1976}
1977
1978static int queue_cmp_cb(void *obj, void *arg, int flags)
1979{
1980 struct call_queue *q = obj, *q2 = arg;
1981 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
1982}
1983
1984/*!
1985 * \brief Return wrapuptime
1986 *
1987 * This function checks if wrapuptime in member is set and return this value.
1988 * Otherwise return value the wrapuptime in the queue configuration
1989 * \return integer value
1990 */
1991static int get_wrapuptime(struct call_queue *q, struct member *member)
1992{
1993 if (member->wrapuptime) {
1994 return member->wrapuptime;
1995 }
1996 return q->wrapuptime;
1997}
1998
1999/*! \internal
2000 * \brief ao2_callback, Decreases queuepos of all followers with a queuepos greater than arg.
2001 * \param obj the member being acted on
2002 * \param arg pointer to an integer containing the position value that was removed and requires reduction for anything above
2003 * \param flag unused
2004 */
2005static int queue_member_decrement_followers(void *obj, void *arg, int flag)
2006{
2007 struct member *mem = obj;
2008 int *decrement_followers_after = arg;
2009
2010 if (mem->queuepos > *decrement_followers_after) {
2011 mem->queuepos--;
2012 }
2013
2014 return 0;
2015}
2016
2017/*! \internal
2018 * \brief ao2_callback, finds members in a queue marked for deletion and in a cascading fashion runs queue_member_decrement_followers
2019 * on them. This callback should always be ran before performing mass unlinking of delmarked members from queues.
2020 * \param obj member being acted on
2021 * \param arg pointer to the queue members are being removed from
2022 * \param flag unused
2023 */
2024static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
2025{
2026 struct member *mem = obj;
2027 struct call_queue *queue = arg;
2028 int rrpos = mem->queuepos;
2029
2030 if (mem->delme) {
2032 }
2033
2034 return 0;
2035}
2036
2037/*! \internal
2038 * \brief Use this to decrement followers during removal of a member
2039 * \param queue which queue the member is being removed from
2040 * \param mem which member is being removed from the queue
2041 */
2042static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
2043{
2044 int pos = mem->queuepos;
2045
2046 /* If the position being removed is less than the current place in the queue, reduce the queue position by one so that we don't skip the member
2047 * who would have been next otherwise. */
2048 if (pos < queue->rrpos) {
2049 queue->rrpos--;
2050 }
2051
2053}
2054
2055#define queue_ref(q) ao2_bump(q)
2056#define queue_unref(q) ({ ao2_cleanup(q); NULL; })
2057#define queue_t_ref(q, tag) ao2_t_bump(q, tag)
2058#define queue_t_unref(q, tag) ({ ao2_t_cleanup(q, tag); NULL; })
2059#define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
2060#define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2061
2062/*! \brief Set variables of queue */
2063static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
2064{
2065 char interfacevar[256]="";
2066 float sl = 0;
2067
2068 ao2_lock(q);
2069
2070 if (q->setqueuevar) {
2071 sl = 0;
2072 if (q->callscompleted > 0) {
2073 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
2074 }
2075
2076 snprintf(interfacevar, sizeof(interfacevar),
2077 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
2079
2080 ao2_unlock(q);
2081
2082 pbx_builtin_setvar_multiple(chan, interfacevar);
2083 } else {
2084 ao2_unlock(q);
2085 }
2086}
2087
2088/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
2089static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
2090{
2091 struct queue_ent *cur;
2092
2093 if (!q || !new)
2094 return;
2095 if (prev) {
2096 cur = prev->next;
2097 prev->next = new;
2098 } else {
2099 cur = q->head;
2100 q->head = new;
2101 }
2102 new->next = cur;
2103
2104 /* every queue_ent must have a reference to it's parent call_queue, this
2105 * reference does not go away until the end of the queue_ent's life, meaning
2106 * that even when the queue_ent leaves the call_queue this ref must remain. */
2107 if (!new->parent) {
2108 queue_ref(q);
2109 new->parent = q;
2110 }
2111 new->pos = ++(*pos);
2112 new->opos = *pos;
2113}
2114
2116{
2118 RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
2119 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2120
2121 channel_string = ast_manager_build_channel_state_string(obj->snapshot);
2122 event_string = ast_manager_str_from_json_object(obj->blob, NULL);
2123 if (!channel_string || !event_string) {
2124 return NULL;
2125 }
2126
2128 "%s"
2129 "%s",
2130 ast_str_buffer(channel_string),
2131 ast_str_buffer(event_string));
2132}
2133
2135{
2136 return queue_channel_to_ami("QueueCallerJoin", message);
2137}
2138
2140{
2141 return queue_channel_to_ami("QueueCallerLeave", message);
2142}
2143
2145{
2146 return queue_channel_to_ami("QueueCallerAbandon", message);
2147}
2148
2149STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_join_type,
2151 );
2152STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_leave_type,
2154 );
2155STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_caller_abandon_type,
2157 );
2158
2160{
2161 struct ast_json_payload *payload = stasis_message_data(message);
2162 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2163
2164 event_string = ast_manager_str_from_json_object(payload->json, NULL);
2165 if (!event_string) {
2166 return NULL;
2167 }
2168
2170 "%s",
2171 ast_str_buffer(event_string));
2172}
2173
2175{
2176 return queue_member_to_ami("QueueMemberStatus", message);
2177}
2178
2180{
2181 return queue_member_to_ami("QueueMemberAdded", message);
2182}
2183
2185{
2186 return queue_member_to_ami("QueueMemberRemoved", message);
2187}
2188
2190{
2191 return queue_member_to_ami("QueueMemberPause", message);
2192}
2193
2195{
2196 return queue_member_to_ami("QueueMemberPenalty", message);
2197}
2198
2200{
2201 return queue_member_to_ami("QueueMemberRinginuse", message);
2202}
2203
2204STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_status_type,
2206 );
2207STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_added_type,
2209 );
2210STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_removed_type,
2212 );
2213STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_pause_type,
2215 );
2216STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_penalty_type,
2218 );
2219STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_member_ringinuse_type,
2221 );
2222
2224{
2227 struct ast_channel_snapshot *agent;
2228 RAII_VAR(struct ast_str *, caller_event_string, NULL, ast_free);
2229 RAII_VAR(struct ast_str *, agent_event_string, NULL, ast_free);
2230 RAII_VAR(struct ast_str *, event_string, NULL, ast_free);
2231
2233 if (caller) {
2234 caller_event_string = ast_manager_build_channel_state_string(caller);
2235 if (!caller_event_string) {
2236 ast_log(LOG_NOTICE, "No caller event string, bailing\n");
2237 return NULL;
2238 }
2239 }
2240
2241 agent = ast_multi_channel_blob_get_channel(obj, "agent");
2242 if (agent) {
2243 agent_event_string = ast_manager_build_channel_state_string_prefix(agent, "Dest");
2244 if (!agent_event_string) {
2245 ast_log(LOG_NOTICE, "No agent event string, bailing\n");
2246 return NULL;
2247 }
2248 }
2249
2251 if (!event_string) {
2252 return NULL;
2253 }
2254
2256 "%s"
2257 "%s"
2258 "%s",
2259 caller_event_string ? ast_str_buffer(caller_event_string) : "",
2260 agent_event_string ? ast_str_buffer(agent_event_string) : "",
2261 ast_str_buffer(event_string));
2262}
2263
2265{
2266 return queue_multi_channel_to_ami("AgentCalled", message);
2267}
2268
2270{
2271 return queue_multi_channel_to_ami("AgentConnect", message);
2272}
2273
2275{
2276 return queue_multi_channel_to_ami("AgentComplete", message);
2277}
2278
2280{
2281 return queue_multi_channel_to_ami("AgentDump", message);
2282}
2283
2285{
2286 return queue_multi_channel_to_ami("AgentRingNoAnswer", message);
2287}
2288
2289STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_called_type,
2291 );
2292STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_connect_type,
2294 );
2295STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_complete_type,
2297 );
2300 );
2301STASIS_MESSAGE_TYPE_DEFN_LOCAL(queue_agent_ringnoanswer_type,
2303 );
2304
2306 struct ast_channel_snapshot *caller_snapshot,
2307 struct ast_channel_snapshot *agent_snapshot,
2308 struct stasis_message_type *type, struct ast_json *blob)
2309{
2310 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
2311 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2312
2313 if (!type) {
2314 return;
2315 }
2316
2317 payload = ast_multi_channel_blob_create(blob);
2318 if (!payload) {
2319 return;
2320 }
2321
2322 if (caller_snapshot) {
2323 ast_multi_channel_blob_add_channel(payload, "caller", caller_snapshot);
2324 } else {
2325 ast_debug(1, "Empty caller_snapshot; sending incomplete event\n");
2326 }
2327
2328 if (agent_snapshot) {
2329 ast_multi_channel_blob_add_channel(payload, "agent", agent_snapshot);
2330 }
2331
2332 msg = stasis_message_create(type, payload);
2333 if (!msg) {
2334 return;
2335 }
2336
2337 stasis_publish(topic, msg);
2338}
2339
2340static void queue_publish_multi_channel_blob(struct ast_channel *caller, struct ast_channel *agent,
2341 struct stasis_message_type *type, struct ast_json *blob)
2342{
2343 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
2344 RAII_VAR(struct ast_channel_snapshot *, agent_snapshot, NULL, ao2_cleanup);
2345
2346 ast_channel_lock(caller);
2347 caller_snapshot = ast_channel_snapshot_create(caller);
2348 ast_channel_unlock(caller);
2349 ast_channel_lock(agent);
2350 agent_snapshot = ast_channel_snapshot_create(agent);
2351 ast_channel_unlock(agent);
2352
2353 if (!caller_snapshot || !agent_snapshot) {
2354 return;
2355 }
2356
2358 agent_snapshot, type, blob);
2359}
2360
2361/*!
2362 * \internal
2363 * \brief Publish the member blob.
2364 * \since 12.0.0
2365 *
2366 * \param type Stasis message type to publish.
2367 * \param blob The information being published.
2368 *
2369 * \note The json blob reference is passed to this function.
2370 */
2372{
2373 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
2374 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
2375
2376 if (!blob || !type) {
2377 ast_json_unref(blob);
2378 return;
2379 }
2380
2381 payload = ast_json_payload_create(blob);
2382 ast_json_unref(blob);
2383 if (!payload) {
2384 return;
2385 }
2386
2387 msg = stasis_message_create(type, payload);
2388 if (!msg) {
2389 return;
2390 }
2391
2393}
2394
2395static struct ast_json *queue_member_blob_create(struct call_queue *q, struct member *mem)
2396{
2397 return ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: i, s: i, s: i, s: i, s: i, s: i, s: i, s: i, s: s, s: i, s: i}",
2398 "Queue", q->name,
2399 "MemberName", mem->membername,
2400 "Interface", mem->interface,
2401 "StateInterface", mem->state_interface,
2402 "Membership", (mem->dynamic ? "dynamic" : (mem->realtime ? "realtime" : "static")),
2403 "Penalty", mem->penalty,
2404 "CallsTaken", mem->calls,
2405 "LastCall", (int)mem->lastcall,
2406 "LastPause", (int)mem->lastpause,
2407 "LoginTime", (int)mem->logintime,
2408 "InCall", mem->starttime ? 1 : 0,
2409 "Status", mem->status,
2410 "Paused", mem->paused,
2411 "PausedReason", mem->reason_paused,
2412 "Ringinuse", mem->ringinuse,
2413 "Wrapuptime", mem->wrapuptime);
2414}
2415
2416/*! \brief Check if members are available
2417 *
2418 * This function checks to see if members are available to be called. If any member
2419 * is available, the function immediately returns 0. If no members are available,
2420 * then -1 is returned.
2421 */
2422static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
2423{
2424 struct member *member;
2425 struct ao2_iterator mem_iter;
2426
2427 ao2_lock(q);
2428 mem_iter = ao2_iterator_init(q->members, 0);
2429 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
2430 int penalty = member->penalty;
2431 if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2432 ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
2433 penalty = raise_penalty;
2434 }
2435 if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2436 if (conditions & QUEUE_EMPTY_PENALTY) {
2437 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
2438 continue;
2439 }
2440 }
2441
2442 switch (devstate ? ast_device_state(member->state_interface) : member->status) {
2443 case AST_DEVICE_INVALID:
2444 if (conditions & QUEUE_EMPTY_INVALID) {
2445 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
2446 break;
2447 }
2448 goto default_case;
2450 if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2451 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
2452 break;
2453 }
2454 goto default_case;
2455 case AST_DEVICE_INUSE:
2456 if (conditions & QUEUE_EMPTY_INUSE) {
2457 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
2458 break;
2459 }
2460 goto default_case;
2461 case AST_DEVICE_RINGING:
2462 if (conditions & QUEUE_EMPTY_RINGING) {
2463 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
2464 break;
2465 }
2466 goto default_case;
2467 case AST_DEVICE_UNKNOWN:
2468 if (conditions & QUEUE_EMPTY_UNKNOWN) {
2469 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
2470 break;
2471 }
2472 /* Fall-through */
2473 default:
2474 default_case:
2475 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2476 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
2477 break;
2478 } else if ((conditions & QUEUE_EMPTY_WRAPUP)
2479 && member->lastcall
2480 && get_wrapuptime(q, member)
2481 && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
2482 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2483 member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
2484 break;
2485 } else {
2486 ao2_ref(member, -1);
2487 ao2_iterator_destroy(&mem_iter);
2488 ao2_unlock(q);
2489 ast_debug(4, "%s is available.\n", member->membername);
2490 return 0;
2491 }
2492 break;
2493 }
2494 }
2495 ao2_iterator_destroy(&mem_iter);
2496 ao2_unlock(q);
2497
2498 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2499 /* member state still may be RINGING due to lag in event message - check again with device state */
2500 return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2501 }
2502 return -1;
2503}
2504
2505/*
2506 * A "pool" of member objects that calls are currently pending on. If an
2507 * agent is a member of multiple queues it's possible for that agent to be
2508 * called by each of the queues at the same time. This happens because device
2509 * state is slow to notify the queue app of one of it's member's being rung.
2510 * This "pool" allows us to track which members are currently being rung while
2511 * we wait on the device state change.
2512 */
2514#define MAX_CALL_ATTEMPT_BUCKETS 353
2515
2516static int pending_members_hash(const void *obj, const int flags)
2517{
2518 const struct member *object;
2519 const char *key;
2520
2521 switch (flags & OBJ_SEARCH_MASK) {
2522 case OBJ_SEARCH_KEY:
2523 key = obj;
2524 break;
2525 case OBJ_SEARCH_OBJECT:
2526 object = obj;
2527 key = object->interface;
2528 break;
2529 default:
2530 ast_assert(0);
2531 return 0;
2532 }
2533 return ast_str_case_hash(key);
2534}
2535
2536static int pending_members_cmp(void *obj, void *arg, int flags)
2537{
2538 const struct member *object_left = obj;
2539 const struct member *object_right = arg;
2540 const char *right_key = arg;
2541 int cmp;
2542
2543 switch (flags & OBJ_SEARCH_MASK) {
2544 case OBJ_SEARCH_OBJECT:
2545 right_key = object_right->interface;
2546 /* Fall through */
2547 case OBJ_SEARCH_KEY:
2548 cmp = strcasecmp(object_left->interface, right_key);
2549 break;
2551 /* Not supported by container. */
2552 ast_assert(0);
2553 return 0;
2554 default:
2555 cmp = 0;
2556 break;
2557 }
2558 if (cmp) {
2559 return 0;
2560 }
2561 return CMP_MATCH;
2562}
2563
2564static void pending_members_remove(struct member *mem)
2565{
2566 ast_debug(3, "Removed %s from pending_members\n", mem->membername);
2568}
2569
2570/*! \brief set a member's status based on device state of that member's state_interface.
2571 *
2572 * Lock interface list find sc, iterate through each queues queue_member list for member to
2573 * update state inside queues
2574*/
2575static void update_status(struct call_queue *q, struct member *m, const int status)
2576{
2577 if (m->status != status) {
2578 /* If this member has transitioned to being available then update their queue
2579 * information. If they are currently in a call then the leg to the agent will be
2580 * considered done and the call finished.
2581 */
2584 }
2585
2586 m->status = status;
2587
2588 /* Remove the member from the pending members pool only when the status changes.
2589 * This is not done unconditionally because we can occasionally see multiple
2590 * device state notifications of not in use after a previous call has ended,
2591 * including after we have initiated a new call. This is more likely to
2592 * happen when there is latency in the connection to the member.
2593 */
2595
2596 queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2597 }
2598}
2599
2600/*!
2601 * \internal
2602 * \brief Determine if a queue member is available
2603 * \retval 1 if the member is available
2604 * \retval 0 if the member is not available
2605 */
2606static int is_member_available(struct call_queue *q, struct member *mem)
2607{
2608 int available = 0;
2609 int wrapuptime;
2610
2611 switch (mem->status) {
2612 case AST_DEVICE_INVALID:
2614 break;
2615 case AST_DEVICE_INUSE:
2616 case AST_DEVICE_BUSY:
2617 case AST_DEVICE_RINGING:
2619 case AST_DEVICE_ONHOLD:
2620 if (!mem->ringinuse) {
2621 break;
2622 }
2623 /* else fall through */
2625 case AST_DEVICE_UNKNOWN:
2626 if (!mem->paused) {
2627 available = 1;
2628 }
2629 break;
2630 }
2631
2632 /* Let wrapuptimes override device state availability */
2633 wrapuptime = get_wrapuptime(q, mem);
2634 if (mem->lastcall && wrapuptime && (time(NULL) - wrapuptime < mem->lastcall)) {
2635 available = 0;
2636 }
2637 return available;
2638}
2639
2640/*! \brief set a member's status based on device state of that member's interface*/
2641static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
2642{
2643 struct ao2_iterator miter, qiter;
2644 struct ast_device_state_message *dev_state;
2645 struct member *m;
2646 struct call_queue *q;
2647 char interface[80], *slash_pos;
2648 int found = 0; /* Found this member in any queue */
2649 int found_member; /* Found this member in this queue */
2650 int avail = 0; /* Found an available member in this queue */
2651
2653 return;
2654 }
2655
2656 dev_state = stasis_message_data(msg);
2657 if (dev_state->eid) {
2658 /* ignore non-aggregate states */
2659 return;
2660 }
2661
2662 qiter = ao2_iterator_init(queues, 0);
2663 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
2664 ao2_lock(q);
2665
2666 avail = 0;
2667 found_member = 0;
2668 miter = ao2_iterator_init(q->members, 0);
2669 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2670 if (!found_member) {
2671 ast_copy_string(interface, m->state_interface, sizeof(interface));
2672
2673 if ((slash_pos = strchr(interface, '/'))) {
2674 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) {
2675 *slash_pos = '\0';
2676 }
2677 }
2678
2679 if (!strcasecmp(interface, dev_state->device)) {
2680 found_member = 1;
2681 update_status(q, m, dev_state->state);
2682 }
2683 }
2684
2685 /* check every member until we find one NOT_INUSE */
2686 if (!avail) {
2687 avail = is_member_available(q, m);
2688 }
2689 if (avail && found_member) {
2690 /* early exit as we've found an available member and the member of interest */
2691 ao2_ref(m, -1);
2692 break;
2693 }
2694 }
2695
2696 if (found_member) {
2697 found = 1;
2698 if (avail) {
2700 } else {
2702 }
2703 }
2704
2705 ao2_iterator_destroy(&miter);
2706
2707 ao2_unlock(q);
2708 queue_t_unref(q, "Done with iterator");
2709 }
2710 ao2_iterator_destroy(&qiter);
2711
2712 if (found) {
2713 ast_debug(1, "Device '%s' changed to state '%u' (%s)\n",
2714 dev_state->device,
2715 dev_state->state,
2716 ast_devstate2str(dev_state->state));
2717 } else {
2718 ast_debug(3, "Device '%s' changed to state '%u' (%s) but we don't care because they're not a member of any queue.\n",
2719 dev_state->device,
2720 dev_state->state,
2721 ast_devstate2str(dev_state->state));
2722 }
2723
2724 return;
2725}
2726
2727/*! \brief Helper function which converts from extension state to device state values */
2729{
2730 switch (state) {
2733 break;
2736 break;
2737 case AST_EXTENSION_BUSY:
2739 break;
2742 break;
2745 break;
2748 break;
2751 break;
2754 break;
2757 default:
2759 break;
2760 }
2761
2762 return state;
2763}
2764
2765/*!
2766 * \brief Returns if one context includes another context
2767 *
2768 * \param parent Parent context to search for child
2769 * \param child Context to check for inclusion in parent
2770 *
2771 * This function recursively checks if the context child is included in the context parent.
2772 *
2773 * \retval 1 if child is included in parent
2774 * \retval 0 if not
2775 */
2776static int context_included(const char *parent, const char *child);
2777static int context_included(const char *parent, const char *child)
2778{
2779 struct ast_context *c = NULL;
2780
2781 c = ast_context_find(parent);
2782 if (!c) {
2783 /* well, if parent doesn't exist, how can the child be included in it? */
2784 return 0;
2785 }
2786 if (!strcmp(ast_get_context_name(c), parent)) {
2787 /* found the context of the hint app_queue is using. Now, see
2788 if that context includes the one that just changed state */
2789 struct ast_include *inc = NULL;
2790
2791 while ((inc = (struct ast_include*) ast_walk_context_includes(c, inc))) {
2792 const char *includename = ast_get_include_name(inc);
2793 if (!strcasecmp(child, includename)) {
2794 return 1;
2795 }
2796 /* recurse on this context, for nested includes. The
2797 PBX extension parser will prevent infinite recursion. */
2798 if (context_included(includename, child)) {
2799 return 1;
2800 }
2801 }
2802 }
2803 return 0;
2804}
2805
2806static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
2807{
2808 struct ao2_iterator miter, qiter;
2809 struct member *m;
2810 struct call_queue *q;
2811 int state = info->exten_state;
2812 int found = 0, device_state = extensionstate2devicestate(state);
2813
2814 /* only interested in extension state updates involving device states */
2815 if (info->reason != AST_HINT_UPDATE_DEVICE) {
2816 return 0;
2817 }
2818
2819 qiter = ao2_iterator_init(queues, 0);
2820 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
2821 ao2_lock(q);
2822
2823 miter = ao2_iterator_init(q->members, 0);
2824 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
2825 if (!strcmp(m->state_exten, exten) &&
2827 /* context could be included in m->state_context. We need to check. */
2828 found = 1;
2829 update_status(q, m, device_state);
2830 }
2831 }
2832 ao2_iterator_destroy(&miter);
2833
2834 ao2_unlock(q);
2835 queue_t_unref(q, "Done with iterator");
2836 }
2837 ao2_iterator_destroy(&qiter);
2838
2839 if (found) {
2840 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
2841 } else {
2842 ast_debug(3, "Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n",
2843 exten, context, device_state, ast_devstate2str(device_state));
2844 }
2845
2846 return 0;
2847}
2848
2849/*! \brief Return the current state of a member */
2850static int get_queue_member_status(struct member *cur)
2851{
2853}
2854
2855static void destroy_queue_member_cb(void *obj)
2856{
2857 struct member *mem = obj;
2858
2859 if (mem->state_id != -1) {
2861 }
2862}
2863
2864/*! \brief allocate space for new queue member and set fields based on parameters passed */
2865static struct member *create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
2866{
2867 struct member *cur;
2868
2869 if ((cur = ao2_alloc(sizeof(*cur), destroy_queue_member_cb))) {
2870 cur->ringinuse = ringinuse;
2871 cur->penalty = penalty;
2872 cur->paused = paused;
2873 cur->wrapuptime = wrapuptime;
2874 if (paused) {
2875 time(&cur->lastpause); /* Update time of last pause */
2876 }
2877 time(&cur->logintime);
2878 ast_copy_string(cur->interface, interface, sizeof(cur->interface));
2881 } else {
2883 }
2885 ast_copy_string(cur->membername, membername, sizeof(cur->membername));
2886 } else {
2887 ast_copy_string(cur->membername, interface, sizeof(cur->membername));
2888 }
2889 if (!strchr(cur->interface, '/')) {
2890 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
2891 }
2892 if (!strncmp(cur->state_interface, "hint:", 5)) {
2893 char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
2894 char *exten = strsep(&context, "@") + 5;
2895
2896 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
2897 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
2898
2900 } else {
2901 cur->state_id = -1;
2902 }
2903 cur->status = get_queue_member_status(cur);
2904 }
2905
2906 return cur;
2907}
2908
2909
2910static int compress_char(const char c)
2911{
2912 if (c < 32) {
2913 return 0;
2914 } else if (c > 96) {
2915 return c - 64;
2916 }
2917 return c - 32;
2918}
2919
2920static int member_hash_fn(const void *obj, const int flags)
2921{
2922 const struct member *mem = obj;
2923 const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
2924 const char *chname = strchr(interface, '/');
2925 int ret = 0, i;
2926
2927 if (!chname) {
2928 chname = interface;
2929 }
2930 for (i = 0; i < 5 && chname[i]; i++) {
2931 ret += compress_char(chname[i]) << (i * 6);
2932 }
2933 return ret;
2934}
2935
2936static int member_cmp_fn(void *obj1, void *obj2, int flags)
2937{
2938 struct member *mem1 = obj1;
2939 struct member *mem2 = obj2;
2940 const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;
2941
2942 return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
2943}
2944
2945/*!
2946 * \brief Initialize Queue default values.
2947 * \note the queue's lock must be held before executing this function
2948*/
2949static void init_queue(struct call_queue *q)
2950{
2951 int i;
2952 struct penalty_rule *pr_iter;
2953
2954 q->dead = 0;
2955 q->retry = DEFAULT_RETRY;
2957 q->maxlen = 0;
2958
2959 ast_string_field_set(q, announce, "");
2961 ast_string_field_set(q, membergosub, "");
2962 ast_string_field_set(q, defaultrule, "");
2963
2964 q->announcefrequency = 0;
2966 q->announceholdtime = 1;
2968 q->announcepositionlimit = 10; /* Default 10 positions */
2969 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
2970 q->roundingseconds = 0; /* Default - don't announce seconds */
2971 q->servicelevel = 0;
2972 q->ringinuse = 1;
2974 q->setinterfacevar = 0;
2975 q->setqueuevar = 0;
2976 q->setqueueentryvar = 0;
2978 q->monfmt[0] = '\0';
2979 q->reportholdtime = 0;
2980 q->wrapuptime = 0;
2981 q->penaltymemberslimit = 0;
2982 q->joinempty = 0;
2983 q->leavewhenempty = 0;
2984 q->memberdelay = 0;
2985 q->weight = 0;
2986 q->timeoutrestart = 0;
2990 q->numperiodicannounce = 0;
2993 q->autopausebusy = 0;
2994 q->autopauseunavail = 0;
2996 q->autopausedelay = 0;
2998 if (!q->members) {
3000 /* linear strategy depends on order, so we have to place all members in a list */
3002 } else {
3005 }
3006 }
3007 q->found = 1;
3008
3009 ast_string_field_set(q, moh, "");
3010 ast_string_field_set(q, sound_next, "queue-youarenext");
3011 ast_string_field_set(q, sound_thereare, "queue-thereare");
3012 ast_string_field_set(q, sound_calls, "queue-callswaiting");
3013 ast_string_field_set(q, queue_quantity1, "queue-quantity1");
3014 ast_string_field_set(q, queue_quantity2, "queue-quantity2");
3015 ast_string_field_set(q, sound_holdtime, "queue-holdtime");
3016 ast_string_field_set(q, sound_minutes, "queue-minutes");
3017 ast_string_field_set(q, sound_minute, "queue-minute");
3018 ast_string_field_set(q, sound_seconds, "queue-seconds");
3019 ast_string_field_set(q, sound_thanks, "queue-thankyou");
3020 ast_string_field_set(q, sound_callerannounce, "");
3021 ast_string_field_set(q, sound_reporthold, "queue-reporthold");
3022
3023 if (!q->sound_periodicannounce[0]) {
3025 }
3026
3027 if (q->sound_periodicannounce[0]) {
3028 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
3029 }
3030
3031 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3032 if (q->sound_periodicannounce[i]) {
3033 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
3034 }
3035 }
3036
3037 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
3038 ast_free(pr_iter);
3039 }
3040
3041 /* On restart assume no members are available.
3042 * The queue_avail hint is a boolean state to indicate whether a member is available or not.
3043 *
3044 * This seems counter intuitive, but is required to light a BLF
3045 * AST_DEVICE_INUSE indicates no members are available.
3046 * AST_DEVICE_NOT_INUSE indicates a member is available.
3047 */
3049}
3050
3051static void clear_queue(struct call_queue *q)
3052{
3053 q->holdtime = 0;
3054 q->callscompleted = 0;
3055 q->callsabandoned = 0;
3056 q->callscompletedinsl = 0;
3057 q->callsabandonedinsl = 0;
3058 q->talktime = 0;
3059
3060 if (q->members) {
3061 struct member *mem;
3062 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3063 while ((mem = ao2_iterator_next(&mem_iter))) {
3064 mem->calls = 0;
3065 mem->callcompletedinsl = 0;
3066 mem->lastcall = 0;
3067 mem->starttime = 0;
3068 ao2_ref(mem, -1);
3069 }
3070 ao2_iterator_destroy(&mem_iter);
3071 }
3072}
3073
3074/*!
3075 * \brief Change queue penalty by adding rule.
3076 *
3077 * Check rule for errors with time or formatting, see if rule is relative to rest
3078 * of queue, iterate list of rules to find correct insertion point, insert and return.
3079 * \retval -1 on failure
3080 * \retval 0 on success
3081 * \note Call this with the rule_lists locked
3082*/
3083static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
3084{
3085 char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3086 struct penalty_rule *rule = NULL, *rule_iter;
3087 struct rule_list *rl_iter;
3088 int penaltychangetime, inserted = 0;
3089
3090 if (!(rule = ast_calloc(1, sizeof(*rule)))) {
3091 return -1;
3092 }
3093
3094 contentdup = ast_strdupa(content);
3095
3096 if (!(maxstr = strchr(contentdup, ','))) {
3097 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3098 ast_free(rule);
3099 return -1;
3100 }
3101
3102 *maxstr++ = '\0';
3103 if ((minstr = strchr(maxstr,','))) {
3104 *minstr++ = '\0';
3105 if ((raisestr = strchr(minstr,','))) {
3106 *raisestr++ = '\0';
3107 }
3108 } else {
3109 raisestr = NULL;
3110 }
3111
3112 timestr = contentdup;
3113 if ((penaltychangetime = atoi(timestr)) < 0) {
3114 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3115 ast_free(rule);
3116 return -1;
3117 }
3118
3119 rule->time = penaltychangetime;
3120
3121 /* The last check will evaluate true if either no penalty change is indicated for a given rule
3122 * OR if a min penalty change is indicated but no max penalty change is */
3123 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
3124 rule->max_relative = 1;
3125 }
3126
3127 rule->max_value = atoi(maxstr);
3128
3129 if (!ast_strlen_zero(minstr)) {
3130 if (*minstr == '+' || *minstr == '-') {
3131 rule->min_relative = 1;
3132 }
3133 rule->min_value = atoi(minstr);
3134 } else { /*there was no minimum specified, so assume this means no change*/
3135 rule->min_relative = 1;
3136 }
3137
3138 if (!ast_strlen_zero(raisestr)) {
3139 if (*raisestr == '+' || *raisestr == '-') {
3140 rule->raise_relative = 1;
3141 }
3142 rule->raise_value = atoi(raisestr);
3143 } else { /*there was no raise specified, so assume this means no change*/
3144 rule->raise_relative = 1;
3145 }
3146
3147 /*We have the rule made, now we need to insert it where it belongs*/
3148 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
3149 if (strcasecmp(rl_iter->name, list_name)) {
3150 continue;
3151 }
3152
3153 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
3154 if (rule->time < rule_iter->time) {
3156 inserted = 1;
3157 break;
3158 }
3159 }
3161
3162 if (!inserted) {
3163 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
3164 inserted = 1;
3165 }
3166
3167 break;
3168 }
3169
3170 if (!inserted) {
3171 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
3172 ast_free(rule);
3173 return -1;
3174 }
3175 return 0;
3176}
3177
3178/*!
3179 * \brief Load queue rules from realtime.
3180 *
3181 * Check rule for errors with time or formatting, see if rule is relative to rest
3182 * of queue, iterate list of rules to find correct insertion point, insert and return.
3183 * \retval -1 on failure
3184 * \retval 0 on success
3185 * \note Call this with the rule_lists locked
3186*/
3187static int load_realtime_rules(void)
3188{
3189 struct ast_config *cfg;
3190 struct rule_list *rl_iter, *new_rl;
3191 struct penalty_rule *pr_iter;
3192 char *rulecat = NULL;
3193
3194 if (!ast_check_realtime("queue_rules")) {
3195 ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
3196 return 0;
3197 }
3198 if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
3199 ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
3200 return 0;
3201 }
3202 while ((rulecat = ast_category_browse(cfg, rulecat))) {
3203 const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3204 int penaltychangetime, rule_exists = 0, inserted = 0;
3205 int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3206 int min_relative = 0, max_relative = 0, raise_relative = 0;
3207 struct penalty_rule *new_penalty_rule = NULL;
3208
3209 rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
3210 if (ast_strlen_zero(rule_name)) {
3211 continue;
3212 }
3213
3214 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
3215 if (!(strcasecmp(rl_iter->name, rule_name))) {
3216 rule_exists = 1;
3217 new_rl = rl_iter;
3218 break;
3219 }
3220 }
3221 if (!rule_exists) {
3222 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
3223 ast_config_destroy(cfg);
3224 return -1;
3225 }
3226 ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
3228 }
3229 timestr = ast_variable_retrieve(cfg, rulecat, "time");
3230 if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
3231 ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
3232 (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
3233 continue;
3234 }
3235 if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
3236 ast_config_destroy(cfg);
3237 return -1;
3238 }
3239 if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
3240 ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
3241 max_penalty = 0;
3242 max_relative = 1;
3243 } else {
3244 if (*maxstr == '+' || *maxstr == '-') {
3245 max_relative = 1;
3246 }
3247 }
3248 if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
3249 ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
3250 min_penalty = 0;
3251 min_relative = 1;
3252 } else {
3253 if (*minstr == '+' || *minstr == '-') {
3254 min_relative = 1;
3255 }
3256 }
3257 if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
3258 ast_strlen_zero(raisestr) || sscanf(raisestr, "%30d", &raise_penalty) != 1) {
3259 raise_penalty = 0;
3260 raise_relative = 1;
3261 } else {
3262 if (*raisestr == '+' || *raisestr == '-') {
3263 raise_relative = 1;
3264 }
3265 }
3266 new_penalty_rule->time = penaltychangetime;
3267 new_penalty_rule->max_relative = max_relative;
3268 new_penalty_rule->max_value = max_penalty;
3269 new_penalty_rule->min_relative = min_relative;
3270 new_penalty_rule->min_value = min_penalty;
3271 new_penalty_rule->raise_relative = raise_relative;
3272 new_penalty_rule->raise_value = raise_penalty;
3273 AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
3274 if (new_penalty_rule->time < pr_iter->time) {
3275 AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
3276 inserted = 1;
3277 }
3278 }
3280 if (!inserted) {
3281 AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
3282 }
3283 }
3284
3285 ast_config_destroy(cfg);
3286 return 0;
3287}
3288
3289static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
3290{
3291 char *value_copy = ast_strdupa(value);
3292 char *option = NULL;
3293 while ((option = strsep(&value_copy, ","))) {
3294 if (!strcasecmp(option, "paused")) {
3295 *empty |= QUEUE_EMPTY_PAUSED;
3296 } else if (!strcasecmp(option, "penalty")) {
3297 *empty |= QUEUE_EMPTY_PENALTY;
3298 } else if (!strcasecmp(option, "inuse")) {
3299 *empty |= QUEUE_EMPTY_INUSE;
3300 } else if (!strcasecmp(option, "ringing")) {
3301 *empty |= QUEUE_EMPTY_RINGING;
3302 } else if (!strcasecmp(option, "invalid")) {
3303 *empty |= QUEUE_EMPTY_INVALID;
3304 } else if (!strcasecmp(option, "wrapup")) {
3305 *empty |= QUEUE_EMPTY_WRAPUP;
3306 } else if (!strcasecmp(option, "unavailable")) {
3307 *empty |= QUEUE_EMPTY_UNAVAILABLE;
3308 } else if (!strcasecmp(option, "unknown")) {
3309 *empty |= QUEUE_EMPTY_UNKNOWN;
3310 } else if (!strcasecmp(option, "loose")) {
3312 } else if (!strcasecmp(option, "strict")) {
3314 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
3316 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
3317 *empty = 0;
3318 } else {
3319 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
3320 }
3321 }
3322}
3323
3324/*! \brief Configure a queue parameter.
3325 *
3326 * The failunknown flag is set for config files (and static realtime) to show
3327 * errors for unknown parameters. It is cleared for dynamic realtime to allow
3328 * extra fields in the tables.
3329 * \note For error reporting, line number is passed for .conf static configuration,
3330 * for Realtime queues, linenum is -1.
3331*/
3332static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
3333{
3334 if (!strcasecmp(param, "musicclass") ||
3335 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
3336 ast_string_field_set(q, moh, val);
3337 } else if (!strcasecmp(param, "announce")) {
3338 ast_string_field_set(q, announce, val);
3339 } else if (!strcasecmp(param, "context")) {
3341 } else if (!strcasecmp(param, "timeout")) {
3342 q->timeout = atoi(val);
3343 if (q->timeout < 0) {
3345 }
3346 } else if (!strcasecmp(param, "ringinuse")) {
3347 q->ringinuse = ast_true(val);
3348 } else if (!strcasecmp(param, "setinterfacevar")) {
3350 } else if (!strcasecmp(param, "setqueuevar")) {
3351 q->setqueuevar = ast_true(val);
3352 } else if (!strcasecmp(param, "setqueueentryvar")) {
3354 } else if (!strcasecmp(param, "monitor-format")) {
3355 ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
3356 } else if (!strcasecmp(param, "membergosub")) {
3357 ast_string_field_set(q, membergosub, val);
3358 } else if (!strcasecmp(param, "queue-youarenext")) {
3359 ast_string_field_set(q, sound_next, val);
3360 } else if (!strcasecmp(param, "queue-thereare")) {
3361 ast_string_field_set(q, sound_thereare, val);
3362 } else if (!strcasecmp(param, "queue-callswaiting")) {
3363 ast_string_field_set(q, sound_calls, val);
3364 } else if (!strcasecmp(param, "queue-quantity1")) {
3365 ast_string_field_set(q, queue_quantity1, val);
3366 } else if (!strcasecmp(param, "queue-quantity2")) {
3367 ast_string_field_set(q, queue_quantity2, val);
3368 } else if (!strcasecmp(param, "queue-holdtime")) {
3369 ast_string_field_set(q, sound_holdtime, val);
3370 } else if (!strcasecmp(param, "queue-minutes")) {
3371 ast_string_field_set(q, sound_minutes, val);
3372 } else if (!strcasecmp(param, "queue-minute")) {
3373 ast_string_field_set(q, sound_minute, val);
3374 } else if (!strcasecmp(param, "queue-seconds")) {
3375 ast_string_field_set(q, sound_seconds, val);
3376 } else if (!strcasecmp(param, "queue-thankyou")) {
3377 ast_string_field_set(q, sound_thanks, val);
3378 } else if (!strcasecmp(param, "queue-callerannounce")) {
3379 ast_string_field_set(q, sound_callerannounce, val);
3380 } else if (!strcasecmp(param, "queue-reporthold")) {
3381 ast_string_field_set(q, sound_reporthold, val);
3382 } else if (!strcasecmp(param, "announce-frequency")) {
3383 q->announcefrequency = atoi(val);
3384 } else if (!strcasecmp(param, "announce-to-first-user")) {
3386 } else if (!strcasecmp(param, "min-announce-frequency")) {
3387 q->minannouncefrequency = atoi(val);
3388 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
3389 } else if (!strcasecmp(param, "announce-round-seconds")) {
3390 q->roundingseconds = atoi(val);
3391 /* Rounding to any other values just doesn't make sense... */
3392 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
3393 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
3394 if (linenum >= 0) {
3395 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3396 "using 0 instead for queue '%s' at line %d of queues.conf\n",
3397 val, param, q->name, linenum);
3398 } else {
3399 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3400 "using 0 instead for queue '%s'\n", val, param, q->name);
3401 }
3402 q->roundingseconds=0;
3403 }
3404 } else if (!strcasecmp(param, "announce-holdtime")) {
3405 if (!strcasecmp(val, "once")) {
3407 } else if (ast_true(val)) {
3409 } else {
3410 q->announceholdtime = 0;
3411 }
3412 } else if (!strcasecmp(param, "announce-position")) {
3413 if (!strcasecmp(val, "limit")) {
3415 } else if (!strcasecmp(val, "more")) {
3417 } else if (ast_true(val)) {
3419 } else {
3421 }
3422 } else if (!strcasecmp(param, "announce-position-only-up")) {
3424 } else if (!strcasecmp(param, "announce-position-limit")) {
3425 q->announcepositionlimit = atoi(val);
3426 } else if (!strcasecmp(param, "periodic-announce")) {
3427 if (strchr(val, ',')) {
3428 char *s, *buf = ast_strdupa(val);
3429 unsigned int i = 0;
3430
3431 while ((s = strsep(&buf, ",|"))) {
3432 if (!q->sound_periodicannounce[i]) {
3434 }
3435 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
3436 i++;
3437 if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
3438 break;
3439 }
3440 }
3441 q->numperiodicannounce = i;
3442 } else {
3443 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
3444 q->numperiodicannounce = 1;
3445 }
3446 } else if (!strcasecmp(param, "periodic-announce-startdelay")) {
3448 } else if (!strcasecmp(param, "periodic-announce-frequency")) {
3449 q->periodicannouncefrequency = atoi(val);
3450 } else if (!strcasecmp(param, "relative-periodic-announce")) {
3452 } else if (!strcasecmp(param, "random-periodic-announce")) {
3454 } else if (!strcasecmp(param, "retry")) {
3455 q->retry = atoi(val);
3456 if (q->retry <= 0) {
3457 q->retry = DEFAULT_RETRY;
3458 }
3459 } else if (!strcasecmp(param, "wrapuptime")) {
3460 q->wrapuptime = atoi(val);
3461 } else if (!strcasecmp(param, "penaltymemberslimit")) {
3462 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
3463 q->penaltymemberslimit = 0;
3464 }
3465 } else if (!strcasecmp(param, "autofill")) {
3466 q->autofill = ast_true(val);
3467 } else if (!strcasecmp(param, "autopause")) {
3469 } else if (!strcasecmp(param, "autopausedelay")) {
3470 q->autopausedelay = atoi(val);
3471 } else if (!strcasecmp(param, "autopausebusy")) {
3473 } else if (!strcasecmp(param, "autopauseunavail")) {
3475 } else if (!strcasecmp(param, "maxlen")) {
3476 q->maxlen = atoi(val);
3477 if (q->maxlen < 0) {
3478 q->maxlen = 0;
3479 }
3480 } else if (!strcasecmp(param, "servicelevel")) {
3481 q->servicelevel= atoi(val);
3482 } else if (!strcasecmp(param, "strategy")) {
3483 int strategy;
3484
3485 /* We are a static queue and already have set this, no need to do it again */
3486 if (failunknown) {
3487 return;
3488 }
3490 if (strategy < 0) {
3491 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3492 val, q->name);
3494 }
3495 if (strategy == q->strategy) {
3496 return;
3497 }
3499 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
3500 return;
3501 }
3502 q->strategy = strategy;
3503 } else if (!strcasecmp(param, "joinempty")) {
3505 } else if (!strcasecmp(param, "leavewhenempty")) {
3507 } else if (!strcasecmp(param, "reportholdtime")) {
3509 } else if (!strcasecmp(param, "memberdelay")) {
3510 q->memberdelay = atoi(val);
3511 } else if (!strcasecmp(param, "weight")) {
3512 q->weight = atoi(val);
3513 } else if (!strcasecmp(param, "timeoutrestart")) {
3515 } else if (!strcasecmp(param, "defaultrule")) {
3516 ast_string_field_set(q, defaultrule, val);
3517 } else if (!strcasecmp(param, "timeoutpriority")) {
3518 if (!strcasecmp(val, "conf")) {
3520 } else {
3522 }
3523 } else if (!strcasecmp(param, "log-restricted-caller-id")) {
3525 } else if (failunknown) {
3526 if (linenum >= 0) {
3527 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3528 q->name, param, linenum);
3529 } else {
3530 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
3531 }
3532 }
3533}
3534
3535
3536#define QUEUE_PAUSED_DEVSTATE AST_DEVICE_INUSE
3537#define QUEUE_UNPAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3538#define QUEUE_UNKNOWN_PAUSED_DEVSTATE AST_DEVICE_NOT_INUSE
3539
3540/*! \internal
3541 * \brief If adding a single new member to a queue, use this function instead of ao2_linking.
3542 * This adds round robin queue position data for a fresh member as well as links it.
3543 * \param queue Which queue the member is being added to
3544 * \param mem Which member is being added to the queue
3545 */
3546static void member_add_to_queue(struct call_queue *queue, struct member *mem)
3547{
3548 ao2_lock(queue->members);
3549 mem->queuepos = ao2_container_count(queue->members);
3550 ao2_link(queue->members, mem);
3552 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", queue->name, mem->interface);
3553 ao2_unlock(queue->members);
3554}
3555
3556/*! \internal
3557 * \brief If removing a single member from a queue, use this function instead of ao2_unlinking.
3558 * This will perform round robin queue position reordering for the remaining members.
3559 * \param queue Which queue the member is being removed from
3560 * \param mem Which member is being removed from the queue
3561 */
3562static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
3563{
3565 ao2_lock(queue->members);
3568 ao2_unlink(queue->members, mem);
3569 ao2_unlock(queue->members);
3570}
3571
3572/*!
3573 * \brief Find rt member record to update otherwise create one.
3574 *
3575 * Search for member in queue, if found update penalty/paused state,
3576 * if no member exists create one flag it as a RT member and add to queue member list.
3577*/
3578static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
3579{
3580 struct member *m;
3581 struct ao2_iterator mem_iter;
3582 int penalty = 0;
3583 int paused = 0;
3584 int found = 0;
3585 int wrapuptime = 0;
3586 int ringinuse = q->ringinuse;
3587
3588 const char *config_val;
3589 const char *interface = ast_variable_retrieve(member_config, category, "interface");
3590 const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
3591 const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
3592 const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
3593 const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
3594 const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
3595 const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
3596 const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
3597
3598 if (ast_strlen_zero(rt_uniqueid)) {
3599 ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
3600 S_OR(membername, "NULL"));
3601 return;
3602 }
3603
3604 if (ast_strlen_zero(interface)) {
3605 ast_log(LOG_WARNING, "Realtime field 'interface' is empty for member %s\n",
3606 S_OR(membername, "NULL"));
3607 return;
3608 }
3609
3610 if (penalty_str) {
3611 penalty = atoi(penalty_str);
3612 if ((penalty < 0) && negative_penalty_invalid) {
3613 return;
3614 } else if (penalty < 0) {
3615 penalty = 0;
3616 }
3617 }
3618
3619 if (paused_str) {
3620 paused = atoi(paused_str);
3621 if (paused < 0) {
3622 paused = 0;
3623 }
3624 }
3625
3626 if (wrapuptime_str) {
3627 wrapuptime = atoi(wrapuptime_str);
3628 if (wrapuptime < 0) {
3629 wrapuptime = 0;
3630 }
3631 }
3632
3633 if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3634 if (ast_true(config_val)) {
3635 ringinuse = 1;
3636 } else if (ast_false(config_val)) {
3637 ringinuse = 0;
3638 } else {
3639 ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
3640 }
3641 }
3642
3643 /* Find member by realtime uniqueid and update */
3644 mem_iter = ao2_iterator_init(q->members, 0);
3645 while ((m = ao2_iterator_next(&mem_iter))) {
3646 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
3647 m->dead = 0; /* Do not delete this one. */
3648 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3649 if (paused_str) {
3650 m->paused = paused;
3651 if (paused && m->lastpause == 0) {
3652 time(&m->lastpause); /* XXX: Should this come from realtime? */
3653 }
3655 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, m->interface);
3656 }
3657 if (strcasecmp(state_interface, m->state_interface)) {
3658 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
3659 }
3660 m->penalty = penalty;
3661 m->ringinuse = ringinuse;
3662 m->wrapuptime = wrapuptime;
3664 ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
3665 }
3666 found = 1;
3667 ao2_ref(m, -1);
3668 break;
3669 }
3670 ao2_ref(m, -1);
3671 }
3672 ao2_iterator_destroy(&mem_iter);
3673
3674 /* Create a new member */
3675 if (!found) {
3676 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3677 m->dead = 0;
3678 m->realtime = 1;
3679 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3680 if (!ast_strlen_zero(reason_paused)) {
3681 ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
3682 }
3684 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3685 } else {
3686 ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3687 }
3688 member_add_to_queue(q, m);
3689 ao2_ref(m, -1);
3690 m = NULL;
3691 }
3692 }
3693}
3694
3695/*! \brief Iterate through queue's member list and delete them */
3696static void free_members(struct call_queue *q, int all)
3697{
3698 /* Free non-dynamic members */
3699 struct member *cur;
3700 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
3701
3702 while ((cur = ao2_iterator_next(&mem_iter))) {
3703 if (all || !cur->dynamic) {
3705 }
3706 ao2_ref(cur, -1);
3707 }
3708 ao2_iterator_destroy(&mem_iter);
3709}
3710
3711/*! \brief Free queue's member list then its string fields */
3712static void destroy_queue(void *obj)
3713{
3714 struct call_queue *q = obj;
3715 int i;
3716
3717 free_members(q, 1);
3719 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3720 if (q->sound_periodicannounce[i]) {
3722 }
3723 }
3724 ao2_ref(q->members, -1);
3725}
3726
3727static struct call_queue *alloc_queue(const char *queuename)
3728{
3729 struct call_queue *q;
3730
3731 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
3732 if (ast_string_field_init(q, 64)) {
3733 queue_t_unref(q, "String field allocation failed");
3734 return NULL;
3735 }
3736 ast_string_field_set(q, name, queuename);
3737 }
3738 return q;
3739}
3740
3741/*!
3742 * \brief Reload a single queue via realtime.
3743 *
3744 * Check for statically defined queue first, check if deleted RT queue,
3745 * check for new RT queue, if queue vars are not defined init them with defaults.
3746 * reload RT queue vars, set RT queue members dead and reload them, return finished queue.
3747 * \retval the queue,
3748 * \retval NULL if it doesn't exist.
3749 * \note Should be called with the "queues" container locked.
3750*/
3751static struct call_queue *find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
3752{
3753 struct ast_variable *v;
3754 struct call_queue *q, tmpq = {
3755 .name = queuename,
3756 };
3757 struct member *m;
3758 struct ao2_iterator mem_iter;
3759 char *category = NULL;
3760 const char *tmp_name;
3761 char *tmp;
3762 char tmpbuf[64]; /* Must be longer than the longest queue param name. */
3763
3764 /* Static queues override realtime. */
3765 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
3766 ao2_lock(q);
3767 if (!q->realtime) {
3768 if (q->dead) {
3769 ao2_unlock(q);
3770 queue_t_unref(q, "Queue is dead; can't return it");
3771 return NULL;
3772 }
3773 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
3774 ao2_unlock(q);
3775 return q;
3776 }
3777 } else if (!member_config) {
3778 /* Not found in the list, and it's not realtime ... */
3779 return NULL;
3780 }
3781 /* Check if queue is defined in realtime. */
3782 if (!queue_vars) {
3783 /* Delete queue from in-core list if it has been deleted in realtime. */
3784 if (q) {
3785 /*! \note Hmm, can't seem to distinguish a DB failure from a not
3786 found condition... So we might delete an in-core queue
3787 in case of DB failure. */
3788 ast_debug(1, "Queue %s not found in realtime.\n", queuename);
3789
3790 q->dead = 1;
3791 /* Delete if unused (else will be deleted when last caller leaves). */
3792 queues_t_unlink(queues, q, "Unused; removing from container");
3793 ao2_unlock(q);
3794 queue_t_unref(q, "Queue is dead; can't return it");
3795 }
3796 return NULL;
3797 }
3798
3799 /* Create a new queue if an in-core entry does not exist yet. */
3800 if (!q) {
3801 struct ast_variable *tmpvar = NULL;
3802 if (!(q = alloc_queue(queuename))) {
3803 return NULL;
3804 }
3805 ao2_lock(q);
3806 clear_queue(q);
3807 q->realtime = 1;
3808 /*Before we initialize the queue, we need to set the strategy, so that linear strategy
3809 * will allocate the members properly
3810 */
3811 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
3812 if (!strcasecmp(tmpvar->name, "strategy")) {
3813 q->strategy = strat2int(tmpvar->value);
3814 if (q->strategy < 0) {
3815 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3816 tmpvar->value, q->name);
3818 }
3819 break;
3820 }
3821 }
3822 /* We traversed all variables and didn't find a strategy */
3823 if (!tmpvar) {
3825 }
3826 queues_t_link(queues, q, "Add queue to container");
3827 }
3828 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
3829
3830 memset(tmpbuf, 0, sizeof(tmpbuf));
3831 for (v = queue_vars; v; v = v->next) {
3832 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
3833 if (strchr(v->name, '_')) {
3834 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
3835 tmp_name = tmpbuf;
3836 tmp = tmpbuf;
3837 while ((tmp = strchr(tmp, '_'))) {
3838 *tmp++ = '-';
3839 }
3840 } else {
3841 tmp_name = v->name;
3842 }
3843
3844 /* NULL values don't get returned from realtime; blank values should
3845 * still get set. If someone doesn't want a value to be set, they
3846 * should set the realtime column to NULL, not blank. */
3847 queue_set_param(q, tmp_name, v->value, -1, 0);
3848 }
3849
3850 /* Temporarily set realtime members dead so we can detect deleted ones. */
3851 mem_iter = ao2_iterator_init(q->members, 0);
3852 while ((m = ao2_iterator_next(&mem_iter))) {
3853 if (m->realtime) {
3854 m->dead = 1;
3855 }
3856 ao2_ref(m, -1);
3857 }
3858 ao2_iterator_destroy(&mem_iter);
3859
3860 while ((category = ast_category_browse(member_config, category))) {
3861 rt_handle_member_record(q, category, member_config);
3862 }
3863
3864 /* Delete all realtime members that have been deleted in DB. */
3865 mem_iter = ao2_iterator_init(q->members, 0);
3866 while ((m = ao2_iterator_next(&mem_iter))) {
3867 if (m->dead) {
3869 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
3870 } else {
3871 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
3872 }
3874 }
3875 ao2_ref(m, -1);
3876 }
3877 ao2_iterator_destroy(&mem_iter);
3878
3879 ao2_unlock(q);
3880
3881 return q;
3882}
3883
3884/*!
3885 * note */
3886
3887/*!
3888 * \internal
3889 * \brief Returns reference to the named queue. If the queue is realtime, it will load the queue as well.
3890 * \param queuename - name of the desired queue
3891 *
3892 * \retval the queue
3893 * \retval NULL if it doesn't exist
3894 */
3895static struct call_queue *find_load_queue_rt_friendly(const char *queuename)
3896{
3897 struct ast_variable *queue_vars;
3898 struct ast_config *member_config = NULL;
3899 struct call_queue *q = NULL, tmpq = {
3900 .name = queuename,
3901 };
3902 int prev_weight = 0;
3903
3904 /* Find the queue in the in-core list first. */
3905 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
3906
3907 if (!q || q->realtime) {
3908 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
3909 queue operations while waiting for the DB.
3910
3911 This will be two separate database transactions, so we might
3912 see queue parameters as they were before another process
3913 changed the queue and member list as it was after the change.
3914 Thus we might see an empty member list when a queue is
3915 deleted. In practise, this is unlikely to cause a problem. */
3916
3917 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
3918 if (queue_vars) {
3919 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
3920 if (!member_config) {
3921 ast_debug(1, "No queue_members defined in config extconfig.conf\n");
3922 member_config = ast_config_new();
3923 }
3924 }
3925 if (q) {
3926 prev_weight = q->weight ? 1 : 0;
3927 queue_t_unref(q, "Need to find realtime queue");
3928 }
3929
3930 q = find_queue_by_name_rt(queuename, queue_vars, member_config);
3931 ast_config_destroy(member_config);
3932 ast_variables_destroy(queue_vars);
3933
3934 /* update the use_weight value if the queue's has gained or lost a weight */
3935 if (q) {
3936 if (!q->weight && prev_weight) {
3938 }
3939 if (q->weight && !prev_weight) {
3941 }
3942 }
3943 /* Other cases will end up with the proper value for use_weight */
3944 } else {
3946 }
3947 return q;
3948}
3949
3950/*!
3951 * \internal
3952 * \brief Load queues and members from realtime.
3953 *
3954 * \param queuename - name of the desired queue to load or empty if need to load all queues
3955*/
3956static void load_realtime_queues(const char *queuename)
3957{
3958 struct ast_config *cfg = NULL;
3959 char *category = NULL;
3960 const char *name = NULL;
3961 struct call_queue *q = NULL;
3962
3963 if (!ast_check_realtime("queues")) {
3964 return;
3965 }
3966
3967 if (ast_strlen_zero(queuename)) {
3968 if ((cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL))) {
3969 while ((category = ast_category_browse(cfg, category))) {
3970 name = ast_variable_retrieve(cfg, category, "name");
3972 queue_unref(q);
3973 }
3974 }
3975 ast_config_destroy(cfg);
3976 }
3977 } else {
3978 if ((q = find_load_queue_rt_friendly(queuename))) {
3979 queue_unref(q);
3980 }
3981 }
3982}
3983
3984static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
3985{
3986 int ret = -1;
3987
3988 if (ast_strlen_zero(mem->rt_uniqueid)) {
3989 return ret;
3990 }
3991
3992 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) >= 0) {
3993 ret = 0;
3994 }
3995
3996 return ret;
3997}
3998
3999
4001{
4002 struct ast_config *member_config = NULL;
4003 struct member *m;
4004 char *category = NULL;
4005 struct ao2_iterator mem_iter;
4006
4007 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
4008 /* This queue doesn't have realtime members. If the queue still has any realtime
4009 * members in memory, they need to be removed.
4010 */
4011 ao2_lock(q);
4012 mem_iter = ao2_iterator_init(q->members, 0);
4013 while ((m = ao2_iterator_next(&mem_iter))) {
4014 if (m->realtime) {
4016 }
4017 ao2_ref(m, -1);
4018 }
4019 ao2_iterator_destroy(&mem_iter);
4020 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
4021 ao2_unlock(q);
4022 return;
4023 }
4024
4025 ao2_lock(q);
4026
4027 /* Temporarily set realtime members dead so we can detect deleted ones.*/
4028 mem_iter = ao2_iterator_init(q->members, 0);
4029 while ((m = ao2_iterator_next(&mem_iter))) {
4030 if (m->realtime) {
4031 m->dead = 1;
4032 }
4033 ao2_ref(m, -1);
4034 }
4035 ao2_iterator_destroy(&mem_iter);
4036
4037 while ((category = ast_category_browse(member_config, category))) {
4038 rt_handle_member_record(q, category, member_config);
4039 }
4040
4041 /* Delete all realtime members that have been deleted in DB. */
4042 mem_iter = ao2_iterator_init(q->members, 0);
4043 while ((m = ao2_iterator_next(&mem_iter))) {
4044 if (m->dead) {
4046 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
4047 } else {
4048 ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
4049 }
4051 }
4052 ao2_ref(m, -1);
4053 }
4054 ao2_iterator_destroy(&mem_iter);
4055 ao2_unlock(q);
4056 ast_config_destroy(member_config);
4057}
4058
4059static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
4060{
4061 struct call_queue *q;
4062 struct queue_ent *cur, *prev = NULL;
4063 int res = -1;
4064 int pos = 0;
4065 int inserted = 0;
4066
4067 if (!(q = find_load_queue_rt_friendly(queuename))) {
4068 return res;
4069 }
4070 ao2_lock(q);
4071
4072 /* This is our one */
4073 if (q->joinempty) {
4074 int status = 0;
4075 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, qe->raise_penalty, q->joinempty, 0))) {
4076 *reason = QUEUE_JOINEMPTY;
4077 ao2_unlock(q);
4078 queue_t_unref(q, "Done with realtime queue");
4079 return res;
4080 }
4081 }
4082 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) {
4083 *reason = QUEUE_FULL;
4084 } else if (*reason == QUEUE_UNKNOWN) {
4085 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4086
4087 /* There's space for us, put us at the right position inside
4088 * the queue.
4089 * Take into account the priority of the calling user */
4090 inserted = 0;
4091 prev = NULL;
4092 cur = q->head;
4093 while (cur) {
4094 /* We have higher priority than the current user, enter
4095 * before him, after all the other users with priority
4096 * higher or equal to our priority. */
4097 if ((!inserted) && (qe->prio > cur->prio)) {
4098 insert_entry(q, prev, qe, &pos);
4099 inserted = 1;
4100 }
4101 /* <= is necessary for the position comparison because it may not be possible to enter
4102 * at our desired position since higher-priority callers may have taken the position we want
4103 */
4104 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
4105 insert_entry(q, prev, qe, &pos);
4106 inserted = 1;
4107 /*pos is incremented inside insert_entry, so don't need to add 1 here*/
4108 if (position < pos) {
4109 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
4110 }
4111 }
4112 cur->pos = ++pos;
4113 prev = cur;
4114 cur = cur->next;
4115 }
4116 /* No luck, join at the end of the queue */
4117 if (!inserted) {
4118 insert_entry(q, prev, qe, &pos);
4119 }
4120 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
4121 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
4122 ast_copy_string(qe->context, q->context, sizeof(qe->context));
4123 q->count++;
4124 if (q->count == 1) {
4126 }
4127
4128 res = 0;
4129
4130 blob = ast_json_pack("{s: s, s: i, s: i}",
4131 "Queue", q->name,
4132 "Position", qe->pos,
4133 "Count", q->count);
4134 ast_channel_publish_cached_blob(qe->chan, queue_caller_join_type(), blob);
4135 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, ast_channel_name(qe->chan), qe->pos );
4136 }
4137 ao2_unlock(q);
4138 queue_t_unref(q, "Done with realtime queue");
4139
4140 return res;
4141}
4142
4143static int play_file(struct ast_channel *chan, const char *filename)
4144{
4145 int res;
4146
4147 if (ast_strlen_zero(filename)) {
4148 return 0;
4149 }
4150
4151 if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
4152 return 0;
4153 }
4154
4156
4157 res = ast_streamfile(chan, filename, ast_channel_language(chan));
4158 if (!res) {
4160 }
4161
4163
4164 return res;
4165}
4166
4167/*!
4168 * \brief Check for valid exit from queue via goto
4169 * \retval 0 if failure
4170 * \retval 1 if successful
4171*/
4172static int valid_exit(struct queue_ent *qe, char digit)
4173{
4174 int digitlen = strlen(qe->digits);
4175
4176 /* Prevent possible buffer overflow */
4177 if (digitlen < sizeof(qe->digits) - 2) {
4178 qe->digits[digitlen] = digit;
4179 qe->digits[digitlen + 1] = '\0';
4180 } else {
4181 qe->digits[0] = '\0';
4182 return 0;
4183 }
4184
4185 /* If there's no context to goto, short-circuit */
4186 if (ast_strlen_zero(qe->context)) {
4187 return 0;
4188 }
4189
4190 /* If the extension is bad, then reset the digits to blank */
4191 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
4193 qe->digits[0] = '\0';
4194 return 0;
4195 }
4196
4197 /* We have an exact match */
4198 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
4199 qe->valid_digits = 1;
4200 /* Return 1 on a successful goto */
4201 return 1;
4202 }
4203
4204 return 0;
4205}
4206
4207static int say_position(struct queue_ent *qe, int ringing)
4208{
4209 int res = 0, say_thanks = 0;
4210 long avgholdmins, avgholdsecs;
4211 time_t now;
4212
4213 /* Let minannouncefrequency seconds pass between the start of each position announcement */
4214 time(&now);
4215 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) {
4216 return 0;
4217 }
4218
4219 /* If either our position has changed, or we are over the freq timer, say position */
4220 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) {
4221 return 0;
4222 }
4223
4224 /* Only announce if the caller's queue position has improved since last time */
4225 if (qe->parent->announceposition_only_up && qe->last_pos_said <= qe->pos) {
4226 return 0;
4227 }
4228
4229 if (ringing) {
4230 ast_indicate(qe->chan,-1);
4231 } else {
4232 ast_moh_stop(qe->chan);
4233 }
4234
4238 qe->pos <= qe->parent->announcepositionlimit)) {
4239 say_thanks = 1;
4240 /* Say we're next, if we are */
4241 if (qe->pos == 1) {
4242 res = play_file(qe->chan, qe->parent->sound_next);
4243 if (!res) {
4244 goto posout;
4245 }
4246 /* Say there are more than N callers */
4248 res = (
4249 play_file(qe->chan, qe->parent->queue_quantity1) ||
4251 ast_channel_language(qe->chan), NULL) || /* Needs gender */
4253 /* Say there are currently N callers waiting */
4254 } else {
4255 res = (
4256 play_file(qe->chan, qe->parent->sound_thereare) ||
4258 ast_channel_language(qe->chan), "n") || /* Needs gender */
4259 play_file(qe->chan, qe->parent->sound_calls));
4260 }
4261 if (res) {
4262 goto playout;
4263 }
4264 }
4265 /* Round hold time to nearest minute */
4266 avgholdmins = labs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
4267
4268 /* If they have specified a rounding then round the seconds as well */
4269 if (qe->parent->roundingseconds) {
4270 avgholdsecs = (labs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
4271 avgholdsecs *= qe->parent->roundingseconds;
4272 } else {
4273 avgholdsecs = 0;
4274 }
4275
4276 ast_verb(3, "Hold time for %s is %ld minute(s) %ld seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
4277
4278 /* If the hold time is >1 min, if it's enabled, and if it's not
4279 supposed to be only once and we have already said it, say it */
4280 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
4283 say_thanks = 1;
4284 res = play_file(qe->chan, qe->parent->sound_holdtime);
4285 if (res) {
4286 goto playout;
4287 }
4288
4289 if (avgholdmins >= 1) {
4290 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4291 if (res) {
4292 goto playout;
4293 }
4294
4295 if (avgholdmins == 1) {
4296 res = play_file(qe->chan, qe->parent->sound_minute);
4297 if (res) {
4298 goto playout;
4299 }
4300 } else {
4301 res = play_file(qe->chan, qe->parent->sound_minutes);
4302 if (res) {
4303 goto playout;
4304 }
4305 }
4306 }
4307 if (avgholdsecs >= 1) {
4308 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, ast_channel_language(qe->chan), "n");
4309 if (res) {
4310 goto playout;
4311 }
4312
4313 res = play_file(qe->chan, qe->parent->sound_seconds);
4314 if (res) {
4315 goto playout;
4316 }
4317 }
4318 }
4319
4320posout:
4321 if (qe->parent->announceposition) {
4322 ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
4323 ast_channel_name(qe->chan), qe->parent->name, qe->pos);
4324 }
4325 if (say_thanks) {
4326 res = play_file(qe->chan, qe->parent->sound_thanks);
4327 }
4328playout:
4329
4330 if ((res > 0 && !valid_exit(qe, res))) {
4331 res = 0;
4332 }
4333
4334 /* Set our last_pos indicators */
4335 qe->last_pos = now;
4336 qe->last_pos_said = qe->pos;
4337
4338 /* Don't restart music on hold if we're about to exit the caller from the queue */
4339 if (!res) {
4340 if (ringing) {
4342 } else {
4343 ast_moh_start(qe->chan, qe->moh, NULL);
4344 }
4345 }
4346 return res;
4347}
4348
4349static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
4350{
4351 int oldvalue;
4352
4353 /* Calculate holdtime using an exponential average */
4354 /* Thanks to SRT for this contribution */
4355 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
4356
4357 ao2_lock(qe->parent);
4358 if ((qe->parent->callscompleted + qe->parent->callsabandoned) == 0) {
4359 qe->parent->holdtime = newholdtime;
4360 } else {
4361 oldvalue = qe->parent->holdtime;
4362 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
4363 }
4364 ao2_unlock(qe->parent);
4365}
4366
4367/*! \brief Caller leaving queue.
4368 *
4369 * Search the queue to find the leaving client, if found remove from queue
4370 * create manager event, move others up the queue.
4371*/
4372static void leave_queue(struct queue_ent *qe)
4373{
4374 struct call_queue *q;
4375 struct queue_ent *current, *prev = NULL;
4376 struct penalty_rule *pr_iter;
4377 int pos = 0;
4378
4379 if (!(q = qe->parent)) {
4380 return;
4381 }
4382 queue_t_ref(q, "Copy queue pointer from queue entry");
4383 ao2_lock(q);
4384
4385 prev = NULL;
4386 for (current = q->head; current; current = current->next) {
4387 if (current == qe) {
4388 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4389 char posstr[20];
4390 q->count--;
4391 if (!q->count) {
4393 }
4394
4395 blob = ast_json_pack("{s: s, s: i, s: i}",
4396 "Queue", q->name,
4397 "Position", qe->pos,
4398 "Count", q->count);
4399 ast_channel_publish_cached_blob(qe->chan, queue_caller_leave_type(), blob);
4400 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
4401 /* Take us out of the queue */
4402 if (prev) {
4403 prev->next = current->next;
4404 } else {
4405 q->head = current->next;
4406 }
4407 /* Free penalty rules */
4408 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
4409 ast_free(pr_iter);
4410 }
4411 qe->pr = NULL;
4412 snprintf(posstr, sizeof(posstr), "%d", qe->pos);
4413 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
4414 } else {
4415 /* Renumber the people after us in the queue based on a new count */
4416 current->pos = ++pos;
4417 prev = current;
4418 }
4419 }
4420 ao2_unlock(q);
4421
4422 /*If the queue is a realtime queue, check to see if it's still defined in real time*/
4423 if (q->realtime) {
4424 struct ast_variable *var;
4425 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
4426 q->dead = 1;
4427 } else {
4429 }
4430 }
4431
4432 if (q->dead) {
4433 /* It's dead and nobody is in it, so kill it */
4434 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
4435 }
4436 /* unref the explicit ref earlier in the function */
4437 queue_t_unref(q, "Expire copied reference");
4438}
4439
4440/*!
4441 * \internal
4442 * \brief Destroy the given callattempt structure and free it.
4443 * \since 1.8
4444 *
4445 * \param doomed callattempt structure to destroy.
4446 */
4447static void callattempt_free(struct callattempt *doomed)
4448{
4449 if (doomed->member) {
4450 ao2_ref(doomed->member, -1);
4451 }
4453 ast_free(doomed->orig_chan_name);
4454 ast_free(doomed);
4455}
4456
4457static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
4458{
4459 struct callattempt *cur;
4460
4461 for (cur = outgoing; cur; cur = cur->q_next) {
4462 if (cur->chan && cur->chan != exception) {
4464 }
4465 }
4466}
4467
4468/*! \brief Hang up a list of outgoing calls */
4469static void hangupcalls(struct queue_ent *qe, struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
4470{
4471 struct callattempt *oo;
4472
4473 while (outgoing) {
4474 /* If someone else answered the call we should indicate this in the CANCEL */
4475 /* Hangup any existing lines we have open */
4476 if (outgoing->chan && (outgoing->chan != exception)) {
4477 if (exception || cancel_answered_elsewhere) {
4479 }
4480 ast_channel_publish_dial(qe->chan, outgoing->chan, outgoing->interface, "CANCEL");
4481
4482 /* When dialing channels it is possible that they may not ever
4483 * leave the not in use state (Local channels in particular) by
4484 * the time we cancel them. If this occurs but we know they were
4485 * dialed we explicitly remove them from the pending members
4486 * container so that subsequent call attempts occur.
4487 */
4488 if (outgoing->member->status == AST_DEVICE_NOT_INUSE) {
4490 }
4491
4492 ast_hangup(outgoing->chan);
4493 }
4494 oo = outgoing;
4495 outgoing = outgoing->q_next;
4497 callattempt_free(oo);
4498 }
4499}
4500
4501/*!
4502 * \brief Get the number of members available to accept a call.
4503 *
4504 * \note The queue passed in should be locked prior to this function call
4505 *
4506 * \param[in] q The queue for which we are counting the number of available members
4507 * \return Return the number of available members in queue q
4508 */
4510{
4511 struct member *mem;
4512 int avl = 0;
4513 struct ao2_iterator mem_iter;
4514
4515 mem_iter = ao2_iterator_init(q->members, 0);
4516 while ((mem = ao2_iterator_next(&mem_iter))) {
4517
4518 avl += is_member_available(q, mem);
4519 ao2_ref(mem, -1);
4520
4521 /* If autofill is not enabled or if the queue's strategy is ringall, then
4522 * we really don't care about the number of available members so much as we
4523 * do that there is at least one available.
4524 *
4525 * In fact, we purposely will return from this function stating that only
4526 * one member is available if either of those conditions hold. That way,
4527 * functions which determine what action to take based on the number of available
4528 * members will operate properly. The reasoning is that even if multiple
4529 * members are available, only the head caller can actually be serviced.
4530 */
4531 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4532 break;
4533 }
4534 }
4535 ao2_iterator_destroy(&mem_iter);
4536
4537 return avl;
4538}
4539
4540/* traverse all defined queues which have calls waiting and contain this member
4541 return 0 if no other queue has precedence (higher weight) or 1 if found */
4542static int compare_weight(struct call_queue *rq, struct member *member)
4543{
4544 struct call_queue *q;
4545 struct member *mem;
4546 int found = 0;
4547 struct ao2_iterator queue_iter;
4548
4549 queue_iter = ao2_iterator_init(queues, 0);
4550 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4551 if (q == rq) { /* don't check myself, could deadlock */
4552 queue_t_unref(q, "Done with iterator");
4553 continue;
4554 }
4555 ao2_lock(q);
4556 if (q->count && q->members) {
4557 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4558 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4559 if (q->weight > rq->weight && q->count >= num_available_members(q)) {
4560 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);
4561 found = 1;
4562 }
4563 ao2_ref(mem, -1);
4564 }
4565 }
4566 ao2_unlock(q);
4567 queue_t_unref(q, "Done with iterator");
4568 if (found) {
4569 break;
4570 }
4571 }
4572 ao2_iterator_destroy(&queue_iter);
4573 return found;
4574}
4575
4576static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
4577{
4578 struct call_queue *q;
4579 struct member *mem;
4580 int is_longest_waiting = 1;
4581 struct ao2_iterator queue_iter;
4582 struct queue_ent *ch;
4583
4584 queue_iter = ao2_iterator_init(queues, 0);
4585 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
4586 if (q == caller->parent) { /* don't check myself, could deadlock */
4587 queue_t_unref(q, "Done with iterator");
4588 continue;
4589 }
4590 ao2_lock(q);
4591 /*
4592 * If the other queue has equal weight, see if we should let that handle
4593 * their call first. If weights are not equal, compare_weights will step in.
4594 */
4595 if (q->weight == caller->parent->weight && q->count && q->members) {
4596 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
4597 ast_debug(2, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
4598
4599 /* Does this queue have a caller that's been waiting longer? */
4600 ch = q->head;
4601 while (ch) {
4602 /* If ch->pending, the other call (which may be waiting for a longer period of time),
4603 * is already ringing at another agent. Ignore such callers; otherwise, all agents
4604 * will be unused until the first caller is picked up.
4605 */
4606 if (ch->start < caller->start && !ch->pending) {
4607 ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",
4608 q->name, ch->pos, ch->start, caller->start);
4609 is_longest_waiting = 0;
4610 break;
4611 }
4612 ch = ch->next;
4613 }
4614 }
4615 }
4616 ao2_unlock(q);
4617 queue_t_unref(q, "Done with iterator");
4618 if (!is_longest_waiting) {
4619 break;
4620 }
4621 }
4622 ao2_iterator_destroy(&queue_iter);
4623 return is_longest_waiting;
4624}
4625
4626/*! \brief common hangup actions */
4627static void do_hang(struct callattempt *o)
4628{
4629 o->stillgoing = 0;
4630 ast_hangup(o->chan);
4632 o->chan = NULL;
4633}
4634
4635/*!
4636 * \internal
4637 * \brief Check if the member status is available.
4638 *
4639 * \param status Member status to check if available.
4640 *
4641 * \retval non-zero if the member status is available.
4642 */
4644{
4646}
4647
4648/*!
4649 * \internal
4650 * \brief Determine if can ring a queue entry.
4651 *
4652 * \param qe Queue entry to check.
4653 * \param call Member call attempt.
4654 *
4655 * \retval non-zero if an entry can be called.
4656 */
4657static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
4658{
4659 struct member *memberp = call->member;
4660 int wrapuptime;
4661
4662 if (memberp->paused) {
4663 ast_debug(1, "%s paused, can't receive call\n", call->interface);
4664 return 0;
4665 }
4666
4667 if (!memberp->ringinuse && !member_status_available(memberp->status)) {
4668 ast_debug(1, "%s not available, can't receive call\n", call->interface);
4669 return 0;
4670 }
4671
4672 if (memberp->lastqueue) {
4673 wrapuptime = get_wrapuptime(memberp->lastqueue, memberp);
4674 } else {
4675 wrapuptime = get_wrapuptime(qe->parent, memberp);
4676 }
4677 if (wrapuptime && (time(NULL) - memberp->lastcall) < wrapuptime) {
4678 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
4679 (memberp->lastqueue ? memberp->lastqueue->name : qe->parent->name),
4680 call->interface);
4681 return 0;
4682 }
4683
4684 if (use_weight && compare_weight(qe->parent, memberp)) {
4685 ast_debug(1, "Priority queue delaying call to %s:%s\n",
4686 qe->parent->name, call->interface);
4687 return 0;
4688 }
4689
4691 ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n",
4692 qe->parent->name, call->interface);
4693 return 0;
4694 }
4695
4696 if (!memberp->ringinuse) {
4697 struct member *mem;
4698
4700
4701 mem = ao2_find(pending_members, memberp,
4703 if (mem) {
4704 /*
4705 * If found that means this member is currently being attempted
4706 * from another calling thread, so stop trying from this thread
4707 */
4708 ast_debug(1, "%s has another call trying, can't receive call\n",
4709 call->interface);
4710 ao2_ref(mem, -1);
4712 return 0;
4713 }
4714
4715 /*
4716 * If not found add it to the container so another queue
4717 * won't attempt to call this member at the same time.
4718 */
4719 ast_debug(3, "Add %s to pending_members\n", memberp->membername);
4720 ao2_link(pending_members, memberp);
4722
4723 /*
4724 * The queue member is available. Get current status to be sure
4725 * because the device state and extension state callbacks may
4726 * not have updated the status yet.
4727 */
4729 ast_debug(1, "%s actually not available, can't receive call\n",
4730 call->interface);
4731 pending_members_remove(memberp);
4732 return 0;
4733 }
4734 }
4735
4736 return 1;
4737}
4738
4739/*!
4740 * \brief Part 2 of ring_one
4741 *
4742 * Does error checking before attempting to request a channel and call a member.
4743 * This function is only called from ring_one().
4744 * Failure can occur if:
4745 * - Agent on call
4746 * - Agent is paused
4747 * - Wrapup time not expired
4748 * - Priority by another queue
4749 *
4750 * \retval 1 on success to reach a free agent
4751 * \retval 0 on failure to get agent.
4752 */
4753static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
4754{
4755 int res;
4756 int status;
4757 char tech[256];
4758 char *location;
4759 struct ast_format_cap *nativeformats;
4760 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4761
4762 /* on entry here, we know that tmp->chan == NULL */
4763 if (!can_ring_entry(qe, tmp)) {
4764 tmp->stillgoing = 0;
4765 ++*busies;
4766 return 0;
4767 }
4768
4769 ast_copy_string(tech, tmp->interface, sizeof(tech));
4770 if ((location = strchr(tech, '/'))) {
4771 *location++ = '\0';
4772 } else {
4773 location = "";
4774 }
4775
4777 nativeformats = ao2_bump(ast_channel_nativeformats(qe->chan));
4779
4780 /* Request the peer */
4781 tmp->chan = ast_request(tech, nativeformats, NULL, qe->chan, location, &status);
4782 ao2_cleanup(nativeformats);
4783 if (!tmp->chan) { /* If we can't, just go on to the next call */
4784 ao2_lock(qe->parent);
4785 qe->parent->rrpos++;
4786 qe->linpos++;
4787 ao2_unlock(qe->parent);
4788
4789 pending_members_remove(tmp->member);
4790
4791 publish_dial_end_event(qe->chan, tmp, NULL, "BUSY");
4792 tmp->stillgoing = 0;
4793 ++*busies;
4794 return 0;
4795 }
4796
4797 ast_channel_lock_both(tmp->chan, qe->chan);
4798
4801 if (qe->cancel_answered_elsewhere) {
4803 }
4804 ast_channel_appl_set(tmp->chan, "AppQueue");
4805 ast_channel_data_set(tmp->chan, "(Outgoing Line)");
4806 memset(ast_channel_whentohangup(tmp->chan), 0, sizeof(*ast_channel_whentohangup(tmp->chan)));
4807
4808 /* If the new channel has no callerid, try to guess what it should be */
4809 if (!ast_channel_caller(tmp->chan)->id.number.valid) {
4811 struct ast_party_caller caller;
4812
4814 caller.id = ast_channel_connected(qe->chan)->id;
4815 caller.ani = ast_channel_connected(qe->chan)->ani;
4816 ast_channel_set_caller_event(tmp->chan, &caller, NULL);
4817 } else if (!ast_strlen_zero(ast_channel_dialed(qe->chan)->number.str)) {
4819 } else if (!ast_strlen_zero(ast_channel_exten(qe->chan))) {
4821 }
4822 tmp->dial_callerid_absent = 1;
4823 }
4824
4826
4828
4830
4831 /* Inherit specially named variables from parent channel */
4835
4836 /* Presense of ADSI CPE on outgoing channel follows ours */
4838
4839 /* Inherit context and extension */
4840 ast_channel_dialcontext_set(tmp->chan, ast_channel_context(qe->chan));
4842
4843 /* Save the original channel name to detect call pickup masquerading in. */
4844 tmp->orig_chan_name = ast_strdup(ast_channel_name(tmp->chan));
4845
4846 ast_channel_unlock(tmp->chan);
4848
4849 /* location is tmp->interface where tech/ has been stripped, so it follow the same syntax as DIALEDPEERNUMBER in app_dial.c */
4850 pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", strlen(location) ? location : tmp->interface);
4851
4852 /* PREDIAL: Run gosub on the callee's channel */
4853 if (qe->predial_callee) {
4854 ast_pre_call(tmp->chan, qe->predial_callee);
4855 }
4856
4857 /* Place the call, but don't wait on the answer */
4858 if ((res = ast_call(tmp->chan, location, 0))) {
4859 /* Again, keep going even if there's an error */
4860 ast_verb(3, "Couldn't call %s\n", tmp->interface);
4861 do_hang(tmp);
4862 ++*busies;
4863 return 0;
4864 }
4865
4866 ast_channel_lock_both(tmp->chan, qe->chan);
4867
4868 blob = ast_json_pack("{s: s, s: s, s: s}",
4869 "Queue", qe->parent->name,
4870 "Interface", tmp->interface,
4871 "MemberName", tmp->member->membername);
4872 queue_publish_multi_channel_blob(qe->chan, tmp->chan, queue_agent_called_type(), blob);
4873
4874 ast_channel_publish_dial(qe->chan, tmp->chan, tmp->interface, NULL);
4875
4876 ast_channel_unlock(tmp->chan);
4878
4879 ast_verb(3, "Called %s\n", tmp->interface);
4880
4881 return 1;
4882}
4883
4884/*! \brief find the entry with the best metric, or NULL */
4886{
4887 struct callattempt *best = NULL, *cur;
4888
4889 for (cur = outgoing; cur; cur = cur->q_next) {
4890 if (cur->stillgoing && /* Not already done */
4891 !cur->chan && /* Isn't already going */
4892 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */
4893 best = cur;
4894 }
4895 }
4896
4897 return best;
4898}
4899
4900/*!
4901 * \brief Place a call to a queue member.
4902 *
4903 * Once metrics have been calculated for each member, this function is used
4904 * to place a call to the appropriate member (or members). The low-level
4905 * channel-handling and error detection is handled in ring_entry
4906 *
4907 * \retval 1 if a member was called successfully
4908 * \retval 0 otherwise
4909 */
4910static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
4911{
4912 int ret = 0;
4913 struct callattempt *cur;
4914
4915 if (qe->predial_callee) {
4917 for (cur = outgoing; cur; cur = cur->q_next) {
4918 if (cur->stillgoing && cur->chan) {
4920 }
4921 }
4922 }
4923
4924 while (ret == 0) {
4925 struct callattempt *best = find_best(outgoing);
4926 if (!best) {
4927 ast_debug(1, "Nobody left to try ringing in queue\n");
4928 break;
4929 }
4931 /* Ring everyone who shares this best metric (for ringall) */
4932 for (cur = outgoing; cur; cur = cur->q_next) {
4933 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
4934 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
4935 ret |= ring_entry(qe, cur, busies);
4936 if (qe->predial_callee && cur->chan) {
4938 }
4939 }
4940 }
4941 } else {
4942 /* Ring just the best channel */
4943 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
4944 ret = ring_entry(qe, best, busies);
4945 if (qe->predial_callee && best->chan) {
4947 }
4948 }
4949
4950 /* If we have timed out, break out */
4951 if (qe->expire && (time(NULL) >= qe->expire)) {
4952 ast_debug(1, "Queue timed out while ringing members.\n");
4953 ret = 0;
4954 break;
4955 }
4956 }
4957 if (qe->predial_callee) {
4958 for (cur = outgoing; cur; cur = cur->q_next) {
4959 if (cur->stillgoing && cur->chan) {
4961 }
4962 }
4964 }
4965
4966 return ret;
4967}
4968
4969/*! \brief Search for best metric and add to Round Robbin queue */
4970static int store_next_rr(struct queue_ent *qe, struct callattempt *outgoing)
4971{
4972 struct callattempt *best = find_best(outgoing);
4973
4974 if (best) {
4975 /* Ring just the best channel */
4976 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
4977 qe->parent->rrpos = best->metric % 1000;
4978 } else {
4979 /* Just increment rrpos */
4980 if (qe->parent->wrapped) {
4981 /* No more channels, start over */
4982 qe->parent->rrpos = 0;
4983 } else {
4984 /* Prioritize next entry */
4985 qe->parent->rrpos++;
4986 }
4987 }
4988 qe->parent->wrapped = 0;
4989
4990 return 0;
4991}
4992
4993/*! \brief Search for best metric and add to Linear queue */
4994static int store_next_lin(struct queue_ent *qe, struct callattempt *outgoing)
4995{
4996 struct callattempt *best = find_best(outgoing);
4997
4998 if (best) {
4999 /* Ring just the best channel */
5000 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
5001 qe->linpos = best->metric % 1000;
5002 } else {
5003 /* Just increment rrpos */
5004 if (qe->linwrapped) {
5005 /* No more channels, start over */
5006 qe->linpos = 0;
5007 } else {
5008 /* Prioritize next entry */
5009 qe->linpos++;
5010 }
5011 }
5012 qe->linwrapped = 0;
5013
5014 return 0;
5015}
5016
5017/*! \brief Playback announcement to queued members if period has elapsed */
5019{
5020 int res = 0;
5021 time_t now;
5022
5023 /* Get the current time */
5024 time(&now);
5025
5026 /* Check to see if it is time to announce */
5028 return 0;
5029 }
5030
5031 /* Stop the music on hold so we can play our own file */
5032 if (ringing) {
5033 ast_indicate(qe->chan,-1);
5034 } else {
5035 ast_moh_stop(qe->chan);
5036 }
5037
5038 ast_verb(3, "Playing periodic announcement\n");
5039
5041 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
5045 }
5046
5047 /* play the announcement */
5049
5050 if (res > 0 && !valid_exit(qe, res)) {
5051 res = 0;
5052 }
5053
5054 /* Resume Music on Hold if the caller is going to stay in the queue */
5055 if (!res) {
5056 if (ringing) {
5058 } else {
5059 ast_moh_start(qe->chan, qe->moh, NULL);
5060 }
5061 }
5062
5063 /* update last_periodic_announce_time */
5065 time(&qe->last_periodic_announce_time);
5066 } else {
5068 }
5069
5070 /* Update the current periodic announcement to the next announcement */
5071 if (!qe->parent->randomperiodicannounce) {
5073 }
5074
5075 return res;
5076}
5077
5078/*! \brief Record that a caller gave up on waiting in queue */
5079static void record_abandoned(struct queue_ent *qe)
5080{
5081 int callabandonedinsl = 0;
5082 time_t now;
5083
5084 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5085
5086 pbx_builtin_setvar_helper(qe->chan, "ABANDONED", "TRUE");
5087
5089 ao2_lock(qe->parent);
5090 blob = ast_json_pack("{s: s, s: i, s: i, s: i}",
5091 "Queue", qe->parent->name,
5092 "Position", qe->pos,
5093 "OriginalPosition", qe->opos,
5094 "HoldTime", (int)(time(NULL) - qe->start));
5095
5096
5097 time(&now);
5098 callabandonedinsl = ((now - qe->start) <= qe->parent->servicelevel);
5099 if (callabandonedinsl) {
5101 }
5102
5103 qe->parent->callsabandoned++;
5104 ao2_unlock(qe->parent);
5105
5106 ast_channel_publish_cached_blob(qe->chan, queue_caller_abandon_type(), blob);
5107}
5108
5109/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
5110static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, char *interface, char *membername, int autopause)
5111{
5112 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
5113
5114 ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
5115
5116 /* Stop ringing, and resume MOH if specified */
5117 if (qe->ring_when_ringing) {
5118 ast_indicate(qe->chan, -1);
5119 ast_moh_start(qe->chan, qe->moh, NULL);
5120 }
5121
5122 blob = ast_json_pack("{s: s, s: s, s: s, s: i}",
5123 "Queue", qe->parent->name,
5124 "Interface", interface,
5125 "MemberName", membername,
5126 "RingTime", rnatime);
5127 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_ringnoanswer_type(), blob);
5128
5129 ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), membername, "RINGNOANSWER", "%d", rnatime);
5131 if (qe->parent->autopausedelay > 0) {
5132 struct member *mem;
5133 ao2_lock(qe->parent);
5134 if ((mem = interface_exists(qe->parent, interface))) {
5135 time_t idletime = time(&idletime)-mem->lastcall;
5136 if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
5137 ao2_unlock(qe->parent);
5138 ao2_ref(mem, -1);
5139 return;
5140 }
5141 ao2_ref(mem, -1);
5142 }
5143 ao2_unlock(qe->parent);
5144 }
5145 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
5146 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
5147 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
5148 interface, qe->parent->name);
5149 } else {
5150 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
5151 }
5152 } else {
5153 /* If queue autopause is mode all, just don't send any queue to stop.
5154 * the function will stop in all queues */
5155 if (!set_member_paused("", interface, "Auto-Pause", 1)) {
5156 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
5157 interface, qe->parent->name);
5158 } else {
5159 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
5160 }
5161 }
5162 }
5163 return;
5164}
5165
5166/*!
5167 * \internal
5168 * \brief Update connected line on chan from peer.
5169 * \since 13.6.0
5170 *
5171 * \param chan Channel to get connected line updated.
5172 * \param peer Channel providing connected line information.
5173 * \param is_caller Non-zero if chan is the calling channel.
5174 */
5175static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
5176{
5177 struct ast_party_connected_line connected_caller;
5178
5179 ast_party_connected_line_init(&connected_caller);
5180
5181 ast_channel_lock(peer);
5183 ast_channel_unlock(peer);
5185 if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)) {
5186 ast_channel_update_connected_line(chan, &connected_caller, NULL);
5187 }
5188 ast_party_connected_line_free(&connected_caller);
5189}
5190
5191#define AST_MAX_WATCHERS 256
5192/*!
5193 * \brief Wait for a member to answer the call
5194 *
5195 * \param[in] qe the queue_ent corresponding to the caller in the queue
5196 * \param[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
5197 * \param[in] to the amount of time (in milliseconds) to wait for a response
5198 * \param[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
5199 * \param[in] prebusies number of busy members calculated prior to calling wait_for_answer
5200 * \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
5201 * \param[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()
5202 *
5203 * \todo eventually all call forward logic should be integrated into and replaced by ast_call_forward()
5204 */
5205static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
5206{
5207 const char *queue = qe->parent->name;
5208 struct callattempt *o, *start = NULL, *prev = NULL;
5209 int status;
5210 int numbusies = prebusies;
5211 int numnochan = 0;
5212 int stillgoing = 0;
5213 int orig = *to;
5214 struct ast_frame *f;
5215 struct callattempt *peer = NULL;
5216 struct ast_channel *winner;
5217 struct ast_channel *in = qe->chan;
5218 char on[80] = "";
5219 char membername[80] = "";
5220 long starttime = 0;
5221 long endtime = 0;
5222 char *inchan_name;
5223 struct timeval start_time_tv = ast_tvnow();
5224 int canceled_by_caller = 0; /* 1 when caller hangs up or press digit or press * */
5225
5227 inchan_name = ast_strdupa(ast_channel_name(qe->chan));
5229
5230 starttime = (long) time(NULL);
5231
5232 while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
5233 int numlines, retry, pos = 1;
5234 struct ast_channel *watchers[AST_MAX_WATCHERS];
5235 watchers[0] = in;
5236 start = NULL;
5237
5238 for (retry = 0; retry < 2; retry++) {
5239 numlines = 0;
5240 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
5241 if (o->stillgoing) { /* Keep track of important channels */
5242 stillgoing = 1;
5243 if (o->chan) {
5244 if (pos < AST_MAX_WATCHERS) {
5245 watchers[pos++] = o->chan;
5246 }
5247 if (!start) {
5248 start = o;
5249 } else {
5250 prev->call_next = o;
5251 }
5252 prev = o;
5253 }
5254 } else if (prev) {
5255 prev->call_next = NULL;
5256 }
5257 numlines++;
5258 }
5259 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
5260 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) {
5261 break;
5262 }
5263 /* On "ringall" strategy we only move to the next penalty level
5264 when *all* ringing phones are done in the current penalty level */
5265 ring_one(qe, outgoing, &numbusies);
5266 /* and retry... */
5267 }
5268 if (pos == 1 /* not found */) {
5269 if (numlines == (numbusies + numnochan)) {
5270 ast_debug(1, "Everyone is busy at this time\n");
5271 } else {
5272 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
5273 }
5274 *to = 0;
5275 return NULL;
5276 }
5277
5278 /* Poll for events from both the incoming channel as well as any outgoing channels */
5279 winner = ast_waitfor_n(watchers, pos, to);
5280
5281 /* Service all of the outgoing channels */
5282 for (o = start; o; o = o->call_next) {
5283 /* We go with a fixed buffer here instead of using ast_strdupa. Using
5284 * ast_strdupa in a loop like this one can cause a stack overflow
5285 */
5286 char ochan_name[AST_CHANNEL_NAME];
5287
5288 if (o->chan) {
5290 ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
5292 }
5293 if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
5294 if (!peer) {
5295 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5296 if (o->orig_chan_name
5297 && strcmp(o->orig_chan_name, ochan_name)) {
5298 /*
5299 * The channel name changed so we must generate COLP update.
5300 * Likely because a call pickup channel masqueraded in.
5301 */
5303 } else if (!o->block_connected_update) {
5304 if (o->pending_connected_update) {
5307 }
5308 } else if (!o->dial_callerid_absent) {
5310 }
5311 }
5312 if (o->aoc_s_rate_list) {
5313 size_t encoded_size;
5314 struct ast_aoc_encoded *encoded;
5315 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5316 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5317 ast_aoc_destroy_encoded(encoded);
5318 }
5319 }
5320 peer = o;
5321 }
5322 } else if (o->chan && (o->chan == winner)) {
5323
5324 ast_copy_string(on, o->member->interface, sizeof(on));
5325 ast_copy_string(membername, o->member->membername, sizeof(membername));
5326
5327 /* Before processing channel, go ahead and check for forwarding */
5328 if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
5329 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
5331 "CANCEL", ast_channel_call_forward(o->chan));
5332 numnochan++;
5333 do_hang(o);
5334 winner = NULL;
5335 continue;
5337 struct ast_channel *original = o->chan;
5338 char forwarder[AST_CHANNEL_NAME];
5339 char tmpchan[256];
5340 char *stuff;
5341 char *tech;
5342 int failed = 0;
5343
5344 ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
5345 ast_copy_string(forwarder, ast_channel_name(o->chan), sizeof(forwarder));
5346 if ((stuff = strchr(tmpchan, '/'))) {
5347 *stuff++ = '\0';
5348 tech = tmpchan;
5349 } else {
5350 const char *forward_context;
5352 forward_context = pbx_builtin_getvar_helper(o->chan, "FORWARD_CONTEXT");
5353 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), forward_context ? forward_context : ast_channel_context(o->chan));
5355 stuff = tmpchan;
5356 tech = "Local";
5357 }
5358 if (!strcasecmp(tech, "Local")) {
5359 /*
5360 * Drop the connected line update block for local channels since
5361 * this is going to run dialplan and the user can change his
5362 * mind about what connected line information he wants to send.
5363 */
5365 }
5366
5367 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
5368 /* Setup parameters */
5370 if (!o->chan) {
5372 "Forwarding failed to create channel to dial '%s/%s'\n",
5373 tech, stuff);
5374 o->stillgoing = 0;
5375 numnochan++;
5376 } else {
5377 ast_channel_lock_both(o->chan, original);
5379 ast_channel_redirecting(original));
5381 ast_channel_unlock(original);
5382
5386 pbx_builtin_setvar_helper(o->chan, "FORWARDERNAME", forwarder);
5388
5389 if (o->pending_connected_update) {
5390 /*
5391 * Re-seed the callattempt's connected line information with
5392 * previously acquired connected line info from the queued
5393 * channel. The previously acquired connected line info could
5394 * have been set through the CONNECTED_LINE dialplan function.
5395 */
5398 }
5399
5402
5404
5407 /*
5408 * The call was not previously redirected so it is
5409 * now redirected from this number.
5410 */
5416 }
5417
5419
5424
5427 && !o->block_connected_update) {
5428 struct ast_party_redirecting redirecting;
5429
5430 /*
5431 * Redirecting updates to the caller make sense only on single
5432 * call at a time strategies.
5433 *
5434 * Need to re-evaluate if calling unlock is still required as we no longer
5435 * use macro.
5436 */
5437 ast_party_redirecting_init(&redirecting);
5440 if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0)) {
5441 ast_channel_update_redirecting(in, &redirecting, NULL);
5442 }
5443 ast_party_redirecting_free(&redirecting);
5444 } else {
5446 }
5447
5448 if (ast_call(o->chan, stuff, 0)) {
5449 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
5450 tech, stuff);
5451 failed = 1;
5452 }
5453 }
5454
5456 "CANCEL", ast_channel_call_forward(original));
5457 if (o->chan) {
5458 ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
5459 }
5460
5461 if (failed) {
5462 do_hang(o);
5463 numnochan++;
5464 }
5465
5466 /* Hangup the original channel now, in case we needed it */
5467 ast_hangup(winner);
5468 continue;
5469 }
5470 f = ast_read(winner);
5471 if (f) {
5472 if (f->frametype == AST_FRAME_CONTROL) {
5473 switch (f->subclass.integer) {
5474 case AST_CONTROL_ANSWER:
5475 /* This is our guy if someone answered. */
5476 if (!peer) {
5477 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5478 ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
5479 publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
5480 if (o->orig_chan_name
5481 && strcmp(o->orig_chan_name, ochan_name)) {
5482 /*
5483 * The channel name changed so we must generate COLP update.
5484 * Likely because a call pickup channel masqueraded in.
5485 */
5487 } else if (!o->block_connected_update) {
5488 if (o->pending_connected_update) {
5491 }
5492 } else if (!o->dial_callerid_absent) {
5494 }
5495 }
5496 if (o->aoc_s_rate_list) {
5497 size_t encoded_size;
5498 struct ast_aoc_encoded *encoded;
5499 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5500 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5501 ast_aoc_destroy_encoded(encoded);
5502 }
5503 }
5504 peer = o;
5505 }
5506 break;
5507 case AST_CONTROL_BUSY:
5508 ast_verb(3, "%s is busy\n", ochan_name);
5509 ast_channel_publish_dial(qe->chan, o->chan, on, "BUSY");
5510 endtime = (long) time(NULL);
5511 endtime -= starttime;
5512 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopausebusy);
5513 do_hang(o);
5515 if (qe->parent->timeoutrestart) {
5516 start_time_tv = ast_tvnow();
5517 }
5518 /* Have enough time for a queue member to answer? */
5519 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5520 ring_one(qe, outgoing, &numbusies);
5521 starttime = (long) time(NULL);
5522 }
5523 }
5524 numbusies++;
5525 break;
5527 ast_verb(3, "%s is circuit-busy\n", ochan_name);
5528 ast_channel_publish_dial(qe->chan, o->chan, on, "CONGESTION");
5529 endtime = (long) time(NULL);
5530 endtime -= starttime;
5531 rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopauseunavail);
5532 do_hang(o);
5534 if (qe->parent->timeoutrestart) {
5535 start_time_tv = ast_tvnow();
5536 }
5537 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5538 ring_one(qe, outgoing, &numbusies);
5539 starttime = (long) time(NULL);
5540 }
5541 }
5542 numbusies++;
5543 break;
5545 ast_verb(3, "%s is ringing\n", ochan_name);
5546
5547 ast_channel_publish_dial(qe->chan, o->chan, on, "RINGING");
5548
5549 /* Start ring indication when the channel is ringing, if specified */
5550 if (qe->ring_when_ringing) {
5551 ast_moh_stop(qe->chan);
5553 }
5554 break;
5556 /* Ignore going off hook */
5557 break;
5559 if (o->block_connected_update) {
5560 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
5561 break;
5562 }
5565
5566 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
5572 break;
5573 }
5574
5575 /*
5576 * Prevent using the CallerID from the outgoing channel since we
5577 * got a connected line update from it.
5578 */
5579 o->dial_callerid_absent = 1;
5580
5581 if (ast_channel_connected_line_sub(o->chan, in, f, 1)) {
5583 }
5584 break;
5585 case AST_CONTROL_AOC:
5586 {
5587 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
5588 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
5590 o->aoc_s_rate_list = decoded;
5591 } else {
5592 ast_aoc_destroy_decoded(decoded);
5593 }
5594 }
5595 break;
5598 /*
5599 * Redirecting updates to the caller make sense only on single
5600 * call at a time strategies.
5601 */
5602 break;
5603 }
5604 if (o->block_connected_update) {
5605 ast_verb(3, "Redirecting update to %s prevented\n",
5606 inchan_name);
5607 break;
5608 }
5609 ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
5610 ochan_name, inchan_name);
5611 if (ast_channel_redirecting_sub(o->chan, in, f, 1)) {
5613 }
5614 break;
5617 break;
5618 default:
5619 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
5620 break;
5621 }
5622 }
5623 ast_frfree(f);
5624 } else { /* ast_read() returned NULL */
5625 endtime = (long) time(NULL) - starttime;
5626 ast_channel_publish_dial(qe->chan, o->chan, on, "NOANSWER");
5627 rna(endtime * 1000, qe, o->chan, on, membername, 1);
5628 do_hang(o);
5630 if (qe->parent->timeoutrestart) {
5631 start_time_tv = ast_tvnow();
5632 }
5633 if (ast_remaining_ms(start_time_tv, orig) > 500) {
5634 ring_one(qe, outgoing, &numbusies);
5635 starttime = (long) time(NULL);
5636 }
5637 }
5638 }
5639 }
5640 }
5641
5642 /* If we received an event from the caller, deal with it. */
5643 if (winner == in) {
5644 f = ast_read(in);
5645 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
5646 /* Got hung up */
5647 *to = -1;
5648 if (f) {
5649 if (f->data.uint32) {
5651 }
5652 ast_frfree(f);
5653 }
5654 canceled_by_caller = 1;
5655 } else if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
5656 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
5657 *to = 0;
5658 ast_frfree(f);
5659 canceled_by_caller = 1;
5660 } else if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
5661 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
5662 *to = 0;
5663 *digit = f->subclass.integer;
5664 ast_frfree(f);
5665 canceled_by_caller = 1;
5666 }
5667 /* When caller hung up or pressed * or digit. */
5668 if (canceled_by_caller) {
5670 for (o = start; o; o = o->call_next) {
5671 if (o->chan) {
5672 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));
5673 }
5674 }
5675 return NULL;
5676 }
5677
5678 /* Send the frame from the in channel to all outgoing channels. */
5679 for (o = start; o; o = o->call_next) {
5680 if (!o->stillgoing || !o->chan) {
5681 /* This outgoing channel has died so don't send the frame to it. */
5682 continue;
5683 }
5684 switch (f->frametype) {
5685 case AST_FRAME_CONTROL:
5686 switch (f->subclass.integer) {
5688 if (o->block_connected_update) {
5689 ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
5690 break;
5691 }
5692 if (ast_channel_connected_line_sub(in, o->chan, f, 1)) {
5694 }
5695 break;
5697 if (o->block_connected_update) {
5698 ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
5699 break;
5700 }
5701 if (ast_channel_redirecting_sub(in, o->chan, f, 1)) {
5703 }
5704 break;
5705 default:
5706 /* We are not going to do anything with this frame. */
5707 goto skip_frame;
5708 }
5709 break;
5710 default:
5711 /* We are not going to do anything with this frame. */
5712 goto skip_frame;
5713 }
5714 }
5715skip_frame:;
5716
5717 ast_frfree(f);
5718 }
5719 }
5720
5721 if (!*to) {
5722 for (o = start; o; o = o->call_next) {
5723 if (o->chan) {
5724 rna(orig, qe, o->chan, o->interface, o->member->membername, 1);
5725 }
5726 }
5727
5728 publish_dial_end_event(qe->chan, outgoing, NULL, "NOANSWER");
5729 }
5730
5731 return peer;
5732}
5733
5734/*!
5735 * \brief Check if we should start attempting to call queue members.
5736 *
5737 * A simple process, really. Count the number of members who are available
5738 * to take our call and then see if we are in a position in the queue at
5739 * which a member could accept our call.
5740 *
5741 * \param[in] qe The caller who wants to know if it is his turn
5742 * \retval 0 It is not our turn
5743 * \retval 1 It is our turn
5744 */
5745static int is_our_turn(struct queue_ent *qe)
5746{
5747 struct queue_ent *ch;
5748 int res;
5749 int avl;
5750 int idx = 0;
5751 /* This needs a lock. How many members are available to be served? */
5752 ao2_lock(qe->parent);
5753
5754 avl = num_available_members(qe->parent);
5755
5756 ch = qe->parent->head;
5757
5758 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
5759
5760 while ((idx < avl) && (ch) && (ch != qe)) {
5761 if (!ch->pending) {
5762 idx++;
5763 }
5764 ch = ch->next;
5765 }
5766
5767 ao2_unlock(qe->parent);
5768 /* If the queue entry is within avl [the number of available members] calls from the top ...
5769 * Autofill and position check added to support autofill=no (as only calls
5770 * from the front of the queue are valid when autofill is disabled)
5771 */
5772 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
5773 ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
5774 res = 1;
5775 } else {
5776 ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
5777 res = 0;
5778 }
5779
5780 /* Update realtime members if this is the first call and number of avalable members is 0 */
5781 if (avl == 0 && qe->pos == 1) {
5783 }
5784
5785 return res;
5786}
5787
5788/*!
5789 * \brief update rules for queues
5790 *
5791 * Calculate min/max penalties making sure if relative they stay within bounds.
5792 * Update queues penalty and set dialplan vars, goto next list entry.
5793*/
5794static void update_qe_rule(struct queue_ent *qe)
5795{
5796 int max_penalty = INT_MAX;
5797
5798 if (qe->max_penalty != INT_MAX) {
5799 char max_penalty_str[20];
5800
5801 if (qe->pr->max_relative) {
5802 max_penalty = qe->max_penalty + qe->pr->max_value;
5803 } else {
5804 max_penalty = qe->pr->max_value;
5805 }
5806
5807 /* a relative change to the penalty could put it below 0 */
5808 if (max_penalty < 0) {
5809 max_penalty = 0;
5810 }
5811
5812 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
5813 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
5815 ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
5816 qe->max_penalty, ast_channel_name(qe->chan), qe->pr->time);
5817 }
5818
5819 if (qe->min_penalty != INT_MAX) {
5820 char min_penalty_str[20];
5821 int min_penalty;
5822
5823 if (qe->pr->min_relative) {
5824 min_penalty = qe->min_penalty + qe->pr->min_value;
5825 } else {
5826 min_penalty = qe->pr->min_value;
5827 }
5828
5829 /* a relative change to the penalty could put it below 0 */
5830 if (min_penalty < 0) {
5831 min_penalty = 0;
5832 }
5833
5834 if (max_penalty != INT_MAX && min_penalty > max_penalty) {
5836 }
5837
5838 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
5839 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
5841 ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
5842 qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
5843 }
5844
5845 if (qe->raise_penalty != INT_MAX) {
5846 char raise_penalty_str[20];
5847 int raise_penalty;
5848
5849 if (qe->pr->raise_relative) {
5851 } else {
5853 }
5854
5855 /* a relative change to the penalty could put it below 0 */
5856 if (raise_penalty < 0) {
5857 raise_penalty = 0;
5858 }
5859
5860 if (max_penalty != INT_MAX && raise_penalty > max_penalty) {
5862 }
5863
5864 snprintf(raise_penalty_str, sizeof(raise_penalty_str), "%d", raise_penalty);
5865 pbx_builtin_setvar_helper(qe->chan, "QUEUE_RAISE_PENALTY", raise_penalty_str);
5867 ast_debug(3, "Setting raised penalty to %d for caller %s since %d seconds have elapsed\n",
5868 qe->raise_penalty, ast_channel_name(qe->chan), qe->pr->time);
5869 }
5870
5871 qe->pr = AST_LIST_NEXT(qe->pr, list);
5872}
5873
5874/*! \brief The waiting areas for callers who are not actively calling members
5875 *
5876 * This function is one large loop. This function will return if a caller
5877 * either exits the queue or it becomes that caller's turn to attempt calling
5878 * queue members. Inside the loop, we service the caller with periodic announcements,
5879 * holdtime announcements, etc. as configured in queues.conf
5880 *
5881 * \retval 0 if the caller's turn has arrived
5882 * \retval -1 if the caller should exit the queue.
5883 */
5884static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason)
5885{
5886 int res = 0;
5887
5888 /* This is the holding pen for callers 2 through maxlen */
5889 for (;;) {
5890
5891 /* A request to withdraw this call from the queue arrived */
5892 if (qe->withdraw) {
5893 *reason = QUEUE_WITHDRAW;
5894 res = 1;
5895 break;
5896 }
5897
5898 if (is_our_turn(qe)) {
5899 break;
5900 }
5901
5902 /* If we have timed out, break out */
5903 if (qe->expire && (time(NULL) >= qe->expire)) {
5904 *reason = QUEUE_TIMEOUT;
5905 break;
5906 }
5907
5908 if (qe->parent->leavewhenempty) {
5909 int status = 0;
5910
5912 record_abandoned(qe);
5913 *reason = QUEUE_LEAVEEMPTY;
5914 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));
5915 res = -1;
5916 qe->handled = -1;
5917 break;
5918 }
5919 }
5920
5921 /* Make a position announcement, if enabled */
5922 if (qe->parent->announcefrequency &&
5923 (res = say_position(qe,ringing))) {
5924 break;
5925 }
5926
5927 /* If we have timed out, break out */
5928 if (qe->expire && (time(NULL) >= qe->expire)) {
5929 *reason = QUEUE_TIMEOUT;
5930 break;
5931 }
5932
5933 /* Make a periodic announcement, if enabled */
5936 break;
5937
5938 /* see if we need to move to the next penalty level for this queue */
5939 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
5940 update_qe_rule(qe);
5941 }
5942
5943 /* If we have timed out, break out */
5944 if (qe->expire && (time(NULL) >= qe->expire)) {
5945 *reason = QUEUE_TIMEOUT;
5946 break;
5947 }
5948
5949 /* Wait a second before checking again */
5950 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
5951 if (res > 0 && !valid_exit(qe, res)) {
5952 res = 0;
5953 } else {
5954 break;
5955 }
5956 }
5957
5958 /* If we have timed out, break out */
5959 if (qe->expire && (time(NULL) >= qe->expire)) {
5960 *reason = QUEUE_TIMEOUT;
5961 break;
5962 }
5963 }
5964
5965 return res;
5966}
5967
5968/*!
5969 * \brief update the queue status
5970 * \retval 0 always
5971*/
5972static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
5973{
5974 int oldtalktime;
5975 int newtalktime = time(NULL) - starttime;
5976 struct member *mem;
5977 struct call_queue *qtmp;
5978 struct ao2_iterator queue_iter;
5979
5980 /* It is possible for us to be called when a call has already been considered terminated
5981 * and data updated, so to ensure we only act on the call that the agent is currently in
5982 * we check when the call was bridged.
5983 */
5984 if (!starttime || (member->starttime != starttime)) {
5985 return 0;
5986 }
5987
5988 if (shared_lastcall) {
5989 queue_iter = ao2_iterator_init(queues, 0);
5990 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
5991 ao2_lock(qtmp);
5992 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
5993 time(&mem->lastcall);
5994 mem->calls++;
5995 mem->callcompletedinsl = 0;
5996 mem->starttime = 0;
5997 mem->lastqueue = q;
5998 ao2_ref(mem, -1);
5999 }
6000 ao2_unlock(qtmp);
6001 queue_t_unref(qtmp, "Done with iterator");
6002 }
6003 ao2_iterator_destroy(&queue_iter);
6004 } else {
6005 ao2_lock(q);
6006 time(&member->lastcall);
6008 member->calls++;
6009 member->starttime = 0;
6010 member->lastqueue = q;
6011 ao2_unlock(q);
6012 }
6013 /* Member might never experience any direct status change (local
6014 * channel with forwarding in particular). If that's the case,
6015 * this is the last chance to remove it from pending or subsequent
6016 * calls will not occur.
6017 */
6019
6020 ao2_lock(q);
6021 q->callscompleted++;
6022 if (callcompletedinsl) {
6023 q->callscompletedinsl++;
6024 }
6025 if (q->callscompleted == 1) {
6026 q->talktime = newtalktime;
6027 } else {
6028 /* Calculate talktime using the same exponential average as holdtime code */
6029 oldtalktime = q->talktime;
6030 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
6031 }
6032 ao2_unlock(q);
6033 return 0;
6034}
6035
6036/*! \brief Calculate the metric of each member in the outgoing callattempts
6037 *
6038 * A numeric metric is given to each member depending on the ring strategy used
6039 * by the queue. Members with lower metrics will be called before members with
6040 * higher metrics
6041 * \retval -1 if penalties are exceeded
6042 * \retval 0 otherwise
6043 */
6044static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
6045{
6046 /* disregarding penalty on too few members? */
6047 int membercount = ao2_container_count(q->members);
6048 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
6049 int penalty = mem->penalty;
6050
6051 if (usepenalty) {
6052 if (qe->raise_penalty != INT_MAX && penalty < qe->raise_penalty) {
6053 /* Low penalty is raised up to the current minimum */
6054 penalty = qe->raise_penalty;
6055 }
6056 if ((qe->max_penalty != INT_MAX && penalty > qe->max_penalty) ||
6057 (qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) {
6058 return -1;
6059 }
6060 } else {
6061 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
6062 membercount, q->penaltymemberslimit);
6063 }
6064
6065 switch (q->strategy) {
6067 /* Everyone equal, except for penalty */
6068 tmp->metric = penalty * 1000000 * usepenalty;
6069 break;
6071 if (pos < qe->linpos) {
6072 tmp->metric = 1000 + pos;
6073 } else {
6074 if (pos > qe->linpos) {
6075 /* Indicate there is another priority */
6076 qe->linwrapped = 1;
6077 }
6078 tmp->metric = pos;
6079 }
6080 tmp->metric += penalty * 1000000 * usepenalty;
6081 break;
6084 pos = mem->queuepos;
6085 if (pos < q->rrpos) {
6086 tmp->metric = 1000 + pos;
6087 } else {
6088 if (pos > q->rrpos) {
6089 /* Indicate there is another priority */
6090 q->wrapped = 1;
6091 }
6092 tmp->metric = pos;
6093 }
6094 tmp->metric += penalty * 1000000 * usepenalty;
6095 break;
6097 tmp->metric = ast_random() % 1000;
6098 tmp->metric += penalty * 1000000 * usepenalty;
6099 break;
6101 tmp->metric = ast_random() % ((1 + penalty) * 1000);
6102 break;
6104 tmp->metric = mem->calls;
6105 tmp->metric += penalty * 1000000 * usepenalty;
6106 break;
6108 if (!mem->lastcall) {
6109 tmp->metric = 0;
6110 } else {
6111 tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
6112 }
6113 tmp->metric += penalty * 1000000 * usepenalty;
6114 break;
6115 default:
6116 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
6117 break;
6118 }
6119 return 0;
6120}
6121
6125 TRANSFER
6127
6128/*! \brief Send out AMI message with member call completion status information */
6129static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller,
6130 struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart,
6131 time_t callstart, enum agent_complete_reason rsn)
6132{
6133 const char *reason = NULL; /* silence dumb compilers */
6134 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
6135
6136 switch (rsn) {
6137 case CALLER:
6138 reason = "caller";
6139 break;
6140 case AGENT:
6141 reason = "agent";
6142 break;
6143 case TRANSFER:
6144 reason = "transfer";
6145 break;
6146 }
6147
6148 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I, s: s}",
6149 "Queue", queuename,
6150 "Interface", member->interface,
6151 "MemberName", member->membername,
6152 "HoldTime", (ast_json_int_t)(callstart - holdstart),
6153 "TalkTime", (ast_json_int_t)(time(NULL) - callstart),
6154 "Reason", reason ?: "");
6155
6157 queue_agent_complete_type(), blob);
6158}
6159
6160static void queue_agent_cb(void *userdata, struct stasis_subscription *sub,
6161 struct stasis_message *msg)
6162{
6163 struct ast_channel_blob *agent_blob;
6164
6165 agent_blob = stasis_message_data(msg);
6166
6168 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6169 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6170 "AGENTLOGIN", "%s", agent_blob->snapshot->base->name);
6172 ast_queue_log("NONE", agent_blob->snapshot->base->uniqueid,
6173 ast_json_string_get(ast_json_object_get(agent_blob->blob, "agent")),
6174 "AGENTLOGOFF", "%s|%ld", agent_blob->snapshot->base->name,
6175 (long) ast_json_integer_get(ast_json_object_get(agent_blob->blob, "logintime")));
6176 }
6177}
6178
6179/*!
6180 * \brief Structure representing relevant data during a local channel optimization
6181 *
6182 * The reason we care about local channel optimizations is that we want to be able
6183 * to accurately report when the caller and queue member have stopped talking to
6184 * each other. A local channel optimization can cause it to appear that the conversation
6185 * has stopped immediately after it has begun. By tracking that the relevant channels
6186 * to monitor have changed due to a local channel optimization, we can give accurate
6187 * reports.
6188 *
6189 * Local channel optimizations for queues are restricted from their normal operation.
6190 * Bridges created by queues can only be the destination of local channel optimizations,
6191 * not the source. In addition, move-swap local channel optimizations are the only
6192 * permitted types of local channel optimization.
6193 *
6194 * This data is populated when we are told that a local channel optimization begin
6195 * is occurring. When we get told the optimization has ended successfully, we then
6196 * apply the data here into the queue_stasis_data.
6197 */
6199 /*! The uniqueid of the channel that will be taking the place of the caller or member */
6201 /*! Indication of whether we think there is a local channel optimization in progress */
6203 /*! The identifier for this local channel optimization */
6204 unsigned int id;
6205};
6206
6207/*!
6208 * \brief User data for stasis subscriptions used for queue calls.
6209 *
6210 * app_queue subscribes to channel and bridge events for all bridged calls.
6211 * app_queue cares about the following events:
6212 *
6213 * \li bridge enter: To determine the unique ID of the bridge created for the call.
6214 * \li blind transfer: To send an appropriate agent complete event.
6215 * \li attended transfer: To send an appropriate agent complete event.
6216 * \li local optimization: To update caller and member unique IDs for the call.
6217 * \li hangup: To send an appropriate agent complete event.
6218 *
6219 * The stasis subscriptions last until we determine that the caller and the member
6220 * are no longer bridged with each other.
6221 */
6224 /*! The unique ID of the caller's channel. */
6226 /*! The unique ID of the queue member's channel */
6228 /*! The unique ID of the bridge created by the queue */
6231 /*! The relevant queue */
6233 /*! The queue member that has answered the call */
6235 /*! The time at which the caller entered the queue. Start of the caller's hold time */
6237 /*! The time at which the member answered the call. */
6239 /*! The original position of the caller when he entered the queue */
6241 /*! Indication if the call was answered within the configured service level of the queue */
6243 /*! Indicates if the stasis subscriptions are shutting down */
6245 /*! The stasis message router for bridge events */
6247 /*! The stasis message router for channel events */
6249 /*! Local channel optimization details for the caller */
6251 /*! Local channel optimization details for the member */
6253};
6254
6255/*!
6256 * \internal
6257 * \brief Free memory for a queue_stasis_data
6258 */
6259static void queue_stasis_data_destructor(void *obj)
6260{
6261 struct queue_stasis_data *queue_data = obj;
6262
6263 /* This can only happen if refcounts for this object have got severely messed up */
6264 ast_assert(queue_data->bridge_router == NULL);
6265 ast_assert(queue_data->channel_router == NULL);
6266
6267 ao2_cleanup(queue_data->member);
6268 queue_unref(queue_data->queue);
6269 ast_string_field_free_memory(queue_data);
6270}
6271
6272/*!
6273 * \internal
6274 * \brief End all stasis subscriptions on a queue_stasis_data
6275 */
6276static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
6277{
6278 SCOPED_AO2LOCK(lock, queue_data);
6279
6280 queue_data->dying = 1;
6282 queue_data->bridge_router = NULL;
6284 queue_data->channel_router = NULL;
6285}
6286
6287/*!
6288 * \internal
6289 * \brief Allocate a queue_stasis_data and initialize its data.
6290 */
6292 struct ast_channel *peer, struct member *mem, time_t holdstart,
6293 time_t starttime, int callcompletedinsl)
6294{
6295 struct queue_stasis_data *queue_data;
6296
6297 queue_data = ao2_alloc(sizeof(*queue_data), queue_stasis_data_destructor);
6298 if (!queue_data) {
6299 return NULL;
6300 }
6301
6302 if (ast_string_field_init(queue_data, 64)) {
6303 ao2_cleanup(queue_data);
6304 return NULL;
6305 }
6306
6309 queue_data->queue = queue_ref(qe->parent);
6310 queue_data->starttime = starttime;
6311 queue_data->holdstart = holdstart;
6313 queue_data->caller_pos = qe->opos;
6314 ao2_ref(mem, +1);
6315 queue_data->member = mem;
6316
6317 return queue_data;
6318}
6319
6320/*!
6321 * \internal
6322 * \brief Log an attended transfer in the queue log.
6323 *
6324 * Attended transfer queue log messages vary based on the method by which the
6325 * attended transfer was completed.
6326 *
6327 * \param queue_data Data pertaining to the particular call in the queue.
6328 * \param atxfer_msg The stasis attended transfer message data.
6329 */
6330static void log_attended_transfer(struct queue_stasis_data *queue_data,
6331 struct ast_attended_transfer_message *atxfer_msg)
6332{
6333 RAII_VAR(struct ast_str *, transfer_str, ast_str_create(32), ast_free);
6334
6335 if (!transfer_str) {
6336 ast_log(LOG_WARNING, "Unable to log attended transfer to queue log\n");
6337 return;
6338 }
6339
6340 switch (atxfer_msg->dest_type) {
6342 ast_str_set(&transfer_str, 0, "BRIDGE|%s", atxfer_msg->dest.bridge);
6343 break;
6346 ast_str_set(&transfer_str, 0, "APP|%s", atxfer_msg->dest.app);
6347 break;
6349 ast_str_set(&transfer_str, 0, "LINK|%s|%s", atxfer_msg->dest.links[0]->base->name,
6350 atxfer_msg->dest.links[1]->base->name);
6351 break;
6354 /* Threeways are headed off and should not be logged here */
6355 ast_assert(0);
6356 return;
6357 }
6358
6359 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername, "ATTENDEDTRANSFER", "%s|%ld|%ld|%d",
6360 ast_str_buffer(transfer_str),
6361 (long) (queue_data->starttime - queue_data->holdstart),
6362 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6363}
6364
6365/*!
6366 * \internal
6367 * \brief Handle a stasis bridge enter event.
6368 *
6369 * We track this particular event in order to learn what bridge
6370 * was created for the queue call.
6371 *
6372 * \param userdata Data pertaining to the particular call in the queue.
6373 * \param sub The stasis subscription on which the message occurred.
6374 * \param msg The stasis message for the bridge enter event
6375 */
6376static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub,
6377 struct stasis_message *msg)
6378{
6379 struct queue_stasis_data *queue_data = userdata;
6380 struct ast_bridge_blob *enter_blob = stasis_message_data(msg);
6381 SCOPED_AO2LOCK(lock, queue_data);
6382
6383 if (queue_data->dying) {
6384 return;
6385 }
6386
6387 if (!ast_strlen_zero(queue_data->bridge_uniqueid)) {
6388 return;
6389 }
6390
6391 if (!strcmp(enter_blob->channel->base->uniqueid, queue_data->caller_uniqueid)) {
6392 ast_string_field_set(queue_data, bridge_uniqueid,
6393 enter_blob->bridge->uniqueid);
6394 ast_debug(3, "Detected entry of caller channel %s into bridge %s\n",
6395 enter_blob->channel->base->name, queue_data->bridge_uniqueid);
6396 }
6397}
6398
6399/*!
6400 * \brief Handle a blind transfer event
6401 *
6402 * This event is important in order to be able to log the end of the
6403 * call to the queue log and to stasis.
6404 *
6405 * \param userdata Data pertaining to the particular call in the queue.
6406 * \param sub The stasis subscription on which the message occurred.
6407 * \param msg The stasis message for the blind transfer event
6408 */
6409static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub,
6410 struct stasis_message *msg)
6411{
6412 struct queue_stasis_data *queue_data = userdata;
6413 struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
6414 const char *exten;
6415 const char *context;
6416 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6417 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6418
6419 if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
6420 return;
6421 }
6422
6423 ao2_lock(queue_data);
6424
6425 if (queue_data->dying) {
6426 ao2_unlock(queue_data);
6427 return;
6428 }
6429
6430 if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
6431 strcmp(queue_data->bridge_uniqueid, transfer_msg->bridge->uniqueid)) {
6432 ao2_unlock(queue_data);
6433 return;
6434 }
6435
6436 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6437 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6438
6439 ao2_unlock(queue_data);
6440
6441 exten = transfer_msg->exten;
6442 context = transfer_msg->context;
6443
6444 ast_debug(3, "Detected blind transfer in queue %s\n", queue_data->queue->name);
6445 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6446 "BLINDTRANSFER", "%s|%s|%ld|%ld|%d",
6447 exten, context,
6448 (long) (queue_data->starttime - queue_data->holdstart),
6449 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6450
6451 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6452 queue_data->holdstart, queue_data->starttime, TRANSFER);
6453 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6454 queue_data->starttime);
6455 remove_stasis_subscriptions(queue_data);
6456}
6457
6458/*!
6459 * \brief Handle an attended transfer event
6460 *
6461 * This event is important in order to be able to log the end of the
6462 * call to the queue log and to stasis.
6463 *
6464 * \param userdata Data pertaining to the particular call in the queue.
6465 * \param sub The stasis subscription on which the message occurred.
6466 * \param msg The stasis message for the attended transfer event.
6467 */
6468static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub,
6469 struct stasis_message *msg)
6470{
6471 struct queue_stasis_data *queue_data = userdata;
6472 struct ast_attended_transfer_message *atxfer_msg = stasis_message_data(msg);
6473 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6474 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6475
6476 if (atxfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS ||
6478 return;
6479 }
6480
6481 ao2_lock(queue_data);
6482
6483 if (queue_data->dying) {
6484 ao2_unlock(queue_data);
6485 return;
6486 }
6487
6488 if (ast_strlen_zero(queue_data->bridge_uniqueid)) {
6489 ao2_unlock(queue_data);
6490 return;
6491 }
6492
6493 if ((!atxfer_msg->to_transferee.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6494 atxfer_msg->to_transferee.bridge_snapshot->uniqueid)) &&
6495 (!atxfer_msg->to_transfer_target.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6497 ao2_unlock(queue_data);
6498 return;
6499 }
6500
6501 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6502 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6503
6504 ao2_unlock(queue_data);
6505
6506 ast_debug(3, "Detected attended transfer in queue %s\n", queue_data->queue->name);
6507 log_attended_transfer(queue_data, atxfer_msg);
6508
6509 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6510 queue_data->holdstart, queue_data->starttime, TRANSFER);
6511 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6512 queue_data->starttime);
6513 remove_stasis_subscriptions(queue_data);
6514}
6515
6516/*!
6517 * \internal
6518 * \brief Callback for all stasis bridge events
6519 *
6520 * Based on the event and what bridge it is on, the task is farmed out to relevant
6521 * subroutines for further processing.
6522 */
6523static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub,
6524 struct stasis_message *msg)
6525{
6527 ao2_cleanup(userdata);
6528 }
6529}
6530
6531/*!
6532 * \internal
6533 * \brief Handler for the beginning of a local channel optimization
6534 *
6535 * This method gathers data relevant to the local channel optimization and stores
6536 * it to be used once the local optimization completes.
6537 *
6538 * \param userdata Data pertaining to the particular call in the queue.
6539 * \param sub The stasis subscription on which the message occurred.
6540 * \param msg The stasis message for the local optimization begin event
6541 */
6542static void handle_local_optimization_begin(void *userdata, struct stasis_subscription *sub,
6543 struct stasis_message *msg)
6544{
6545 struct queue_stasis_data *queue_data = userdata;
6546 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6547 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6548 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6549 struct ast_channel_snapshot *source = ast_multi_channel_blob_get_channel(optimization_blob, "source");
6550 struct local_optimization *optimization;
6551 unsigned int id;
6552 SCOPED_AO2LOCK(lock, queue_data);
6553
6554 if (queue_data->dying) {
6555 return;
6556 }
6557
6558 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6559 optimization = &queue_data->member_optimize;
6560 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6561 optimization = &queue_data->caller_optimize;
6562 } else {
6563 return;
6564 }
6565
6566 /* We only allow move-swap optimizations, so there had BETTER be a source */
6567 ast_assert(source != NULL);
6568
6569 optimization->source_chan_uniqueid = ast_strdup(source->base->uniqueid);
6570 if (!optimization->source_chan_uniqueid) {
6571 ast_log(LOG_ERROR, "Unable to track local channel optimization for channel %s. Expect further errors\n", local_one->base->name);
6572 return;
6573 }
6575
6576 optimization->id = id;
6577 optimization->in_progress = 1;
6578}
6579
6580/*!
6581 * \internal
6582 * \brief Handler for the end of a local channel optimization
6583 *
6584 * This method takes the data gathered during the local channel optimization begin
6585 * event and applies it to the queue stasis data appropriately. This generally involves
6586 * updating the caller or member unique ID with the channel that is taking the place of
6587 * the previous caller or member.
6588 *
6589 * \param userdata Data pertaining to the particular call in the queue.
6590 * \param sub The stasis subscription on which the message occurred.
6591 * \param msg The stasis message for the local optimization end event
6592 */
6593static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub,
6594 struct stasis_message *msg)
6595{
6596 struct queue_stasis_data *queue_data = userdata;
6597 struct ast_multi_channel_blob *optimization_blob = stasis_message_data(msg);
6598 struct ast_channel_snapshot *local_one = ast_multi_channel_blob_get_channel(optimization_blob, "1");
6599 struct ast_channel_snapshot *local_two = ast_multi_channel_blob_get_channel(optimization_blob, "2");
6600 struct local_optimization *optimization;
6601 int is_caller;
6602 unsigned int id;
6603 SCOPED_AO2LOCK(lock, queue_data);
6604
6605 if (queue_data->dying) {
6606 return;
6607 }
6608
6609 if (!strcmp(local_one->base->uniqueid, queue_data->member_uniqueid)) {
6610 optimization = &queue_data->member_optimize;
6611 is_caller = 0;
6612 } else if (!strcmp(local_two->base->uniqueid, queue_data->caller_uniqueid)) {
6613 optimization = &queue_data->caller_optimize;
6614 is_caller = 1;
6615 } else {
6616 return;
6617 }
6618
6620
6621 if (!optimization->in_progress) {
6622 ast_log(LOG_WARNING, "Told of a local optimization end when we had no previous begin\n");
6623 return;
6624 }
6625
6626 if (id != optimization->id) {
6627 ast_log(LOG_WARNING, "Local optimization end event ID does not match begin (%u != %u)\n",
6628 id, optimization->id);
6629 return;
6630 }
6631
6632 if (is_caller) {
6633 ast_debug(3, "Local optimization: Changing queue caller uniqueid from %s to %s\n",
6634 queue_data->caller_uniqueid, optimization->source_chan_uniqueid);
6635 ast_string_field_set(queue_data, caller_uniqueid, optimization->source_chan_uniqueid);
6636 } else {
6637 ast_debug(3, "Local optimization: Changing queue member uniqueid from %s to %s\n",
6638 queue_data->member_uniqueid, optimization->source_chan_uniqueid);
6639 ast_string_field_set(queue_data, member_uniqueid, optimization->source_chan_uniqueid);
6640 }
6641
6642 optimization->in_progress = 0;
6643}
6644
6645/*!
6646 * \internal
6647 * \brief Handler for hangup stasis event
6648 *
6649 * This is how we determine that the caller or member has hung up and the call
6650 * has ended. An appropriate queue log and stasis message are raised in this
6651 * callback.
6652 *
6653 * \param userdata Data pertaining to the particular call in the queue.
6654 * \param sub The stasis subscription on which the message occurred.
6655 * \param msg The stasis message for the hangup event.
6656 */
6657static void handle_hangup(void *userdata, struct stasis_subscription *sub,
6658 struct stasis_message *msg)
6659{
6660 struct queue_stasis_data *queue_data = userdata;
6661 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6662 RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6663 RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6664 RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
6665 enum agent_complete_reason reason;
6666
6667 ao2_lock(queue_data);
6668
6669 if (queue_data->dying) {
6670 ao2_unlock(queue_data);
6671 return;
6672 }
6673
6674 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6675 reason = CALLER;
6676 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6677 reason = AGENT;
6678 } else {
6679 ao2_unlock(queue_data);
6680 return;
6681 }
6682
6683 chan = ast_channel_get_by_name(channel_blob->snapshot->base->name);
6684 if (chan && (ast_channel_has_role(chan, AST_TRANSFERER_ROLE_NAME) ||
6685 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "ATTENDEDTRANSFER")) ||
6686 !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER")))) {
6687 /* Channel that is hanging up is doing it as part of a transfer.
6688 * We'll get a transfer event later
6689 */
6690 ao2_unlock(queue_data);
6691 return;
6692 }
6693
6694 caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6695 member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6696
6697 ao2_unlock(queue_data);
6698
6699 ast_debug(3, "Detected hangup of queue %s channel %s\n", reason == CALLER ? "caller" : "member",
6700 channel_blob->snapshot->base->name);
6701
6702 ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6703 reason == CALLER ? "COMPLETECALLER" : "COMPLETEAGENT", "%ld|%ld|%d",
6704 (long) (queue_data->starttime - queue_data->holdstart),
6705 (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6706
6707 send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6708 queue_data->holdstart, queue_data->starttime, reason);
6709 update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6710 queue_data->starttime);
6711 remove_stasis_subscriptions(queue_data);
6712}
6713
6714static void handle_masquerade(void *userdata, struct stasis_subscription *sub,
6715 struct stasis_message *msg)
6716{
6717 struct queue_stasis_data *queue_data = userdata;
6718 struct ast_channel_blob *channel_blob = stasis_message_data(msg);
6719 const char *new_channel_id;
6720
6721 new_channel_id = ast_json_string_get(ast_json_object_get(channel_blob->blob, "newchanneluniqueid"));
6722
6723 ao2_lock(queue_data);
6724
6725 if (queue_data->dying) {
6726 ao2_unlock(queue_data);
6727 return;
6728 }
6729
6730 if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->caller_uniqueid)) {
6731 ast_debug(1, "Replacing caller channel %s with %s due to masquerade\n", queue_data->caller_uniqueid, new_channel_id);
6732 ast_string_field_set(queue_data, caller_uniqueid, new_channel_id);
6733 } else if (!strcmp(channel_blob->snapshot->base->uniqueid, queue_data->member_uniqueid)) {
6734 ast_debug(1, "Replacing member channel %s with %s due to masquerade\n", queue_data->member_uniqueid, new_channel_id);
6735 ast_string_field_set(queue_data, member_uniqueid, new_channel_id);
6736 }
6737
6738 ao2_unlock(queue_data);
6739}
6740
6741/*!
6742 * \internal
6743 * \brief Callback for all stasis channel events
6744 *
6745 * Based on the event and the channels involved, the work is farmed out into
6746 * subroutines for further processing.
6747 */
6748static void queue_channel_cb(void *userdata, struct stasis_subscription *sub,
6749 struct stasis_message *msg)
6750{
6752 ao2_cleanup(userdata);
6753 }
6754}
6755
6756/*!
6757 * \internal
6758 * \brief Create stasis subscriptions for a particular call in the queue.
6759 *
6760 * These subscriptions are created once the call has been answered. The subscriptions
6761 * are put in place so that call progress may be tracked. Once the call can be determined
6762 * to have ended, then messages are logged to the queue log and stasis events are emitted.
6763 *
6764 * \param qe The queue entry representing the caller
6765 * \param peer The channel that has answered the call
6766 * \param mem The queue member that answered the call
6767 * \param holdstart The time at which the caller entered the queue
6768 * \param starttime The time at which the call was answered
6769 * \param callcompletedinsl Indicates if the call was answered within the configured service level of the queue.
6770 * \retval 0 Success
6771 * \retval non-zero Failure
6772 */
6773static int setup_stasis_subs(struct queue_ent *qe, struct ast_channel *peer, struct member *mem,
6774 time_t holdstart, time_t starttime, int callcompletedinsl)
6775{
6776 struct queue_stasis_data *queue_data = queue_stasis_data_alloc(qe, peer, mem, holdstart, starttime, callcompletedinsl);
6777
6778 if (!queue_data) {
6779 return -1;
6780 }
6781
6783 if (!queue_data->bridge_router) {
6784 ao2_ref(queue_data, -1);
6785 return -1;
6786 }
6787
6789 handle_bridge_enter, queue_data);
6791 handle_blind_transfer, queue_data);
6793 handle_attended_transfer, queue_data);
6795 queue_bridge_cb, queue_data);
6796
6798 if (!queue_data->channel_router) {
6799 /* Unsubscribing from the bridge router will remove the only ref of queue_data,
6800 * thus beginning the destruction process
6801 */
6803 queue_data->bridge_router = NULL;
6804 return -1;
6805 }
6806
6807 ao2_ref(queue_data, +1);
6811 handle_local_optimization_end, queue_data);
6813 handle_hangup, queue_data);
6815 handle_masquerade, queue_data);
6817 queue_channel_cb, queue_data);
6818
6819 return 0;
6820}
6821
6823 struct call_queue *q;
6825};
6826
6827static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
6828{
6829 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
6830 ao2_ref(qeb, +1);
6831 qeb->chan = originator;
6832}
6833
6834static void end_bridge_callback(void *data)
6835{
6836 struct queue_end_bridge *qeb = data;
6837 struct call_queue *q = qeb->q;
6838 struct ast_channel *chan = qeb->chan;
6839
6840 if (ao2_ref(qeb, -1) == 1) {
6841 set_queue_variables(q, chan);
6842 /* This unrefs the reference we made in try_calling when we allocated qeb */
6843 queue_t_unref(q, "Expire bridge_config reference");
6844 }
6845}
6846
6847/*!
6848 * \internal
6849 * \brief Setup the after bridge goto location on the peer.
6850 * \since 12.0.0
6851 *
6852 * \param chan Calling channel for bridge.
6853 * \param peer Peer channel for bridge.
6854 * \param opts Dialing option flags.
6855 * \param opt_args Dialing option argument strings.
6856 */
6857static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
6858{
6859 const char *context;
6860 const char *extension;
6861 int priority;
6862
6863 if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
6864 ast_channel_lock(chan);
6868 ast_channel_unlock(chan);
6870 opt_args[OPT_ARG_CALLEE_GO_ON]);
6871 }
6872}
6873
6874static void escape_and_substitute(struct ast_channel *chan, const char *input,
6875 char *output, size_t size)
6876{
6877 const char *m = input;
6878 char escaped[size];
6879 char *p;
6880
6881 for (p = escaped; p < escaped + size - 1; p++, m++) {
6882 switch (*m) {
6883 case '^':
6884 if (*(m + 1) == '{') {
6885 *p = '$';
6886 }
6887 break;
6888 case ',':
6889 *p++ = '\\';
6890 /* Fall through */
6891 default:
6892 *p = *m;
6893 }
6894 if (*m == '\0')
6895 break;
6896 }
6897
6898 if (p == escaped + size) {
6899 escaped[size - 1] = '\0';
6900 }
6901
6902 pbx_substitute_variables_helper(chan, escaped, output, size - 1);
6903}
6904
6905static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
6906{
6907 char escaped_filename[256];
6908 char file_with_ext[sizeof(escaped_filename) + sizeof(qe->parent->monfmt)];
6909 char mixmonargs[1512];
6910 char escaped_monitor_exec[1024];
6911 const char *monitor_options;
6912 const char *monitor_exec;
6913
6914 escaped_monitor_exec[0] = '\0';
6915
6916 if (filename) {
6917 escape_and_substitute(qe->chan, filename, escaped_filename, sizeof(escaped_filename));
6918 } else {
6919 ast_copy_string(escaped_filename, ast_channel_uniqueid(qe->chan), sizeof(escaped_filename));
6920 }
6921
6923 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
6924 monitor_exec = ast_strdupa(monitor_exec);
6925 }
6926 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
6927 monitor_options = ast_strdupa(monitor_options);
6928 } else {
6929 monitor_options = "";
6930 }
6932
6933 if (monitor_exec) {
6934 escape_and_substitute(qe->chan, monitor_exec, escaped_monitor_exec, sizeof(escaped_monitor_exec));
6935 }
6936
6937 snprintf(file_with_ext, sizeof(file_with_ext), "%s.%s", escaped_filename, qe->parent->monfmt);
6938
6939 if (!ast_strlen_zero(escaped_monitor_exec)) {
6940 snprintf(mixmonargs, sizeof(mixmonargs), "b%s,%s", monitor_options, escaped_monitor_exec);
6941 } else {
6942 snprintf(mixmonargs, sizeof(mixmonargs), "b%s", monitor_options);
6943 }
6944
6945 ast_debug(1, "Arguments being passed to MixMonitor: %s,%s\n", file_with_ext, mixmonargs);
6946
6947 if (ast_start_mixmonitor(qe->chan, file_with_ext, mixmonargs)) {
6948 ast_log(LOG_WARNING, "Unable to start mixmonitor. Is the MixMonitor app loaded?\n");
6949 }
6950}
6951
6952/*!
6953 * \internal
6954 * \brief A large function which calls members, updates statistics, and bridges the caller and a member
6955 *
6956 * Here is the process of this function
6957 * 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue()
6958 * 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member.
6959 * 3. Call ring_one to place a call to the appropriate member(s)
6960 * 4. Call wait_for_answer to wait for an answer. If no one answers, return.
6961 * 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered.
6962 * 6. Start the monitor or mixmonitor if the option is set
6963 * 7. Remove the caller from the queue to allow other callers to advance
6964 * 8. Bridge the call.
6965 * 9. Do any post processing after the call has disconnected.
6966 *
6967 * \param[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
6968 * \param[in] opts the options passed as the third parameter to the Queue() application
6969 * \param[in] opt_args the options passed as the third parameter to the Queue() application
6970 * \param[in] announceoverride filename to play to user when waiting
6971 * \param[in] url the url passed as the fourth parameter to the Queue() application
6972 * \param[in,out] tries the number of times we have tried calling queue members
6973 * \param[out] noption set if the call to Queue() has the 'n' option set.
6974 * \param[in] agi the agi passed as the fifth parameter to the Queue() application
6975 * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
6976 * \param[in] ringing 1 if the 'r' option is set, otherwise 0
6977 */
6978static 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)
6979{
6980 struct member *cur;
6981 struct callattempt *outgoing = NULL; /* the list of calls we are building */
6982 int to, orig;
6983 char oldexten[AST_MAX_EXTENSION]="";
6984 char oldcontext[AST_MAX_CONTEXT]="";
6985 char queuename[256]="";
6986 struct ast_channel *peer;
6987 struct callattempt *lpeer;
6988 struct member *member;
6989 struct ast_app *application;
6990 int res = 0, bridge = 0;
6991 int numbusies = 0;
6992 int x=0;
6993 char *announce = NULL;
6994 char digit = 0;
6995 time_t now = time(NULL);
6996 struct ast_bridge_config bridge_config;
6997 char nondataquality = 1;
6998 char *agiexec = NULL;
6999 char *gosubexec = NULL;
7000 const char *monitorfilename;
7001 int forwardsallowed = 1;
7002 int block_connected_line = 0;
7003 struct ao2_iterator memi;
7005 int callcompletedinsl;
7006 time_t starttime;
7007
7008 memset(&bridge_config, 0, sizeof(bridge_config));
7009 time(&now);
7010
7011 /* If we've already exceeded our timeout, then just stop
7012 * This should be extremely rare. queue_exec will take care
7013 * of removing the caller and reporting the timeout as the reason.
7014 */
7015 if (qe->expire && now >= qe->expire) {
7016 res = 0;
7017 goto out;
7018 }
7019
7020 if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) {
7022 }
7023 if (ast_test_flag(&opts, OPT_CALLER_TRANSFER)) {
7025 }
7026 if (ast_test_flag(&opts, OPT_CALLEE_AUTOMON)) {
7028 }
7029 if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
7031 }
7032 if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
7033 nondataquality = 0;
7034 }
7035 if (ast_test_flag(&opts, OPT_CALLEE_HANGUP)) {
7037 }
7038 if (ast_test_flag(&opts, OPT_CALLER_HANGUP)) {
7040 }
7041 if (ast_test_flag(&opts, OPT_CALLEE_PARK)) {
7043 }
7044 if (ast_test_flag(&opts, OPT_CALLER_PARK)) {
7046 }
7047 if (ast_test_flag(&opts, OPT_NO_RETRY)) {
7050 (*tries)++;
7051 } else {
7052 *tries = ao2_container_count(qe->parent->members);
7053 }
7054 *noption = 1;
7055 }
7056 if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
7057 forwardsallowed = 0;
7058 }
7060 block_connected_line = 1;
7061 }
7064 }
7067 }
7068 if (ast_test_flag(&opts, OPT_MARK_AS_ANSWERED)) {
7070 }
7071
7072 /* if the calling channel has AST_CAUSE_ANSWERED_ELSEWHERE set, make sure this is inherited.
7073 (this is mainly to support unreal/local channels)
7074 */
7077 }
7078
7079 ao2_lock(qe->parent);
7080 ast_debug(1, "%s is trying to call a queue member.\n",
7081 ast_channel_name(qe->chan));
7082 ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
7083 if (!ast_strlen_zero(qe->announce)) {
7084 announce = qe->announce;
7085 }
7086 if (!ast_strlen_zero(announceoverride)) {
7087 announce = announceoverride;
7088 }
7089
7090 memi = ao2_iterator_init(qe->parent->members, 0);
7091 while ((cur = ao2_iterator_next(&memi))) {
7092 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
7093 if (!tmp) {
7094 ao2_ref(cur, -1);
7095 ao2_iterator_destroy(&memi);
7096 ao2_unlock(qe->parent);
7097 goto out;
7098 }
7099
7100 /*
7101 * Seed the callattempt's connected line information with previously
7102 * acquired connected line info from the queued channel. The
7103 * previously acquired connected line info could have been set
7104 * through the CONNECTED_LINE dialplan function.
7105 */
7109
7110 tmp->block_connected_update = block_connected_line;
7111 tmp->stillgoing = 1;
7112 tmp->member = cur; /* Place the reference for cur into callattempt. */
7113 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
7114 /* Calculate the metric for the appropriate strategy. */
7115 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
7116 /* Put them in the list of outgoing thingies... We're ready now.
7117 XXX If we're forcibly removed, these outgoing calls won't get
7118 hung up XXX */
7119 tmp->q_next = outgoing;
7120 outgoing = tmp;
7121 } else {
7123 }
7124 }
7125 ao2_iterator_destroy(&memi);
7126
7128 /* Application arguments have higher timeout priority (behaviour for <=1.6) */
7129 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) {
7130 to = (qe->expire - now) * 1000;
7131 } else {
7132 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
7133 }
7134 } else {
7135 /* Config timeout is higher priority thatn application timeout */
7136 if (qe->expire && qe->expire<=now) {
7137 to = 0;
7138 } else if (qe->parent->timeout) {
7139 to = qe->parent->timeout * 1000;
7140 } else {
7141 to = -1;
7142 }
7143 }
7144 orig = to;
7145 ++qe->pending;
7146 ao2_unlock(qe->parent);
7147 /* Call the queue members with the best metric now. */
7148 ring_one(qe, outgoing, &numbusies);
7149 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
7151 forwardsallowed);
7152
7153 ao2_lock(qe->parent);
7156
7157 }
7160 }
7161 ao2_unlock(qe->parent);
7162 peer = lpeer ? lpeer->chan : NULL;
7163 if (!peer) {
7164 qe->pending = 0;
7165 if (to) {
7166 /* Must gotten hung up */
7167 res = -1;
7168 } else {
7169 /* User exited by pressing a digit */
7170 res = digit;
7171 }
7172 if (res == -1) {
7173 ast_debug(1, "%s: Nobody answered.\n", ast_channel_name(qe->chan));
7174 }
7175 } else { /* peer is valid */
7176 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
7177 RAII_VAR(struct ast_str *, interfacevar, ast_str_create(325), ast_free);
7178 /* Ah ha! Someone answered within the desired timeframe. Of course after this
7179 we will always return with -1 so that it is hung up properly after the
7180 conversation. */
7181 if (!strcmp(ast_channel_tech(qe->chan)->type, "DAHDI")) {
7182 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7183 }
7184 if (!strcmp(ast_channel_tech(peer)->type, "DAHDI")) {
7185 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
7186 }
7187 /* Update parameters for the queue */
7188 time(&now);
7189 recalc_holdtime(qe, (now - qe->start));
7190 member = lpeer->member;
7191 ao2_lock(qe->parent);
7192 callcompletedinsl = member->callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
7193 ao2_unlock(qe->parent);
7194 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
7195 ao2_ref(member, 1);
7197 outgoing = NULL;
7198 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
7199 int res2;
7200
7201 res2 = ast_autoservice_start(qe->chan);
7202 if (!res2) {
7203 if (qe->parent->memberdelay) {
7204 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
7205 res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
7206 }
7207 if (!res2 && announce) {
7208 char *front;
7209 char *announcefiles = ast_strdupa(announce);
7210 while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
7211 if (play_file(peer, front) < 0) {
7212 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
7213 }
7214 }
7215 }
7216 if (!res2 && qe->parent->reportholdtime) {
7217 if (!play_file(peer, qe->parent->sound_reporthold)) {
7218 long holdtime, holdtimesecs;
7219
7220 time(&now);
7221 holdtime = labs((now - qe->start) / 60);
7222 holdtimesecs = labs((now - qe->start) % 60);
7223 if (holdtime > 0) {
7224 ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7225 if (play_file(peer, qe->parent->sound_minutes) < 0) {
7226 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, ast_channel_name(peer));
7227 }
7228 }
7229 if (holdtimesecs > 1) {
7230 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, ast_channel_language(peer), "n");
7231 if (play_file(peer, qe->parent->sound_seconds) < 0) {
7232 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, ast_channel_name(peer));
7233 }
7234 }
7235 }
7236 }
7238 }
7239 if (ast_check_hangup(peer)) {
7240 /* Agent must have hung up */
7241 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", ast_channel_name(peer));
7242 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "AGENTDUMP", "%s", "");
7243
7244 blob = ast_json_pack("{s: s, s: s, s: s}",
7245 "Queue", queuename,
7246 "Interface", member->interface,
7247 "MemberName", member->membername);
7248 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_dump_type(), blob);
7249
7253 ao2_ref(member, -1);
7254 goto out;
7255 } else if (ast_check_hangup(qe->chan)) {
7256 /* Caller must have hung up just before being connected */
7257 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
7258 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
7259 record_abandoned(qe);
7260 qe->handled = -1;
7264 ao2_ref(member, -1);
7265 return -1;
7266 }
7267 }
7268 /* Stop music on hold */
7269 if (ringing) {
7270 ast_indicate(qe->chan,-1);
7271 } else {
7272 ast_moh_stop(qe->chan);
7273 }
7274
7275 /* Make sure channels are compatible */
7276 res = ast_channel_make_compatible(qe->chan, peer);
7277 if (res < 0) {
7278 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "SYSCOMPAT", "%s", "");
7279 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));
7280 record_abandoned(qe);
7284 ao2_ref(member, -1);
7285 return -1;
7286 }
7287
7288 /* Play announcement to the caller telling it's his turn if defined */
7290 if (play_file(qe->chan, qe->parent->sound_callerannounce)) {
7291 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
7292 }
7293 }
7294
7295 ao2_lock(qe->parent);
7296 /* if setinterfacevar is defined, make member variables available to the channel */
7297 /* use pbx_builtin_setvar to set a load of variables with one call */
7298 if (qe->parent->setinterfacevar && interfacevar) {
7299 ast_str_set(&interfacevar, 0, "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
7302 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7303 }
7304
7305 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
7306 /* use pbx_builtin_setvar to set a load of variables with one call */
7307 if (qe->parent->setqueueentryvar && interfacevar) {
7308 ast_str_set(&interfacevar, 0, "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
7309 (long) (time(NULL) - qe->start), qe->opos);
7311 pbx_builtin_setvar_multiple(peer, ast_str_buffer(interfacevar));
7312 }
7313
7314 ao2_unlock(qe->parent);
7315
7316 /* try to set queue variables if configured to do so*/
7318 set_queue_variables(qe->parent, peer);
7319
7320 setup_peer_after_bridge_goto(qe->chan, peer, &opts, opt_args);
7322 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
7323 monitorfilename = ast_strdupa(monitorfilename);
7324 }
7326
7327 /* Begin Monitoring */
7328 if (*qe->parent->monfmt) {
7329 setup_mixmonitor(qe, monitorfilename);
7330 }
7331 /* Drop out of the queue at this point, to prepare for next caller */
7332 leave_queue(qe);
7334 ast_debug(1, "app_queue: sendurl=%s.\n", url);
7335 ast_channel_sendurl(peer, url);
7336 }
7337
7338 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
7339 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
7340 if (!ast_strlen_zero(gosub)) {
7341 gosubexec = ast_strdupa(gosub);
7342 } else {
7343 if (qe->parent->membergosub) {
7344 gosubexec = ast_strdupa(qe->parent->membergosub);
7345 }
7346 }
7347
7348 if (!ast_strlen_zero(gosubexec)) {
7349 char *gosub_args = NULL;
7350 char *gosub_argstart;
7351
7352 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
7353
7354 gosub_argstart = strchr(gosubexec, ',');
7355 if (gosub_argstart) {
7356 const char *what_is_s = "s";
7357 *gosub_argstart = 0;
7358 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)) &&
7359 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7360 what_is_s = "~~s~~";
7361 }
7362 if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
7363 gosub_args = NULL;
7364 }
7365 *gosub_argstart = ',';
7366 } else {
7367 const char *what_is_s = "s";
7368 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)) &&
7369 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
7370 what_is_s = "~~s~~";
7371 }
7372 if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
7373 gosub_args = NULL;
7374 }
7375 }
7376 if (gosub_args) {
7377 ast_app_exec_sub(qe->chan, peer, gosub_args, 0);
7378 ast_free(gosub_args);
7379 } else {
7380 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
7381 }
7382 }
7383
7384 if (!ast_strlen_zero(agi)) {
7385 ast_debug(1, "app_queue: agi=%s.\n", agi);
7386 application = pbx_findapp("agi");
7387 if (application) {
7388 agiexec = ast_strdupa(agi);
7389 pbx_exec(qe->chan, application, agiexec);
7390 } else {
7391 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
7392 }
7393 }
7394 qe->handled++;
7395
7396 ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer),
7397 (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
7398
7399 blob = ast_json_pack("{s: s, s: s, s: s, s: I, s: I}",
7400 "Queue", queuename,
7401 "Interface", member->interface,
7402 "MemberName", member->membername,
7403 "HoldTime", (ast_json_int_t)(time(NULL) - qe->start),
7404 "RingTime", (ast_json_int_t)(orig - to > 0 ? (orig - to) / 1000 : 0));
7405 queue_publish_multi_channel_blob(qe->chan, peer, queue_agent_connect_type(), blob);
7406
7407 ast_copy_string(oldcontext, ast_channel_context(qe->chan), sizeof(oldcontext));
7408 ast_copy_string(oldexten, ast_channel_exten(qe->chan), sizeof(oldexten));
7409
7410 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
7411 queue_end_bridge->q = qe->parent;
7412 queue_end_bridge->chan = qe->chan;
7416 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
7417 * to make sure to increase the refcount of this queue so it cannot be freed until we
7418 * are done with it. We remove this reference in end_bridge_callback.
7419 */
7420 queue_t_ref(qe->parent, "For bridge_config reference");
7421 }
7422
7423 ao2_lock(qe->parent);
7424 time(&member->starttime);
7425 starttime = member->starttime;
7426 ao2_unlock(qe->parent);
7427 /* As a queue member may end up in multiple calls at once if a transfer occurs with
7428 * a Local channel in the mix we pass the current call information (starttime) to the
7429 * Stasis subscriptions so when they update the queue member data it becomes a noop
7430 * if this call is no longer between the caller and the queue member.
7431 */
7432 setup_stasis_subs(qe, peer, member, qe->start, starttime, callcompletedinsl);
7433 bridge = ast_bridge_call_with_flags(qe->chan, peer, &bridge_config,
7435
7436 res = bridge ? bridge : 1;
7437 ao2_ref(member, -1);
7438 }
7439out:
7441
7442 return res;
7443}
7444
7445static int wait_a_bit(struct queue_ent *qe)
7446{
7447 /* Don't need to hold the lock while we setup the outgoing calls */
7448 int retrywait = qe->parent->retry * 1000;
7449
7450 int res = ast_waitfordigit(qe->chan, retrywait);
7451 if (res > 0 && !valid_exit(qe, res)) {
7452 res = 0;
7453 }
7454
7455 return res;
7456}
7457
7458static struct member *interface_exists(struct call_queue *q, const char *interface)
7459{
7460 struct member *mem;
7461 struct ao2_iterator mem_iter;
7462
7463 if (!q) {
7464 return NULL;
7465 }
7466 mem_iter = ao2_iterator_init(q->members, 0);
7467 while ((mem = ao2_iterator_next(&mem_iter))) {
7468 if (!strcasecmp(interface, mem->interface)) {
7469 ao2_iterator_destroy(&mem_iter);
7470 return mem;
7471 }
7472 ao2_ref(mem, -1);
7473 }
7474 ao2_iterator_destroy(&mem_iter);
7475
7476 return NULL;
7477}
7478
7479
7480/*! \brief Dump all members in a specific queue to the database
7481 * \code
7482 * <pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]
7483 * \endcode
7484 */
7485static void dump_queue_members(struct call_queue *pm_queue)
7486{
7487 struct member *cur_member;
7488 struct ast_str *value;
7489 struct ao2_iterator mem_iter;
7490
7491 if (!pm_queue) {
7492 return;
7493 }
7494
7495 /* 4K is a reasonable default for most applications, but we grow to
7496 * accommodate more if necessary. */
7497 if (!(value = ast_str_create(4096))) {
7498 return;
7499 }
7500
7501 mem_iter = ao2_iterator_init(pm_queue->members, 0);
7502 while ((cur_member = ao2_iterator_next(&mem_iter))) {
7503 if (!cur_member->dynamic) {
7504 ao2_ref(cur_member, -1);
7505 continue;
7506 }
7507
7508 ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s;%s;%d",
7509 ast_str_strlen(value) ? "|" : "",
7510 cur_member->interface,
7511 cur_member->penalty,
7512 cur_member->paused,
7513 cur_member->membername,
7514 cur_member->state_interface,
7515 cur_member->reason_paused,
7516 cur_member->wrapuptime);
7517
7518 ao2_ref(cur_member, -1);
7519 }
7520 ao2_iterator_destroy(&mem_iter);
7521
7522 if (ast_str_strlen(value) && !cur_member) {
7523 if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) {
7524 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
7525 }
7526 } else {
7527 /* Delete the entry if the queue is empty or there is an error */
7528 ast_db_del(pm_family, pm_queue->name);
7529 }
7530
7531 ast_free(value);
7532}
7533
7534/*! \brief Remove member from queue
7535 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7536 * \retval RES_NOSUCHQUEUE queue does not exist
7537 * \retval RES_OKAY removed member from queue
7538 * \retval RES_EXISTS queue exists but no members
7539*/
7540static int remove_from_queue(const char *queuename, const char *interface)
7541{
7542 struct call_queue *q, tmpq = {
7543 .name = queuename,
7544 };
7545 struct member *mem, tmpmem;
7546 int res = RES_NOSUCHQUEUE;
7547
7548 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
7549 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
7550 ao2_lock(q);
7551 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
7552 /* XXX future changes should beware of this assumption!! */
7553 /*Change Penalty on realtime users*/
7555 update_realtime_member_field(mem, q->name, "penalty", "-1");
7556 } else if (!mem->dynamic) {
7557 ao2_ref(mem, -1);
7558 ao2_unlock(q);
7559 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
7560 return RES_NOT_DYNAMIC;
7561 }
7562 queue_publish_member_blob(queue_member_removed_type(), queue_member_blob_create(q, mem));
7563
7565 ao2_ref(mem, -1);
7566
7569 }
7570
7571 if (!num_available_members(q)) {
7573 }
7574
7575 res = RES_OKAY;
7576 } else {
7577 res = RES_EXISTS;
7578 }
7579 ao2_unlock(q);
7580 queue_t_unref(q, "Expiring temporary reference");
7581 }
7582
7583 return res;
7584}
7585
7586/*! \brief Add member to queue
7587 * \retval RES_NOT_DYNAMIC when they aren't a RT member
7588 * \retval RES_NOSUCHQUEUE queue does not exist
7589 * \retval RES_OKAY added member from queue
7590 * \retval RES_EXISTS queue exists but no members
7591 * \retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
7592*/
7593static 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)
7594{
7595 struct call_queue *q;
7596 struct member *new_member, *old_member;
7597 int res = RES_NOSUCHQUEUE;
7598
7599 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7600 * short-circuits if the queue is already in memory. */
7601 if (!(q = find_load_queue_rt_friendly(queuename))) {
7602 return res;
7603 }
7604
7605 ao2_lock(q);
7606 if ((old_member = interface_exists(q, interface)) == NULL) {
7608 new_member->dynamic = 1;
7609 if (reason_paused) {
7610 ast_copy_string(new_member->reason_paused, reason_paused, sizeof(new_member->reason_paused));
7611 }
7612 member_add_to_queue(q, new_member);
7613 queue_publish_member_blob(queue_member_added_type(), queue_member_blob_create(q, new_member));
7614
7615 if (is_member_available(q, new_member)) {
7617 }
7618
7619 ao2_ref(new_member, -1);
7620 new_member = NULL;
7621
7622 if (dump) {
7624 }
7625
7626 res = RES_OKAY;
7627 } else {
7628 res = RES_OUTOFMEMORY;
7629 }
7630 } else {
7631 ao2_ref(old_member, -1);
7632 res = RES_EXISTS;
7633 }
7634 ao2_unlock(q);
7635 queue_t_unref(q, "Expiring temporary reference");
7636
7637 return res;
7638}
7639
7640
7641/*! \brief Change priority caller into a queue
7642 * \retval RES_NOSUCHQUEUE queue does not exist
7643 * \retval RES_OKAY change priority
7644 * \retval RES_NOT_CALLER queue exists but no caller
7645*/
7646static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
7647{
7648 struct call_queue *q;
7649 struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
7650 int res = RES_NOSUCHQUEUE;
7651
7652 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7653 * short-circuits if the queue is already in memory. */
7654 if (!(q = find_load_queue_rt_friendly(queuename))) {
7655 return res;
7656 }
7657
7658 ao2_lock(q);
7659 res = RES_NOT_CALLER;
7660 for (current = q->head; current; current = current->next) {
7661 if (strcmp(ast_channel_name(current->chan), caller) == 0) {
7662 ast_debug(1, "%s Caller new priority %d in queue %s\n",
7663 caller, priority, queuename);
7664 current->prio = priority;
7665 if (immediate) {
7666 /* This caller is being immediately moved in the queue so remove them */
7667 if (prev) {
7668 prev->next = current->next;
7669 } else {
7670 q->head = current->next;
7671 }
7672 caller_qe = current;
7673 /* The position for all callers is not recalculated in here as it will
7674 * be updated when the moved caller is inserted back into the queue
7675 */
7676 }
7677 res = RES_OKAY;
7678 break;
7679 } else if (immediate) {
7680 prev = current;
7681 }
7682 }
7683
7684 if (caller_qe) {
7685 int inserted = 0, pos = 0;
7686
7687 /* If a caller queue entry exists, we are applying their priority immediately
7688 * and have to reinsert them at the correct position.
7689 */
7690 prev = NULL;
7691 current = q->head;
7692 while (current) {
7693 if (!inserted && (caller_qe->prio > current->prio)) {
7694 insert_entry(q, prev, caller_qe, &pos);
7695 inserted = 1;
7696 }
7697
7698 /* We always update the position as it may have changed */
7699 current->pos = ++pos;
7700
7701 /* Move to the next caller in the queue */
7702 prev = current;
7703 current = current->next;
7704 }
7705
7706 if (!inserted) {
7707 insert_entry(q, prev, caller_qe, &pos);
7708 }
7709 }
7710
7711 ao2_unlock(q);
7712 return res;
7713}
7714
7715
7716/*! \brief Request to withdraw a caller from a queue
7717 * \retval RES_NOSUCHQUEUE queue does not exist
7718 * \retval RES_OKAY withdraw request sent
7719 * \retval RES_NOT_CALLER queue exists but no caller
7720 * \retval RES_EXISTS a withdraw request was already sent for this caller (channel) and queue
7721*/
7722static int request_withdraw_caller_from_queue(const char *queuename, const char *caller, const char *withdraw_info)
7723{
7724 struct call_queue *q;
7725 struct queue_ent *qe;
7726 int res = RES_NOSUCHQUEUE;
7727
7728 /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7729 * short-circuits if the queue is already in memory. */
7730 if (!(q = find_load_queue_rt_friendly(queuename))) {
7731 return res;
7732 }
7733
7734 ao2_lock(q);
7735 res = RES_NOT_CALLER;
7736 for (qe = q->head; qe; qe = qe->next) {
7737 if (!strcmp(ast_channel_name(qe->chan), caller)) {
7738 if (qe->withdraw) {
7739 ast_debug(1, "Ignoring duplicate withdraw request of caller %s from queue %s\n", caller, queuename);
7740 res = RES_EXISTS;
7741 } else {
7742 ast_debug(1, "Requested withdraw of caller %s from queue %s\n", caller, queuename);
7743 /* It is not possible to change the withdraw info by further withdraw requests for this caller (channel)
7744 in this queue, so we do not need to worry about a memory leak here. */
7745 if (withdraw_info) {
7747 }
7748 qe->withdraw = 1;
7749 res = RES_OKAY;
7750 }
7751 break;
7752 }
7753 }
7754 ao2_unlock(q);
7755 queue_unref(q);
7756
7757 return res;
7758}
7759
7760
7762{
7763 struct ast_json *json_blob = queue_member_blob_create(q, member);
7764
7765 if (!json_blob) {
7766 return -1;
7767 }
7768
7769 queue_publish_member_blob(queue_member_pause_type(), json_blob);
7770
7771 return 0;
7772}
7773
7774/*!
7775 * \internal
7776 * \brief Set the pause status of the specific queue member.
7777 *
7778 * \param q Which queue the member belongs.
7779 * \param mem Queue member being paused/unpaused.
7780 * \param reason Why is this happening (Can be NULL/empty for no reason given.)
7781 * \param paused Set to 1 if the member is being paused or 0 to unpause.
7782 *
7783 * \pre The q is locked on entry.
7784 */
7785static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
7786{
7787 if (mem->paused == paused) {
7788 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n",
7789 (paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
7790 }
7791
7792 if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid)) {
7794 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "reason_paused", S_OR(reason, ""), "paused", paused ? "1" : "0", SENTINEL) < 0) {
7795 ast_log(LOG_WARNING, "Failed update of realtime queue member %s:%s %spause and reason '%s'\n",
7796 q->name, mem->interface, (paused ? "" : "un"), S_OR(reason, ""));
7797 }
7798 } else {
7799 if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "paused", paused ? "1" : "0", SENTINEL) < 0) {
7800 ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
7801 (paused ? "" : "un"), q->name, mem->interface);
7802 }
7803 }
7804 }
7805
7806 mem->paused = paused;
7807 if (paused) {
7808 time(&mem->lastpause); /* update last pause field */
7809 }
7810 if (paused && !ast_strlen_zero(reason)) {
7811 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
7812 } else {
7813 /* We end up filling this in again later (temporarily) but we need it
7814 * empty for now so that the intervening code - specifically
7815 * dump_queue_members() - has the correct view of things. */
7816 mem->reason_paused[0] = '\0';
7817 }
7818
7820 AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, mem->interface);
7821
7824 }
7825
7826 if (is_member_available(q, mem)) {
7828 "Queue:%s_avail", q->name);
7829 } else if (!num_available_members(q)) {
7831 "Queue:%s_avail", q->name);
7832 }
7833
7834 if (!paused && !ast_strlen_zero(reason)) {
7835 /* Because we've been unpaused with a 'reason' we need to ensure that
7836 * that reason is emitted when the subsequent PauseQueueMember event
7837 * is raised. So temporarily set it on the member and clear it out
7838 * again right after. */
7839 ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
7840 }
7841
7842 ast_queue_log(q->name, "NONE", mem->membername, paused ? "PAUSE" : "UNPAUSE",
7843 "%s", mem->reason_paused);
7844
7846
7847 if (!paused) {
7848 mem->reason_paused[0] = '\0';
7849 }
7850}
7851
7852static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
7853{
7854 int found = 0;
7855 struct call_queue *q;
7856 struct ao2_iterator queue_iter;
7857
7858 if (ast_check_realtime("queues")) {
7859 load_realtime_queues(queuename);
7860 }
7861
7862 queue_iter = ao2_iterator_init(queues, 0);
7863 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
7864 ao2_lock(q);
7865 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
7866 struct member *mem;
7867
7868 if ((mem = interface_exists(q, interface))) {
7869 /*
7870 * Before we do the PAUSE/UNPAUSE, log if this was a
7871 * PAUSEALL/UNPAUSEALL but only on the first found entry.
7872 */
7873 ++found;
7874 if (found == 1
7875 && ast_strlen_zero(queuename)) {
7876 /*
7877 * XXX In all other cases, we use the queue name,
7878 * but since this affects all queues, we cannot.
7879 */
7880 ast_queue_log("NONE", "NONE", mem->membername,
7881 (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", S_OR(reason, ""));
7882 }
7883
7884 set_queue_member_pause(q, mem, reason, paused);
7885 ao2_ref(mem, -1);
7886 }
7887
7888 if (!ast_strlen_zero(queuename)) {
7889 ao2_unlock(q);
7890 queue_t_unref(q, "Done with iterator");
7891 break;
7892 }
7893 }
7894
7895 ao2_unlock(q);
7896 queue_t_unref(q, "Done with iterator");
7897 }
7898 ao2_iterator_destroy(&queue_iter);
7899
7900 return found ? RESULT_SUCCESS : RESULT_FAILURE;
7901}
7902
7903/*!
7904 * \internal
7905 * \brief helper function for set_member_penalty - given a queue, sets all member penalties with the interface
7906 * \param[in] q queue which is having its member's penalty changed - must be unlocked prior to calling
7907 * \param[in] interface String of interface used to search for queue members being changed
7908 * \param[in] penalty Value penalty is being changed to for the member.
7909 * \retval 0 if the there is no member with interface belonging to q and no change is made
7910 * \retval 1 if the there is a member with interface belonging to q and changes are made
7911 */
7912static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
7913{
7914 struct member *mem;
7915 int foundinterface = 0;
7916
7917 ao2_lock(q);
7918 if ((mem = interface_exists(q, interface))) {
7919 foundinterface++;
7920 if (mem->realtime) {
7921 char rtpenalty[80];
7922
7923 sprintf(rtpenalty, "%i", penalty);
7924 update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
7925 }
7926
7927 mem->penalty = penalty;
7928
7929 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
7930 queue_publish_member_blob(queue_member_penalty_type(), queue_member_blob_create(q, mem));
7931 ao2_ref(mem, -1);
7932 }
7933 ao2_unlock(q);
7934
7935 return foundinterface;
7936}
7937
7938/*!
7939 * \internal
7940 * \brief Set the ringinuse value of the specific queue member.
7941 *
7942 * \param q Which queue the member belongs.
7943 * \param mem Queue member being set.
7944 * \param ringinuse Set to 1 if the member is called when inuse.
7945 *
7946 * \pre The q is locked on entry.
7947 */
7948static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
7949{
7950 if (mem->realtime) {
7952 ringinuse ? "1" : "0");
7953 }
7954
7955 mem->ringinuse = ringinuse;
7956
7957 ast_queue_log(q->name, "NONE", mem->interface, "RINGINUSE", "%d", ringinuse);
7958 queue_publish_member_blob(queue_member_ringinuse_type(), queue_member_blob_create(q, mem));
7959}
7960
7962{
7963 struct member *mem;
7964 int foundinterface = 0;
7965
7966 ao2_lock(q);
7967 if ((mem = interface_exists(q, interface))) {
7968 foundinterface++;
7970 ao2_ref(mem, -1);
7971 }
7972 ao2_unlock(q);
7973
7974 return foundinterface;
7975}
7976
7977static int set_member_value_help_members(struct call_queue *q, const char *interface, int property, int value)
7978{
7979 switch(property) {
7980 case MEMBER_PENALTY:
7982
7983 case MEMBER_RINGINUSE:
7985
7986 default:
7987 ast_log(LOG_ERROR, "Attempted to set invalid property\n");
7988 return 0;
7989 }
7990}
7991
7992/*!
7993 * \internal
7994 * \brief Sets members penalty, if queuename=NULL we set member penalty in all the queues.
7995 * \param[in] queuename If specified, only act on a member if it belongs to this queue
7996 * \param[in] interface Interface of queue member(s) having priority set.
7997 * \param[in] property Which queue property is being set
7998 * \param[in] value Value penalty is being changed to for each member
7999 */
8000static int set_member_value(const char *queuename, const char *interface, int property, int value)
8001{
8002 int foundinterface = 0, foundqueue = 0;
8003 struct call_queue *q;
8004 struct ast_config *queue_config = NULL;
8005 struct ao2_iterator queue_iter;
8006
8007 /* property dependent restrictions on values should be checked in this switch */
8008 switch (property) {
8009 case MEMBER_PENALTY:
8010 if (value < 0 && !negative_penalty_invalid) {
8011 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", value);
8012 return RESULT_FAILURE;
8013 }
8014 }
8015
8016 if (ast_strlen_zero(queuename)) { /* This means we need to iterate through all the queues. */
8017 if (ast_check_realtime("queues")) {
8018 queue_config = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
8019 if (queue_config) {
8020 char *category = NULL;
8021 while ((category = ast_category_browse(queue_config, category))) {
8022 const char *name = ast_variable_retrieve(queue_config, category, "name");
8023 if (ast_strlen_zero(name)) {
8024 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
8025 continue;
8026 }
8027 if ((q = find_load_queue_rt_friendly(name))) {
8028 foundqueue++;
8029 foundinterface += set_member_value_help_members(q, interface, property, value);
8030 queue_unref(q);
8031 }
8032 }
8033
8034 ast_config_destroy(queue_config);
8035 }
8036 }
8037
8038 /* After hitting realtime queues, go back and get the regular ones. */
8039 queue_iter = ao2_iterator_init(queues, 0);
8040 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
8041 foundqueue++;
8042 foundinterface += set_member_value_help_members(q, interface, property, value);
8043 queue_unref(q);
8044 }
8045 ao2_iterator_destroy(&queue_iter);
8046 } else { /* We actually have a queuename, so we can just act on the single queue. */
8047 if ((q = find_load_queue_rt_friendly(queuename))) {
8048 foundqueue++;
8049 foundinterface += set_member_value_help_members(q, interface, property, value);
8050 queue_unref(q);
8051 }
8052 }
8053
8054 if (foundinterface) {
8055 return RESULT_SUCCESS;
8056 } else if (!foundqueue) {
8057 ast_log (LOG_ERROR, "Invalid queuename\n");
8058 } else {
8059 ast_log (LOG_ERROR, "Invalid interface\n");
8060 }
8061
8062 return RESULT_FAILURE;
8063}
8064
8065/*!
8066 * \brief Gets members penalty.
8067 * \return Return the members penalty or RESULT_FAILURE on error.
8068 */
8069static int get_member_penalty(char *queuename, char *interface)
8070{
8071 int foundqueue = 0, penalty;
8072 struct call_queue *q;
8073 struct member *mem;
8074
8075 if ((q = find_load_queue_rt_friendly(queuename))) {
8076 foundqueue = 1;
8077 ao2_lock(q);
8078 if ((mem = interface_exists(q, interface))) {
8079 penalty = mem->penalty;
8080 ao2_ref(mem, -1);
8081 ao2_unlock(q);
8082 queue_t_unref(q, "Search complete");
8083 return penalty;
8084 }
8085 ao2_unlock(q);
8086 queue_t_unref(q, "Search complete");
8087 }
8088
8089 /* some useful debugging */
8090 if (foundqueue) {
8091 ast_log (LOG_ERROR, "Invalid queuename\n");
8092 } else {
8093 ast_log (LOG_ERROR, "Invalid interface\n");
8094 }
8095
8096 return RESULT_FAILURE;
8097}
8098
8099/*! \brief Reload dynamic queue members persisted into the astdb */
8100static void reload_queue_members(void)
8101{
8102 char *cur_ptr;
8103 const char *queue_name;
8104 char *member;
8105 char *interface;
8106 char *membername = NULL;
8107 char *state_interface;
8108 char *penalty_tok;
8109 int penalty = 0;
8110 char *paused_tok;
8111 int paused = 0;
8112 char *wrapuptime_tok;
8113 int wrapuptime = 0;
8114 char *reason_paused;
8115 struct ast_db_entry *db_tree;
8116 struct ast_db_entry *entry;
8117 struct call_queue *cur_queue;
8118 char *queue_data;
8119
8120 /* Each key in 'pm_family' is the name of a queue */
8121 db_tree = ast_db_gettree(pm_family, NULL);
8122 for (entry = db_tree; entry; entry = entry->next) {
8123
8124 queue_name = entry->key + strlen(pm_family) + 2;
8125
8126 {
8127 struct call_queue tmpq = {
8128 .name = queue_name,
8129 };
8130 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
8131 }
8132
8133 if (!cur_queue) {
8134 cur_queue = find_load_queue_rt_friendly(queue_name);
8135 }
8136
8137 if (!cur_queue) {
8138 /* If the queue no longer exists, remove it from the
8139 * database */
8140 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
8141 ast_db_del(pm_family, queue_name);
8142 continue;
8143 }
8144
8145 if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
8146 queue_t_unref(cur_queue, "Expire reload reference");
8147 continue;
8148 }
8149
8150 cur_ptr = queue_data;
8151 while ((member = strsep(&cur_ptr, ",|"))) {
8152 if (ast_strlen_zero(member)) {
8153 continue;
8154 }
8155
8156 interface = strsep(&member, ";");
8157 penalty_tok = strsep(&member, ";");
8158 paused_tok = strsep(&member, ";");
8159 membername = strsep(&member, ";");
8160 state_interface = strsep(&member, ";");
8161 reason_paused = strsep(&member, ";");
8162 wrapuptime_tok = strsep(&member, ";");
8163
8164 if (!penalty_tok) {
8165 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
8166 break;
8167 }
8168 penalty = strtol(penalty_tok, NULL, 10);
8169 if (errno == ERANGE) {
8170 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
8171 break;
8172 }
8173
8174 if (!paused_tok) {
8175 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
8176 break;
8177 }
8178 paused = strtol(paused_tok, NULL, 10);
8179 if ((errno == ERANGE) || paused < 0 || paused > 1) {
8180 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
8181 break;
8182 }
8183
8184 if (!ast_strlen_zero(wrapuptime_tok)) {
8185 wrapuptime = strtol(wrapuptime_tok, NULL, 10);
8186 if (errno == ERANGE) {
8187 ast_log(LOG_WARNING, "Error converting wrapuptime: %s: Out of range.\n", wrapuptime_tok);
8188 break;
8189 }
8190 }
8191
8192 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d ReasonPause: %s Wrapuptime: %d\n",
8193 queue_name, interface, membername, penalty, paused, reason_paused, wrapuptime);
8194
8195 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface, reason_paused, wrapuptime) == RES_OUTOFMEMORY) {
8196 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
8197 break;
8198 }
8199 }
8200 queue_t_unref(cur_queue, "Expire reload reference");
8201 ast_free(queue_data);
8202 }
8203
8204 if (db_tree) {
8205 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
8206 ast_db_freetree(db_tree);
8207 }
8208}
8209
8210/*! \brief PauseQueueMember application */
8211static int pqm_exec(struct ast_channel *chan, const char *data)
8212{
8213 char *parse;
8215 AST_APP_ARG(queuename);
8216 AST_APP_ARG(interface);
8218 AST_APP_ARG(reason);
8219 );
8220
8221 if (ast_strlen_zero(data)) {
8222 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
8223 return -1;
8224 }
8225
8226 parse = ast_strdupa(data);
8227
8229
8230 if (ast_strlen_zero(args.interface)) {
8231 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
8232 return -1;
8233 }
8234
8235 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
8236 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
8237 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
8238 return 0;
8239 }
8240
8241 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
8242
8243 return 0;
8244}
8245
8246/*! \brief UnpauseQueueMember application */
8247static int upqm_exec(struct ast_channel *chan, const char *data)
8248{
8249 char *parse;
8251 AST_APP_ARG(queuename);
8252 AST_APP_ARG(interface);
8254 AST_APP_ARG(reason);
8255 );
8256
8257 if (ast_strlen_zero(data)) {
8258 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
8259 return -1;
8260 }
8261
8262 parse = ast_strdupa(data);
8263
8265
8266 if (ast_strlen_zero(args.interface)) {
8267 ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
8268 return -1;
8269 }
8270
8271 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
8272 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
8273 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
8274 return 0;
8275 }
8276
8277 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
8278
8279 return 0;
8280}
8281
8282/*! \brief RemoveQueueMember application */
8283static int rqm_exec(struct ast_channel *chan, const char *data)
8284{
8285 int res=-1;
8286 char *parse, *temppos = NULL;
8287 struct member *mem = NULL;
8288
8290 AST_APP_ARG(queuename);
8292 );
8293
8294
8295 if (ast_strlen_zero(data)) {
8296 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
8297 return -1;
8298 }
8299
8300 parse = ast_strdupa(data);
8301
8303
8304 if (ast_strlen_zero(args.interface)) {
8305 args.interface = ast_strdupa(ast_channel_name(chan));
8306 temppos = strrchr(args.interface, '-');
8307 if (temppos) {
8308 *temppos = '\0';
8309 }
8310 }
8311
8312 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
8313
8315 mem = find_member_by_queuename_and_interface(args.queuename, args.interface);
8316 }
8317
8318 switch (remove_from_queue(args.queuename, args.interface)) {
8319 case RES_OKAY:
8320 if (!mem || ast_strlen_zero(mem->membername)) {
8321 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "REMOVEMEMBER", "%s", "");
8322 } else {
8323 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), mem->membername, "REMOVEMEMBER", "%s", "");
8324 }
8325 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
8326 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
8327 res = 0;
8328 break;
8329 case RES_EXISTS:
8330 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
8331 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
8332 res = 0;
8333 break;
8334 case RES_NOSUCHQUEUE:
8335 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
8336 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
8337 res = 0;
8338 break;
8339 case RES_NOT_DYNAMIC:
8340 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
8341 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
8342 res = 0;
8343 break;
8344 }
8345
8346 if (mem) {
8347 ao2_ref(mem, -1);
8348 }
8349
8350 return res;
8351}
8352
8353/*! \brief AddQueueMember application */
8354static int aqm_exec(struct ast_channel *chan, const char *data)
8355{
8356 int res=-1;
8357 char *parse, *tmp, *temppos = NULL;
8359 AST_APP_ARG(queuename);
8366 );
8367 int penalty = 0;
8368 int wrapuptime;
8369
8370 if (ast_strlen_zero(data)) {
8371 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface][,wrapuptime]]]]])\n");
8372 return -1;
8373 }
8374
8375 parse = ast_strdupa(data);
8376
8378
8379 if (ast_strlen_zero(args.interface)) {
8380 args.interface = ast_strdupa(ast_channel_name(chan));
8381 temppos = strrchr(args.interface, '-');
8382 if (temppos) {
8383 *temppos = '\0';
8384 }
8385 }
8386
8387 if (!ast_strlen_zero(args.penalty)) {
8388 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
8389 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
8390 penalty = 0;
8391 }
8392 }
8393
8394 if (!ast_strlen_zero(args.wrapuptime)) {
8395 tmp = args.wrapuptime;
8396 ast_strip(tmp);
8397 wrapuptime = atoi(tmp);
8398 if (wrapuptime < 0) {
8399 wrapuptime = 0;
8400 }
8401 } else {
8402 wrapuptime = 0;
8403 }
8404
8405 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface, NULL, wrapuptime)) {
8406 case RES_OKAY:
8407 if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
8408 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "ADDMEMBER", "%s", "");
8409 } else {
8410 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.membername, "ADDMEMBER", "%s", "");
8411 }
8412 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
8413 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
8414 res = 0;
8415 break;
8416 case RES_EXISTS:
8417 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
8418 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
8419 res = 0;
8420 break;
8421 case RES_NOSUCHQUEUE:
8422 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
8423 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
8424 res = 0;
8425 break;
8426 case RES_OUTOFMEMORY:
8427 ast_log(LOG_ERROR, "Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
8428 break;
8429 }
8430
8431 return res;
8432}
8433
8434/*! \brief QueueLog application */
8435static int ql_exec(struct ast_channel *chan, const char *data)
8436{
8437 char *parse;
8438
8440 AST_APP_ARG(queuename);
8441 AST_APP_ARG(uniqueid);
8444 AST_APP_ARG(params);
8445 );
8446
8447 if (ast_strlen_zero(data)) {
8448 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
8449 return -1;
8450 }
8451
8452 parse = ast_strdupa(data);
8453
8455
8456 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
8457 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
8458 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
8459 return -1;
8460 }
8461
8462 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
8463 "%s", args.params ? args.params : "");
8464
8465 return 0;
8466}
8467
8468/*! \brief Copy rule from global list into specified queue */
8469static void copy_rules(struct queue_ent *qe, const char *rulename)
8470{
8471 struct penalty_rule *pr_iter;
8472 struct rule_list *rl_iter;
8473 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
8475 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
8476 if (!strcasecmp(rl_iter->name, tmp)) {
8477 break;
8478 }
8479 }
8480 if (rl_iter) {
8481 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
8482 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
8483 if (!new_pr) {
8484 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
8485 break;
8486 }
8487 new_pr->time = pr_iter->time;
8488 new_pr->max_value = pr_iter->max_value;
8489 new_pr->min_value = pr_iter->min_value;
8490 new_pr->raise_value = pr_iter->raise_value;
8491 new_pr->max_relative = pr_iter->max_relative;
8492 new_pr->min_relative = pr_iter->min_relative;
8493 new_pr->raise_relative = pr_iter->raise_relative;
8494 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
8495 }
8496 }
8498}
8499
8500/*!\brief The starting point for all queue calls
8501 *
8502 * The process involved here is to
8503 * 1. Parse the options specified in the call to Queue()
8504 * 2. Join the queue
8505 * 3. Wait in a loop until it is our turn to try calling a queue member
8506 * 4. Attempt to call a queue member
8507 * 5. If 4. did not result in a bridged call, then check for between
8508 * call options such as periodic announcements etc.
8509 * 6. Try 4 again unless some condition (such as an expiration time) causes us to
8510 * exit the queue.
8511 */
8512static int queue_exec(struct ast_channel *chan, const char *data)
8513{
8514 int res=-1;
8515 int ringing=0;
8516 const char *user_priority;
8517 const char *max_penalty_str;
8518 const char *min_penalty_str;
8519 const char *raise_penalty_str;
8520 int prio;
8521 int qcontinue = 0;
8522 int max_penalty, min_penalty, raise_penalty;
8523 enum queue_result reason = QUEUE_UNKNOWN;
8524 /* whether to exit Queue application after the timeout hits */
8525 int tries = 0;
8526 int noption = 0;
8527 char *parse;
8528 int makeannouncement = 0;
8529 int position = 0;
8531 AST_APP_ARG(queuename);
8534 AST_APP_ARG(announceoverride);
8535 AST_APP_ARG(queuetimeoutstr);
8536 AST_APP_ARG(agi);
8537 AST_APP_ARG(gosub);
8539 AST_APP_ARG(position);
8540 );
8541 /* Our queue entry */
8542 struct queue_ent qe = { 0 };
8543 struct ast_flags opts = { 0, };
8544 char *opt_args[OPT_ARG_ARRAY_SIZE];
8545 int max_forwards;
8546 int cid_allow;
8547
8548 if (ast_strlen_zero(data)) {
8549 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,gosub[,rule[,position]]]]]]]]\n");
8550 return -1;
8551 }
8552
8553 ast_channel_lock(chan);
8555 ast_channel_unlock(chan);
8556
8557 if (max_forwards <= 0) {
8558 ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
8559 return -1;
8560 }
8561
8562 parse = ast_strdupa(data);
8564
8565 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, gosub: %s, rule: %s, position: %s\n",
8566 args.queuename,
8567 S_OR(args.options, ""),
8568 S_OR(args.url, ""),
8569 S_OR(args.announceoverride, ""),
8570 S_OR(args.queuetimeoutstr, ""),
8571 S_OR(args.agi, ""),
8572 S_OR(args.gosub, ""),
8573 S_OR(args.rule, ""),
8574 S_OR(args.position, ""));
8575
8576 if (!ast_strlen_zero(args.options)) {
8577 ast_app_parse_options(queue_exec_options, &opts, opt_args, args.options);
8578 }
8579
8580 /* Setup our queue entry */
8581 qe.start = time(NULL);
8582
8583 pbx_builtin_setvar_helper(chan, "ABANDONED", NULL);
8584
8585 /* set the expire time based on the supplied timeout; */
8586 if (!ast_strlen_zero(args.queuetimeoutstr)) {
8587 qe.expire = qe.start + atoi(args.queuetimeoutstr);
8588 } else {
8589 qe.expire = 0;
8590 }
8591
8592 /* Get the priority from the variable ${QUEUE_PRIO} */
8593 ast_channel_lock(chan);
8594 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
8595 if (user_priority) {
8596 if (sscanf(user_priority, "%30d", &prio) == 1) {
8597 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
8598 } else {
8599 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
8600 user_priority, ast_channel_name(chan));
8601 prio = 0;
8602 }
8603 } else {
8604 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
8605 prio = 0;
8606 }
8607
8608 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
8609
8610 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
8611 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
8612 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
8613 } else {
8614 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
8615 max_penalty_str, ast_channel_name(chan));
8616 max_penalty = INT_MAX;
8617 }
8618 } else {
8619 max_penalty = INT_MAX;
8620 }
8621
8622 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
8623 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
8624 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
8625 } else {
8626 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
8627 min_penalty_str, ast_channel_name(chan));
8628 min_penalty = INT_MAX;
8629 }
8630 } else {
8631 min_penalty = INT_MAX;
8632 }
8633
8634 if ((raise_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_RAISE_PENALTY"))) {
8635 if (sscanf(raise_penalty_str, "%30d", &raise_penalty) == 1) {
8636 ast_debug(1, "%s: Got raise penalty %d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), raise_penalty);
8637 } else {
8638 ast_log(LOG_WARNING, "${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n",
8639 raise_penalty_str, ast_channel_name(chan));
8640 raise_penalty = INT_MAX;
8641 }
8642 } else {
8643 raise_penalty = INT_MAX;
8644 }
8645 ast_channel_unlock(chan);
8646
8647 if (ast_test_flag(&opts, OPT_RINGING)) {
8648 ringing = 1;
8649 }
8650
8651 if (ringing != 1 && ast_test_flag(&opts, OPT_RING_WHEN_RINGING)) {
8652 qe.ring_when_ringing = 1;
8653 }
8654
8655 if (ast_test_flag(&opts, OPT_GO_ON)) {
8656 qcontinue = 1;
8657 }
8658
8659 if (args.position) {
8660 position = atoi(args.position);
8661 if (position < 0) {
8662 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
8663 position = 0;
8664 }
8665 }
8666
8667 ast_debug(1, "queue: %s, expires: %ld, priority: %d\n",
8668 args.queuename, (long)qe.expire, prio);
8669
8670 qe.chan = chan;
8671 qe.prio = prio;
8672 qe.max_penalty = max_penalty;
8673 qe.min_penalty = min_penalty;
8674 qe.raise_penalty = raise_penalty;
8675 qe.last_pos_said = 0;
8676 qe.last_pos = 0;
8679 qe.valid_digits = 0;
8680 if (join_queue(args.queuename, &qe, &reason, position)) {
8681 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
8682 set_queue_result(chan, reason);
8683 return 0;
8684 }
8685 ast_assert(qe.parent != NULL);
8686
8687 if (qe.parent->periodicannouncestartdelay >= 0) {
8690 }
8691
8693
8694 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
8695 S_OR(args.url, ""),
8696 S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8697 qe.opos);
8698
8699 /* PREDIAL: Preprocess any callee gosub arguments. */
8701 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
8704 }
8705
8706 /* PREDIAL: Run gosub on the caller's channel */
8708 && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
8710 ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
8711 }
8712
8713 /* Music on hold class override */
8716 ast_copy_string(qe.moh, opt_args[OPT_ARG_MUSICONHOLD_CLASS], sizeof(qe.moh));
8717 }
8718
8719 copy_rules(&qe, args.rule);
8720 qe.pr = AST_LIST_FIRST(&qe.qe_rules);
8721check_turns:
8722 if (ringing) {
8724 } else {
8725 ast_moh_start(chan, qe.moh, NULL);
8726 }
8727
8728 /* This is the wait loop for callers 2 through maxlen */
8729 res = wait_our_turn(&qe, ringing, &reason);
8730 if (res) {
8731 goto stop;
8732 }
8733
8734 makeannouncement = qe.parent->announce_to_first_user;
8735
8736 for (;;) {
8737 /* This is the wait loop for the head caller*/
8738 /* To exit, they may get their call answered; */
8739 /* they may dial a digit from the queue context; */
8740 /* or, they may timeout. */
8741
8742 /* A request to withdraw this call from the queue arrived */
8743 if (qe.withdraw) {
8744 reason = QUEUE_WITHDRAW;
8745 res = 1;
8746 break;
8747 }
8748
8749 /* Leave if we have exceeded our queuetimeout */
8750 if (qe.expire && (time(NULL) >= qe.expire)) {
8751 record_abandoned(&qe);
8752 reason = QUEUE_TIMEOUT;
8753 res = 0;
8754 ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
8755 qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8756 break;
8757 }
8758
8759 if (makeannouncement) {
8760 /* Make a position announcement, if enabled */
8761 if (qe.parent->announcefrequency) {
8762 if ((res = say_position(&qe, ringing))) {
8763 goto stop;
8764 }
8765 }
8766 }
8767 makeannouncement = 1;
8768
8769 /* Make a periodic announcement, if enabled */
8771 if ((res = say_periodic_announcement(&qe, ringing))) {
8772 goto stop;
8773 }
8774 }
8775
8776 /* A request to withdraw this call from the queue arrived */
8777 if (qe.withdraw) {
8778 reason = QUEUE_WITHDRAW;
8779 res = 1;
8780 break;
8781 }
8782
8783 /* Leave if we have exceeded our queuetimeout */
8784 if (qe.expire && (time(NULL) >= qe.expire)) {
8785 record_abandoned(&qe);
8786 reason = QUEUE_TIMEOUT;
8787 res = 0;
8788 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
8789 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8790 break;
8791 }
8792
8793 /* see if we need to move to the next penalty level for this queue */
8794 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
8795 update_qe_rule(&qe);
8796 }
8797
8798 /* Try calling all queue members for 'timeout' seconds */
8799 res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.gosub, ringing);
8800 if (res) {
8801 goto stop;
8802 }
8803
8804 if (qe.parent->leavewhenempty) {
8805 int status = 0;
8807 record_abandoned(&qe);
8808 reason = QUEUE_LEAVEEMPTY;
8809 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
8810 res = 0;
8811 break;
8812 }
8813 }
8814
8815 /* exit after 'timeout' cycle if 'n' option enabled */
8816 if (noption && tries >= ao2_container_count(qe.parent->members)) {
8817 ast_verb(3, "Exiting on time-out cycle\n");
8818 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
8819 "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8820 record_abandoned(&qe);
8821 reason = QUEUE_TIMEOUT;
8822 res = 0;
8823 break;
8824 }
8825
8826
8827 /* Leave if we have exceeded our queuetimeout */
8828 if (qe.expire && (time(NULL) >= qe.expire)) {
8829 record_abandoned(&qe);
8830 reason = QUEUE_TIMEOUT;
8831 res = 0;
8832 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));
8833 break;
8834 }
8835
8836 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
8837 res = wait_a_bit(&qe);
8838 if (res) {
8839 goto stop;
8840 }
8841
8842 /* If using dynamic realtime members, we should regenerate the member list for this queue */
8844
8845 /* Since this is a priority queue and
8846 * it is not sure that we are still at the head
8847 * of the queue, go and check for our turn again.
8848 */
8849 if (!is_our_turn(&qe)) {
8850 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.chan));
8851 goto check_turns;
8852 }
8853 }
8854
8855stop:
8856 if (res) {
8857 if (reason == QUEUE_WITHDRAW) {
8858 record_abandoned(&qe);
8859 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 : "");
8860 if (qe.withdraw_info) {
8861 pbx_builtin_setvar_helper(qe.chan, "QUEUE_WITHDRAW_INFO", qe.withdraw_info);
8862 }
8863 res = 0;
8864 } else if (res < 0) {
8865 if (!qe.handled) {
8866 record_abandoned(&qe);
8867 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
8868 "%d|%d|%ld", qe.pos, qe.opos,
8869 (long) (time(NULL) - qe.start));
8870 res = -1;
8871 } else if (reason == QUEUE_LEAVEEMPTY) {
8872 /* Return back to dialplan, don't hang up */
8873 res = 0;
8874 } else if (qcontinue) {
8875 reason = QUEUE_CONTINUE;
8876 res = 0;
8877 }
8878 } else if (qe.valid_digits) {
8879 ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
8880 "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8881 }
8882 }
8883
8884 /* Free the optional withdraw info if present */
8885 /* This is done here to catch all cases. e.g. if the call eventually wasn't withdrawn, e.g. answered */
8886 if (qe.withdraw_info) {
8888 qe.withdraw_info = NULL;
8889 }
8890
8891 /* Don't allow return code > 0 */
8892 if (res >= 0) {
8893 res = 0;
8894 if (ringing) {
8895 ast_indicate(chan, -1);
8896 } else {
8897 ast_moh_stop(chan);
8898 }
8899 ast_stopstream(chan);
8900 }
8901
8903
8904 leave_queue(&qe);
8905 if (reason != QUEUE_UNKNOWN)
8906 set_queue_result(chan, reason);
8907
8908 /*
8909 * every queue_ent is given a reference to it's parent
8910 * call_queue when it joins the queue. This ref must be taken
8911 * away right before the queue_ent is destroyed. In this case
8912 * the queue_ent is about to be returned on the stack
8913 */
8914 qe.parent = queue_unref(qe.parent);
8915
8916 return res;
8917}
8918
8919/*!
8920 * \brief create interface var with all queue details.
8921 * \retval 0 on success
8922 * \retval -1 on error
8923*/
8924static int queue_function_var(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
8925{
8926 int res = -1;
8927 struct call_queue *q;
8928 char interfacevar[256] = "";
8929 float sl = 0;
8930
8931 if (ast_strlen_zero(data)) {
8932 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
8933 return -1;
8934 }
8935
8936 if ((q = find_load_queue_rt_friendly(data))) {
8937 ao2_lock(q);
8938 if (q->setqueuevar) {
8939 sl = 0;
8940 res = 0;
8941
8942 if (q->callscompleted > 0) {
8943 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
8944 }
8945
8946 snprintf(interfacevar, sizeof(interfacevar),
8947 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
8949
8950 pbx_builtin_setvar_multiple(chan, interfacevar);
8951 }
8952
8953 ao2_unlock(q);
8954 queue_t_unref(q, "Done with QUEUE() function");
8955 } else {
8956 ast_log(LOG_WARNING, "queue %s was not found\n", data);
8957 }
8958
8959 snprintf(buf, len, "%d", res);
8960
8961 return 0;
8962}
8963
8964/*!
8965 * \brief Check if a given queue exists
8966 *
8967 */
8968static int queue_function_exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
8969{
8970 struct call_queue *q;
8971
8972 buf[0] = '\0';
8973
8974 if (ast_strlen_zero(data)) {
8975 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
8976 return -1;
8977 }
8979 snprintf(buf, len, "%d", q != NULL? 1 : 0);
8980 if (q) {
8981 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
8982 }
8983
8984 return 0;
8985}
8986
8987static struct member *get_interface_helper(struct call_queue *q, const char *interface)
8988{
8989 struct member *m;
8990
8992 ast_log(LOG_ERROR, "QUEUE_MEMBER: Missing required interface argument.\n");
8993 return NULL;
8994 }
8995
8997 if (!m) {
8998 ast_log(LOG_ERROR, "Queue member interface '%s' not in queue '%s'.\n",
8999 interface, q->name);
9000 }
9001 return m;
9002}
9003
9004/*!
9005 * \brief Get number either busy / free / ready or total members of a specific queue
9006 * \brief Get or set member properties penalty / paused / ringinuse
9007 * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ringinuse)
9008 * \retval -1 on error
9009 */
9010static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9011{
9012 int count = 0;
9013 struct member *m;
9014 struct ao2_iterator mem_iter;
9015 struct call_queue *q;
9016
9018 AST_APP_ARG(queuename);
9019 AST_APP_ARG(option);
9020 AST_APP_ARG(interface);
9021 );
9022 /* Make sure the returned value on error is zero length string. */
9023 buf[0] = '\0';
9024
9025 if (ast_strlen_zero(data)) {
9027 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9028 cmd);
9029 return -1;
9030 }
9031
9033
9034 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
9036 "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9037 cmd);
9038 return -1;
9039 }
9040
9041 if ((q = find_load_queue_rt_friendly(args.queuename))) {
9042 ao2_lock(q);
9043 if (!strcasecmp(args.option, "logged")) {
9044 mem_iter = ao2_iterator_init(q->members, 0);
9045 while ((m = ao2_iterator_next(&mem_iter))) {
9046 /* Count the agents who are logged in and presently answering calls */
9047 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9048 count++;
9049 }
9050 ao2_ref(m, -1);
9051 }
9052 ao2_iterator_destroy(&mem_iter);
9053 } else if (!strcasecmp(args.option, "free")) {
9054 mem_iter = ao2_iterator_init(q->members, 0);
9055 while ((m = ao2_iterator_next(&mem_iter))) {
9056 /* Count the agents who are logged in and presently answering calls */
9057 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
9058 count++;
9059 }
9060 ao2_ref(m, -1);
9061 }
9062 ao2_iterator_destroy(&mem_iter);
9063 } else if (!strcasecmp(args.option, "ready")) {
9064 time_t now;
9065 time(&now);
9066 mem_iter = ao2_iterator_init(q->members, 0);
9067 while ((m = ao2_iterator_next(&mem_iter))) {
9068 /* Count the agents who are logged in, not paused and not wrapping up */
9069 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
9070 !(m->lastcall && get_wrapuptime(q, m) && ((now - get_wrapuptime(q, m)) < m->lastcall))) {
9071 count++;
9072 }
9073 ao2_ref(m, -1);
9074 }
9075 ao2_iterator_destroy(&mem_iter);
9076 } else if (!strcasecmp(args.option, "count")) {
9078 } else if (!strcasecmp(args.option, "penalty")) {
9079 m = get_interface_helper(q, args.interface);
9080 if (m) {
9081 count = m->penalty;
9082 ao2_ref(m, -1);
9083 }
9084 } else if (!strcasecmp(args.option, "paused")) {
9085 m = get_interface_helper(q, args.interface);
9086 if (m) {
9087 count = m->paused;
9088 ao2_ref(m, -1);
9089 }
9090 } else if ((!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9091 || !strcasecmp(args.option, "ringinuse"))) {
9092 m = get_interface_helper(q, args.interface);
9093 if (m) {
9094 count = m->ringinuse;
9095 ao2_ref(m, -1);
9096 }
9097 } else {
9098 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9099 }
9100 ao2_unlock(q);
9101 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
9102 } else {
9103 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9104 }
9105
9106 snprintf(buf, len, "%d", count);
9107
9108 return 0;
9109}
9110
9111/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse. */
9112static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9113{
9114 int memvalue;
9115
9117 AST_APP_ARG(queuename);
9118 AST_APP_ARG(option);
9119 AST_APP_ARG(interface);
9120 );
9121
9122 if (ast_strlen_zero(data)) {
9124 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9125 cmd);
9126 return -1;
9127 }
9128
9130
9131 if (ast_strlen_zero(args.option)
9132 || ast_strlen_zero(args.interface)) {
9134 "Missing required argument. %s([<queuename>],<option>,<interface>)\n",
9135 cmd);
9136 return -1;
9137 }
9138
9139 /*
9140 * If queuename is empty then the option will be
9141 * set for the interface in all queues.
9142 */
9143
9144 memvalue = atoi(value);
9145 if (!strcasecmp(args.option, "penalty")) {
9146 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
9147 ast_log(LOG_ERROR, "Invalid interface, queue, or penalty\n");
9148 return -1;
9149 }
9150 } else if (!strcasecmp(args.option, "paused")) {
9151 memvalue = (memvalue <= 0) ? 0 : 1;
9152 if (set_member_paused(args.queuename, args.interface, NULL, memvalue)) {
9153 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9154 return -1;
9155 }
9156 } else if (!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9157 || !strcasecmp(args.option, "ringinuse")) {
9158 memvalue = (memvalue <= 0) ? 0 : 1;
9159 if (set_member_value(args.queuename, args.interface, MEMBER_RINGINUSE, memvalue)) {
9160 ast_log(LOG_ERROR, "Invalid interface or queue\n");
9161 return -1;
9162 }
9163 } else {
9164 ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9165 return -1;
9166 }
9167 return 0;
9168}
9169
9170/*!
9171 * \brief Get the total number of members in a specific queue (Deprecated)
9172 * \retval number of members
9173 * \retval -1 on error
9174*/
9175static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9176{
9177 int count = 0;
9178 struct member *m;
9179 struct call_queue *q;
9180 struct ao2_iterator mem_iter;
9181 static int depflag = 1;
9182
9183 if (depflag) {
9184 depflag = 0;
9185 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");
9186 }
9187
9188 if (ast_strlen_zero(data)) {
9189 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9190 return -1;
9191 }
9192
9193 if ((q = find_load_queue_rt_friendly(data))) {
9194 ao2_lock(q);
9195 mem_iter = ao2_iterator_init(q->members, 0);
9196 while ((m = ao2_iterator_next(&mem_iter))) {
9197 /* Count the agents who are logged in and presently answering calls */
9198 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9199 count++;
9200 }
9201 ao2_ref(m, -1);
9202 }
9203 ao2_iterator_destroy(&mem_iter);
9204 ao2_unlock(q);
9205 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
9206 } else {
9207 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9208 }
9209
9210 snprintf(buf, len, "%d", count);
9211
9212 return 0;
9213}
9214
9215/*! \brief Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue */
9216static int queue_function_queuegetchannel(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9217{
9218 int position;
9219 char *parse;
9220 struct call_queue *q;
9221 struct ast_variable *var;
9222
9224 AST_APP_ARG(queuename);
9225 AST_APP_ARG(position);
9226 );
9227
9228 buf[0] = '\0';
9229
9230 if (ast_strlen_zero(data)) {
9231 ast_log(LOG_ERROR, "Missing argument. QUEUE_GET_CHANNEL(<queuename>,<position>)\n");
9232 return -1;
9233 }
9234
9235 parse = ast_strdupa(data);
9237
9238 if (ast_strlen_zero(args.queuename)) {
9239 ast_log (LOG_ERROR, "The <queuename> parameter is required.\n");
9240 return -1;
9241 }
9242
9243 if (ast_strlen_zero(args.position)) {
9244 position = 1;
9245 } else {
9246 if (sscanf(args.position, "%30d", &position) != 1) {
9247 ast_log (LOG_ERROR, "<position> parameter must be an integer.\n");
9248 return -1;
9249 }
9250 if (position < 1) {
9251 ast_log (LOG_ERROR, "<position> parameter must be an integer greater than zero.\n");
9252 return -1;
9253 }
9254 }
9255
9256 {
9257 struct call_queue tmpq = {
9258 .name = args.queuename,
9259 };
9260
9261 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_GET_CHANNEL()");
9262 }
9263 if (q) {
9264 ao2_lock(q);
9265 if (q->count >= position) {
9266 struct queue_ent *qe;
9267
9268 for (qe = q->head; qe; qe = qe->next) {
9269 if (qe->pos == position) {
9271 break;
9272 }
9273 }
9274 }
9275 ao2_unlock(q);
9276 queue_t_unref(q, "Done with reference in QUEUE_GET_CHANNEL()");
9277 return 0;
9278 }
9279
9280 var = ast_load_realtime("queues", "name", args.queuename, SENTINEL);
9281 if (var) {
9282 /* if the queue is realtime but was not found in memory, this
9283 * means that the queue had been deleted from memory since it was
9284 * "dead."
9285 */
9287 return 0;
9288 }
9289
9290 ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9291 return 0;
9292}
9293
9294/*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
9295static int queue_function_queuewaitingcount(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9296{
9297 int count = 0;
9298 struct call_queue *q, tmpq = {
9299 .name = data,
9300 };
9301 struct ast_variable *var = NULL;
9302
9303 buf[0] = '\0';
9304
9305 if (ast_strlen_zero(data)) {
9306 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
9307 return -1;
9308 }
9309
9310 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
9311 ao2_lock(q);
9312 count = q->count;
9313 ao2_unlock(q);
9314 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
9315 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
9316 /* if the queue is realtime but was not found in memory, this
9317 * means that the queue had been deleted from memory since it was
9318 * "dead." This means it has a 0 waiting count
9319 */
9320 count = 0;
9322 } else {
9323 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9324 }
9325
9326 snprintf(buf, len, "%d", count);
9327
9328 return 0;
9329}
9330
9331/*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
9332static int queue_function_queuememberlist(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9333{
9334 struct call_queue *q;
9335 struct member *m;
9336
9337 /* Ensure an otherwise empty list doesn't return garbage */
9338 buf[0] = '\0';
9339
9340 if (ast_strlen_zero(data)) {
9341 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
9342 return -1;
9343 }
9344
9345 if ((q = find_load_queue_rt_friendly(data))) {
9346 int buflen = 0, count = 0;
9347 struct ao2_iterator mem_iter;
9348
9349 ao2_lock(q);
9350 mem_iter = ao2_iterator_init(q->members, 0);
9351 while ((m = ao2_iterator_next(&mem_iter))) {
9352 /* strcat() is always faster than printf() */
9353 if (count++) {
9354 strncat(buf + buflen, ",", len - buflen - 1);
9355 buflen++;
9356 }
9357 strncat(buf + buflen, m->interface, len - buflen - 1);
9358 buflen += strlen(m->interface);
9359 /* Safeguard against overflow (negative length) */
9360 if (buflen >= len - 2) {
9361 ao2_ref(m, -1);
9362 ast_log(LOG_WARNING, "Truncating list\n");
9363 break;
9364 }
9365 ao2_ref(m, -1);
9366 }
9367 ao2_iterator_destroy(&mem_iter);
9368 ao2_unlock(q);
9369 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
9370 } else
9371 ast_log(LOG_WARNING, "queue %s was not found\n", data);
9372
9373 /* We should already be terminated, but let's make sure. */
9374 buf[len - 1] = '\0';
9375
9376 return 0;
9377}
9378
9379/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
9380static int queue_function_memberpenalty_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
9381{
9382 int penalty;
9384 AST_APP_ARG(queuename);
9385 AST_APP_ARG(interface);
9386 );
9387 /* Make sure the returned value on error is NULL. */
9388 buf[0] = '\0';
9389
9390 if (ast_strlen_zero(data)) {
9391 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9392 return -1;
9393 }
9394
9396
9397 if (args.argc < 2) {
9398 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9399 return -1;
9400 }
9401
9402 penalty = get_member_penalty (args.queuename, args.interface);
9403
9404 if (penalty >= 0) { /* remember that buf is already '\0' */
9405 snprintf (buf, len, "%d", penalty);
9406 }
9407
9408 return 0;
9409}
9410
9411/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
9412static int queue_function_memberpenalty_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
9413{
9414 int penalty;
9416 AST_APP_ARG(queuename);
9417 AST_APP_ARG(interface);
9418 );
9419
9420 if (ast_strlen_zero(data)) {
9421 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9422 return -1;
9423 }
9424
9426
9427 if (args.argc < 2) {
9428 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
9429 return -1;
9430 }
9431
9432 penalty = atoi(value);
9433
9434 if (ast_strlen_zero(args.interface)) {
9435 ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
9436 return -1;
9437 }
9438
9439 /* if queuename = NULL then penalty will be set for interface in all the queues. */
9440 if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, penalty)) {
9441 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
9442 return -1;
9443 }
9444
9445 return 0;
9446}
9447
9449 .name = "QUEUE_EXISTS",
9450 .read = queue_function_exists,
9451};
9452
9454 .name = "QUEUE_VARIABLES",
9455 .read = queue_function_var,
9456};
9457
9459 .name = "QUEUE_MEMBER",
9461 .write = queue_function_mem_write,
9462};
9463
9465 .name = "QUEUE_MEMBER_COUNT",
9466 .read = queue_function_qac_dep,
9467};
9468
9470 .name = "QUEUE_GET_CHANNEL",
9472};
9473
9475 .name = "QUEUE_WAITING_COUNT",
9477};
9478
9480 .name = "QUEUE_MEMBER_LIST",
9482};
9483
9485 .name = "QUEUE_MEMBER_PENALTY",
9488};
9489
9490/*! Reset the global queue rules parameters even if there is no "general" section of queuerules.conf */
9492{
9493 realtime_rules = 0;
9494}
9495
9496/*! Set the global queue rules parameters as defined in the "general" section of queuerules.conf */
9498{
9499 const char *general_val = NULL;
9500 if ((general_val = ast_variable_retrieve(cfg, "general", "realtime_rules"))) {
9501 realtime_rules = ast_true(general_val);
9502 }
9503}
9504
9505/*! \brief Reload the rules defined in queuerules.conf
9506 *
9507 * \param reload If 1, then only process queuerules.conf if the file
9508 * has changed since the last time we inspected it.
9509 * \return Always returns AST_MODULE_LOAD_SUCCESS
9510 */
9512{
9513 struct ast_config *cfg;
9514 struct rule_list *rl_iter, *new_rl;
9515 struct penalty_rule *pr_iter;
9516 char *rulecat = NULL;
9517 struct ast_variable *rulevar = NULL;
9518 struct ast_flags config_flags = { (reload && !realtime_rules) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9519
9520 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
9521 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
9523 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9524 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
9526 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
9527 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n");
9529 }
9530
9532 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
9533 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
9534 ast_free(pr_iter);
9535 ast_free(rl_iter);
9536 }
9538 while ((rulecat = ast_category_browse(cfg, rulecat))) {
9539 if (!strcasecmp(rulecat, "general")) {
9541 continue;
9542 }
9543 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
9545 ast_config_destroy(cfg);
9547 } else {
9548 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
9549 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
9550 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
9551 if(!strcasecmp(rulevar->name, "penaltychange"))
9552 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
9553 else
9554 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
9555 }
9556 }
9557
9558 ast_config_destroy(cfg);
9559
9563 }
9564
9567}
9568
9569/*! Always set the global queue defaults, even if there is no "general" section in queues.conf */
9571{
9573 autofill_default = 0;
9574 montype_default = 0;
9575 shared_lastcall = 0;
9579}
9580
9581/*! Set the global queue parameters as defined in the "general" section of queues.conf */
9582static void queue_set_global_params(struct ast_config *cfg)
9583{
9584 const char *general_val = NULL;
9585 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
9586 queue_persistent_members = ast_true(general_val);
9587 }
9588 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) {
9589 autofill_default = ast_true(general_val);
9590 }
9591 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
9592 if (!strcasecmp(general_val, "mixmonitor"))
9593 montype_default = 1;
9594 }
9595 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) {
9596 shared_lastcall = ast_true(general_val);
9597 }
9598 if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid"))) {
9599 negative_penalty_invalid = ast_true(general_val);
9600 }
9601 if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
9602 log_membername_as_agent = ast_true(general_val);
9603 }
9604 if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) {
9606 }
9607}
9608
9609/*! \brief reload information pertaining to a single member
9610 *
9611 * This function is called when a member = line is encountered in
9612 * queues.conf.
9613 *
9614 * \param memberdata The part after member = in the config file
9615 * \param q The queue to which this member belongs
9616 */
9617static void reload_single_member(const char *memberdata, struct call_queue *q)
9618{
9619 char *membername, *interface, *state_interface, *tmp;
9620 char *parse;
9621 struct member *cur, *newm;
9622 struct member tmpmem;
9623 int penalty;
9624 int ringinuse;
9625 int wrapuptime;
9633 );
9634
9635 if (ast_strlen_zero(memberdata)) {
9636 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
9637 return;
9638 }
9639
9640 /* Add a new member */
9641 parse = ast_strdupa(memberdata);
9642
9644
9645 interface = args.interface;
9646 if (!ast_strlen_zero(args.penalty)) {
9647 tmp = args.penalty;
9648 ast_strip(tmp);
9649 penalty = atoi(tmp);
9650 if (penalty < 0) {
9651 penalty = 0;
9652 }
9653 } else {
9654 penalty = 0;
9655 }
9656
9657 if (!ast_strlen_zero(args.membername)) {
9658 membername = args.membername;
9659 ast_strip(membername);
9660 } else {
9661 membername = interface;
9662 }
9663
9664 if (!ast_strlen_zero(args.state_interface)) {
9665 state_interface = args.state_interface;
9666 ast_strip(state_interface);
9667 } else {
9668 state_interface = interface;
9669 }
9670
9671 if (!ast_strlen_zero(args.ringinuse)) {
9672 tmp = args.ringinuse;
9673 ast_strip(tmp);
9674 if (ast_true(tmp)) {
9675 ringinuse = 1;
9676 } else if (ast_false(tmp)) {
9677 ringinuse = 0;
9678 } else {
9679 ast_log(LOG_ERROR, "Member %s has an invalid ringinuse value. Using %s ringinuse value.\n",
9680 membername, q->name);
9681 ringinuse = q->ringinuse;
9682 }
9683 } else {
9684 ringinuse = q->ringinuse;
9685 }
9686
9687 if (!ast_strlen_zero(args.wrapuptime)) {
9688 tmp = args.wrapuptime;
9689 ast_strip(tmp);
9690 wrapuptime = atoi(tmp);
9691 if (wrapuptime < 0) {
9692 wrapuptime = 0;
9693 }
9694 } else {
9695 wrapuptime = 0;
9696 }
9697
9698 /* Find the old position in the list */
9699 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
9700 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
9701
9702 if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface, ringinuse, wrapuptime))) {
9703 newm->wrapuptime = wrapuptime;
9704 if (cur) {
9705 ao2_lock(q->members);
9706 /* Round Robin Queue Position must be copied if this is replacing an existing member */
9707 newm->queuepos = cur->queuepos;
9708 /* Don't reset agent stats either */
9709 newm->calls = cur->calls;
9710 newm->lastcall = cur->lastcall;
9711
9712 ao2_link(q->members, newm);
9713 ao2_unlink(q->members, cur);
9714 ao2_unlock(q->members);
9715 } else {
9716 /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
9717 member_add_to_queue(q, newm);
9718 }
9719 ao2_ref(newm, -1);
9720 }
9721 newm = NULL;
9722
9723 if (cur) {
9724 ao2_ref(cur, -1);
9725 }
9726}
9727
9728static int mark_member_dead(void *obj, void *arg, int flags)
9729{
9730 struct member *member = obj;
9731 if (!member->dynamic && !member->realtime) {
9732 member->delme = 1;
9733 }
9734 return 0;
9735}
9736
9737static int kill_dead_members(void *obj, void *arg, int flags)
9738{
9739 struct member *member = obj;
9740
9741 if (!member->delme) {
9743 return 0;
9744 } else {
9745 return CMP_MATCH;
9746 }
9747}
9748
9749/*! \brief Reload information pertaining to a particular queue
9750 *
9751 * Once we have isolated a queue within reload_queues, we call this. This will either
9752 * reload information for the queue or if we're just reloading member information, we'll just
9753 * reload that without touching other settings within the queue
9754 *
9755 * \param cfg The configuration which we are reading
9756 * \param mask Tells us what information we need to reload
9757 * \param queuename The name of the queue we are reloading information from
9758 */
9759static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
9760{
9761 int new;
9762 struct call_queue *q = NULL;
9763 struct member *member;
9764 /*We're defining a queue*/
9765 struct call_queue tmpq = {
9766 .name = queuename,
9767 };
9768 const char *tmpvar;
9769 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
9770 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
9771 int prev_weight = 0;
9772 struct ast_variable *var;
9773 struct ao2_iterator mem_iter;
9774
9775 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
9776 if (queue_reload) {
9777 /* Make one then */
9778 if (!(q = alloc_queue(queuename))) {
9779 return;
9780 }
9781 } else {
9782 /* Since we're not reloading queues, this means that we found a queue
9783 * in the configuration file which we don't know about yet. Just return.
9784 */
9785 return;
9786 }
9787 new = 1;
9788 } else {
9789 new = 0;
9790 }
9791
9792 if (!new) {
9793 ao2_lock(q);
9794 prev_weight = q->weight ? 1 : 0;
9795 }
9796 /* Check if we already found a queue with this name in the config file */
9797 if (q->found) {
9798 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
9799 if (!new) {
9800 /* It should be impossible to *not* hit this case*/
9801 ao2_unlock(q);
9802 }
9803 queue_t_unref(q, "We exist! Expiring temporary pointer");
9804 return;
9805 }
9806 /* Due to the fact that the "linear" strategy will have a different allocation
9807 * scheme for queue members, we must devise the queue's strategy before other initializations.
9808 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
9809 * container used will have only a single bucket instead of the typical number.
9810 */
9811 if (queue_reload) {
9812 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
9813 q->strategy = strat2int(tmpvar);
9814 if (q->strategy < 0) {
9815 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
9816 tmpvar, q->name);
9818 }
9819 } else {
9821 }
9822 init_queue(q);
9823 }
9824 if (member_reload) {
9826 q->found = 1;
9827 }
9828
9829 /* On the first pass we just read the parameters of the queue */
9830 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
9831 if (queue_reload && strcasecmp(var->name, "member")) {
9832 queue_set_param(q, var->name, var->value, var->lineno, 1);
9833 }
9834 }
9835
9836 /* On the second pass, we read members */
9837 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
9838 if (member_reload && !strcasecmp(var->name, "member")) {
9839 reload_single_member(var->value, q);
9840 }
9841 }
9842
9843 /* Update ringinuse for dynamic members */
9844 if (member_reload) {
9845 ao2_lock(q->members);
9847 while ((member = ao2_iterator_next(&mem_iter))) {
9848 if (member->dynamic) {
9850 }
9851 ao2_ref(member, -1);
9852 }
9853 ao2_iterator_destroy(&mem_iter);
9854 ao2_unlock(q->members);
9855 }
9856
9857 /* At this point, we've determined if the queue has a weight, so update use_weight
9858 * as appropriate
9859 */
9860 if (!q->weight && prev_weight) {
9862 } else if (q->weight && !prev_weight) {
9864 }
9865
9866 /* Free remaining members marked as delme */
9867 if (member_reload) {
9868 ao2_lock(q->members);
9871 ao2_unlock(q->members);
9872 }
9873
9874 if (new) {
9875 queues_t_link(queues, q, "Add queue to container");
9876 } else {
9877 ao2_unlock(q);
9878 }
9879 queue_t_unref(q, "Expiring creation reference");
9880}
9881
9882static int mark_unfound(void *obj, void *arg, int flags)
9883{
9884 struct call_queue *q = obj;
9885 char *queuename = arg;
9886 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
9887 q->found = 0;
9888 }
9889 return 0;
9890}
9891
9892static int kill_if_unfound(void *obj, void *arg, int flags)
9893{
9894 struct call_queue *q = obj;
9895 char *queuename = arg;
9896 if (!q->realtime && !q->found && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
9897 q->dead = 1;
9898 return CMP_MATCH;
9899 } else {
9900 return 0;
9901 }
9902}
9903
9904/*! \brief reload the queues.conf file
9905 *
9906 * This function reloads the information in the general section of the queues.conf
9907 * file and potentially more, depending on the value of mask.
9908 *
9909 * \param reload 0 if we are calling this the first time, 1 every other time
9910 * \param mask Gives flags telling us what information to actually reload
9911 * \param queuename If set to a non-zero string, then only reload information from
9912 * that particular queue. Otherwise inspect all queues
9913 * \retval -1 Failure occurred
9914 * \retval 0 All clear!
9915 */
9916static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
9917{
9918 struct ast_config *cfg;
9919 char *cat;
9920 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9921 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
9922
9923 if (!(cfg = ast_config_load("queues.conf", config_flags))) {
9924 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
9925 return -1;
9926 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9927 return 0;
9928 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
9929 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n");
9930 return -1;
9931 }
9932
9933 /* We've made it here, so it looks like we're doing operations on all queues. */
9935
9936 /* Mark non-realtime queues not found at the beginning. */
9937 ao2_callback(queues, OBJ_NODATA, mark_unfound, (char *) queuename);
9938
9939 /* Chug through config file. */
9940 cat = NULL;
9942 while ((cat = ast_category_browse(cfg, cat)) ) {
9943 if (!strcasecmp(cat, "general") && queue_reload) {
9945 continue;
9946 }
9947 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
9948 reload_single_queue(cfg, mask, cat);
9949 }
9950
9951 ast_config_destroy(cfg);
9952 if (queue_reload) {
9953 /* Unlink and mark dead all non-realtime queues that were not found in the configuration file. */
9955 }
9957 return 0;
9958}
9959
9960/*! \brief Facilitates resetting statistics for a queue
9961 *
9962 * This function actually does not reset any statistics, but
9963 * rather finds a call_queue struct which corresponds to the
9964 * passed-in queue name and passes that structure to the
9965 * clear_queue function. If no queuename is passed in, then
9966 * all queues will have their statistics reset.
9967 *
9968 * \param queuename The name of the queue to reset the statistics
9969 * for. If this is NULL or zero-length, then this means to reset
9970 * the statistics for all queues
9971 * \retval 0 always
9972 */
9973static int clear_stats(const char *queuename)
9974{
9975 struct call_queue *q;
9976 struct ao2_iterator queue_iter;
9977
9978 queue_iter = ao2_iterator_init(queues, 0);
9979 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
9980 ao2_lock(q);
9981 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
9982 clear_queue(q);
9983 ao2_unlock(q);
9984 queue_t_unref(q, "Done with iterator");
9985 }
9986 ao2_iterator_destroy(&queue_iter);
9987 return 0;
9988}
9989
9990/*! \brief The command center for all reload operations
9991 *
9992 * Whenever any piece of queue information is to be reloaded, this function
9993 * is called. It interprets the flags set in the mask parameter and acts
9994 * based on how they are set.
9995 *
9996 * \param reload True if we are reloading information, false if we are loading
9997 * information for the first time.
9998 * \param mask A bitmask which tells the handler what actions to take
9999 * \param queuename The name of the queue on which we wish to take action
10000 * \retval 0 All reloads were successful
10001 * \retval non-zero There was a failure
10002 */
10003static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
10004{
10005 int res = 0;
10006
10007 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
10008 res |= reload_queue_rules(reload);
10009 }
10010 if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
10011 res |= clear_stats(queuename);
10012 }
10014 res |= reload_queues(reload, mask, queuename);
10015 }
10016 return res;
10017}
10018
10019/*! \brief direct output to manager or cli with proper terminator */
10020static void do_print(struct mansession *s, int fd, const char *str)
10021{
10022 if (s) {
10023 astman_append(s, "%s\r\n", str);
10024 } else {
10025 ast_cli(fd, "%s\n", str);
10026 }
10027}
10028
10029/*! \brief Print a single queue to AMI or the CLI */
10030static void print_queue(struct mansession *s, int fd, struct call_queue *q)
10031{
10032 float sl;
10033 float sl2;
10034 struct ao2_iterator mem_iter;
10035 struct ast_str *out = ast_str_alloca(512);
10036 time_t now = time(NULL);
10037
10038 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
10039 if (q->maxlen) {
10040 ast_str_append(&out, 0, "%d", q->maxlen);
10041 } else {
10042 ast_str_append(&out, 0, "unlimited");
10043 }
10044 sl = 0;
10045 sl2 = 0;
10046 if (q->callscompleted > 0) {
10047 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
10048 }
10049 if (q->callscompleted + q->callsabandoned > 0) {
10050 sl2 =100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted));
10051 }
10052
10053 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",
10055 do_print(s, fd, ast_str_buffer(out));
10056 if (!ao2_container_count(q->members)) {
10057 do_print(s, fd, " No Members");
10058 } else {
10059 struct member *mem;
10060
10061 do_print(s, fd, " Members: ");
10062 mem_iter = ao2_iterator_init(q->members, 0);
10063 while ((mem = ao2_iterator_next(&mem_iter))) {
10064 ast_str_set(&out, 0, " %s", mem->membername);
10065 if (strcasecmp(mem->membername, mem->interface)) {
10066 ast_str_append(&out, 0, " (%s", mem->interface);
10068 && strcmp(mem->state_interface, mem->interface)) {
10069 ast_str_append(&out, 0, " from %s", mem->state_interface);
10070 }
10071 ast_str_append(&out, 0, ")");
10072 }
10073 if (mem->penalty) {
10074 ast_str_append(&out, 0, " with penalty %d", mem->penalty);
10075 }
10076
10077 ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");
10078
10079 ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
10080 mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
10081 mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
10082 mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset());
10083
10084 if (mem->paused) {
10085 ast_str_append(&out, 0, " %s(paused%s%s was %ld secs ago)%s",
10087 ast_strlen_zero(mem->reason_paused) ? "" : ":",
10089 (long) (now - mem->lastpause),
10090 ast_term_reset());
10091 }
10092
10093 ast_str_append(&out, 0, " (%s%s%s)",
10098 if (mem->calls) {
10099 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
10100 mem->calls, (long) (now - mem->lastcall));
10101 } else {
10102 ast_str_append(&out, 0, " has taken no calls yet");
10103 }
10104 ast_str_append(&out, 0, " %s(login was %ld secs ago)%s",
10106 (long) (now - mem->logintime),
10107 ast_term_reset());
10108 do_print(s, fd, ast_str_buffer(out));
10109 ao2_ref(mem, -1);
10110 }
10111 ao2_iterator_destroy(&mem_iter);
10112 }
10113 if (!q->head) {
10114 do_print(s, fd, " No Callers");
10115 } else {
10116 struct queue_ent *qe;
10117 int pos = 1;
10118
10119 do_print(s, fd, " Callers: ");
10120 for (qe = q->head; qe; qe = qe->next) {
10121 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)",
10122 pos++, ast_channel_name(qe->chan), (long) (now - qe->start) / 60,
10123 (long) (now - qe->start) % 60, qe->prio);
10124 do_print(s, fd, ast_str_buffer(out));
10125 }
10126 }
10127 do_print(s, fd, ""); /* blank line between entries */
10128}
10129
10131
10132/*!
10133 * \brief Show queue(s) status and statistics
10134 *
10135 * List the queues strategy, calls processed, members logged in,
10136 * other queue statistics such as avg hold time.
10137*/
10138static char *__queues_show(struct mansession *s, int fd, int argc, const char * const *argv)
10139{
10140 struct call_queue *q;
10141 struct ast_str *out = ast_str_alloca(512);
10142 struct ao2_container *sorted_queues;
10143
10144 struct ao2_iterator queue_iter;
10145 int found = 0;
10146
10147 if (argc != 2 && argc != 3) {
10148 return CLI_SHOWUSAGE;
10149 }
10150
10151 if (argc == 3) { /* specific queue */
10152 if ((q = find_load_queue_rt_friendly(argv[2]))) {
10153 ao2_lock(q);
10154 print_queue(s, fd, q);
10155 ao2_unlock(q);
10156 queue_unref(q);
10157 } else {
10158 ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
10159 do_print(s, fd, ast_str_buffer(out));
10160 }
10161 return CLI_SUCCESS;
10162 }
10163
10164 if (ast_check_realtime("queues")) {
10165 /* This block is to find any queues which are defined in realtime but
10166 * which have not yet been added to the in-core container
10167 */
10168 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
10169 if (cfg) {
10170 char *category = NULL;
10171 while ((category = ast_category_browse(cfg, category))) {
10172 const char *queuename = ast_variable_retrieve(cfg, category, "name");
10173 if (ast_strlen_zero(queuename)) {
10174 ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
10175 continue;
10176 }
10177 if ((q = find_load_queue_rt_friendly(queuename))) {
10178 queue_t_unref(q, "Done with temporary pointer");
10179 }
10180 }
10181 ast_config_destroy(cfg);
10182 }
10183 }
10184
10185 /*
10186 * Snapping a copy of the container prevents having to lock both the queues container
10187 * and the queue itself at the same time. It also allows us to sort the entries.
10188 */
10189 sorted_queues = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, call_queue_sort_fn, NULL);
10190 if (!sorted_queues) {
10191 return CLI_SUCCESS;
10192 }
10193 if (ao2_container_dup(sorted_queues, queues, 0)) {
10194 ao2_ref(sorted_queues, -1);
10195 return CLI_SUCCESS;
10196 }
10197
10198 /*
10199 * No need to lock the container since it's temporary and static.
10200 * We also unlink the entries as we use them so the container is
10201 * empty when the iterator finishes. We can then just unref the container.
10202 */
10203 queue_iter = ao2_iterator_init(sorted_queues, AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK);
10204 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10205 struct call_queue *realtime_queue = NULL;
10206 ao2_lock(q);
10207 /* This check is to make sure we don't print information for realtime
10208 * queues which have been deleted from realtime but which have not yet
10209 * been deleted from the in-core container. Only do this if we're not
10210 * looking for a specific queue.
10211 */
10212 if (q->realtime) {
10213 realtime_queue = find_load_queue_rt_friendly(q->name);
10214 if (!realtime_queue) {
10215 ao2_unlock(q);
10216 queue_t_unref(q, "Done with iterator");
10217 continue;
10218 }
10219 queue_t_unref(realtime_queue, "Queue is already in memory");
10220 }
10221
10222 found = 1;
10223 print_queue(s, fd, q);
10224
10225 ao2_unlock(q);
10226 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
10227 }
10228 ao2_iterator_destroy(&queue_iter);
10229 ao2_ref(sorted_queues, -1);
10230 if (!found) {
10231 ast_str_set(&out, 0, "No queues.");
10232 do_print(s, fd, ast_str_buffer(out));
10233 }
10234 return CLI_SUCCESS;
10235}
10236
10237/*!
10238 * \brief Check if a given word is in a space-delimited list
10239 *
10240 * \param list Space delimited list of words
10241 * \param word The word used to search the list
10242 *
10243 * \note This function will not return 1 if the word is at the very end of the
10244 * list (followed immediately by a \0, not a space) since it is used for
10245 * checking tab-completion and a word at the end is still being tab-completed.
10246 *
10247 * \retval 1 if the word is found
10248 * \retval 0 if the word is not found
10249*/
10250static int word_in_list(const char *list, const char *word) {
10251 int list_len, word_len = strlen(word);
10252 const char *find, *end_find, *end_list;
10253
10254 /* strip whitespace from front */
10255 while(isspace(*list)) {
10256 list++;
10257 }
10258
10259 while((find = strstr(list, word))) {
10260 /* beginning of find starts inside another word? */
10261 if (find != list && *(find - 1) != ' ') {
10262 list = find;
10263 /* strip word from front */
10264 while(!isspace(*list) && *list != '\0') {
10265 list++;
10266 }
10267 /* strip whitespace from front */
10268 while(isspace(*list)) {
10269 list++;
10270 }
10271 continue;
10272 }
10273
10274 /* end of find ends inside another word or at very end of list? */
10275 list_len = strlen(list);
10276 end_find = find + word_len;
10277 end_list = list + list_len;
10278 if (end_find == end_list || *end_find != ' ') {
10279 list = find;
10280 /* strip word from front */
10281 while(!isspace(*list) && *list != '\0') {
10282 list++;
10283 }
10284 /* strip whitespace from front */
10285 while(isspace(*list)) {
10286 list++;
10287 }
10288 continue;
10289 }
10290
10291 /* terminating conditions satisfied, word at beginning or separated by ' ' */
10292 return 1;
10293 }
10294
10295 return 0;
10296}
10297
10298/*!
10299 * \brief Check if a given word is in a space-delimited list
10300 *
10301 * \param line The line as typed not including the current word being completed
10302 * \param word The word currently being completed
10303 * \param pos The number of completed words in line
10304 * \param state The nth desired completion option
10305 * \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.
10306 *
10307 * \return Returns the queue tab-completion for the given word and state
10308*/
10309static char *complete_queue(const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset)
10310{
10311 struct call_queue *q;
10312 char *ret = NULL;
10313 int which = 0;
10314 int wordlen = strlen(word);
10315 struct ao2_iterator queue_iter;
10316 const char *word_list = NULL;
10317
10318 /* for certain commands, already completed items should be left out of
10319 * the list */
10320 if (word_list_offset && strlen(line) >= word_list_offset) {
10321 word_list = line + word_list_offset;
10322 }
10323
10324 queue_iter = ao2_iterator_init(queues, 0);
10325 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10326 if (!strncasecmp(word, q->name, wordlen) && ++which > state
10327 && (!word_list_offset || !word_in_list(word_list, q->name))) {
10328 ret = ast_strdup(q->name);
10329 queue_t_unref(q, "Done with iterator");
10330 break;
10331 }
10332 queue_t_unref(q, "Done with iterator");
10333 }
10334 ao2_iterator_destroy(&queue_iter);
10335
10336 /* Pretend "rules" is at the end of the queues list in certain
10337 * circumstances since it is an alternate command that should be
10338 * tab-completable for "queue show" */
10339 if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
10340 ret = ast_strdup("rules");
10341 }
10342
10343 return ret;
10344}
10345
10346static char *complete_queue_show(const char *line, const char *word, int pos, int state)
10347{
10348 if (pos == 2) {
10349 return complete_queue(line, word, pos, state, 0);
10350 }
10351 return NULL;
10352}
10353
10354static char *queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10355{
10356 switch ( cmd ) {
10357 case CLI_INIT:
10358 e->command = "queue show";
10359 e->usage =
10360 "Usage: queue show\n"
10361 " Provides summary information on a specified queue.\n";
10362 return NULL;
10363 case CLI_GENERATE:
10364 return complete_queue_show(a->line, a->word, a->pos, a->n);
10365 }
10366
10367 return __queues_show(NULL, a->fd, a->argc, a->argv);
10368}
10369
10370static int manager_queue_rule_show(struct mansession *s, const struct message *m)
10371{
10372 const char *rule = astman_get_header(m, "Rule");
10373 const char *id = astman_get_header(m, "ActionID");
10374 struct rule_list *rl_iter;
10375 struct penalty_rule *pr_iter;
10376
10377 astman_append(s, "Response: Success\r\n");
10378 if (!ast_strlen_zero(id)) {
10379 astman_append(s, "ActionID: %s\r\n", id);
10380 }
10381
10383 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
10384 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
10385 astman_append(s, "RuleList: %s\r\n", rl_iter->name);
10386 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
10387 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 );
10388 }
10389 if (!ast_strlen_zero(rule)) {
10390 break;
10391 }
10392 }
10393 }
10395
10396 /*
10397 * Two blank lines instead of one because the Response and
10398 * ActionID headers used to not be present.
10399 */
10400 astman_append(s, "\r\n\r\n");
10401
10402 return RESULT_SUCCESS;
10403}
10404
10405/*! \brief Summary of queue info via the AMI */
10406static int manager_queues_summary(struct mansession *s, const struct message *m)
10407{
10408 time_t now;
10409 int qmemcount = 0;
10410 int qmemavail = 0;
10411 int qchancount = 0;
10412 int qlongestholdtime = 0;
10413 int qsummaries = 0;
10414 const char *id = astman_get_header(m, "ActionID");
10415 const char *queuefilter = astman_get_header(m, "Queue");
10416 char idText[256];
10417 struct call_queue *q;
10418 struct queue_ent *qe;
10419 struct member *mem;
10420 struct ao2_iterator queue_iter;
10421 struct ao2_iterator mem_iter;
10422
10423 if (ast_check_realtime("queues")) {
10424 load_realtime_queues(queuefilter);
10425 }
10426
10427 astman_send_listack(s, m, "Queue summary will follow", "start");
10428 time(&now);
10429 idText[0] = '\0';
10430 if (!ast_strlen_zero(id)) {
10431 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10432 }
10433 queue_iter = ao2_iterator_init(queues, 0);
10434 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10435 ao2_lock(q);
10436
10437 /* List queue properties */
10438 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10439 /* Reset the necessary local variables if no queuefilter is set*/
10440 qmemcount = 0;
10441 qmemavail = 0;
10442 qchancount = 0;
10443 qlongestholdtime = 0;
10444
10445 /* List Queue Members */
10446 mem_iter = ao2_iterator_init(q->members, 0);
10447 while ((mem = ao2_iterator_next(&mem_iter))) {
10448 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
10449 ++qmemcount;
10450 if (member_status_available(mem->status) && !mem->paused) {
10451 ++qmemavail;
10452 }
10453 }
10454 ao2_ref(mem, -1);
10455 }
10456 ao2_iterator_destroy(&mem_iter);
10457 for (qe = q->head; qe; qe = qe->next) {
10458 if ((now - qe->start) > qlongestholdtime) {
10459 qlongestholdtime = now - qe->start;
10460 }
10461 ++qchancount;
10462 }
10463 astman_append(s, "Event: QueueSummary\r\n"
10464 "Queue: %s\r\n"
10465 "LoggedIn: %d\r\n"
10466 "Available: %d\r\n"
10467 "Callers: %d\r\n"
10468 "HoldTime: %d\r\n"
10469 "TalkTime: %d\r\n"
10470 "LongestHoldTime: %d\r\n"
10471 "%s"
10472 "\r\n",
10473 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
10474 ++qsummaries;
10475 }
10476 ao2_unlock(q);
10477 queue_t_unref(q, "Done with iterator");
10478 }
10479 ao2_iterator_destroy(&queue_iter);
10480
10481 astman_send_list_complete_start(s, m, "QueueSummaryComplete", qsummaries);
10483
10484 return RESULT_SUCCESS;
10485}
10486
10487/*! \brief Queue status info via AMI */
10488static int manager_queues_status(struct mansession *s, const struct message *m)
10489{
10490 time_t now;
10491 int pos;
10492 int q_items = 0;
10493 const char *id = astman_get_header(m,"ActionID");
10494 const char *queuefilter = astman_get_header(m,"Queue");
10495 const char *memberfilter = astman_get_header(m,"Member");
10496 char idText[256];
10497 struct call_queue *q;
10498 struct queue_ent *qe;
10499 float sl = 0;
10500 float sl2 = 0;
10501 struct member *mem;
10502 struct ao2_iterator queue_iter;
10503 struct ao2_iterator mem_iter;
10504
10505 if (ast_check_realtime("queues")) {
10506 load_realtime_queues(queuefilter);
10507 }
10508
10509 astman_send_listack(s, m, "Queue status will follow", "start");
10510 time(&now);
10511 idText[0] = '\0';
10512 if (!ast_strlen_zero(id)) {
10513 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
10514 }
10515
10516 queue_iter = ao2_iterator_init(queues, 0);
10517 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10518 ao2_lock(q);
10519
10520 /* List queue properties */
10521 if (ast_strlen_zero(queuefilter) || !strcasecmp(q->name, queuefilter)) {
10522 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
10523 sl2 = (((q->callscompleted + q->callsabandoned) > 0) ? 100 * (((float)q->callsabandonedinsl + (float)q->callscompletedinsl) / ((float)q->callsabandoned + (float)q->callscompleted)) : 0);
10524
10525 astman_append(s, "Event: QueueParams\r\n"
10526 "Queue: %s\r\n"
10527 "Max: %d\r\n"
10528 "Strategy: %s\r\n"
10529 "Calls: %d\r\n"
10530 "Holdtime: %d\r\n"
10531 "TalkTime: %d\r\n"
10532 "Completed: %d\r\n"
10533 "Abandoned: %d\r\n"
10534 "ServiceLevel: %d\r\n"
10535 "ServicelevelPerf: %2.1f\r\n"
10536 "ServicelevelPerf2: %2.1f\r\n"
10537 "Weight: %d\r\n"
10538 "%s"
10539 "\r\n",
10540 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
10541 q->callsabandoned, q->servicelevel, sl, sl2, q->weight, idText);
10542 ++q_items;
10543
10544 /* List Queue Members */
10545 mem_iter = ao2_iterator_init(q->members, 0);
10546 while ((mem = ao2_iterator_next(&mem_iter))) {
10547 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
10548 astman_append(s, "Event: QueueMember\r\n"
10549 "Queue: %s\r\n"
10550 "Name: %s\r\n"
10551 "Location: %s\r\n"
10552 "StateInterface: %s\r\n"
10553 "Membership: %s\r\n"
10554 "Penalty: %d\r\n"
10555 "CallsTaken: %d\r\n"
10556 "LastCall: %d\r\n"
10557 "LastPause: %d\r\n"
10558 "LoginTime: %d\r\n"
10559 "InCall: %d\r\n"
10560 "Status: %d\r\n"
10561 "Paused: %d\r\n"
10562 "PausedReason: %s\r\n"
10563 "Wrapuptime: %d\r\n"
10564 "%s"
10565 "\r\n",
10566 q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
10567 mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, (int)mem->logintime, mem->starttime ? 1 : 0, mem->status,
10568 mem->paused, mem->reason_paused, mem->wrapuptime, idText);
10569 ++q_items;
10570 }
10571 ao2_ref(mem, -1);
10572 }
10573 ao2_iterator_destroy(&mem_iter);
10574
10575 /* List Queue Entries */
10576 pos = 1;
10577 for (qe = q->head; qe; qe = qe->next) {
10578 astman_append(s, "Event: QueueEntry\r\n"
10579 "Queue: %s\r\n"
10580 "Position: %d\r\n"
10581 "Channel: %s\r\n"
10582 "Uniqueid: %s\r\n"
10583 "CallerIDNum: %s\r\n"
10584 "CallerIDName: %s\r\n"
10585 "ConnectedLineNum: %s\r\n"
10586 "ConnectedLineName: %s\r\n"
10587 "Wait: %ld\r\n"
10588 "Priority: %d\r\n"
10589 "%s"
10590 "\r\n",
10591 q->name, pos++, ast_channel_name(qe->chan), ast_channel_uniqueid(qe->chan),
10596 (long) (now - qe->start), qe->prio, idText);
10597 ++q_items;
10598 }
10599 }
10600 ao2_unlock(q);
10601 queue_t_unref(q, "Done with iterator");
10602 }
10603 ao2_iterator_destroy(&queue_iter);
10604
10605 astman_send_list_complete_start(s, m, "QueueStatusComplete", q_items);
10607
10608 return RESULT_SUCCESS;
10609}
10610
10611static int manager_add_queue_member(struct mansession *s, const struct message *m)
10612{
10613 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface, *wrapuptime_s;
10614 int paused, penalty, wrapuptime = 0;
10615
10616 queuename = astman_get_header(m, "Queue");
10617 interface = astman_get_header(m, "Interface");
10618 penalty_s = astman_get_header(m, "Penalty");
10619 paused_s = astman_get_header(m, "Paused");
10620 membername = astman_get_header(m, "MemberName");
10621 state_interface = astman_get_header(m, "StateInterface");
10622 wrapuptime_s = astman_get_header(m, "Wrapuptime");
10623
10624 if (ast_strlen_zero(queuename)) {
10625 astman_send_error(s, m, "'Queue' not specified.");
10626 return 0;
10627 }
10628
10629 if (ast_strlen_zero(interface)) {
10630 astman_send_error(s, m, "'Interface' not specified.");
10631 return 0;
10632 }
10633
10634 if (ast_strlen_zero(penalty_s)) {
10635 penalty = 0;
10636 } else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) {
10637 penalty = 0;
10638 }
10639
10640 if (ast_strlen_zero(wrapuptime_s)) {
10641 wrapuptime = 0;
10642 } else if (sscanf(wrapuptime_s, "%30d", &wrapuptime) != 1 || wrapuptime < 0) {
10643 wrapuptime = 0;
10644 }
10645
10646 if (ast_strlen_zero(paused_s)) {
10647 paused = 0;
10648 } else {
10649 paused = abs(ast_true(paused_s));
10650 }
10651
10652 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface, NULL, wrapuptime)) {
10653 case RES_OKAY:
10654 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
10655 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10656 } else {
10657 ast_queue_log(queuename, "MANAGER", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
10658 }
10659 astman_send_ack(s, m, "Added interface to queue");
10660 break;
10661 case RES_EXISTS:
10662 astman_send_error(s, m, "Unable to add interface: Already there");
10663 break;
10664 case RES_NOSUCHQUEUE:
10665 astman_send_error(s, m, "Unable to add interface to queue: No such queue");
10666 break;
10667 case RES_OUTOFMEMORY:
10668 astman_send_error(s, m, "Out of memory");
10669 break;
10670 }
10671
10672 return 0;
10673}
10674
10675static int manager_remove_queue_member(struct mansession *s, const struct message *m)
10676{
10677 const char *queuename, *interface;
10678 struct member *mem = NULL;
10679
10680 queuename = astman_get_header(m, "Queue");
10681 interface = astman_get_header(m, "Interface");
10682
10683 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
10684 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
10685 return 0;
10686 }
10687
10689 mem = find_member_by_queuename_and_interface(queuename, interface);
10690 }
10691
10692 switch (remove_from_queue(queuename, interface)) {
10693 case RES_OKAY:
10694 if (!mem || ast_strlen_zero(mem->membername)) {
10695 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
10696 } else {
10697 ast_queue_log(queuename, "MANAGER", mem->membername, "REMOVEMEMBER", "%s", "");
10698 }
10699 astman_send_ack(s, m, "Removed interface from queue");
10700 break;
10701 case RES_EXISTS:
10702 astman_send_error(s, m, "Unable to remove interface: Not there");
10703 break;
10704 case RES_NOSUCHQUEUE:
10705 astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
10706 break;
10707 case RES_OUTOFMEMORY:
10708 astman_send_error(s, m, "Out of memory");
10709 break;
10710 case RES_NOT_DYNAMIC:
10711 astman_send_error(s, m, "Member not dynamic");
10712 break;
10713 }
10714
10715 if (mem) {
10716 ao2_ref(mem, -1);
10717 }
10718
10719 return 0;
10720}
10721
10722static int manager_pause_queue_member(struct mansession *s, const struct message *m)
10723{
10724 const char *queuename, *interface, *paused_s, *reason;
10725 int paused;
10726
10727 interface = astman_get_header(m, "Interface");
10728 paused_s = astman_get_header(m, "Paused");
10729 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */
10730 reason = astman_get_header(m, "Reason"); /* Optional */
10731
10732 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
10733 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
10734 return 0;
10735 }
10736
10737 paused = abs(ast_true(paused_s));
10738
10739 if (set_member_paused(queuename, interface, reason, paused)) {
10740 astman_send_error(s, m, "Interface not found");
10741 } else {
10742 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
10743 }
10744 return 0;
10745}
10746
10747static int manager_queue_log_custom(struct mansession *s, const struct message *m)
10748{
10749 const char *queuename, *event, *message, *interface, *uniqueid;
10750
10751 queuename = astman_get_header(m, "Queue");
10752 uniqueid = astman_get_header(m, "UniqueId");
10753 interface = astman_get_header(m, "Interface");
10754 event = astman_get_header(m, "Event");
10755 message = astman_get_header(m, "Message");
10756
10757 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
10758 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
10759 return 0;
10760 }
10761
10762 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
10763 astman_send_ack(s, m, "Event added successfully");
10764
10765 return 0;
10766}
10767
10768static int manager_queue_reload(struct mansession *s, const struct message *m)
10769{
10770 struct ast_flags mask = {0,};
10771 const char *queuename = NULL;
10772 int header_found = 0;
10773
10774 queuename = astman_get_header(m, "Queue");
10775 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
10777 header_found = 1;
10778 }
10779 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
10781 header_found = 1;
10782 }
10783 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
10785 header_found = 1;
10786 }
10787
10788 if (!header_found) {
10790 }
10791
10792 if (!reload_handler(1, &mask, queuename)) {
10793 astman_send_ack(s, m, "Queue reloaded successfully");
10794 } else {
10795 astman_send_error(s, m, "Error encountered while reloading queue");
10796 }
10797 return 0;
10798}
10799
10800static int manager_queue_reset(struct mansession *s, const struct message *m)
10801{
10802 const char *queuename = NULL;
10803 struct ast_flags mask = {QUEUE_RESET_STATS,};
10804
10805 queuename = astman_get_header(m, "Queue");
10806
10807 if (!reload_handler(1, &mask, queuename)) {
10808 astman_send_ack(s, m, "Queue stats reset successfully");
10809 } else {
10810 astman_send_error(s, m, "Error encountered while resetting queue stats");
10811 }
10812 return 0;
10813}
10814
10815static char *complete_queue_add_member(const char *line, const char *word, int pos, int state)
10816{
10817 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
10818 switch (pos) {
10819 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
10820 return NULL;
10821 case 4: /* only one possible match, "to" */
10822 return state == 0 ? ast_strdup("to") : NULL;
10823 case 5: /* <queue> */
10824 return complete_queue(line, word, pos, state, 0);
10825 case 6: /* only one possible match, "penalty" */
10826 return state == 0 ? ast_strdup("penalty") : NULL;
10827 case 7:
10828 if (0 <= state && state < 100) { /* 0-99 */
10829 char *num;
10830 if ((num = ast_malloc(3))) {
10831 sprintf(num, "%d", state);
10832 }
10833 return num;
10834 } else {
10835 return NULL;
10836 }
10837 case 8: /* only one possible match, "as" */
10838 return state == 0 ? ast_strdup("as") : NULL;
10839 case 9: /* Don't attempt to complete name of member (infinite possibilities) */
10840 return NULL;
10841 default:
10842 return NULL;
10843 }
10844}
10845
10846static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
10847{
10848 const char *queuename, *interface, *ringinuse_s;
10849 int ringinuse;
10850
10851 interface = astman_get_header(m, "Interface");
10852 ringinuse_s = astman_get_header(m, "RingInUse");
10853
10854 /* Optional - if not supplied, set the ringinuse value for the given Interface in all queues */
10855 queuename = astman_get_header(m, "Queue");
10856
10857 if (ast_strlen_zero(interface) || ast_strlen_zero(ringinuse_s)) {
10858 astman_send_error(s, m, "Need 'Interface' and 'RingInUse' parameters.");
10859 return 0;
10860 }
10861
10862 if (ast_true(ringinuse_s)) {
10863 ringinuse = 1;
10864 } else if (ast_false(ringinuse_s)) {
10865 ringinuse = 0;
10866 } else {
10867 astman_send_error(s, m, "'RingInUse' parameter must be a truth value (yes/no, on/off, 0/1, etc)");
10868 return 0;
10869 }
10870
10871 if (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
10872 astman_send_error(s, m, "Invalid interface, queuename, or ringinuse value\n");
10873 } else {
10874 astman_send_ack(s, m, "Interface ringinuse set successfully");
10875 }
10876
10877 return 0;
10878}
10879
10880static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
10881{
10882 const char *queuename, *interface, *penalty_s;
10883 int penalty;
10884
10885 interface = astman_get_header(m, "Interface");
10886 penalty_s = astman_get_header(m, "Penalty");
10887 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
10888 queuename = astman_get_header(m, "Queue");
10889
10890 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
10891 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
10892 return 0;
10893 }
10894
10895 penalty = atoi(penalty_s);
10896
10897 if (set_member_value((char *)queuename, (char *)interface, MEMBER_PENALTY, penalty)) {
10898 astman_send_error(s, m, "Invalid interface, queuename or penalty");
10899 } else {
10900 astman_send_ack(s, m, "Interface penalty set successfully");
10901 }
10902
10903 return 0;
10904}
10905
10906static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
10907{
10908 const char *queuename, *caller, *priority_s, *immediate_s;
10909 int priority = 0, immediate = 0;
10910
10911 queuename = astman_get_header(m, "Queue");
10912 caller = astman_get_header(m, "Caller");
10913 priority_s = astman_get_header(m, "Priority");
10914 immediate_s = astman_get_header(m, "Immediate");
10915
10916 if (ast_strlen_zero(queuename)) {
10917 astman_send_error(s, m, "'Queue' not specified.");
10918 return 0;
10919 }
10920
10921 if (ast_strlen_zero(caller)) {
10922 astman_send_error(s, m, "'Caller' not specified.");
10923 return 0;
10924 }
10925
10926 if (ast_strlen_zero(priority_s)) {
10927 astman_send_error(s, m, "'Priority' not specified.");
10928 return 0;
10929 } else if (sscanf(priority_s, "%30d", &priority) != 1) {
10930 astman_send_error(s, m, "'Priority' need integer.");
10931 return 0;
10932 }
10933
10934 if (!ast_strlen_zero(immediate_s)) {
10935 immediate = ast_true(immediate_s);
10936 }
10937
10938 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
10939 case RES_OKAY:
10940 astman_send_ack(s, m, "Priority change for caller on queue");
10941 break;
10942 case RES_NOSUCHQUEUE:
10943 astman_send_error(s, m, "Unable to change priority caller on queue: No such queue");
10944 break;
10945 case RES_NOT_CALLER:
10946 astman_send_error(s, m, "Unable to change priority caller on queue: No such caller");
10947 break;
10948 }
10949
10950 return 0;
10951}
10952
10954{
10955 const char *queuename, *caller, *withdraw_info;
10956
10957 queuename = astman_get_header(m, "Queue");
10958 caller = astman_get_header(m, "Caller");
10959 withdraw_info = astman_get_header(m, "WithdrawInfo");
10960
10961 if (ast_strlen_zero(queuename)) {
10962 astman_send_error(s, m, "'Queue' not specified.");
10963 return 0;
10964 }
10965
10966 if (ast_strlen_zero(caller)) {
10967 astman_send_error(s, m, "'Caller' not specified.");
10968 return 0;
10969 }
10970
10971 switch (request_withdraw_caller_from_queue(queuename, caller, withdraw_info)) {
10972 case RES_OKAY:
10973 astman_send_ack(s, m, "Withdraw requested successfully");
10974 break;
10975 case RES_NOSUCHQUEUE:
10976 astman_send_error(s, m, "Unable to request withdraw from queue: No such queue");
10977 break;
10978 case RES_NOT_CALLER:
10979 astman_send_error(s, m, "Unable to request withdraw from queue: No such caller");
10980 break;
10981 case RES_EXISTS:
10982 astman_send_error(s, m, "Unable to request withdraw from queue: Already requested");
10983 break;
10984 }
10985
10986 return 0;
10987}
10988
10989
10990static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10991{
10992 const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
10993 int penalty;
10994
10995 switch ( cmd ) {
10996 case CLI_INIT:
10997 e->command = "queue add member";
10998 e->usage =
10999 "Usage: queue add member <dial string> to <queue> [penalty <penalty> [as <membername> [state_interface <interface>]]]\n"
11000 " 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";
11001 return NULL;
11002 case CLI_GENERATE:
11003 return complete_queue_add_member(a->line, a->word, a->pos, a->n);
11004 }
11005
11006 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
11007 return CLI_SHOWUSAGE;
11008 } else if (strcmp(a->argv[4], "to")) {
11009 return CLI_SHOWUSAGE;
11010 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
11011 return CLI_SHOWUSAGE;
11012 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
11013 return CLI_SHOWUSAGE;
11014 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
11015 return CLI_SHOWUSAGE;
11016 }
11017
11018 queuename = a->argv[5];
11019 interface = a->argv[3];
11020 if (a->argc >= 8) {
11021 if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
11022 if (penalty < 0) {
11023 ast_cli(a->fd, "Penalty must be >= 0\n");
11024 penalty = 0;
11025 }
11026 } else {
11027 ast_cli(a->fd, "Penalty must be an integer >= 0\n");
11028 penalty = 0;
11029 }
11030 } else {
11031 penalty = 0;
11032 }
11033
11034 if (a->argc >= 10) {
11035 membername = a->argv[9];
11036 }
11037
11038 if (a->argc >= 12) {
11039 state_interface = a->argv[11];
11040 }
11041
11042 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface, NULL, 0)) {
11043 case RES_OKAY:
11044 if (ast_strlen_zero(membername) || !log_membername_as_agent) {
11045 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
11046 } else {
11047 ast_queue_log(queuename, "CLI", membername, "ADDMEMBER", "%s", "");
11048 }
11049 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
11050 return CLI_SUCCESS;
11051 case RES_EXISTS:
11052 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
11053 return CLI_FAILURE;
11054 case RES_NOSUCHQUEUE:
11055 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
11056 return CLI_FAILURE;
11057 case RES_OUTOFMEMORY:
11058 ast_cli(a->fd, "Out of memory\n");
11059 return CLI_FAILURE;
11060 case RES_NOT_DYNAMIC:
11061 ast_cli(a->fd, "Member not dynamic\n");
11062 return CLI_FAILURE;
11063 default:
11064 return CLI_FAILURE;
11065 }
11066}
11067
11068static char *complete_queue_remove_member(const char *line, const char *word, int pos, int state)
11069{
11070 int which = 0;
11071 struct call_queue *q;
11072 struct member *m;
11073 struct ao2_iterator queue_iter;
11074 struct ao2_iterator mem_iter;
11075 int wordlen = strlen(word);
11076
11077 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
11078 if (pos > 5 || pos < 3) {
11079 return NULL;
11080 }
11081 if (pos == 4) { /* only one possible match, 'from' */
11082 return (state == 0 ? ast_strdup("from") : NULL);
11083 }
11084
11085 if (pos == 5) { /* No need to duplicate code */
11086 return complete_queue(line, word, pos, state, 0);
11087 }
11088
11089 /* here is the case for 3, <member> */
11090 queue_iter = ao2_iterator_init(queues, 0);
11091 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
11092 ao2_lock(q);
11093 mem_iter = ao2_iterator_init(q->members, 0);
11094 while ((m = ao2_iterator_next(&mem_iter))) {
11095 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
11096 char *tmp;
11097 tmp = ast_strdup(m->interface);
11098 ao2_ref(m, -1);
11099 ao2_iterator_destroy(&mem_iter);
11100 ao2_unlock(q);
11101 queue_t_unref(q, "Done with iterator, returning interface name");
11102 ao2_iterator_destroy(&queue_iter);
11103 return tmp;
11104 }
11105 ao2_ref(m, -1);
11106 }
11107 ao2_iterator_destroy(&mem_iter);
11108 ao2_unlock(q);
11109 queue_t_unref(q, "Done with iterator");
11110 }
11111 ao2_iterator_destroy(&queue_iter);
11112
11113 return NULL;
11114}
11115
11116static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11117{
11118 const char *queuename, *interface;
11119 struct member *mem = NULL;
11120 char *res = CLI_FAILURE;
11121
11122 switch (cmd) {
11123 case CLI_INIT:
11124 e->command = "queue remove member";
11125 e->usage =
11126 "Usage: queue remove member <channel> from <queue>\n"
11127 " Remove a specific channel from a queue.\n";
11128 return NULL;
11129 case CLI_GENERATE:
11130 return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
11131 }
11132
11133 if (a->argc != 6) {
11134 return CLI_SHOWUSAGE;
11135 } else if (strcmp(a->argv[4], "from")) {
11136 return CLI_SHOWUSAGE;
11137 }
11138
11139 queuename = a->argv[5];
11140 interface = a->argv[3];
11141
11143 mem = find_member_by_queuename_and_interface(queuename, interface);
11144 }
11145
11146 switch (remove_from_queue(queuename, interface)) {
11147 case RES_OKAY:
11148 if (!mem || ast_strlen_zero(mem->membername)) {
11149 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
11150 } else {
11151 ast_queue_log(queuename, "CLI", mem->membername, "REMOVEMEMBER", "%s", "");
11152 }
11153 ast_cli(a->fd, "Removed interface %s from queue '%s'\n", interface, queuename);
11154 res = CLI_SUCCESS;
11155 break;
11156 case RES_EXISTS:
11157 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
11158 break;
11159 case RES_NOSUCHQUEUE:
11160 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
11161 break;
11162 case RES_OUTOFMEMORY:
11163 ast_cli(a->fd, "Out of memory\n");
11164 break;
11165 case RES_NOT_DYNAMIC:
11166 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
11167 break;
11168 }
11169
11170 if (mem) {
11171 ao2_ref(mem, -1);
11172 }
11173
11174 return res;
11175}
11176
11177
11178
11179static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11180{
11181 const char *queuename, *caller;
11182 int priority, immediate = 0;
11183 char *res = CLI_FAILURE;
11184
11185 switch (cmd) {
11186 case CLI_INIT:
11187 e->command = "queue priority caller";
11188 e->usage =
11189 "Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
11190 " Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
11191 return NULL;
11192 case CLI_GENERATE:
11193 return NULL;
11194 }
11195
11196 if (a->argc < 8) {
11197 return CLI_SHOWUSAGE;
11198 } else if (strcmp(a->argv[4], "on")) {
11199 return CLI_SHOWUSAGE;
11200 } else if (strcmp(a->argv[6], "to")) {
11201 return CLI_SHOWUSAGE;
11202 } else if (sscanf(a->argv[7], "%30d", &priority) != 1) {
11203 ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
11204 return CLI_SHOWUSAGE;
11205 } else if (a->argc == 9) {
11206 if (strcmp(a->argv[8], "immediate")) {
11207 return CLI_SHOWUSAGE;
11208 }
11209 immediate = 1;
11210 }
11211
11212 caller = a->argv[3];
11213 queuename = a->argv[5];
11214
11215 switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
11216 case RES_OKAY:
11217 res = CLI_SUCCESS;
11218 break;
11219 case RES_NOSUCHQUEUE:
11220 ast_cli(a->fd, "Unable change priority caller %s on queue '%s': No such queue\n", caller, queuename);
11221 break;
11222 case RES_NOT_CALLER:
11223 ast_cli(a->fd, "Unable to change priority caller '%s' on queue '%s': Not there\n", caller, queuename);
11224
11225 break;
11226 }
11227
11228 return res;
11229}
11230
11231
11232
11233static char *complete_queue_pause_member(const char *line, const char *word, int pos, int state)
11234{
11235 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
11236 switch (pos) {
11237 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
11238 return NULL;
11239 case 4: /* only one possible match, "queue" */
11240 return state == 0 ? ast_strdup("queue") : NULL;
11241 case 5: /* <queue> */
11242 return complete_queue(line, word, pos, state, 0);
11243 case 6: /* "reason" */
11244 return state == 0 ? ast_strdup("reason") : NULL;
11245 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
11246 return NULL;
11247 default:
11248 return NULL;
11249 }
11250}
11251
11252static char *handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11253{
11254 const char *queuename, *interface, *reason;
11255 int paused;
11256
11257 switch (cmd) {
11258 case CLI_INIT:
11259 e->command = "queue {pause|unpause} member";
11260 e->usage =
11261 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
11262 " Pause or unpause a queue member. Not specifying a particular queue\n"
11263 " will pause or unpause a member across all queues to which the member\n"
11264 " belongs.\n";
11265 return NULL;
11266 case CLI_GENERATE:
11267 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
11268 }
11269
11270 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
11271 return CLI_SHOWUSAGE;
11272 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
11273 return CLI_SHOWUSAGE;
11274 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
11275 return CLI_SHOWUSAGE;
11276 }
11277
11278
11279 interface = a->argv[3];
11280 queuename = a->argc >= 6 ? a->argv[5] : NULL;
11281 reason = a->argc == 8 ? a->argv[7] : NULL;
11282 paused = !strcasecmp(a->argv[1], "pause");
11283
11284 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
11285 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
11286 if (!ast_strlen_zero(queuename)) {
11287 ast_cli(a->fd, " in queue '%s'", queuename);
11288 }
11289 if (!ast_strlen_zero(reason)) {
11290 ast_cli(a->fd, " for reason '%s'", reason);
11291 }
11292 ast_cli(a->fd, "\n");
11293 return CLI_SUCCESS;
11294 } else {
11295 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
11296 if (!ast_strlen_zero(queuename)) {
11297 ast_cli(a->fd, " in queue '%s'", queuename);
11298 }
11299 if (!ast_strlen_zero(reason)) {
11300 ast_cli(a->fd, " for reason '%s'", reason);
11301 }
11302 ast_cli(a->fd, "\n");
11303 return CLI_FAILURE;
11304 }
11305}
11306
11307static char *complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
11308{
11309 /* 0 - queue; 1 - set; 2 - penalty/ringinuse; 3 - <value>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
11310 switch (pos) {
11311 case 4:
11312 if (state == 0) {
11313 return ast_strdup("on");
11314 } else {
11315 return NULL;
11316 }
11317 case 6:
11318 if (state == 0) {
11319 return ast_strdup("in");
11320 } else {
11321 return NULL;
11322 }
11323 case 7:
11324 return complete_queue(line, word, pos, state, 0);
11325 default:
11326 return NULL;
11327 }
11328}
11329
11330static char *handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11331{
11332 const char *queuename = NULL, *interface;
11333 int ringinuse;
11334
11335 switch (cmd) {
11336 case CLI_INIT:
11337 e->command = "queue set ringinuse";
11338 e->usage =
11339 "Usage: queue set ringinuse <yes/no> on <interface> [in <queue>]\n"
11340 " Set a member's ringinuse in the queue specified. If no queue is specified\n"
11341 " then that interface's penalty is set in all queues to which that interface is a member.\n";
11342 break;
11343 return NULL;
11344 case CLI_GENERATE:
11345 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11346 }
11347
11348 /* Sensible argument counts */
11349 if (a->argc != 6 && a->argc != 8) {
11350 return CLI_SHOWUSAGE;
11351 }
11352
11353 /* Uses proper indicational words */
11354 if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11355 return CLI_SHOWUSAGE;
11356 }
11357
11358 /* Set the queue name if applicable */
11359 if (a->argc == 8) {
11360 queuename = a->argv[7];
11361 }
11362
11363 /* Interface being set */
11364 interface = a->argv[5];
11365
11366 /* Check and set the ringinuse value */
11367 if (ast_true(a->argv[3])) {
11368 ringinuse = 1;
11369 } else if (ast_false(a->argv[3])) {
11370 ringinuse = 0;
11371 } else {
11372 return CLI_SHOWUSAGE;
11373 }
11374
11375 switch (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
11376 case RESULT_SUCCESS:
11377 ast_cli(a->fd, "Set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11378 return CLI_SUCCESS;
11379 case RESULT_FAILURE:
11380 ast_cli(a->fd, "Failed to set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
11381 return CLI_FAILURE;
11382 default:
11383 return CLI_FAILURE;
11384 }
11385}
11386
11387static char *handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11388{
11389 const char *queuename = NULL, *interface;
11390 int penalty = 0;
11391
11392 switch (cmd) {
11393 case CLI_INIT:
11394 e->command = "queue set penalty";
11395 e->usage =
11396 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
11397 " Set a member's penalty in the queue specified. If no queue is specified\n"
11398 " then that interface's penalty is set in all queues to which that interface is a member\n";
11399 return NULL;
11400 case CLI_GENERATE:
11401 return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
11402 }
11403
11404 if (a->argc != 6 && a->argc != 8) {
11405 return CLI_SHOWUSAGE;
11406 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
11407 return CLI_SHOWUSAGE;
11408 }
11409
11410 if (a->argc == 8) {
11411 queuename = a->argv[7];
11412 }
11413 interface = a->argv[5];
11414 penalty = atoi(a->argv[3]);
11415
11416 switch (set_member_value(queuename, interface, MEMBER_PENALTY, penalty)) {
11417 case RESULT_SUCCESS:
11418 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11419 return CLI_SUCCESS;
11420 case RESULT_FAILURE:
11421 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
11422 return CLI_FAILURE;
11423 default:
11424 return CLI_FAILURE;
11425 }
11426}
11427
11428static char *complete_queue_rule_show(const char *line, const char *word, int pos, int state)
11429{
11430 int which = 0;
11431 struct rule_list *rl_iter;
11432 int wordlen = strlen(word);
11433 char *ret = NULL;
11434 if (pos != 3) /* Wha? */ {
11435 return NULL;
11436 }
11437
11439 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11440 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
11441 ret = ast_strdup(rl_iter->name);
11442 break;
11443 }
11444 }
11446
11447 return ret;
11448}
11449
11450static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11451{
11452 const char *rule;
11453 struct rule_list *rl_iter;
11454 struct penalty_rule *pr_iter;
11455 switch (cmd) {
11456 case CLI_INIT:
11457 e->command = "queue show rules";
11458 e->usage =
11459 "Usage: queue show rules [rulename]\n"
11460 " Show the list of rules associated with rulename. If no\n"
11461 " rulename is specified, list all rules defined in queuerules.conf\n";
11462 return NULL;
11463 case CLI_GENERATE:
11464 return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
11465 }
11466
11467 if (a->argc != 3 && a->argc != 4) {
11468 return CLI_SHOWUSAGE;
11469 }
11470
11471 rule = a->argc == 4 ? a->argv[3] : "";
11473 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
11474 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
11475 ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
11476 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
11477 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);
11478 }
11479 }
11480 }
11482 return CLI_SUCCESS;
11483}
11484
11485static char *handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11486{
11487 struct ast_flags mask = {QUEUE_RESET_STATS,};
11488 int i;
11489
11490 switch (cmd) {
11491 case CLI_INIT:
11492 e->command = "queue reset stats";
11493 e->usage =
11494 "Usage: queue reset stats [<queuenames>]\n"
11495 "\n"
11496 "Issuing this command will reset statistics for\n"
11497 "<queuenames>, or for all queues if no queue is\n"
11498 "specified.\n";
11499 return NULL;
11500 case CLI_GENERATE:
11501 if (a->pos >= 3) {
11502 return complete_queue(a->line, a->word, a->pos, a->n, 17);
11503 } else {
11504 return NULL;
11505 }
11506 }
11507
11508 if (a->argc < 3) {
11509 return CLI_SHOWUSAGE;
11510 }
11511
11512 if (a->argc == 3) {
11513 reload_handler(1, &mask, NULL);
11514 return CLI_SUCCESS;
11515 }
11516
11517 for (i = 3; i < a->argc; ++i) {
11518 reload_handler(1, &mask, a->argv[i]);
11519 }
11520
11521 return CLI_SUCCESS;
11522}
11523
11524static char *handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11525{
11526 struct ast_flags mask = {0,};
11527 int i;
11528
11529 switch (cmd) {
11530 case CLI_INIT:
11531 e->command = "queue reload {parameters|members|rules|all}";
11532 e->usage =
11533 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
11534 "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
11535 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
11536 "specified in order to know what information to reload. Below is an explanation\n"
11537 "of each of these qualifiers.\n"
11538 "\n"
11539 "\t'members' - reload queue members from queues.conf\n"
11540 "\t'parameters' - reload all queue options except for queue members\n"
11541 "\t'rules' - reload the queuerules.conf file\n"
11542 "\t'all' - reload queue rules, parameters, and members\n"
11543 "\n"
11544 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
11545 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
11546 "one queue is specified when using this command, reloading queue rules may cause\n"
11547 "other queues to be affected\n";
11548 return NULL;
11549 case CLI_GENERATE:
11550 if (a->pos >= 3) {
11551 /* find the point at which the list of queue names starts */
11552 const char *command_end = a->line + strlen("queue reload ");
11553 command_end = strchr(command_end, ' ');
11554 if (!command_end) {
11555 command_end = a->line + strlen(a->line);
11556 }
11557 return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
11558 } else {
11559 return NULL;
11560 }
11561 }
11562
11563 if (a->argc < 3)
11564 return CLI_SHOWUSAGE;
11565
11566 if (!strcasecmp(a->argv[2], "rules")) {
11568 } else if (!strcasecmp(a->argv[2], "members")) {
11570 } else if (!strcasecmp(a->argv[2], "parameters")) {
11572 } else if (!strcasecmp(a->argv[2], "all")) {
11574 }
11575
11576 if (a->argc == 3) {
11577 reload_handler(1, &mask, NULL);
11578 return CLI_SUCCESS;
11579 }
11580
11581 for (i = 3; i < a->argc; ++i) {
11582 reload_handler(1, &mask, a->argv[i]);
11583 }
11584
11585 return CLI_SUCCESS;
11586}
11587
11588/*!
11589 * \brief Update Queue with data of an outgoing call
11590*/
11591static int qupd_exec(struct ast_channel *chan, const char *data)
11592{
11593 int oldtalktime;
11594 char *parse;
11595 struct call_queue *q;
11596 struct member *mem;
11597 int newtalktime = 0;
11598
11600 AST_APP_ARG(queuename);
11601 AST_APP_ARG(uniqueid);
11602 AST_APP_ARG(agent);
11604 AST_APP_ARG(talktime);
11605 AST_APP_ARG(params););
11606
11607 if (ast_strlen_zero(data)) {
11608 ast_log(LOG_WARNING, "QueueUpdate requires arguments (queuename,uniqueid,agent,status,talktime,params[totaltime,callednumber])\n");
11609 return -1;
11610 }
11611
11612 parse = ast_strdupa(data);
11613
11615
11616 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) || ast_strlen_zero(args.agent) || ast_strlen_zero(args.status)) {
11617 ast_log(LOG_WARNING, "Missing argument to QueueUpdate (queuename,uniqueid,agent,status,talktime,params[totaltime|callednumber])\n");
11618 return -1;
11619 }
11620
11621 if (!ast_strlen_zero(args.talktime)) {
11622 newtalktime = atoi(args.talktime);
11623 }
11624
11625 q = find_load_queue_rt_friendly(args.queuename);
11626 if (!q) {
11627 ast_log(LOG_WARNING, "QueueUpdate could not find requested queue '%s'\n", args.queuename);
11628 return 0;
11629 }
11630
11631 ao2_lock(q);
11632 if (q->members) {
11633 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
11634 while ((mem = ao2_iterator_next(&mem_iter))) {
11635 if (!strcasecmp(mem->membername, args.agent)) {
11636 if (!strcasecmp(args.status, "ANSWER")) {
11637 oldtalktime = q->talktime;
11638 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
11639 time(&mem->lastcall);
11640 mem->calls++;
11641 mem->lastqueue = q;
11642 q->callscompleted++;
11643
11644 if (newtalktime <= q->servicelevel) {
11645 q->callscompletedinsl++;
11646 }
11647 } else {
11648
11649 time(&mem->lastcall);
11650 q->callsabandoned++;
11651 }
11652
11653 ast_queue_log(args.queuename, args.uniqueid, args.agent, "OUTCALL", "%s|%s|%s", args.status, args.talktime, args.params);
11654 }
11655
11656 ao2_ref(mem, -1);
11657 }
11658
11659 ao2_iterator_destroy(&mem_iter);
11660 }
11661
11662 ao2_unlock(q);
11663 queue_t_unref(q, "Done with temporary pointer");
11664
11665 return 0;
11666}
11667
11668static struct ast_cli_entry cli_queue[] = {
11669 AST_CLI_DEFINE(queue_show, "Show status of a specified queue"),
11670 AST_CLI_DEFINE(handle_queue_rule_show, "Show the rules defined in queuerules.conf"),
11671 AST_CLI_DEFINE(handle_queue_add_member, "Add a channel to a specified queue"),
11672 AST_CLI_DEFINE(handle_queue_remove_member, "Removes a channel from a specified queue"),
11673 AST_CLI_DEFINE(handle_queue_pause_member, "Pause or unpause a queue member"),
11674 AST_CLI_DEFINE(handle_queue_set_member_penalty, "Set penalty for a channel of a specified queue"),
11675 AST_CLI_DEFINE(handle_queue_set_member_ringinuse, "Set ringinuse for a channel of a specified queue"),
11676 AST_CLI_DEFINE(handle_queue_reload, "Reload queues, members, queue rules, or parameters"),
11677 AST_CLI_DEFINE(handle_queue_reset, "Reset statistics for a queue"),
11678 AST_CLI_DEFINE(handle_queue_change_priority_caller, "Change priority caller on queue"),
11679};
11680
11683
11684static int unload_module(void)
11685{
11688
11690
11691 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_join_type);
11692 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_leave_type);
11693 STASIS_MESSAGE_TYPE_CLEANUP(queue_caller_abandon_type);
11694
11695 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_status_type);
11696 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_added_type);
11697 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_removed_type);
11698 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_pause_type);
11699 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_penalty_type);
11700 STASIS_MESSAGE_TYPE_CLEANUP(queue_member_ringinuse_type);
11701
11702 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_called_type);
11703 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_connect_type);
11704 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_complete_type);
11705 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_dump_type);
11706 STASIS_MESSAGE_TYPE_CLEANUP(queue_agent_ringnoanswer_type);
11707
11709 ast_manager_unregister("QueueStatus");
11710 ast_manager_unregister("QueueRule");
11711 ast_manager_unregister("QueueSummary");
11712 ast_manager_unregister("QueueAdd");
11713 ast_manager_unregister("QueueRemove");
11714 ast_manager_unregister("QueuePause");
11715 ast_manager_unregister("QueueLog");
11716 ast_manager_unregister("QueueUpdate");
11717 ast_manager_unregister("QueuePenalty");
11718 ast_manager_unregister("QueueReload");
11719 ast_manager_unregister("QueueReset");
11720 ast_manager_unregister("QueueMemberRingInUse");
11721 ast_manager_unregister("QueueChangePriorityCaller");
11722 ast_manager_unregister("QueueWithdrawCaller");
11738
11740
11741 ast_unload_realtime("queue_members");
11744
11745 queues = NULL;
11746 return 0;
11747}
11748
11749/*!
11750 * \brief Load the module
11751 *
11752 * Module loading including tests for configuration or dependencies.
11753 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
11754 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
11755 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
11756 * configuration file or other non-critical problem return
11757 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
11758 */
11759static int load_module(void)
11760{
11761 int err = 0;
11762 struct ast_flags mask = {AST_FLAGS_ALL, };
11763 struct ast_config *member_config;
11764 struct stasis_topic *queue_topic;
11766
11769 if (!queues) {
11771 }
11772
11775 if (!pending_members) {
11776 unload_module();
11778 }
11779
11780 use_weight = 0;
11781
11782 if (reload_handler(0, &mask, NULL)) {
11783 unload_module();
11785 }
11786
11787 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, "reason_paused", RQ_CHAR, 80, SENTINEL);
11788
11789 /*
11790 * This section is used to determine which name for 'ringinuse' to use in realtime members
11791 * Necessary for supporting older setups.
11792 *
11793 * It also checks if 'reason_paused' exists in the realtime backend
11794 */
11795 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
11796 if (!member_config) {
11797 realtime_ringinuse_field = "ringinuse";
11798 } else {
11799 const char *config_val;
11800
11801 if ((config_val = ast_variable_retrieve(member_config, NULL, "ringinuse"))) {
11802 ast_log(LOG_NOTICE, "ringinuse field entries found in queue_members table. Using 'ringinuse'\n");
11803 realtime_ringinuse_field = "ringinuse";
11804 } else if ((config_val = ast_variable_retrieve(member_config, NULL, "ignorebusy"))) {
11805 ast_log(LOG_NOTICE, "ignorebusy field found in queue_members table with no ringinuse field. Using 'ignorebusy'\n");
11806 realtime_ringinuse_field = "ignorebusy";
11807 } else {
11808 ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
11809 realtime_ringinuse_field = "ringinuse";
11810 }
11811
11812 if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
11814 }
11815 }
11816 ast_config_destroy(member_config);
11817
11820 }
11821
11830 err |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
11831 err |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
11838 err |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
11839 err |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
11840 err |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
11841 err |= ast_manager_register_xml("QueueChangePriorityCaller", 0, manager_change_priority_caller_on_queue);
11851
11852 /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
11854 if (!device_state_sub) {
11855 err = -1;
11856 }
11859
11861 queue_topic = ast_queue_topic_all();
11862 if (!manager_topic || !queue_topic) {
11863 unload_module();
11865 }
11867 if (!topic_forwarder) {
11868 unload_module();
11870 }
11871
11874 unload_module();
11876 }
11878 if (!agent_router) {
11879 unload_module();
11881 }
11885 NULL);
11889 NULL);
11890
11891 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_join_type);
11892 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_leave_type);
11893 err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_abandon_type);
11894
11895 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_status_type);
11896 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_added_type);
11897 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_removed_type);
11898 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_pause_type);
11899 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_penalty_type);
11900 err |= STASIS_MESSAGE_TYPE_INIT(queue_member_ringinuse_type);
11901
11902 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_called_type);
11903 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_connect_type);
11904 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_complete_type);
11905 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_dump_type);
11906 err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_ringnoanswer_type);
11907
11908 if (err) {
11909 unload_module();
11911 }
11913}
11914
11915static int reload(void)
11916{
11917 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
11918 ast_unload_realtime("queue_members");
11919 reload_handler(1, &mask, NULL);
11920 return 0;
11921}
11922
11923/*!
11924 * \brief Find a member by looking up queuename and interface.
11925 * \return member or NULL if member not found.
11926 */
11927static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface)
11928{
11929 struct member *mem = NULL;
11930 struct call_queue *q;
11931
11932 if ((q = find_load_queue_rt_friendly(queuename))) {
11933 ao2_lock(q);
11934 mem = ao2_find(q->members, interface, OBJ_KEY);
11935 ao2_unlock(q);
11936 queue_t_unref(q, "Expiring temporary reference.");
11937 }
11938 return mem;
11939}
11940
11942 .support_level = AST_MODULE_SUPPORT_CORE,
11943 .load = load_module,
11944 .unload = unload_module,
11945 .reload = reload,
11946 .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:313
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:892
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:449
void * ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Definition: aoc.c:307
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:650
@ AST_AOC_S
Definition: aoc.h:64
char digit
jack_status_t status
Definition: app_jack.c:146
const char * str
Definition: app_jack.c:147
static struct ast_custom_function queuevar_function
Definition: app_queue.c:9453
static void handle_bridge_enter(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6376
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:2865
static void member_remove_from_queue(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:3562
static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
Definition: app_queue.c:4576
static void load_realtime_queues(const char *queuename)
Definition: app_queue.c:3956
static struct member * interface_exists(struct call_queue *q, const char *interface)
Definition: app_queue.c:7458
static int is_our_turn(struct queue_ent *qe)
Check if we should start attempting to call queue members.
Definition: app_queue.c:5745
static void record_abandoned(struct queue_ent *qe)
Record that a caller gave up on waiting in queue.
Definition: app_queue.c:5079
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:9010
static char * queue_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:10354
static int get_wrapuptime(struct call_queue *q, struct member *member)
Return wrapuptime.
Definition: app_queue.c:1991
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:3578
static int context_included(const char *parent, const char *child)
Returns if one context includes another context.
Definition: app_queue.c:2777
static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
Set variables of queue.
Definition: app_queue.c:2063
static int manager_queue_reset(struct mansession *s, const struct message *m)
Definition: app_queue.c:10800
static struct ast_manager_event_blob * queue_member_ringinuse_to_ami(struct stasis_message *message)
Definition: app_queue.c:2199
static struct ast_manager_event_blob * queue_member_penalty_to_ami(struct stasis_message *message)
Definition: app_queue.c:2194
member_properties
Definition: app_queue.c:1765
@ MEMBER_RINGINUSE
Definition: app_queue.c:1767
@ MEMBER_PENALTY
Definition: app_queue.c:1766
#define RES_NOT_CALLER
Definition: app_queue.c:1570
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:6773
#define ANNOUNCEPOSITION_MORE_THAN
Definition: app_queue.c:1788
static int pending_members_cmp(void *obj, void *arg, int flags)
Definition: app_queue.c:2536
static void queue_reset_global_params(void)
Definition: app_queue.c:9570
static char * complete_queue_rule_show(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11428
static void dump_queue_members(struct call_queue *pm_queue)
Dump all members in a specific queue to the database.
Definition: app_queue.c:7485
#define QUEUE_UNPAUSED_DEVSTATE
Definition: app_queue.c:3537
static struct ast_custom_function queuemembercount_function
Definition: app_queue.c:9458
static struct ast_custom_function queuewaitingcount_function
Definition: app_queue.c:9474
static int play_file(struct ast_channel *chan, const char *filename)
Definition: app_queue.c:4143
static int queue_persistent_members
queues.conf [general] option
Definition: app_queue.c:1590
static const struct @50 queue_results[]
static int montype_default
queues.conf [general] option
Definition: app_queue.c:1599
static int mark_member_dead(void *obj, void *arg, int flags)
Definition: app_queue.c:9728
static char * app_pqm
Definition: app_queue.c:1578
static struct ast_custom_function queuememberlist_function
Definition: app_queue.c:9479
static struct ast_manager_event_blob * queue_channel_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2115
static void set_queue_member_ringinuse(struct call_queue *q, struct member *mem, int ringinuse)
Definition: app_queue.c:7948
static char * realtime_ringinuse_field
name of the ringinuse field in the realtime database
Definition: app_queue.c:1620
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
Check if members are available.
Definition: app_queue.c:2422
static void queue_bridge_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6523
static int remove_from_queue(const char *queuename, const char *interface)
Remove member from queue.
Definition: app_queue.c:7540
static int manager_add_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10611
#define MAX_PERIODIC_ANNOUNCEMENTS
Definition: app_queue.c:1556
static void parse_empty_options(const char *value, enum empty_conditions *empty, int joinempty)
Definition: app_queue.c:3289
static int aqm_exec(struct ast_channel *chan, const char *data)
AddQueueMember application.
Definition: app_queue.c:8354
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
sets the QUEUESTATUS channel variable
Definition: app_queue.c:1910
static struct ast_manager_event_blob * queue_member_pause_to_ami(struct stasis_message *message)
Definition: app_queue.c:2189
static void leave_queue(struct queue_ent *qe)
Caller leaving queue.
Definition: app_queue.c:4372
#define MAX_QUEUE_BUCKETS
Definition: app_queue.c:1563
static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
The command center for all reload operations.
Definition: app_queue.c:10003
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:8968
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:9380
static char * complete_queue_pause_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11233
static void do_hang(struct callattempt *o)
common hangup actions
Definition: app_queue.c:4627
static int set_member_value(const char *queuename, const char *interface, int property, int value)
Definition: app_queue.c:8000
static void reload_single_member(const char *memberdata, struct call_queue *q)
reload information pertaining to a single member
Definition: app_queue.c:9617
static struct ast_manager_event_blob * queue_agent_ringnoanswer_to_ami(struct stasis_message *message)
Definition: app_queue.c:2284
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:6129
static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
reload the queues.conf file
Definition: app_queue.c:9916
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
Playback announcement to queued members if period has elapsed.
Definition: app_queue.c:5018
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:9759
static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
Definition: app_queue.c:6905
static struct ast_manager_event_blob * queue_member_removed_to_ami(struct stasis_message *message)
Definition: app_queue.c:2184
#define DEFAULT_RETRY
Definition: app_queue.c:1553
static int upqm_exec(struct ast_channel *chan, const char *data)
UnpauseQueueMember application.
Definition: app_queue.c:8247
static void clear_queue(struct call_queue *q)
Definition: app_queue.c:3051
static void handle_attended_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle an attended transfer event.
Definition: app_queue.c:6468
static void recalc_holdtime(struct queue_ent *qe, int newholdtime)
Definition: app_queue.c:4349
#define RES_NOT_DYNAMIC
Definition: app_queue.c:1569
static int compare_weight(struct call_queue *rq, struct member *member)
Definition: app_queue.c:4542
static char * handle_queue_pause_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11252
static int set_member_ringinuse_help_members(struct call_queue *q, const char *interface, int ringinuse)
Definition: app_queue.c:7961
static void queue_agent_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6160
queue_timeout_priority
Definition: app_queue.c:1652
@ TIMEOUT_PRIORITY_CONF
Definition: app_queue.c:1654
@ TIMEOUT_PRIORITY_APP
Definition: app_queue.c:1653
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:9332
static char * app_ql
Definition: app_queue.c:1582
static void queue_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9582
static void reload_queue_members(void)
Reload dynamic queue members persisted into the astdb.
Definition: app_queue.c:8100
static int rqm_exec(struct ast_channel *chan, const char *data)
RemoveQueueMember application.
Definition: app_queue.c:8283
static int valid_exit(struct queue_ent *qe, char digit)
Check for valid exit from queue via goto.
Definition: app_queue.c:4172
static int is_member_available(struct call_queue *q, struct member *mem)
Definition: app_queue.c:2606
#define QUEUE_PAUSED_DEVSTATE
Definition: app_queue.c:3536
#define ANNOUNCEPOSITION_NO
Definition: app_queue.c:1787
static char * handle_queue_set_member_penalty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11387
static void handle_masquerade(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6714
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:5884
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:10030
char * text
Definition: app_queue.c:1639
#define MAX_CALL_ATTEMPT_BUCKETS
Definition: app_queue.c:2514
static char * app_rqm
Definition: app_queue.c:1576
static void copy_rules(struct queue_ent *qe, const char *rulename)
Copy rule from global list into specified queue.
Definition: app_queue.c:8469
static int manager_queue_rule_show(struct mansession *s, const struct message *m)
Definition: app_queue.c:10370
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:7722
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
Definition: app_queue.c:7852
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:7646
static struct ao2_container * queues
Definition: app_queue.c:1901
static int negative_penalty_invalid
queues.conf [general] option
Definition: app_queue.c:1611
#define AST_MAX_WATCHERS
Definition: app_queue.c:5191
static int load_realtime_rules(void)
Load queue rules from realtime.
Definition: app_queue.c:3187
static void queue_rules_reset_global_params(void)
Definition: app_queue.c:9491
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
Definition: app_queue.c:5972
static void escape_and_substitute(struct ast_channel *chan, const char *input, char *output, size_t size)
Definition: app_queue.c:6874
#define RES_OUTOFMEMORY
Definition: app_queue.c:1567
static int member_hash_fn(const void *obj, const int flags)
Definition: app_queue.c:2920
static int member_cmp_fn(void *obj1, void *obj2, int flags)
Definition: app_queue.c:2936
#define queues_t_unlink(c, q, tag)
Definition: app_queue.c:2060
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY
The minimum number of seconds between position announcements.
Definition: app_queue.c:1561
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:4469
static void queue_stasis_data_destructor(void *obj)
Definition: app_queue.c:6259
static struct ast_manager_event_blob * queue_member_added_to_ami(struct stasis_message *message)
Definition: app_queue.c:2179
static struct ast_manager_event_blob * queue_agent_dump_to_ami(struct stasis_message *message)
Definition: app_queue.c:2279
static int extensionstate2devicestate(int state)
Helper function which converts from extension state to device state values.
Definition: app_queue.c:2728
#define queues_t_link(c, q, tag)
Definition: app_queue.c:2059
static int extension_state_cb(const char *context, const char *exten, struct ast_state_cb_info *info, void *data)
Definition: app_queue.c:2806
static int manager_queue_reload(struct mansession *s, const struct message *m)
Definition: app_queue.c:10768
static int get_member_penalty(char *queuename, char *interface)
Gets members penalty.
Definition: app_queue.c:8069
static int realtime_rules
queuerules.conf [general] option
Definition: app_queue.c:1605
static void handle_local_optimization_begin(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6542
static int pqm_exec(struct ast_channel *chan, const char *data)
PauseQueueMember application.
Definition: app_queue.c:8211
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:10309
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:6044
static char * handle_queue_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11485
static int qupd_exec(struct ast_channel *chan, const char *data)
Update Queue with data of an outgoing call.
Definition: app_queue.c:11591
static char * complete_queue_show(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:10346
static char * handle_queue_set_member_ringinuse(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11330
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:8924
static int reload_queue_rules(int reload)
Reload the rules defined in queuerules.conf.
Definition: app_queue.c:9511
static char * complete_queue_add_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:10815
static char * handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11179
static char * handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11450
static int manager_queue_member_ringinuse(struct mansession *s, const struct message *m)
Definition: app_queue.c:10846
static char * complete_queue_set_member_value(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11307
@ OPT_CALLER_AUTOMON
Definition: app_queue.c:1465
@ OPT_CALLEE_PARK
Definition: app_queue.c:1455
@ OPT_PREDIAL_CALLER
Definition: app_queue.c:1467
@ OPT_GO_ON
Definition: app_queue.c:1448
@ OPT_IGNORE_CONNECTEDLINE
Definition: app_queue.c:1454
@ OPT_CALLEE_AUTOMON
Definition: app_queue.c:1464
@ OPT_CALLEE_TRANSFER
Definition: app_queue.c:1460
@ OPT_CALLEE_GO_ON
Definition: app_queue.c:1450
@ OPT_MARK_AS_ANSWERED
Definition: app_queue.c:1447
@ OPT_IGNORE_CALL_FW
Definition: app_queue.c:1453
@ OPT_CALLER_PARK
Definition: app_queue.c:1456
@ OPT_NO_RETRY
Definition: app_queue.c:1457
@ OPT_DATA_QUALITY
Definition: app_queue.c:1449
@ OPT_CALLER_HANGUP
Definition: app_queue.c:1452
@ OPT_MUSICONHOLD_CLASS
Definition: app_queue.c:1468
@ OPT_CALLEE_AUTOMIXMON
Definition: app_queue.c:1462
@ OPT_CALLEE_HANGUP
Definition: app_queue.c:1451
@ OPT_CALLER_AUTOMIXMON
Definition: app_queue.c:1463
@ OPT_RINGING
Definition: app_queue.c:1458
@ OPT_CALLER_TRANSFER
Definition: app_queue.c:1461
@ OPT_PREDIAL_CALLEE
Definition: app_queue.c:1466
@ OPT_RING_WHEN_RINGING
Definition: app_queue.c:1459
static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
Definition: app_queue.c:4657
static const char * int2strat(int strategy)
Definition: app_queue.c:1922
#define RES_NOSUCHQUEUE
Definition: app_queue.c:1568
static void free_members(struct call_queue *q, int all)
Iterate through queue's member list and delete them.
Definition: app_queue.c:3696
static int publish_queue_member_pause(struct call_queue *q, struct member *member)
Definition: app_queue.c:7761
static void callattempt_free(struct callattempt *doomed)
Definition: app_queue.c:4447
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:4994
#define queue_t_unref(q, tag)
Definition: app_queue.c:2058
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:5205
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:2575
static char * app_qupd
Definition: app_queue.c:1584
#define queue_unref(q)
Definition: app_queue.c:2056
#define ANNOUNCEHOLDTIME_ALWAYS
Definition: app_queue.c:1771
static const struct ast_app_option queue_exec_options[128]
Definition: app_queue.c:1503
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:2340
static int shared_lastcall
queues.conf [general] option
Definition: app_queue.c:1602
static int manager_queue_member_penalty(struct mansession *s, const struct message *m)
Definition: app_queue.c:10880
#define queue_t_ref(q, tag)
Definition: app_queue.c:2057
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:10138
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:3332
#define ANNOUNCEPOSITION_LIMIT
Definition: app_queue.c:1789
static char * app_upqm
Definition: app_queue.c:1580
static int clear_stats(const char *queuename)
Facilitates resetting statistics for a queue.
Definition: app_queue.c:9973
static struct ast_custom_function queuememberpenalty_function
Definition: app_queue.c:9484
static void set_queue_member_pause(struct call_queue *q, struct member *mem, const char *reason, int paused)
Definition: app_queue.c:7785
static int queue_cmp_cb(void *obj, void *arg, int flags)
Definition: app_queue.c:1978
queue_result
Definition: app_queue.c:1625
@ QUEUE_FULL
Definition: app_queue.c:1632
@ QUEUE_UNKNOWN
Definition: app_queue.c:1626
@ QUEUE_WITHDRAW
Definition: app_queue.c:1634
@ QUEUE_CONTINUE
Definition: app_queue.c:1633
@ QUEUE_LEAVEEMPTY
Definition: app_queue.c:1629
@ QUEUE_LEAVEUNAVAIL
Definition: app_queue.c:1631
@ QUEUE_JOINUNAVAIL
Definition: app_queue.c:1630
@ QUEUE_JOINEMPTY
Definition: app_queue.c:1628
@ QUEUE_TIMEOUT
Definition: app_queue.c:1627
static int manager_request_withdraw_caller_from_queue(struct mansession *s, const struct message *m)
Definition: app_queue.c:10953
static char * app
Definition: app_queue.c:1572
static int set_member_value_help_members(struct call_queue *q, const char *interface, int property, int value)
Definition: app_queue.c:7977
static int queue_hash_cb(const void *obj, const int flags)
Definition: app_queue.c:1971
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:3751
static const char *const pm_family
Persistent Members astdb family.
Definition: app_queue.c:1587
#define queue_ref(q)
Definition: app_queue.c:2055
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:9175
static int log_membername_as_agent
queues.conf [general] option
Definition: app_queue.c:1614
static void update_qe_rule(struct queue_ent *qe)
update rules for queues
Definition: app_queue.c:5794
static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
Definition: app_queue.c:5175
static struct ast_manager_event_blob * queue_agent_called_to_ami(struct stasis_message *message)
Definition: app_queue.c:2264
static int queue_exec(struct ast_channel *chan, const char *data)
The starting point for all queue calls.
Definition: app_queue.c:8512
static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
Definition: app_queue.c:4059
static char * handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11116
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:5110
static void destroy_queue_member_cb(void *obj)
Definition: app_queue.c:2855
static void init_queue(struct call_queue *q)
Initialize Queue default values.
Definition: app_queue.c:2949
static struct ao2_container * pending_members
Definition: app_queue.c:2513
static struct ast_manager_event_blob * queue_multi_channel_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2223
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:11927
static int force_longest_waiting_caller
queues.conf [general] option
Definition: app_queue.c:1617
#define RES_OKAY
Definition: app_queue.c:1565
static struct ast_manager_event_blob * queue_member_status_to_ami(struct stasis_message *message)
Definition: app_queue.c:2174
static int kill_dead_members(void *obj, void *arg, int flags)
Definition: app_queue.c:9737
#define DEFAULT_TIMEOUT
Definition: app_queue.c:1554
static void remove_stasis_subscriptions(struct queue_stasis_data *queue_data)
Definition: app_queue.c:6276
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:9295
static int pending_members_hash(const void *obj, const int flags)
Definition: app_queue.c:2516
static int autofill_default
queues.conf [general] option
Definition: app_queue.c:1596
static int manager_pause_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10722
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:2089
#define ANNOUNCEHOLDTIME_ONCE
Definition: app_queue.c:1772
static struct ast_manager_event_blob * queue_caller_join_to_ami(struct stasis_message *message)
Definition: app_queue.c:2134
static int queue_delme_members_decrement_followers(void *obj, void *arg, int flag)
Definition: app_queue.c:2024
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
Change queue penalty by adding rule.
Definition: app_queue.c:3083
static char * handle_queue_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:11524
static int num_available_members(struct call_queue *q)
Get the number of members available to accept a call.
Definition: app_queue.c:4509
static int ql_exec(struct ast_channel *chan, const char *data)
QueueLog application.
Definition: app_queue.c:8435
static int manager_remove_queue_member(struct mansession *s, const struct message *m)
Definition: app_queue.c:10675
static int update_realtime_member_field(struct member *mem, const char *queue_name, const char *field, const char *value)
Definition: app_queue.c:3984
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:4970
static struct stasis_message_router * agent_router
Definition: app_queue.c:11681
static void queue_member_follower_removal(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:2042
static struct member * get_interface_helper(struct call_queue *q, const char *interface)
Definition: app_queue.c:8987
static int mark_unfound(void *obj, void *arg, int flags)
Definition: app_queue.c:9882
queue_reload_mask
Definition: app_queue.c:1522
@ QUEUE_RELOAD_RULES
Definition: app_queue.c:1525
@ QUEUE_RELOAD_MEMBER
Definition: app_queue.c:1524
@ QUEUE_RESET_STATS
Definition: app_queue.c:1526
@ QUEUE_RELOAD_PARAMETERS
Definition: app_queue.c:1523
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:2641
static int load_module(void)
Load the module.
Definition: app_queue.c:11759
static void member_add_to_queue(struct call_queue *queue, struct member *mem)
Definition: app_queue.c:3546
#define RECHECK
Definition: app_queue.c:1555
static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
Part 2 of ring_one.
Definition: app_queue.c:4753
static void pending_members_remove(struct member *mem)
Definition: app_queue.c:2564
static void end_bridge_callback(void *data)
Definition: app_queue.c:6834
enum queue_result id
Definition: app_queue.c:1638
static struct stasis_subscription * device_state_sub
Subscription to device state change messages.
Definition: app_queue.c:1608
static int manager_queues_summary(struct mansession *s, const struct message *m)
Summary of queue info via the AMI.
Definition: app_queue.c:10406
static struct call_queue * alloc_queue(const char *queuename)
Definition: app_queue.c:3727
static struct callattempt * find_best(struct callattempt *outgoing)
find the entry with the best metric, or NULL
Definition: app_queue.c:4885
static int get_queue_member_status(struct member *cur)
Return the current state of a member.
Definition: app_queue.c:2850
static int unload_module(void)
Definition: app_queue.c:11684
static int reload(void)
Definition: app_queue.c:11915
#define QUEUE_UNKNOWN_PAUSED_DEVSTATE
Definition: app_queue.c:3538
static void publish_dial_end_event(struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
Definition: app_queue.c:4457
static int manager_queues_status(struct mansession *s, const struct message *m)
Queue status info via AMI.
Definition: app_queue.c:10488
static void handle_local_optimization_end(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6593
static int say_position(struct queue_ent *qe, int ringing)
Definition: app_queue.c:4207
static int realtime_reason_paused
does realtime backend support reason_paused
Definition: app_queue.c:1623
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:6291
static void update_realtime_members(struct call_queue *q)
Definition: app_queue.c:4000
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:9412
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:2305
static struct ast_custom_function queuemembercount_dep
Definition: app_queue.c:9464
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:7593
static struct ast_manager_event_blob * queue_agent_connect_to_ami(struct stasis_message *message)
Definition: app_queue.c:2269
static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
Definition: app_queue.c:6827
empty_conditions
Definition: app_queue.c:1754
@ QUEUE_EMPTY_INVALID
Definition: app_queue.c:1760
@ QUEUE_EMPTY_UNKNOWN
Definition: app_queue.c:1761
@ QUEUE_EMPTY_PENALTY
Definition: app_queue.c:1755
@ QUEUE_EMPTY_RINGING
Definition: app_queue.c:1758
@ QUEUE_EMPTY_INUSE
Definition: app_queue.c:1757
@ QUEUE_EMPTY_UNAVAILABLE
Definition: app_queue.c:1759
@ QUEUE_EMPTY_WRAPUP
Definition: app_queue.c:1762
@ QUEUE_EMPTY_PAUSED
Definition: app_queue.c:1756
static struct ast_json * queue_member_blob_create(struct call_queue *q, struct member *mem)
Definition: app_queue.c:2395
static void queue_channel_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6748
static int use_weight
Records that one or more queues use weight.
Definition: app_queue.c:1593
#define ANNOUNCEPOSITION_YES
Definition: app_queue.c:1786
static int wait_a_bit(struct queue_ent *qe)
Definition: app_queue.c:7445
static char * app_aqm
Definition: app_queue.c:1574
static void destroy_queue(void *obj)
Free queue's member list then its string fields.
Definition: app_queue.c:3712
static int kill_if_unfound(void *obj, void *arg, int flags)
Definition: app_queue.c:9892
static void handle_hangup(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Definition: app_queue.c:6657
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:10020
static char * complete_queue_remove_member(const char *line, const char *word, int pos, int state)
Definition: app_queue.c:11068
static char * handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: app_queue.c:10990
#define RES_EXISTS
Definition: app_queue.c:1566
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:9112
static void queue_rules_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9497
static int member_status_available(int status)
Definition: app_queue.c:4643
static int queue_member_decrement_followers(void *obj, void *arg, int flag)
Definition: app_queue.c:2005
static struct ast_cli_entry cli_queue[]
Definition: app_queue.c:11668
@ QUEUE_STRATEGY_RINGALL
Definition: app_queue.c:1506
@ QUEUE_STRATEGY_RRMEMORY
Definition: app_queue.c:1510
@ QUEUE_STRATEGY_LINEAR
Definition: app_queue.c:1511
@ QUEUE_STRATEGY_LEASTRECENT
Definition: app_queue.c:1507
@ QUEUE_STRATEGY_RANDOM
Definition: app_queue.c:1509
@ QUEUE_STRATEGY_FEWESTCALLS
Definition: app_queue.c:1508
@ QUEUE_STRATEGY_RRORDERED
Definition: app_queue.c:1513
@ QUEUE_STRATEGY_WRANDOM
Definition: app_queue.c:1512
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
static struct ast_custom_function queuegetchannel_function
Definition: app_queue.c:9469
static struct ast_manager_event_blob * queue_caller_abandon_to_ami(struct stasis_message *message)
Definition: app_queue.c:2144
static struct stasis_forward * topic_forwarder
Definition: app_queue.c:11682
static struct ast_custom_function queueexists_function
Definition: app_queue.c:9448
static int strat2int(const char *strategy)
Definition: app_queue.c:1935
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:9216
static struct ast_manager_event_blob * queue_agent_complete_to_ami(struct stasis_message *message)
Definition: app_queue.c:2274
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:6857
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:10250
static void log_attended_transfer(struct queue_stasis_data *queue_data, struct ast_attended_transfer_message *atxfer_msg)
Definition: app_queue.c:6330
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
Place a call to a queue member.
Definition: app_queue.c:4910
@ OPT_ARG_CALLEE_GO_ON
Definition: app_queue.c:1472
@ OPT_ARG_PREDIAL_CALLEE
Definition: app_queue.c:1473
@ OPT_ARG_MUSICONHOLD_CLASS
Definition: app_queue.c:1475
@ OPT_ARG_PREDIAL_CALLER
Definition: app_queue.c:1474
@ OPT_ARG_ARRAY_SIZE
Definition: app_queue.c:1477
static void handle_blind_transfer(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Handle a blind transfer event.
Definition: app_queue.c:6409
static const struct autopause autopausesmodes[]
agent_complete_reason
Definition: app_queue.c:6122
@ AGENT
Definition: app_queue.c:6124
@ CALLER
Definition: app_queue.c:6123
@ TRANSFER
Definition: app_queue.c:6125
static struct ast_manager_event_blob * queue_member_to_ami(const char *type, struct stasis_message *message)
Definition: app_queue.c:2159
@ QUEUE_AUTOPAUSE_ON
Definition: app_queue.c:1518
@ QUEUE_AUTOPAUSE_OFF
Definition: app_queue.c:1517
@ QUEUE_AUTOPAUSE_ALL
Definition: app_queue.c:1519
static int autopause2int(const char *autopause)
Definition: app_queue.c:1948
static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
Definition: app_queue.c:10906
static int compress_char(const char c)
Definition: app_queue.c:2910
static void queue_publish_member_blob(struct stasis_message_type *type, struct ast_json *blob)
Definition: app_queue.c:2371
static int manager_queue_log_custom(struct mansession *s, const struct message *m)
Definition: app_queue.c:10747
static struct ast_manager_event_blob * queue_caller_leave_to_ami(struct stasis_message *message)
Definition: app_queue.c:2139
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:6978
static int set_member_penalty_help_members(struct call_queue *q, const char *interface, int penalty)
Definition: app_queue.c:7912
unsigned int stop
Definition: app_sla.c:336
ast_mutex_t lock
Definition: app_sla.c:331
#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: main/db.c:342
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: main/db.c:437
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: main/db.c:610
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:677
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:1100
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
static int tmp()
Definition: bt_open.c:389
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:393
static int priority
static int available(struct dahdi_pvt **pvt, int is_specific_channel)
Definition: chan_dahdi.c:13472
static const char type[]
Definition: chan_ooh323.c:109
static int call(void *data)
Definition: chan_pjsip.c:2391
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:3175
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:2122
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Definition: channel.c:6461
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:10338
void ast_party_number_init(struct ast_party_number *init)
Initialize the given number structure.
Definition: channel.c:1644
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
@ AST_CHANNEL_REQUESTOR_BRIDGE_PEER
Definition: channel.h:1477
int ast_party_id_presentation(const struct ast_party_id *id)
Determine the overall presentation value for the given party.
Definition: channel.c:1821
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2072
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:7372
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:3157
#define ast_channel_lock(chan)
Definition: channel.h:2922
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
Make the frame formats of two channels compatible.
Definition: channel.c:6720
struct ast_format_cap * ast_channel_nativeformats(const struct ast_channel *chan)
void ast_channel_data_set(struct ast_channel *chan, const char *value)
@ AST_FEATURE_AUTOMIXMON
Definition: channel.h:1069
@ AST_FEATURE_REDIRECT
Definition: channel.h:1064
@ AST_FEATURE_PARKCALL
Definition: channel.h:1068
@ AST_FEATURE_AUTOMON
Definition: channel.h:1067
@ AST_FEATURE_DISCONNECT
Definition: channel.h:1065
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:2031
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:2054
int ast_channel_priority(const struct ast_channel *chan)
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2929
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:2368
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:6434
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:4257
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:9093
ast_channel_adsicpe
Definition: channel.h:868
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:1999
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:7334
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:10383
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:6771
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:8785
int ast_channel_supports_html(struct ast_channel *channel)
Checks for HTML support on a channel.
Definition: channel.c:6623
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
int ast_channel_hangupcause(const struct ast_channel *chan)
struct ast_party_dialed * ast_channel_dialed(struct ast_channel *chan)
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
Indicates condition of channel, with payload.
Definition: channel.c:4653
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:10284
#define AST_CHANNEL_NAME
Definition: channel.h:171
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:1691
#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:2022
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:6635
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:2179
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:8293
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:6439
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:6444
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:7422
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:2045
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:342
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
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:4277
int ast_safe_sleep(struct ast_channel *chan, int ms)
Wait for a specified amount of time, looking for hangups.
Definition: channel.c:1574
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:6354
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2923
#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:2135
#define MAX_MUSICCLASS
Definition: channel.h:173
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_UP
Definition: channelstate.h:42
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define RESULT_SUCCESS
Definition: cli.h:40
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define RESULT_FAILURE
Definition: cli.h:42
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
short word
#define SENTINEL
Definition: compiler.h:87
Local proxy channel special access.
struct stasis_message_type * ast_local_optimization_end_type(void)
Message type for when a local channel optimization completes.
struct stasis_message_type * ast_local_optimization_begin_type(void)
Message type for when a local channel optimization begins.
Device state management.
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
@ AST_DEVSTATE_CACHABLE
Definition: devicestate.h:70
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:510
const char * ast_devstate2str(enum ast_device_state devstate) attribute_pure
Convert device state to text string for output.
Definition: devicestate.c:237
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
Definition: devicestate.c:668
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:595
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:1293
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
#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:1840
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:3431
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3389
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:3467
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:1880
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3421
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:1981
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3050
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3475
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3310
static struct stasis_topic * manager_topic
A stasis_topic that all topics AMI cares about will be forwarded to.
Definition: manager.c:1644
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8057
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
char * strsep(char **str, const char *delims)
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:3549
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3576
#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:3530
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:783
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3659
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3506
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
@ CONFIG_FLAG_FILEUNCHANGED
#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:965
#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:604
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:757
size_t current
Definition: main/cli.c:113
int errno
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
struct ast_str * ast_manager_build_channel_state_string(const struct ast_channel_snapshot *snapshot)
Generate the AMI message body from a channel snapshot.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
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:10564
#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:7766
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7776
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:8650
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:4175
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:1558
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8781
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:4190
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:3856
@ 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:3823
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:3170
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:8235
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:1548
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:1023
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:1077
#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:1174
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1134
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:1578
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
#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.
union ast_attended_transfer_message::@284 dest
enum ast_attended_transfer_dest_type dest_type
struct ast_channel_snapshot * links[2]
struct ast_bridge_channel_snapshot_pair to_transfer_target
enum ast_transfer_result result
struct ast_bridge_channel_snapshot_pair to_transferee
char bridge[AST_UUID_STR_LEN]
Message published during a blind transfer.
char exten[AST_MAX_EXTENSION]
struct ast_bridge_snapshot * bridge
enum ast_transfer_result result
char context[AST_MAX_CONTEXT]
Blob of data associated with a bridge.
struct ast_bridge_snapshot * bridge
struct ast_channel_snapshot * channel
struct ast_bridge_snapshot * bridge_snapshot
bridge configuration
Definition: channel.h:1076
void * end_bridge_callback_data
Definition: channel.h:1091
struct ast_flags features_callee
Definition: channel.h:1078
struct ast_flags features_caller
Definition: channel.h:1077
void(* end_bridge_callback)(void *)
Definition: channel.h:1090
void(* end_bridge_callback_data_fixup)(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
Definition: channel.h:1095
const ast_string_field uniqueid
Definition: bridge.h:328
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:629
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:284
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Definition: astdb.h:31
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
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
union ast_frame::@226 data
enum ast_frame_type frametype
ast_include: include= support in extensions.conf
Definition: pbx_include.c:37
struct ast_json * json
Definition: json.h:1083
Abstract JSON element (object, array, string, int, ...).
Struct containing info for an AMI event to send out.
Definition: manager.h:502
A multi channel blob data structure for multi_channel_blob stasis messages.
Caller Party information.
Definition: channel.h:418
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
struct ast_party_id ani
Automatic Number Identification (ANI)
Definition: channel.h:427
Connected Line/Party information.
Definition: channel.h:456
int source
Information about the source of an update.
Definition: channel.h:482
struct ast_party_id id
Connected party ID.
Definition: channel.h:458
struct ast_party_id ani
Automatic Number Identification (ANI)
Definition: channel.h:465
struct ast_party_dialed::@208 number
Dialed/Called number.
char * str
Subscriber phone number (Malloced)
Definition: channel.h:386
int transit_network_select
Transit Network Select.
Definition: channel.h:397
struct ast_party_name name
Subscriber name.
Definition: channel.h:340
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:279
char * str
Subscriber name (Malloced)
Definition: channel.h:264
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
Redirecting Line information. RDNIS (Redirecting Directory Number Information Service) Where a call d...
Definition: channel.h:522
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
Definition: channel.h:527
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:1545
const char * name
Definition: app_queue.c:1546
unsigned int autopauseunavail
Definition: app_queue.c:1849
int talktime
Definition: app_queue.c:1861
const ast_string_field sound_thereare
Definition: app_queue.c:1829
unsigned int setinterfacevar
Definition: app_queue.c:1835
struct call_queue::@53 list
struct ast_str * sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS]
Definition: app_queue.c:1831
const ast_string_field sound_callerannounce
Definition: app_queue.c:1829
int announcefrequency
Definition: app_queue.c:1853
const ast_string_field sound_reporthold
Definition: app_queue.c:1829
unsigned int announceholdtime
Definition: app_queue.c:1841
const ast_string_field sound_holdtime
Definition: app_queue.c:1829
unsigned int dead
Definition: app_queue.c:1832
unsigned int reportholdtime
Definition: app_queue.c:1838
unsigned int setqueueentryvar
Definition: app_queue.c:1837
const ast_string_field sound_seconds
Definition: app_queue.c:1829
unsigned int timeoutrestart
Definition: app_queue.c:1840
struct ao2_container * members
Definition: app_queue.c:1887
int periodicannouncefrequency
Definition: app_queue.c:1856
const ast_string_field sound_thanks
Definition: app_queue.c:1829
unsigned int announceposition_only_up
Definition: app_queue.c:1843
unsigned int setqueuevar
Definition: app_queue.c:1836
int announcepositionlimit
Definition: app_queue.c:1852
unsigned int announce_to_first_user
Definition: app_queue.c:1834
int randomperiodicannounce
Definition: app_queue.c:1858
int autopause
Definition: app_queue.c:1876
int periodicannouncestartdelay
Definition: app_queue.c:1855
const ast_string_field defaultrule
Definition: app_queue.c:1829
int log_restricted_caller_id
Definition: app_queue.c:1885
struct queue_ent * head
Definition: app_queue.c:1888
struct call_queue::@54 rules
unsigned int realtime
Definition: app_queue.c:1845
const ast_string_field queue_quantity2
Definition: app_queue.c:1829
int servicelevel
Definition: app_queue.c:1865
const ast_string_field moh
Definition: app_queue.c:1829
int autofill
Definition: app_queue.c:1883
int minannouncefrequency
Definition: app_queue.c:1854
enum empty_conditions leavewhenempty
Definition: app_queue.c:1851
int penaltymemberslimit
Definition: app_queue.c:1871
unsigned int found
Definition: app_queue.c:1846
const ast_string_field context
Definition: app_queue.c:1829
const ast_string_field sound_calls
Definition: app_queue.c:1829
unsigned int ringinuse
Definition: app_queue.c:1833
int callsabandoned
Definition: app_queue.c:1863
int roundingseconds
Definition: app_queue.c:1859
int numperiodicannounce
Definition: app_queue.c:1857
int wrapuptime
Definition: app_queue.c:1870
const ast_string_field sound_minute
Definition: app_queue.c:1829
int callscompleted
Definition: app_queue.c:1862
int callsabandonedinsl
Definition: app_queue.c:1864
const ast_string_field sound_minutes
Definition: app_queue.c:1829
unsigned int announceposition
Definition: app_queue.c:1842
const ast_string_field queue_quantity1
Definition: app_queue.c:1829
const ast_string_field membergosub
Definition: app_queue.c:1829
char monfmt[8]
Definition: app_queue.c:1867
enum empty_conditions joinempty
Definition: app_queue.c:1850
unsigned int wrapped
Definition: app_queue.c:1839
int strategy
Definition: app_queue.c:1844
const ast_string_field name
Definition: app_queue.c:1829
int callscompletedinsl
Definition: app_queue.c:1866
int memberdelay
Definition: app_queue.c:1882
const ast_string_field sound_next
Definition: app_queue.c:1829
unsigned int autopausebusy
Definition: app_queue.c:1848
int holdtime
Definition: app_queue.c:1860
const ast_string_field announce
Definition: app_queue.c:1829
int autopausedelay
Definition: app_queue.c:1877
int timeoutpriority
Definition: app_queue.c:1878
unsigned int relativeperiodicannounce
Definition: app_queue.c:1847
We define a custom "local user" structure because we use it not only for keeping track of what is in ...
Definition: app_queue.c:1669
unsigned int dial_callerid_absent
Definition: app_queue.c:1683
unsigned int block_connected_update
Definition: app_queue.c:1681
struct ast_aoc_decoded * aoc_s_rate_list
Definition: app_queue.c:1686
struct ast_party_connected_line connected
Definition: app_queue.c:1677
char interface[256]
Definition: app_queue.c:1673
char * orig_chan_name
Definition: app_queue.c:1688
struct callattempt * call_next
Definition: app_queue.c:1671
struct ast_channel * chan
Definition: app_queue.c:1672
unsigned int stillgoing
Definition: app_queue.c:1685
struct callattempt * q_next
Definition: app_queue.c:1670
struct member * member
Definition: app_queue.c:1675
unsigned int pending_connected_update
Definition: app_queue.c:1679
Definition: search.h:40
char * key
Definition: search.h:41
Definition: astman.c:222
structure to hold extensions
Structure representing relevant data during a local channel optimization.
Definition: app_queue.c:6198
const char * source_chan_uniqueid
Definition: app_queue.c:6200
unsigned int id
Definition: app_queue.c:6204
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:1785
Channel datastore data for max forwards.
Definition: max_forwards.c:29
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
unsigned int dead
Definition: app_queue.c:1748
unsigned int delme
Definition: app_queue.c:1749
int queuepos
Definition: app_queue.c:1740
time_t starttime
Definition: app_queue.c:1743
time_t lastcall
Definition: app_queue.c:1744
int dynamic
Definition: app_queue.c:1735
time_t logintime
Definition: app_queue.c:1746
char membername[80]
Definition: app_queue.c:1732
char rt_uniqueid[80]
Definition: app_queue.c:1750
int calls
Definition: app_queue.c:1734
int status
Definition: app_queue.c:1737
int penalty
Definition: app_queue.c:1733
int paused
Definition: app_queue.c:1738
unsigned int ringinuse
Definition: app_queue.c:1751
int wrapuptime
Definition: app_queue.c:1742
int callcompletedinsl
Definition: app_queue.c:1741
char state_exten[AST_MAX_EXTENSION]
Definition: app_queue.c:1728
char state_context[AST_MAX_CONTEXT]
Definition: app_queue.c:1729
int realtime
Definition: app_queue.c:1736
int state_id
Definition: app_queue.c:1731
time_t lastpause
Definition: app_queue.c:1745
char reason_paused[80]
Definition: app_queue.c:1739
struct call_queue * lastqueue
Definition: app_queue.c:1747
char state_interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1730
Number structure.
Definition: app_followme.c:154
int raise_relative
Definition: app_queue.c:1782
struct penalty_rule::@52 list
int min_relative
Definition: app_queue.c:1781
int max_relative
Definition: app_queue.c:1780
struct call_queue * q
Definition: app_queue.c:6823
struct ast_channel * chan
Definition: app_queue.c:6824
char digits[AST_MAX_EXTENSION]
Definition: app_queue.c:1697
int valid_digits
Definition: app_queue.c:1699
time_t last_pos
Definition: app_queue.c:1706
time_t last_periodic_announce_time
Definition: app_queue.c:1704
time_t expire
Definition: app_queue.c:1716
unsigned int withdraw
Definition: app_queue.c:1718
struct penalty_rule * pr
Definition: app_queue.c:1722
int linpos
Definition: app_queue.c:1713
int max_penalty
Definition: app_queue.c:1710
int pending
Definition: app_queue.c:1709
int raise_penalty
Definition: app_queue.c:1712
struct ast_channel * chan
Definition: app_queue.c:1720
int min_penalty
Definition: app_queue.c:1711
int ring_when_ringing
Definition: app_queue.c:1703
int cancel_answered_elsewhere
Definition: app_queue.c:1717
struct queue_ent::@51 qe_rules
char announce[PATH_MAX]
Definition: app_queue.c:1695
char * withdraw_info
Definition: app_queue.c:1719
int handled
Definition: app_queue.c:1708
int linwrapped
Definition: app_queue.c:1714
char moh[MAX_MUSICCLASS]
Definition: app_queue.c:1694
int last_pos_said
Definition: app_queue.c:1702
char context[AST_MAX_CONTEXT]
Definition: app_queue.c:1696
int last_periodic_announce_sound
Definition: app_queue.c:1705
const char * predial_callee
Definition: app_queue.c:1698
time_t start
Definition: app_queue.c:1715
struct call_queue * parent
Definition: app_queue.c:1693
struct queue_ent * next
Definition: app_queue.c:1723
User data for stasis subscriptions used for queue calls.
Definition: app_queue.c:6222
const ast_string_field caller_uniqueid
Definition: app_queue.c:6230
const ast_string_field member_uniqueid
Definition: app_queue.c:6230
struct local_optimization member_optimize
Definition: app_queue.c:6252
struct stasis_message_router * channel_router
Definition: app_queue.c:6248
struct call_queue * queue
Definition: app_queue.c:6232
const ast_string_field bridge_uniqueid
Definition: app_queue.c:6230
struct stasis_message_router * bridge_router
Definition: app_queue.c:6246
struct local_optimization caller_optimize
Definition: app_queue.c:6250
struct member * member
Definition: app_queue.c:6234
struct rule_list::@55 rules
char name[80]
Definition: app_queue.c:1894
struct rule_list::@56 list
Forwarding information.
Definition: stasis.c:1531
const char * name
Definition: app_queue.c:1531
int strategy
Definition: app_queue.c:1530
Definition: ast_expr2.c:325
int value
Definition: syslog.c:37
An API for managing task processing threads that can be shared across modules.
Handy terminal functions for vt* terms.
const char * ast_term_reset(void)
Returns the terminal reset code.
Definition: term.c:357
#define COLOR_CYAN
Definition: term.h:62
#define COLOR_MAGENTA
Definition: term.h:60
#define COLOR_BROWN
Definition: term.h:56
const char * ast_term_color(int fgcolor, int bgcolor)
Return a color sequence string.
Definition: term.c:341
#define COLOR_BLACK
Definition: term.h:50
#define COLOR_RED
Definition: term.h:52
#define COLOR_GREEN
Definition: term.h:54
const char * args
static struct test_options options
static struct test_val a
static struct test_val c
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Support for translation of data formats. translate.c.
FILE * out
Definition: utils/frame.c:33
FILE * in
Definition: utils/frame.c:33
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
long int ast_random(void)
Definition: utils.c:2312
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition: utils.c:2343
#define AST_FLAGS_ALL
Definition: utils.h:196