2002-09-02 23:15:40 +00:00
/*
2005-09-15 15:44:26 +00:00
* Asterisk - - An open source telephony toolkit .
2002-09-02 23:15:40 +00:00
*
2006-02-12 04:28:58 +00:00
* Copyright ( C ) 1999 - 2006 , Digium , Inc .
2002-09-02 23:15:40 +00:00
*
2004-10-02 00:58:31 +00:00
* Mark Spencer < markster @ digium . com >
2002-09-02 23:15:40 +00:00
*
2005-09-15 15:44:26 +00:00
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
* This program is free software , distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-15 15:44:26 +00:00
*
2005-10-24 20:12:06 +00:00
* \ brief True call queues with optional send URL on answer
2005-09-15 15:44:26 +00:00
*
2005-12-30 21:18:06 +00:00
* \ author Mark Spencer < markster @ digium . com >
*
2005-11-06 15:09:47 +00:00
* \ arg Config in \ ref Config_qu queues . conf
2006-07-14 05:42:06 +00:00
*
2005-11-06 15:09:47 +00:00
* \ par Development notes
2005-10-24 20:12:06 +00:00
* \ note 2004 - 11 - 25 : Persistent Dynamic Members added by :
2004-12-06 05:54:16 +00:00
* NetNation Communications ( www . netnation . com )
* Kevin Lindsay < kevinl @ netnation . com >
2006-07-14 05:42:06 +00:00
*
2004-12-06 05:54:16 +00:00
* Each dynamic agent in each queue is now stored in the astdb .
* When asterisk is restarted , each agent will be automatically
* readded into their recorded queues . This feature can be
2005-03-28 20:48:24 +00:00
* configured with the ' persistent_members = < 1 | 0 > ' setting in the
* ' [ general ] ' category in queues . conf . The default is on .
2006-07-14 05:42:06 +00:00
*
2005-10-24 20:12:06 +00:00
* \ note 2004 - 06 - 04 : Priorities in queues added by inAccess Networks ( work funded by Hellas On Line ( HOL ) www . hol . gr ) .
2004-06-23 20:05:18 +00:00
*
2005-10-24 20:12:06 +00:00
* \ note These features added by David C . Troy < dave @ toad . net > :
2004-03-13 06:00:41 +00:00
* - Per - queue holdtime calculation
* - Estimated holdtime announcement
* - Position announcement
* - Abandoned / completed call counters
* - Failout timer passed as optional app parameter
* - Optional monitoring of calls , started when call is answered
*
* Patch Version 1.07 2003 - 12 - 24 01
*
* Added servicelevel statistic by Michiel Betel < michiel @ betel . nl >
2004-04-26 06:57:15 +00:00
* Added Priority jumping code for adding and removing queue members by Jonathan Stanton < asterisk @ doilooklikeicare . com >
2004-03-13 06:00:41 +00:00
*
2004-12-06 05:54:16 +00:00
* Fixed to work with CVS as of 2004 - 02 - 25 and released as 1.07 a
2004-03-13 06:00:41 +00:00
* by Matthew Enger < m . enger @ xi . com . au >
*
2005-11-06 15:09:47 +00:00
* \ ingroup applications
2002-09-02 23:15:40 +00:00
*/
2007-12-17 21:12:24 +00:00
/*** MODULEINFO
2011-07-05 22:11:40 +00:00
< use type = " module " > res_monitor < / use >
2011-07-14 20:28:54 +00:00
< support_level > core < / support_level >
2007-12-17 21:12:24 +00:00
* * */
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2005-06-06 22:39:32 +00:00
# include <sys/time.h>
# include <sys/signal.h>
# include <netinet/in.h>
2008-11-14 16:53:38 +00:00
# include <ctype.h>
2005-06-06 22:39:32 +00:00
2005-04-21 06:02:45 +00:00
# include "asterisk/lock.h"
# include "asterisk/file.h"
# include "asterisk/channel.h"
# include "asterisk/pbx.h"
2005-11-08 04:48:00 +00:00
# include "asterisk/app.h"
# include "asterisk/linkedlists.h"
2005-04-21 06:02:45 +00:00
# include "asterisk/module.h"
# include "asterisk/translate.h"
# include "asterisk/say.h"
# include "asterisk/features.h"
# include "asterisk/musiconhold.h"
# include "asterisk/cli.h"
# include "asterisk/manager.h"
# include "asterisk/config.h"
# include "asterisk/monitor.h"
# include "asterisk/utils.h"
# include "asterisk/causes.h"
# include "asterisk/astdb.h"
2005-07-08 21:14:34 +00:00
# include "asterisk/devicestate.h"
2006-02-01 23:05:28 +00:00
# include "asterisk/stringfields.h"
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
# include "asterisk/event.h"
2007-09-10 18:32:59 +00:00
# include "asterisk/astobj2.h"
2007-11-14 01:35:28 +00:00
# include "asterisk/strings.h"
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
# include "asterisk/global_datastores.h"
2008-05-03 03:57:42 +00:00
# include "asterisk/taskprocessor.h"
Generic Advice of Charge.
Asterisk Generic AOC Representation
- Generic AOC encode/decode routines.
(Generic AOC must be encoded to be passed on the wire in the AST_CONTROL_AOC frame)
- AST_CONTROL_AOC frame type to represent generic encoded AOC data
- Manager events for AOC-S, AOC-D, and AOC-E messages
Asterisk App Support
- app_dial AOC-S pass-through support on call setup
- app_queue AOC-S pass-through support on call setup
AOC Unit Tests
- AOC Unit Tests for encode/decode routines
- AOC Unit Test for manager event representation.
SIP AOC Support
- Pass-through of generic AOC-D and AOC-E messages to snom phones via the
snom AOC specification.
- Creation of chan_sip page3 flags for the addition of the new
'snom_aoc_enabled' sip.conf option.
IAX AOC Support
- Natively supports AOC pass-through through the use of the new
AST_CONTROL_AOC frame type
DAHDI AOC Support
- ETSI PRI full AOC Pass-through support
- 'aoc_enable' chan_dahdi.conf option for independently enabling
pass-through of AOC-S, AOC-D, AOC-E.
- 'aoce_delayhangup' option for retrieving AOC-E on disconnect.
- DAHDI A() dial string option for requesting AOC services.
example usage:
;requests AOC-S, AOC-D, and AOC-E on call setup
exten=>1111,1,Dial(DAHDI/g1/1112/A(s,d,e))
Review: https://reviewboard.asterisk.org/r/552/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@267096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-06-02 18:10:15 +00:00
# include "asterisk/aoc.h"
2009-04-03 22:41:46 +00:00
# include "asterisk/callerid.h"
2009-06-26 15:28:53 +00:00
# include "asterisk/cel.h"
2010-04-22 18:07:02 +00:00
# include "asterisk/data.h"
2004-02-12 22:11:02 +00:00
2009-11-24 20:31:28 +00:00
/* Define, to debug reference counts on queues, without debugging reference counts on queue members */
/* #define REF_DEBUG_ONLY_QUEUES */
2008-03-10 15:45:13 +00:00
/*!
* \ par Please read before modifying this file .
2008-02-08 18:54:53 +00:00
* There are three locks which are regularly used
* throughout this file , the queue list lock , the lock
* for each individual queue , and the interface list lock .
* Please be extra careful to always lock in the following order
* 1 ) queue list lock
* 2 ) individual queue lock
* 3 ) interface list lock
* This order has sort of " evolved " over the lifetime of this
* application , but it is now in place this way , so please adhere
* to this order !
*/
2008-11-01 21:10:07 +00:00
/*** DOCUMENTATION
< application name = " Queue " language = " en_US " >
< synopsis >
Queue a call for a call queue .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< parameter name = " options " >
< optionlist >
2009-01-28 14:37:16 +00:00
< option name = " C " >
< para > Mark all calls as " answered elsewhere " when cancelled . < / para >
< / option >
2008-11-01 21:10:07 +00:00
< option name = " c " >
< para > Continue in the dialplan if the callee hangs up . < / para >
< / option >
< option name = " d " >
< para > data - quality ( modem ) call ( minimum delay ) . < / para >
< / option >
2012-03-02 16:57:12 +00:00
< option name = " F " argsep = " ^ " >
< argument name = " context " required = " false " / >
< argument name = " exten " required = " false " / >
< argument name = " priority " required = " true " / >
< para > When the caller hangs up , transfer the < emphasis > called member < / emphasis >
to the specified destination and < emphasis > start < / emphasis > execution at that location . < / para >
< note >
< para > Any channel variables you want the called channel to inherit from the caller channel must be
prefixed with one or two underbars ( ' _ ' ) . < / para >
< / note >
< / option >
< option name = " F " >
< para > When the caller hangs up , transfer the < emphasis > called member < / emphasis > to the next priority of
the current extension and < emphasis > start < / emphasis > execution at that location . < / para >
< note >
< para > Any channel variables you want the called channel to inherit from the caller channel must be
prefixed with one or two underbars ( ' _ ' ) . < / para >
< / note >
< note >
< para > Using this option from a Macro ( ) or GoSub ( ) might not make sense as there would be no return points . < / para >
< / note >
< / option >
2008-11-01 21:10:07 +00:00
< option name = " h " >
< para > Allow < emphasis > callee < / emphasis > to hang up by pressing < literal > * < / literal > . < / para >
< / option >
< option name = " H " >
< para > Allow < emphasis > caller < / emphasis > to hang up by pressing < literal > * < / literal > . < / para >
< / option >
< option name = " n " >
< para > No retries on the timeout ; will exit this application and
go to the next step . < / para >
< / option >
< option name = " i " >
< para > Ignore call forward requests from queue members and do nothing
when they are requested . < / para >
< / option >
2009-04-03 22:41:46 +00:00
< option name = " I " >
< para > Asterisk will ignore any connected line update requests or any redirecting party
update requests it may receive on this dial attempt . < / para >
< / option >
2008-11-01 21:10:07 +00:00
< option name = " r " >
< para > Ring instead of playing MOH . Periodic Announcements are still made , if applicable . < / para >
< / option >
2009-12-23 18:39:37 +00:00
< option name = " R " >
< para > Ring instead of playing MOH when a member channel is actually ringing . < / para >
< / option >
2008-11-01 21:10:07 +00:00
< option name = " t " >
< para > Allow the < emphasis > called < / emphasis > user to transfer the calling user . < / para >
< / option >
< option name = " T " >
< para > Allow the < emphasis > calling < / emphasis > user to transfer the call . < / para >
< / option >
< option name = " w " >
< para > Allow the < emphasis > called < / emphasis > user to write the conversation to
disk via Monitor . < / para >
< / option >
< option name = " W " >
< para > Allow the < emphasis > calling < / emphasis > user to write the conversation to
disk via Monitor . < / para >
< / option >
< option name = " k " >
< para > Allow the < emphasis > called < / emphasis > party to enable parking of the call by sending
the DTMF sequence defined for call parking in < filename > features . conf < / filename > . < / para >
< / option >
< option name = " K " >
< para > Allow the < emphasis > calling < / emphasis > party to enable parking of the call by sending
the DTMF sequence defined for call parking in < filename > features . conf < / filename > . < / para >
< / option >
< option name = " x " >
< para > Allow the < emphasis > called < / emphasis > user to write the conversation
to disk via MixMonitor . < / para >
< / option >
< option name = " X " >
< para > Allow the < emphasis > calling < / emphasis > user to write the conversation to
disk via MixMonitor . < / para >
< / option >
< / optionlist >
< / parameter >
< parameter name = " URL " >
< para > < replaceable > URL < / replaceable > will be sent to the called party if the channel supports it . < / para >
< / parameter >
< parameter name = " announceoverride " / >
< parameter name = " timeout " >
< para > Will cause the queue to fail out after a specified number of
seconds , checked between each < filename > queues . conf < / filename > < replaceable > timeout < / replaceable > and
< replaceable > retry < / replaceable > cycle . < / para >
< / parameter >
< parameter name = " AGI " >
< para > Will setup an AGI script to be executed on the calling party ' s channel once they are
connected to a queue member . < / para >
< / parameter >
< parameter name = " macro " >
< para > Will run a macro on the calling party ' s channel once they are connected to a queue member . < / para >
< / parameter >
< parameter name = " gosub " >
< para > Will run a gosub on the calling party ' s channel once they are connected to a queue member . < / para >
< / parameter >
< parameter name = " rule " >
< para > Will cause the queue ' s defaultrule to be overridden by the rule specified . < / para >
< / parameter >
2009-04-27 16:37:51 +00:00
< parameter name = " position " >
< para > Attempt to enter the caller into the queue at the numerical position specified . < literal > 1 < / literal >
would attempt to enter the caller at the head of the queue , and < literal > 3 < / literal > would attempt to place
the caller third in the queue . < / para >
2009-04-29 08:56:13 +00:00
< / parameter >
2008-11-01 21:10:07 +00:00
< / syntax >
< description >
< para > In addition to transferring the call , a call may be parked and then picked
up by another user . < / para >
< para > This application will return to the dialplan if the queue does not exist , or
any of the join options cause the caller to not enter the queue . < / para >
2011-09-19 20:23:29 +00:00
< para > This application does not automatically answer and should be preceeded
by an application such as Answer ( ) , Progress ( ) , or Ringing ( ) . < / para >
2008-11-01 21:10:07 +00:00
< para > This application sets the following channel variable upon completion : < / para >
< variablelist >
< variable name = " QUEUESTATUS " >
< para > The status of the call as a text string . < / para >
< value name = " TIMEOUT " / >
< value name = " FULL " / >
< value name = " JOINEMPTY " / >
< value name = " LEAVEEMPTY " / >
< value name = " JOINUNAVAIL " / >
< value name = " LEAVEUNAVAIL " / >
< value name = " CONTINUE " / >
< / variable >
< / variablelist >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
2011-01-30 00:22:59 +00:00
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_EXISTS < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / application >
< application name = " AddQueueMember " language = " en_US " >
< synopsis >
Dynamically adds queue members .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< parameter name = " interface " / >
< parameter name = " penalty " / >
< parameter name = " options " / >
< parameter name = " membername " / >
< parameter name = " stateinterface " / >
< / syntax >
< description >
< para > Dynamically adds interface to an existing queue . If the interface is
already in the queue it will return an error . < / para >
< para > This application sets the following channel variable upon completion : < / para >
< variablelist >
< variable name = " AQMSTATUS " >
< para > The status of the attempt to add a queue member as a text string . < / para >
< value name = " ADDED " / >
< value name = " MEMBERALREADY " / >
< value name = " NOSUCHQUEUE " / >
< / variable >
< / variablelist >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
2011-01-30 00:22:59 +00:00
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / application >
< application name = " RemoveQueueMember " language = " en_US " >
< synopsis >
Dynamically removes queue members .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< parameter name = " interface " / >
< parameter name = " options " / >
< / syntax >
< description >
< para > If the interface is < emphasis > NOT < / emphasis > in the queue it will return an error . < / para >
< para > This application sets the following channel variable upon completion : < / para >
< variablelist >
< variable name = " RQMSTATUS " >
< value name = " REMOVED " / >
< value name = " NOTINQUEUE " / >
< value name = " NOSUCHQUEUE " / >
< / variable >
< / variablelist >
< para > Example : RemoveQueueMember ( techsupport , SIP / 3000 ) < / para >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
< ref type = " application " > Queue < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " application " > QueueLog < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " application " > AddQueueMember < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " application " > RemoveQueueMember < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / application >
< application name = " PauseQueueMember " language = " en_US " >
< synopsis >
Pauses a queue member .
< / synopsis >
< syntax >
< parameter name = " queuename " / >
< parameter name = " interface " required = " true " / >
< parameter name = " options " / >
< parameter name = " reason " >
< para > Is used to add extra information to the appropriate queue_log entries and manager events . < / para >
< / parameter >
< / syntax >
< description >
< para > Pauses ( blocks calls for ) a queue member . The given interface will be paused in the given queue .
This prevents any calls from being sent from the queue to the interface until it is
unpaused with UnpauseQueueMember or the manager interface . If no queuename is given ,
the interface is paused in every queue it is a member of . The application will fail if the
interface is not found . < / para >
< para > This application sets the following channel variable upon completion : < / para >
< variablelist >
< variable name = " PQMSTATUS " >
< para > The status of the attempt to pause a queue member as a text string . < / para >
< value name = " PAUSED " / >
< value name = " NOTFOUND " / >
< / variable >
< / variablelist >
< para > Example : PauseQueueMember ( , SIP / 3000 ) < / para >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
2011-01-30 00:22:59 +00:00
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " application " > UnpauseQueueMember < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / application >
< application name = " UnpauseQueueMember " language = " en_US " >
< synopsis >
Unpauses a queue member .
< / synopsis >
< syntax >
< parameter name = " queuename " / >
< parameter name = " interface " required = " true " / >
< parameter name = " options " / >
< parameter name = " reason " >
< para > Is used to add extra information to the appropriate queue_log entries and manager events . < / para >
< / parameter >
< / syntax >
< description >
< para > Unpauses ( resumes calls to ) a queue member . This is the counterpart to < literal > PauseQueueMember ( ) < / literal >
and operates exactly the same way , except it unpauses instead of pausing the given interface . < / para >
< para > This application sets the following channel variable upon completion : < / para >
< variablelist >
< variable name = " UPQMSTATUS " >
< para > The status of the attempt to unpause a queue member as a text string . < / para >
< value name = " UNPAUSED " / >
< value name = " NOTFOUND " / >
< / variable >
< / variablelist >
< para > Example : UnpauseQueueMember ( , SIP / 3000 ) < / para >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
2011-01-30 00:22:59 +00:00
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " application " > PauseQueueMember < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / application >
< application name = " QueueLog " language = " en_US " >
< synopsis >
Writes to the queue_log file .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< parameter name = " uniqueid " required = " true " / >
< parameter name = " agent " required = " true " / >
< parameter name = " event " required = " true " / >
< parameter name = " additionalinfo " / >
< / syntax >
< description >
< para > Allows you to write your own events into the queue log . < / para >
< para > Example : QueueLog ( 101 , $ { UNIQUEID } , $ { AGENT } , WENTONBREAK , 600 ) < / para >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
< ref type = " application " > Queue < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / application >
< function name = " QUEUE_VARIABLES " language = " en_US " >
< synopsis >
Return Queue information in variables .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " >
< enumlist >
< enum name = " QUEUEMAX " >
< para > Maxmimum number of calls allowed . < / para >
< / enum >
< enum name = " QUEUESTRATEGY " >
< para > The strategy of the queue . < / para >
< / enum >
< enum name = " QUEUECALLS " >
< para > Number of calls currently in the queue . < / para >
< / enum >
< enum name = " QUEUEHOLDTIME " >
< para > Current average hold time . < / para >
< / enum >
< enum name = " QUEUECOMPLETED " >
< para > Number of completed calls for the queue . < / para >
< / enum >
< enum name = " QUEUEABANDONED " >
< para > Number of abandoned calls . < / para >
< / enum >
< enum name = " QUEUESRVLEVEL " >
< para > Queue service level . < / para >
< / enum >
< enum name = " QUEUESRVLEVELPERF " >
< para > Current service level performance . < / para >
< / enum >
< / enumlist >
< / parameter >
< / syntax >
< description >
< para > Makes the following queue variables available . < / para >
< para > Returns < literal > 0 < / literal > if queue is found and setqueuevar is defined , < literal > - 1 < / literal > otherwise . < / para >
< / description >
2011-01-30 00:22:59 +00:00
< see - also >
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
< / see - also >
2008-11-01 21:10:07 +00:00
< / function >
< function name = " QUEUE_MEMBER " language = " en_US " >
< synopsis >
Count number of members answering a queue .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< parameter name = " option " required = " true " >
< enumlist >
< enum name = " logged " >
< para > Returns the number of logged - in members for the specified queue . < / para >
< / enum >
< enum name = " free " >
2009-12-23 19:14:05 +00:00
< 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 >
< / enum >
< enum name = " ready " >
< para > Returns the number of logged - in members for the specified queue that are immediately available to answer a call . < / para >
2008-11-01 21:10:07 +00:00
< / enum >
< enum name = " count " >
< para > Returns the total number of members for the specified queue . < / para >
< / enum >
2011-06-29 06:39:26 +00:00
< enum name = " penalty " >
< para > Gets or sets queue member penalty . < / para >
< / enum >
< enum name = " paused " >
< para > Gets or sets queue member paused status . < / para >
< / enum >
< enum name = " ignorebusy " >
< para > Gets or sets queue member ignorebusy . < / para >
< / enum >
2008-11-01 21:10:07 +00:00
< / enumlist >
< / parameter >
2011-06-29 06:39:26 +00:00
< parameter name = " interface " required = " false " / >
2008-11-01 21:10:07 +00:00
< / syntax >
< description >
2011-06-29 06:39:26 +00:00
< para > Allows access to queue counts [ R ] and member information [ R / W ] . < / para >
< para >
< replaceable > queuename < / replaceable > is required for all operations
< replaceable > interface < / replaceable > is required for all member operations .
< / para >
2008-11-01 21:10:07 +00:00
< / description >
2011-01-30 00:22:59 +00:00
< see - also >
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
< / see - also >
2008-11-01 21:10:07 +00:00
< / function >
< function name = " QUEUE_MEMBER_COUNT " language = " en_US " >
< synopsis >
Count number of members answering a queue .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< / syntax >
< description >
< para > Returns the number of members currently associated with the specified < replaceable > queuename < / replaceable > . < / para >
< warning > < para > This function has been deprecated in favor of the < literal > QUEUE_MEMBER ( ) < / literal > function < / para > < / warning >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
2011-01-30 00:22:59 +00:00
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / function >
2010-07-16 09:25:48 +00:00
< function name = " QUEUE_EXISTS " language = " en_US " >
< synopsis >
Check if a named queue exists on this server
< / synopsis >
< syntax >
< parameter name = " queuename " / >
< / syntax >
< description >
< para > Returns 1 if the specified queue exists , 0 if it does not < / para >
< / description >
2011-01-30 00:22:59 +00:00
< see - also >
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
< / see - also >
2010-07-16 09:25:48 +00:00
< / function >
2008-11-01 21:10:07 +00:00
< function name = " QUEUE_WAITING_COUNT " language = " en_US " >
< synopsis >
Count number of calls currently waiting in a queue .
< / synopsis >
< syntax >
< parameter name = " queuename " / >
< / syntax >
< description >
< para > Returns the number of callers currently waiting in the specified < replaceable > queuename < / replaceable > . < / para >
< / description >
2011-01-30 00:22:59 +00:00
< see - also >
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
< / see - also >
2008-11-01 21:10:07 +00:00
< / function >
< function name = " QUEUE_MEMBER_LIST " language = " en_US " >
< synopsis >
Returns a list of interfaces on a queue .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< / syntax >
< description >
< para > Returns a comma - separated list of members associated with the specified < replaceable > queuename < / replaceable > . < / para >
< / description >
2008-11-05 12:13:57 +00:00
< see - also >
2011-01-30 00:22:59 +00:00
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
2008-11-05 12:13:57 +00:00
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
2011-01-30 00:22:59 +00:00
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
2008-11-05 12:13:57 +00:00
< / see - also >
2008-11-01 21:10:07 +00:00
< / function >
< function name = " QUEUE_MEMBER_PENALTY " language = " en_US " >
< synopsis >
Gets or sets queue members penalty .
< / synopsis >
< syntax >
< parameter name = " queuename " required = " true " / >
< parameter name = " interface " required = " true " / >
< / syntax >
< description >
< para > Gets or sets queue members penalty . < / para >
2011-06-29 06:39:26 +00:00
< warning > < para > This function has been deprecated in favor of the < literal > QUEUE_MEMBER ( ) < / literal > function < / para > < / warning >
2008-11-01 21:10:07 +00:00
< / description >
2011-01-30 00:22:59 +00:00
< see - also >
< ref type = " application " > Queue < / ref >
< ref type = " application " > QueueLog < / ref >
< ref type = " application " > AddQueueMember < / ref >
< ref type = " application " > RemoveQueueMember < / ref >
< ref type = " application " > PauseQueueMember < / ref >
< ref type = " application " > UnpauseQueueMember < / ref >
< ref type = " function " > QUEUE_VARIABLES < / ref >
< ref type = " function " > QUEUE_MEMBER < / ref >
< ref type = " function " > QUEUE_MEMBER_COUNT < / ref >
< ref type = " function " > QUEUE_EXISTS < / ref >
< ref type = " function " > QUEUE_WAITING_COUNT < / ref >
< ref type = " function " > QUEUE_MEMBER_LIST < / ref >
< ref type = " function " > QUEUE_MEMBER_PENALTY < / ref >
< / see - also >
2008-11-01 21:10:07 +00:00
< / function >
2009-05-22 17:52:35 +00:00
< manager name = " Queues " language = " en_US " >
< synopsis >
Queues .
< / synopsis >
< syntax >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueStatus " language = " en_US " >
< synopsis >
Show queue status .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " / >
< parameter name = " Member " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueSummary " language = " en_US " >
< synopsis >
Show queue summary .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueAdd " language = " en_US " >
< synopsis >
Add interface to queue .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " required = " true " / >
< parameter name = " Interface " required = " true " / >
< parameter name = " Penalty " / >
< parameter name = " Paused " / >
< parameter name = " MemberName " / >
< parameter name = " StateInterface " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueRemove " language = " en_US " >
< synopsis >
Remove interface from queue .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " required = " true " / >
< parameter name = " Interface " required = " true " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueuePause " language = " en_US " >
< synopsis >
Makes a queue member temporarily unavailable .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Interface " required = " true " / >
< parameter name = " Paused " required = " true " / >
< parameter name = " Queue " / >
< parameter name = " Reason " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueLog " language = " en_US " >
< synopsis >
Adds custom entry in queue_log .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " required = " true " / >
< parameter name = " Event " required = " true " / >
< parameter name = " Uniqueid " / >
< parameter name = " Interface " / >
< parameter name = " Message " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueuePenalty " language = " en_US " >
< synopsis >
Set the penalty for a queue member .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Interface " required = " true " / >
< parameter name = " Penalty " required = " true " / >
< parameter name = " Queue " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueRule " language = " en_US " >
< synopsis >
Queue Rules .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Rule " / >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueReload " language = " en_US " >
< synopsis >
Reload a queue , queues , or any sub - section of a queue or queues .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " / >
< parameter name = " Members " >
< enumlist >
< enum name = " yes " / >
< enum name = " no " / >
< / enumlist >
< / parameter >
< parameter name = " Rules " >
< enumlist >
< enum name = " yes " / >
< enum name = " no " / >
< / enumlist >
< / parameter >
< parameter name = " Parameters " >
< enumlist >
< enum name = " yes " / >
< enum name = " no " / >
< / enumlist >
< / parameter >
< / syntax >
< description >
< / description >
< / manager >
< manager name = " QueueReset " language = " en_US " >
< synopsis >
Reset queue statistics .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " Queue " / >
< / syntax >
< description >
< / description >
< / manager >
2008-11-01 21:10:07 +00:00
* * */
2012-03-02 16:57:12 +00:00
enum {
OPT_MARK_AS_ANSWERED = ( 1 < < 0 ) ,
OPT_GO_ON = ( 1 < < 1 ) ,
OPT_DATA_QUALITY = ( 1 < < 2 ) ,
OPT_CALLEE_GO_ON = ( 1 < < 3 ) ,
OPT_CALLEE_HANGUP = ( 1 < < 4 ) ,
OPT_CALLER_HANGUP = ( 1 < < 5 ) ,
OPT_IGNORE_CALL_FW = ( 1 < < 6 ) ,
OPT_UPDATE_CONNECTED = ( 1 < < 7 ) ,
OPT_CALLEE_PARK = ( 1 < < 8 ) ,
OPT_CALLER_PARK = ( 1 < < 9 ) ,
OPT_NO_RETRY = ( 1 < < 10 ) ,
OPT_RINGING = ( 1 < < 11 ) ,
OPT_RING_WHEN_RINGING = ( 1 < < 12 ) ,
OPT_CALLEE_TRANSFER = ( 1 < < 13 ) ,
OPT_CALLER_TRANSFER = ( 1 < < 14 ) ,
OPT_CALLEE_AUTOMIXMON = ( 1 < < 15 ) ,
OPT_CALLER_AUTOMIXMON = ( 1 < < 16 ) ,
OPT_CALLEE_AUTOMON = ( 1 < < 17 ) ,
OPT_CALLER_AUTOMON = ( 1 < < 18 ) ,
} ;
enum {
OPT_ARG_CALLEE_GO_ON = 0 ,
/* note: this entry _MUST_ be the last one in the enum */
OPT_ARG_ARRAY_SIZE
} ;
AST_APP_OPTIONS ( queue_exec_options , BEGIN_OPTIONS
AST_APP_OPTION ( ' C ' , OPT_MARK_AS_ANSWERED ) ,
AST_APP_OPTION ( ' c ' , OPT_GO_ON ) ,
AST_APP_OPTION ( ' d ' , OPT_DATA_QUALITY ) ,
AST_APP_OPTION_ARG ( ' F ' , OPT_CALLEE_GO_ON , OPT_ARG_CALLEE_GO_ON ) ,
AST_APP_OPTION ( ' h ' , OPT_CALLEE_HANGUP ) ,
AST_APP_OPTION ( ' H ' , OPT_CALLER_HANGUP ) ,
AST_APP_OPTION ( ' i ' , OPT_IGNORE_CALL_FW ) ,
AST_APP_OPTION ( ' I ' , OPT_UPDATE_CONNECTED ) ,
AST_APP_OPTION ( ' k ' , OPT_CALLEE_PARK ) ,
AST_APP_OPTION ( ' K ' , OPT_CALLER_PARK ) ,
AST_APP_OPTION ( ' n ' , OPT_NO_RETRY ) ,
AST_APP_OPTION ( ' r ' , OPT_RINGING ) ,
AST_APP_OPTION ( ' R ' , OPT_RING_WHEN_RINGING ) ,
AST_APP_OPTION ( ' t ' , OPT_CALLEE_TRANSFER ) ,
AST_APP_OPTION ( ' T ' , OPT_CALLER_TRANSFER ) ,
AST_APP_OPTION ( ' x ' , OPT_CALLEE_AUTOMIXMON ) ,
AST_APP_OPTION ( ' X ' , OPT_CALLER_AUTOMIXMON ) ,
AST_APP_OPTION ( ' w ' , OPT_CALLEE_AUTOMON ) ,
AST_APP_OPTION ( ' W ' , OPT_CALLER_AUTOMON ) ,
END_OPTIONS ) ;
2006-05-09 07:27:03 +00:00
enum {
QUEUE_STRATEGY_RINGALL = 0 ,
QUEUE_STRATEGY_LEASTRECENT ,
QUEUE_STRATEGY_FEWESTCALLS ,
QUEUE_STRATEGY_RANDOM ,
2007-10-26 15:19:46 +00:00
QUEUE_STRATEGY_RRMEMORY ,
2007-11-06 22:36:55 +00:00
QUEUE_STRATEGY_LINEAR ,
2011-01-20 17:14:01 +00:00
QUEUE_STRATEGY_WRANDOM ,
QUEUE_STRATEGY_RRORDERED ,
2006-05-09 07:27:03 +00:00
} ;
2003-07-27 01:55:22 +00:00
2010-05-04 22:46:42 +00:00
enum {
QUEUE_AUTOPAUSE_OFF = 0 ,
QUEUE_AUTOPAUSE_ON ,
QUEUE_AUTOPAUSE_ALL
} ;
2009-02-13 20:57:37 +00:00
enum queue_reload_mask {
QUEUE_RELOAD_PARAMETERS = ( 1 < < 0 ) ,
QUEUE_RELOAD_MEMBER = ( 1 < < 1 ) ,
QUEUE_RELOAD_RULES = ( 1 < < 2 ) ,
QUEUE_RESET_STATS = ( 1 < < 3 ) ,
} ;
2008-03-05 04:34:29 +00:00
static const struct strategy {
2003-07-30 16:10:51 +00:00
int strategy ;
2008-03-05 04:34:29 +00:00
const char * name ;
2003-07-30 16:10:51 +00:00
} strategies [ ] = {
{ QUEUE_STRATEGY_RINGALL , " ringall " } ,
{ QUEUE_STRATEGY_LEASTRECENT , " leastrecent " } ,
{ QUEUE_STRATEGY_FEWESTCALLS , " fewestcalls " } ,
{ QUEUE_STRATEGY_RANDOM , " random " } ,
2004-05-18 05:41:53 +00:00
{ QUEUE_STRATEGY_RRMEMORY , " rrmemory " } ,
2008-07-30 19:17:38 +00:00
{ QUEUE_STRATEGY_RRMEMORY , " roundrobin " } ,
2007-10-26 15:19:46 +00:00
{ QUEUE_STRATEGY_LINEAR , " linear " } ,
2007-11-06 22:36:55 +00:00
{ QUEUE_STRATEGY_WRANDOM , " wrandom " } ,
2011-01-20 17:14:01 +00:00
{ QUEUE_STRATEGY_RRORDERED , " rrordered " } ,
2003-07-30 16:10:51 +00:00
} ;
2010-05-04 22:46:42 +00:00
static const struct autopause {
int autopause ;
const char * name ;
} autopausesmodes [ ] = {
{ QUEUE_AUTOPAUSE_OFF , " no " } ,
{ QUEUE_AUTOPAUSE_ON , " yes " } ,
{ QUEUE_AUTOPAUSE_ALL , " all " } ,
} ;
2008-05-03 03:57:42 +00:00
static struct ast_taskprocessor * devicestate_tps ;
2002-09-02 23:15:40 +00:00
# define DEFAULT_RETRY 5
# define DEFAULT_TIMEOUT 15
2008-03-10 15:45:13 +00:00
# define RECHECK 1 /*!< Recheck every second to see we we're at the top yet */
# define MAX_PERIODIC_ANNOUNCEMENTS 10 /*!< The maximum periodic announcements we can have */
2008-04-25 20:20:10 +00:00
# define DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 / *!< The minimum number of seconds between position announcements \
2008-03-10 15:45:13 +00:00
The default value of 15 provides backwards compatibility */
2007-09-20 21:21:28 +00:00
# define MAX_QUEUE_BUCKETS 53
2002-09-02 23:15:40 +00:00
2008-03-10 15:45:13 +00:00
# define RES_OKAY 0 /*!< Action completed */
# define RES_EXISTS (-1) /*!< Entry already exists */
# define RES_OUTOFMEMORY (-2) /*!< Out of memory */
# define RES_NOSUCHQUEUE (-3) /*!< No such queue */
# define RES_NOT_DYNAMIC (-4) /*!< Member is not dynamic */
2004-07-28 02:55:22 +00:00
2002-09-02 23:15:40 +00:00
static char * app = " Queue " ;
2003-06-27 23:07:59 +00:00
static char * app_aqm = " AddQueueMember " ;
static char * app_rqm = " RemoveQueueMember " ;
2005-01-21 04:14:26 +00:00
static char * app_pqm = " PauseQueueMember " ;
static char * app_upqm = " UnpauseQueueMember " ;
2006-07-16 19:36:29 +00:00
static char * app_ql = " QueueLog " ;
2005-11-06 15:09:47 +00:00
/*! \brief Persistent Members astdb family */
2009-05-12 13:59:35 +00:00
static const char * const pm_family = " Queue/PersistentMembers " ;
2006-01-16 17:37:44 +00:00
/* The maximum length of each persistent member queue database entry */
2006-08-11 17:49:35 +00:00
# define PM_MAX_LEN 8192
2005-03-28 20:48:24 +00:00
2005-11-06 15:09:47 +00:00
/*! \brief queues.conf [general] option */
2004-12-06 05:54:16 +00:00
static int queue_persistent_members = 0 ;
2005-03-28 20:48:24 +00:00
2005-11-06 15:09:47 +00:00
/*! \brief queues.conf per-queue weight option */
2005-01-07 04:05:22 +00:00
static int use_weight = 0 ;
2006-05-03 20:01:30 +00:00
/*! \brief queues.conf [general] option */
2010-09-16 22:00:15 +00:00
static int autofill_default = 1 ;
2006-05-03 20:01:30 +00:00
2006-05-05 22:02:38 +00:00
/*! \brief queues.conf [general] option */
static int montype_default = 0 ;
2007-10-24 21:26:27 +00:00
/*! \brief queues.conf [general] option */
2010-09-16 22:00:15 +00:00
static int shared_lastcall = 1 ;
2007-10-24 21:26:27 +00:00
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
/*! \brief Subscription to device state change events */
static struct ast_event_sub * device_state_sub ;
2007-09-18 16:16:36 +00:00
/*! \brief queues.conf [general] option */
static int update_cdr = 0 ;
2011-06-29 06:39:26 +00:00
/*! \brief queues.conf [general] option */
static int negative_penalty_invalid = 0 ;
2011-08-08 20:28:20 +00:00
/*! \brief queues.conf [general] option */
static int log_membername_as_agent = 0 ;
2011-10-20 17:34:54 +00:00
/*! \brief queues.conf [general] option */
static int check_state_unknown = 0 ;
2005-03-28 20:48:24 +00:00
enum queue_result {
QUEUE_UNKNOWN = 0 ,
QUEUE_TIMEOUT = 1 ,
QUEUE_JOINEMPTY = 2 ,
QUEUE_LEAVEEMPTY = 3 ,
QUEUE_JOINUNAVAIL = 4 ,
QUEUE_LEAVEUNAVAIL = 5 ,
QUEUE_FULL = 6 ,
2007-05-14 17:38:36 +00:00
QUEUE_CONTINUE = 7 ,
2005-03-28 20:48:24 +00:00
} ;
2004-12-06 05:54:16 +00:00
2009-05-21 21:13:09 +00:00
static const struct {
2005-03-28 20:48:24 +00:00
enum queue_result id ;
char * text ;
} queue_results [ ] = {
{ QUEUE_UNKNOWN , " UNKNOWN " } ,
{ QUEUE_TIMEOUT , " TIMEOUT " } ,
{ QUEUE_JOINEMPTY , " JOINEMPTY " } ,
{ QUEUE_LEAVEEMPTY , " LEAVEEMPTY " } ,
{ QUEUE_JOINUNAVAIL , " JOINUNAVAIL " } ,
{ QUEUE_LEAVEUNAVAIL , " LEAVEUNAVAIL " } ,
{ QUEUE_FULL , " FULL " } ,
2007-05-14 17:38:36 +00:00
{ QUEUE_CONTINUE , " CONTINUE " } ,
2005-03-28 20:48:24 +00:00
} ;
2004-12-19 21:44:24 +00:00
2008-07-03 14:34:25 +00:00
enum queue_timeout_priority {
TIMEOUT_PRIORITY_APP ,
TIMEOUT_PRIORITY_CONF ,
} ;
2005-11-06 15:09:47 +00:00
/*! \brief We define a custom "local user" structure because we
2008-03-10 15:45:13 +00:00
* use it not only for keeping track of what is in use but
* also for keeping track of who we ' re dialing .
*
* There are two " links " defined in this structure , q_next and call_next .
* q_next links ALL defined callattempt structures into a linked list . call_next is
* a link which allows for a subset of the callattempts to be traversed . This subset
* is used in wait_for_answer so that irrelevant callattempts are not traversed . This
* also is helpful so that queue logs are always accurate in the case where a call to
* a member times out , especially if using the ringall strategy .
*/
2002-09-02 23:15:40 +00:00
2006-02-14 21:50:35 +00:00
struct callattempt {
struct callattempt * q_next ;
2008-01-02 18:05:57 +00:00
struct callattempt * call_next ;
2002-09-02 23:15:40 +00:00
struct ast_channel * chan ;
2004-12-19 21:30:55 +00:00
char interface [ 256 ] ;
2002-09-02 23:15:40 +00:00
int stillgoing ;
2003-07-27 01:55:22 +00:00
int metric ;
2004-06-26 16:26:39 +00:00
time_t lastcall ;
2007-10-24 21:26:27 +00:00
struct call_queue * lastqueue ;
2003-08-02 21:10:06 +00:00
struct member * member ;
2010-05-20 19:40:03 +00:00
/*! Saved connected party info from an AST_CONTROL_CONNECTED_LINE. */
2009-04-03 22:41:46 +00:00
struct ast_party_connected_line connected ;
2010-05-20 19:40:03 +00:00
/*! TRUE if an AST_CONTROL_CONNECTED_LINE update was saved to the connected element. */
unsigned int pending_connected_update : 1 ;
/*! TRUE if caller id is not available for connected line */
unsigned int dial_callerid_absent : 1 ;
Generic Advice of Charge.
Asterisk Generic AOC Representation
- Generic AOC encode/decode routines.
(Generic AOC must be encoded to be passed on the wire in the AST_CONTROL_AOC frame)
- AST_CONTROL_AOC frame type to represent generic encoded AOC data
- Manager events for AOC-S, AOC-D, and AOC-E messages
Asterisk App Support
- app_dial AOC-S pass-through support on call setup
- app_queue AOC-S pass-through support on call setup
AOC Unit Tests
- AOC Unit Tests for encode/decode routines
- AOC Unit Test for manager event representation.
SIP AOC Support
- Pass-through of generic AOC-D and AOC-E messages to snom phones via the
snom AOC specification.
- Creation of chan_sip page3 flags for the addition of the new
'snom_aoc_enabled' sip.conf option.
IAX AOC Support
- Natively supports AOC pass-through through the use of the new
AST_CONTROL_AOC frame type
DAHDI AOC Support
- ETSI PRI full AOC Pass-through support
- 'aoc_enable' chan_dahdi.conf option for independently enabling
pass-through of AOC-S, AOC-D, AOC-E.
- 'aoce_delayhangup' option for retrieving AOC-E on disconnect.
- DAHDI A() dial string option for requesting AOC services.
example usage:
;requests AOC-S, AOC-D, and AOC-E on call setup
exten=>1111,1,Dial(DAHDI/g1/1112/A(s,d,e))
Review: https://reviewboard.asterisk.org/r/552/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@267096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-06-02 18:10:15 +00:00
struct ast_aoc_decoded * aoc_s_rate_list ;
2002-09-02 23:15:40 +00:00
} ;
2005-11-08 04:48:00 +00:00
2002-09-02 23:15:40 +00:00
struct queue_ent {
2007-12-21 00:44:17 +00:00
struct call_queue * parent ; /*!< What queue is our parent */
char moh [ 80 ] ; /*!< Name of musiconhold to be used */
2011-05-05 19:56:44 +00:00
char announce [ PATH_MAX ] ; /*!< Announcement to play for member when call is answered */
2007-12-21 00:44:17 +00:00
char context [ AST_MAX_CONTEXT ] ; /*!< Context when user exits queue */
char digits [ AST_MAX_EXTENSION ] ; /*!< Digits entered while in queue */
2008-03-10 15:45:13 +00:00
int valid_digits ; /*!< Digits entered correspond to valid extension. Exited */
2007-12-21 00:44:17 +00:00
int pos ; /*!< Where we are in the queue */
int prio ; /*!< Our priority */
int last_pos_said ; /*!< Last position we told the user */
2009-12-23 18:39:37 +00:00
int ring_when_ringing ; /*!< Should we only use ring indication when a channel is ringing? */
2007-12-21 00:44:17 +00:00
time_t last_periodic_announce_time ; /*!< The last time we played a periodic announcement */
int last_periodic_announce_sound ; /*!< The last periodic announcement we made */
time_t last_pos ; /*!< Last time we told the user their position */
int opos ; /*!< Where we started in the queue */
int handled ; /*!< Whether our call was handled */
int pending ; /*!< Non-zero if we are attempting to call a member */
int max_penalty ; /*!< Limit the members that can take this call to this penalty or lower */
int min_penalty ; /*!< Limit the members that can take this call to this penalty or higher */
2008-03-10 15:45:13 +00:00
int linpos ; /*!< If using linear strategy, what position are we at? */
int linwrapped ; /*!< Is the linpos wrapped? */
2007-12-21 00:44:17 +00:00
time_t start ; /*!< When we started holding */
time_t expire ; /*!< When this entry should expire (time out of queue) */
2009-01-29 17:08:22 +00:00
int cancel_answered_elsewhere ; /*!< Whether we should force the CAE flag on this call (C) option*/
2007-12-21 00:44:17 +00:00
struct ast_channel * chan ; /*!< Our channel */
AST_LIST_HEAD_NOLOCK ( , penalty_rule ) qe_rules ; /*!< Local copy of the queue's penalty rules */
struct penalty_rule * pr ; /*!< Pointer to the next penalty rule to implement */
struct queue_ent * next ; /*!< The next queue entry */
2002-09-02 23:15:40 +00:00
} ;
struct member {
2009-11-03 21:16:14 +00:00
char interface [ 80 ] ; /*!< Technology/Location to dial to reach this member*/
char state_exten [ AST_MAX_EXTENSION ] ; /*!< Extension to get state from (if using hint) */
char state_context [ AST_MAX_CONTEXT ] ; /*!< Context to use when getting state (if using hint) */
char state_interface [ 80 ] ; /*!< Technology/Location from which to read devicestate changes */
char membername [ 80 ] ; /*!< Member name to use in queue logs */
int penalty ; /*!< Are we a last resort? */
int calls ; /*!< Number of calls serviced by this member */
int dynamic ; /*!< Are we dynamically added? */
int realtime ; /*!< Is this member realtime? */
int status ; /*!< Status of queue member */
int paused ; /*!< Are we paused (not accepting calls)? */
time_t lastcall ; /*!< When last successful call was hungup */
struct call_queue * lastqueue ; /*!< Last queue we received a call */
unsigned int dead : 1 ; /*!< Used to detect members deleted in realtime */
unsigned int delme : 1 ; /*!< Flag to delete entry on reload */
char rt_uniqueid [ 80 ] ; /*!< Unique id of realtime member entry */
2011-06-29 06:39:26 +00:00
unsigned int ignorebusy : 1 ; /*!< Flag to ignore member if the status is not available */
2002-09-02 23:15:40 +00:00
} ;
2008-10-06 15:29:56 +00:00
enum empty_conditions {
QUEUE_EMPTY_PENALTY = ( 1 < < 0 ) ,
QUEUE_EMPTY_PAUSED = ( 1 < < 1 ) ,
QUEUE_EMPTY_INUSE = ( 1 < < 2 ) ,
QUEUE_EMPTY_RINGING = ( 1 < < 3 ) ,
QUEUE_EMPTY_UNAVAILABLE = ( 1 < < 4 ) ,
QUEUE_EMPTY_INVALID = ( 1 < < 5 ) ,
QUEUE_EMPTY_UNKNOWN = ( 1 < < 6 ) ,
QUEUE_EMPTY_WRAPUP = ( 1 < < 7 ) ,
} ;
2006-06-15 13:35:04 +00:00
/* values used in multi-bit flags in call_queue */
2005-03-28 20:48:24 +00:00
# define ANNOUNCEHOLDTIME_ALWAYS 1
# define ANNOUNCEHOLDTIME_ONCE 2
2006-06-22 15:43:02 +00:00
# define QUEUE_EVENT_VARIABLES 3
2005-03-28 20:48:24 +00:00
2007-12-21 00:44:17 +00:00
struct penalty_rule {
int time ; /*!< Number of seconds that need to pass before applying this rule */
int max_value ; /*!< The amount specified in the penalty rule for max penalty */
int min_value ; /*!< The amount specified in the penalty rule for min penalty */
int max_relative ; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
int min_relative ; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
AST_LIST_ENTRY ( penalty_rule ) list ; /*!< Next penalty_rule */
} ;
2008-04-30 19:30:41 +00:00
# define ANNOUNCEPOSITION_YES 1 /*!< We announce position */
# define ANNOUNCEPOSITION_NO 2 /*!< We don't announce position */
# define ANNOUNCEPOSITION_MORE_THAN 3 /*!< We say "Currently there are more than <limit>" */
# define ANNOUNCEPOSITION_LIMIT 4 /*!< We not announce position more than <limit> */
2006-06-15 13:35:04 +00:00
struct call_queue {
2007-11-14 01:15:26 +00:00
AST_DECLARE_STRING_FIELDS (
/*! Queue name */
AST_STRING_FIELD ( name ) ;
/*! Music on Hold class */
AST_STRING_FIELD ( moh ) ;
/*! Announcement to play when call is answered */
AST_STRING_FIELD ( announce ) ;
/*! Exit context */
AST_STRING_FIELD ( context ) ;
/*! Macro to run upon member connection */
AST_STRING_FIELD ( membermacro ) ;
/*! Gosub to run upon member connection */
AST_STRING_FIELD ( membergosub ) ;
2007-12-21 00:44:17 +00:00
/*! Default rule to use if none specified in call to Queue() */
AST_STRING_FIELD ( defaultrule ) ;
2007-11-14 01:15:26 +00:00
/*! Sound file: "Your call is now first in line" (def. queue-youarenext) */
AST_STRING_FIELD ( sound_next ) ;
/*! Sound file: "There are currently" (def. queue-thereare) */
AST_STRING_FIELD ( sound_thereare ) ;
/*! Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting) */
AST_STRING_FIELD ( sound_calls ) ;
2008-04-30 19:30:41 +00:00
/*! Sound file: "Currently there are more than" (def. queue-quantity1) */
AST_STRING_FIELD ( queue_quantity1 ) ;
/*! Sound file: "callers waiting to speak with a representative" (def. queue-quantity2) */
AST_STRING_FIELD ( queue_quantity2 ) ;
2007-11-14 01:15:26 +00:00
/*! Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
AST_STRING_FIELD ( sound_holdtime ) ;
/*! Sound file: "minutes." (def. queue-minutes) */
AST_STRING_FIELD ( sound_minutes ) ;
2008-02-14 20:46:00 +00:00
/*! Sound file: "minute." (def. queue-minute) */
AST_STRING_FIELD ( sound_minute ) ;
2007-11-14 01:15:26 +00:00
/*! Sound file: "seconds." (def. queue-seconds) */
AST_STRING_FIELD ( sound_seconds ) ;
/*! Sound file: "Thank you for your patience." (def. queue-thankyou) */
AST_STRING_FIELD ( sound_thanks ) ;
/*! Sound file: Custom announce for caller, no default */
AST_STRING_FIELD ( sound_callerannounce ) ;
/*! Sound file: "Hold time" (def. queue-reporthold) */
AST_STRING_FIELD ( sound_reporthold ) ;
) ;
/*! Sound files: Custom announce, no default */
2007-11-14 01:35:28 +00:00
struct ast_str * sound_periodicannounce [ MAX_PERIODIC_ANNOUNCEMENTS ] ;
2006-01-13 17:39:56 +00:00
unsigned int dead : 1 ;
2006-06-22 15:43:02 +00:00
unsigned int eventwhencalled : 2 ;
2006-02-15 02:52:19 +00:00
unsigned int ringinuse : 1 ;
2006-05-05 21:22:45 +00:00
unsigned int setinterfacevar : 1 ;
2006-10-27 18:59:16 +00:00
unsigned int setqueuevar : 1 ;
unsigned int setqueueentryvar : 1 ;
2006-01-13 17:39:56 +00:00
unsigned int reportholdtime : 1 ;
unsigned int wrapped : 1 ;
unsigned int timeoutrestart : 1 ;
unsigned int announceholdtime : 2 ;
2008-04-30 19:30:41 +00:00
unsigned int announceposition : 3 ;
2007-10-17 21:41:06 +00:00
int strategy : 4 ;
2006-01-13 17:39:56 +00:00
unsigned int maskmemberstatus : 1 ;
unsigned int realtime : 1 ;
2007-09-13 16:27:42 +00:00
unsigned int found : 1 ;
2009-11-09 16:28:31 +00:00
unsigned int relativeperiodicannounce : 1 ;
2011-09-28 16:59:11 +00:00
unsigned int autopausebusy : 1 ;
unsigned int autopauseunavail : 1 ;
2008-10-06 15:29:56 +00:00
enum empty_conditions joinempty ;
enum empty_conditions leavewhenempty ;
2008-04-30 19:30:41 +00:00
int announcepositionlimit ; /*!< How many positions we announce? */
2006-07-14 05:42:06 +00:00
int announcefrequency ; /*!< How often to announce their position */
2007-04-27 22:08:54 +00:00
int minannouncefrequency ; /*!< The minimum number of seconds between position announcements (def. 15) */
2006-07-14 05:42:06 +00:00
int periodicannouncefrequency ; /*!< How often to play periodic announcement */
2008-03-18 18:58:42 +00:00
int numperiodicannounce ; /*!< The number of periodic announcements configured */
int randomperiodicannounce ; /*!< Are periodic announcments randomly chosen */
2006-07-14 05:42:06 +00:00
int roundingseconds ; /*!< How many seconds do we round to? */
2008-06-17 15:57:43 +00:00
int holdtime ; /*!< Current avg holdtime, based on an exponential average */
2009-01-08 19:48:42 +00:00
int talktime ; /*!< Current avg talktime, based on the same exponential average */
2006-07-14 05:42:06 +00:00
int callscompleted ; /*!< Number of queue calls completed */
int callsabandoned ; /*!< Number of queue calls abandoned */
int servicelevel ; /*!< seconds setting for servicelevel*/
int callscompletedinsl ; /*!< Number of calls answered with servicelevel*/
char monfmt [ 8 ] ; /*!< Format to use when recording calls */
int montype ; /*!< Monitor type Monitor vs. MixMonitor */
int count ; /*!< How many entries */
int maxlen ; /*!< Max number of entries */
int wrapuptime ; /*!< Wrapup Time */
2009-12-04 18:48:31 +00:00
int penaltymemberslimit ; /*!< Disregard penalty when queue has fewer than this many members */
2006-07-14 05:42:06 +00:00
int retry ; /*!< Retry calling everyone after this amount of time */
int timeout ; /*!< How long to wait for an answer */
int weight ; /*!< Respective weight */
int autopause ; /*!< Auto pause queue members if they fail to answer */
2011-06-29 06:39:26 +00:00
int autopausedelay ; /*!< Delay auto pause for autopausedelay seconds since last call */
2008-07-03 14:34:25 +00:00
int timeoutpriority ; /*!< Do we allow a fraction of the timeout to occur for a ring? */
2006-01-13 17:39:56 +00:00
2003-07-30 16:10:51 +00:00
/* Queue strategy things */
2006-07-14 05:42:06 +00:00
int rrpos ; /*!< Round Robin - position */
int memberdelay ; /*!< Seconds to delay connecting member to caller */
int autofill ; /*!< Ignore the head call status and ring an available agent */
2006-01-13 17:39:56 +00:00
2007-09-10 18:32:59 +00:00
struct ao2_container * members ; /*!< Head of the list of members */
2006-07-14 05:42:06 +00:00
struct queue_ent * head ; /*!< Head of the list of callers */
AST_LIST_ENTRY ( call_queue ) list ; /*!< Next call queue */
2007-12-21 00:44:17 +00:00
AST_LIST_HEAD_NOLOCK ( , penalty_rule ) rules ; /*!< The list of penalty rules to invoke */
2002-09-02 23:15:40 +00:00
} ;
2007-12-21 00:44:17 +00:00
struct rule_list {
char name [ 80 ] ;
AST_LIST_HEAD_NOLOCK ( , penalty_rule ) rules ;
AST_LIST_ENTRY ( rule_list ) list ;
} ;
2009-06-15 19:10:10 +00:00
static AST_LIST_HEAD_STATIC ( rule_lists , rule_list ) ;
2007-12-21 00:44:17 +00:00
2007-09-20 21:21:28 +00:00
static struct ao2_container * queues ;
2002-09-02 23:15:40 +00:00
2007-08-13 15:39:48 +00:00
static void update_realtime_members ( struct call_queue * q ) ;
2011-06-29 06:39:26 +00:00
static struct member * interface_exists ( struct call_queue * q , const char * interface ) ;
2007-09-07 16:13:57 +00:00
static int set_member_paused ( const char * queuename , const char * interface , const char * reason , int paused ) ;
2006-01-13 17:39:56 +00:00
2011-06-29 06:39:26 +00:00
static void queue_transfer_fixup ( void * data , struct ast_channel * old_chan , struct ast_channel * new_chan ) ;
2011-08-08 20:28:20 +00:00
static struct member * find_member_by_queuename_and_interface ( const char * queuename , const char * interface ) ;
2008-01-09 18:53:29 +00:00
/*! \brief sets the QUEUESTATUS channel variable */
2005-03-28 20:48:24 +00:00
static void set_queue_result ( struct ast_channel * chan , enum queue_result res )
{
int i ;
2008-03-05 04:34:29 +00:00
for ( i = 0 ; i < ARRAY_LEN ( queue_results ) ; i + + ) {
2005-03-28 20:48:24 +00:00
if ( queue_results [ i ] . id = = res ) {
pbx_builtin_setvar_helper ( chan , " QUEUESTATUS " , queue_results [ i ] . text ) ;
return ;
}
}
}
2005-01-03 01:42:37 +00:00
2012-03-02 16:57:12 +00:00
/*!
* \ internal
* \ brief Converts delimited ' ^ ' characters in a target priority / extension / context string
* to commas so that they can be used with ast_parseable_goto .
* \ param s string that ' ^ ' characters are being replaced in .
*/
static void replace_macro_delimiter ( char * s )
{
for ( ; * s ; s + + ) {
if ( * s = = ' ^ ' ) {
* s = ' , ' ;
}
}
}
2008-03-05 04:34:29 +00:00
static const char * int2strat ( int strategy )
2003-07-30 16:10:51 +00:00
{
int x ;
2006-06-14 23:20:08 +00:00
2008-03-05 04:34:29 +00:00
for ( x = 0 ; x < ARRAY_LEN ( strategies ) ; x + + ) {
2003-07-30 16:10:51 +00:00
if ( strategy = = strategies [ x ] . strategy )
return strategies [ x ] . name ;
}
2006-06-14 23:20:08 +00:00
2003-07-30 16:10:51 +00:00
return " <unknown> " ;
}
2005-06-02 21:42:49 +00:00
static int strat2int ( const char * strategy )
2003-07-30 16:10:51 +00:00
{
int x ;
2006-06-14 23:20:08 +00:00
2008-03-05 04:34:29 +00:00
for ( x = 0 ; x < ARRAY_LEN ( strategies ) ; x + + ) {
2003-07-30 16:10:51 +00:00
if ( ! strcasecmp ( strategy , strategies [ x ] . name ) )
return strategies [ x ] . strategy ;
}
2006-06-14 23:20:08 +00:00
2003-07-30 16:10:51 +00:00
return - 1 ;
}
2002-09-02 23:15:40 +00:00
2010-05-04 22:46:42 +00:00
static int autopause2int ( const char * autopause )
{
int x ;
/*This 'double check' that default value is OFF */
if ( ast_strlen_zero ( autopause ) )
return QUEUE_AUTOPAUSE_OFF ;
/*This 'double check' is to ensure old values works */
if ( ast_true ( autopause ) )
return QUEUE_AUTOPAUSE_ON ;
for ( x = 0 ; x < ARRAY_LEN ( autopausesmodes ) ; x + + ) {
if ( ! strcasecmp ( autopause , autopausesmodes [ x ] . name ) )
return autopausesmodes [ x ] . autopause ;
}
/*This 'double check' that default value is OFF */
return QUEUE_AUTOPAUSE_OFF ;
}
2007-09-20 21:21:28 +00:00
static int queue_hash_cb ( const void * obj , const int flags )
{
const struct call_queue * q = obj ;
2008-11-14 16:53:38 +00:00
return ast_str_case_hash ( q - > name ) ;
2007-09-20 21:21:28 +00:00
}
2008-11-25 01:01:49 +00:00
static int queue_cmp_cb ( void * obj , void * arg , int flags )
2007-09-20 21:21:28 +00:00
{
struct call_queue * q = obj , * q2 = arg ;
2008-08-29 17:47:17 +00:00
return ! strcasecmp ( q - > name , q2 - > name ) ? CMP_MATCH | CMP_STOP : 0 ;
2007-09-20 21:21:28 +00:00
}
2009-11-24 20:31:28 +00:00
# ifdef REF_DEBUG_ONLY_QUEUES
2012-02-28 20:34:11 +00:00
# define queue_ref(q) _queue_ref(q, "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
# define queue_unref(q) _queue_unref(q, "", __FILE__, __LINE__, __PRETTY_FUNCTION__)
# define queue_t_ref(q, tag) _queue_ref(q, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__)
# define queue_t_unref(q, tag) _queue_unref(q, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__)
# define queues_t_link(c, q, tag) __ao2_link_debug(c, q, 0, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__)
# define queues_t_unlink(c, q, tag) __ao2_unlink_debug(c, q, 0, tag, __FILE__, __LINE__, __PRETTY_FUNCTION__)
static inline struct call_queue * _queue_ref ( struct call_queue * q , const char * tag , const char * file , int line , const char * filename )
{
__ao2_ref_debug ( q , 1 , tag , file , line , filename ) ;
return q ;
}
static inline struct call_queue * _queue_unref ( struct call_queue * q , const char * tag , const char * file , int line , const char * filename )
{
__ao2_ref_debug ( q , - 1 , tag , file , line , filename ) ;
return NULL ;
}
2009-11-24 20:31:28 +00:00
# else
2012-02-28 20:34:11 +00:00
# define queue_t_ref(q, tag) queue_ref(q)
# define queue_t_unref(q, tag) queue_unref(q)
# define queues_t_link(c, q, tag) ao2_t_link(c, q, tag)
# define queues_t_unlink(c, q, tag) ao2_t_unlink(c, q, tag)
2007-09-20 21:21:28 +00:00
static inline struct call_queue * queue_ref ( struct call_queue * q )
{
ao2_ref ( q , 1 ) ;
return q ;
}
static inline struct call_queue * queue_unref ( struct call_queue * q )
{
ao2_ref ( q , - 1 ) ;
2011-08-22 20:01:30 +00:00
return NULL ;
2007-09-20 21:21:28 +00:00
}
2009-11-24 20:31:28 +00:00
# endif
2007-09-20 21:21:28 +00:00
2008-03-10 15:45:13 +00:00
/*! \brief Set variables of queue */
2009-01-27 19:30:54 +00:00
static void set_queue_variables ( struct call_queue * q , struct ast_channel * chan )
2006-10-27 18:59:16 +00:00
{
char interfacevar [ 256 ] = " " ;
float sl = 0 ;
2008-04-25 20:20:10 +00:00
2010-11-19 21:42:10 +00:00
ao2_lock ( q ) ;
2009-01-27 19:30:54 +00:00
if ( q - > setqueuevar ) {
2008-02-11 03:03:58 +00:00
sl = 0 ;
2009-01-27 19:30:54 +00:00
if ( q - > callscompleted > 0 )
sl = 100 * ( ( float ) q - > callscompletedinsl / ( float ) q - > callscompleted ) ;
2008-02-11 03:03:58 +00:00
2008-03-04 23:10:45 +00:00
snprintf ( interfacevar , sizeof ( interfacevar ) ,
2009-01-08 19:48:42 +00:00
" QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f " ,
2009-01-27 19:30:54 +00:00
q - > name , q - > maxlen , int2strat ( q - > strategy ) , q - > count , q - > holdtime , q - > talktime , q - > callscompleted , q - > callsabandoned , q - > servicelevel , sl ) ;
2010-11-19 21:42:10 +00:00
ao2_unlock ( q ) ;
2006-10-27 18:59:16 +00:00
2009-01-27 19:30:54 +00:00
pbx_builtin_setvar_multiple ( chan , interfacevar ) ;
2010-11-19 21:42:10 +00:00
} else {
ao2_unlock ( q ) ;
2006-10-27 18:59:16 +00:00
}
}
2005-11-06 15:09:47 +00:00
/*! \brief Insert the 'new' entry after the 'prev' entry of queue 'q' */
2006-06-15 13:35:04 +00:00
static inline void insert_entry ( struct call_queue * q , struct queue_ent * prev , struct queue_ent * new , int * pos )
2004-06-23 20:05:18 +00:00
{
struct queue_ent * cur ;
if ( ! q | | ! new )
return ;
if ( prev ) {
cur = prev - > next ;
prev - > next = new ;
} else {
cur = q - > head ;
q - > head = new ;
}
new - > next = cur ;
2009-11-30 18:55:07 +00:00
/* every queue_ent must have a reference to it's parent call_queue, this
* reference does not go away until the end of the queue_ent ' s life , meaning
* that even when the queue_ent leaves the call_queue this ref must remain . */
queue_ref ( q ) ;
2004-06-23 20:05:18 +00:00
new - > parent = q ;
new - > pos = + + ( * pos ) ;
new - > opos = * pos ;
}
2008-01-09 18:53:29 +00:00
/*! \brief Check if members are available
*
* This function checks to see if members are available to be called . If any member
2008-10-06 15:29:56 +00:00
* is available , the function immediately returns 0. If no members are available ,
* then - 1 is returned .
2008-01-09 18:53:29 +00:00
*/
2008-10-06 15:29:56 +00:00
static int get_member_status ( struct call_queue * q , int max_penalty , int min_penalty , enum empty_conditions conditions )
2004-10-26 22:25:43 +00:00
{
struct member * member ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2005-03-28 20:48:24 +00:00
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
2007-09-13 13:34:24 +00:00
for ( ; ( member = ao2_iterator_next ( & mem_iter ) ) ; ao2_ref ( member , - 1 ) ) {
2008-10-06 15:29:56 +00:00
if ( ( max_penalty & & ( member - > penalty > max_penalty ) ) | | ( min_penalty & & ( member - > penalty < min_penalty ) ) ) {
if ( conditions & QUEUE_EMPTY_PENALTY ) {
ast_debug ( 4 , " %s is unavailable because his penalty is not between %d and %d \n " , member - > membername , min_penalty , max_penalty ) ;
continue ;
}
}
2006-01-06 03:40:12 +00:00
2005-03-28 20:48:24 +00:00
switch ( member - > status ) {
2004-11-16 01:24:31 +00:00
case AST_DEVICE_INVALID :
2008-10-06 15:29:56 +00:00
if ( conditions & QUEUE_EMPTY_INVALID ) {
ast_debug ( 4 , " %s is unavailable because his device state is 'invalid' \n " , member - > membername ) ;
break ;
}
2010-07-02 16:57:28 +00:00
goto default_case ;
2005-03-28 20:48:24 +00:00
case AST_DEVICE_UNAVAILABLE :
2008-10-06 15:29:56 +00:00
if ( conditions & QUEUE_EMPTY_UNAVAILABLE ) {
ast_debug ( 4 , " %s is unavailable because his device state is 'unavailable' \n " , member - > membername ) ;
break ;
}
2010-07-02 16:57:28 +00:00
goto default_case ;
2008-10-06 15:29:56 +00:00
case AST_DEVICE_INUSE :
if ( conditions & QUEUE_EMPTY_INUSE ) {
ast_debug ( 4 , " %s is unavailable because his device state is 'inuse' \n " , member - > membername ) ;
break ;
}
2010-07-02 16:57:28 +00:00
goto default_case ;
2010-07-16 19:22:49 +00:00
case AST_DEVICE_RINGING :
if ( conditions & QUEUE_EMPTY_RINGING ) {
ast_debug ( 4 , " %s is unavailable because his device state is 'ringing' \n " , member - > membername ) ;
break ;
}
2011-02-04 19:24:54 +00:00
goto default_case ;
2008-10-06 15:29:56 +00:00
case AST_DEVICE_UNKNOWN :
if ( conditions & QUEUE_EMPTY_UNKNOWN ) {
ast_debug ( 4 , " %s is unavailable because his device state is 'unknown' \n " , member - > membername ) ;
break ;
}
2010-07-02 16:57:28 +00:00
/* Fall-through */
2004-10-26 22:25:43 +00:00
default :
2010-07-02 16:57:28 +00:00
default_case :
2008-10-06 15:29:56 +00:00
if ( member - > paused & & ( conditions & QUEUE_EMPTY_PAUSED ) ) {
ast_debug ( 4 , " %s is unavailable because he is paused' \n " , member - > membername ) ;
break ;
} else if ( ( conditions & QUEUE_EMPTY_WRAPUP ) & & member - > lastcall & & q - > wrapuptime & & ( time ( NULL ) - q - > wrapuptime < member - > lastcall ) ) {
ast_debug ( 4 , " %s is unavailable because it has only been %d seconds since his last call (wrapup time is %d) \n " , member - > membername , ( int ) ( time ( NULL ) - member - > lastcall ) , q - > wrapuptime ) ;
break ;
2006-11-13 18:23:55 +00:00
} else {
2007-09-10 18:32:59 +00:00
ao2_ref ( member , - 1 ) ;
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2012-01-27 18:47:16 +00:00
ao2_unlock ( q ) ;
2008-10-06 15:29:56 +00:00
ast_debug ( 4 , " %s is available. \n " , member - > membername ) ;
return 0 ;
2006-11-13 18:23:55 +00:00
}
break ;
2004-10-26 22:25:43 +00:00
}
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2007-09-10 18:32:59 +00:00
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2008-10-06 15:29:56 +00:00
return - 1 ;
2004-10-26 22:25:43 +00:00
}
2004-11-13 22:44:33 +00:00
struct statechange {
2007-05-21 06:56:21 +00:00
AST_LIST_ENTRY ( statechange ) entry ;
2004-11-13 22:44:33 +00:00
int state ;
char dev [ 0 ] ;
} ;
2008-03-10 15:45:13 +00:00
/*! \brief set a member's status based on device state of that member's state_interface.
2011-10-23 11:37:50 +00:00
*
2008-03-10 15:45:13 +00:00
* Lock interface list find sc , iterate through each queues queue_member list for member to
* update state inside queues
*/
2008-09-09 17:15:29 +00:00
static int update_status ( struct call_queue * q , struct member * m , const int status )
2004-11-13 22:44:33 +00:00
{
2008-09-09 17:15:29 +00:00
m - > status = status ;
2006-06-14 22:35:49 +00:00
2008-09-09 17:15:29 +00:00
if ( q - > maskmemberstatus )
return 0 ;
2006-06-14 22:35:49 +00:00
2008-09-09 17:15:29 +00:00
manager_event ( EVENT_FLAG_AGENT , " QueueMemberStatus " ,
" Queue: %s \r \n "
" Location: %s \r \n "
" MemberName: %s \r \n "
2011-08-08 15:00:26 +00:00
" StateInterface: %s \r \n "
2008-09-09 17:15:29 +00:00
" Membership: %s \r \n "
" Penalty: %d \r \n "
" CallsTaken: %d \r \n "
" LastCall: %d \r \n "
" Status: %d \r \n "
" Paused: %d \r \n " ,
2011-08-08 15:00:26 +00:00
q - > name , m - > interface , m - > membername , m - > state_interface , m - > dynamic ? " dynamic " : m - > realtime ? " realtime " : " static " ,
2008-09-09 17:15:29 +00:00
m - > penalty , m - > calls , ( int ) m - > lastcall , m - > status , m - > paused
) ;
2006-06-14 22:35:49 +00:00
2008-03-18 20:59:02 +00:00
return 0 ;
}
/*! \brief set a member's status based on device state of that member's interface*/
2008-05-03 03:57:42 +00:00
static int handle_statechange ( void * datap )
2008-03-18 20:59:02 +00:00
{
2008-05-03 03:57:42 +00:00
struct statechange * sc = datap ;
2008-09-09 17:15:29 +00:00
struct ao2_iterator miter , qiter ;
struct member * m ;
struct call_queue * q ;
char interface [ 80 ] , * slash_pos ;
int found = 0 ;
2008-03-18 20:59:02 +00:00
2008-09-09 17:15:29 +00:00
qiter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & qiter , " Iterate over queues " ) ) ) {
2008-09-09 17:15:29 +00:00
ao2_lock ( q ) ;
2008-03-18 20:59:02 +00:00
2008-09-09 17:15:29 +00:00
miter = ao2_iterator_init ( q - > members , 0 ) ;
for ( ; ( m = ao2_iterator_next ( & miter ) ) ; ao2_ref ( m , - 1 ) ) {
ast_copy_string ( interface , m - > state_interface , sizeof ( interface ) ) ;
2008-03-18 20:59:02 +00:00
2008-09-09 17:15:29 +00:00
if ( ( slash_pos = strchr ( interface , ' / ' ) ) )
2009-01-21 00:33:32 +00:00
if ( ! strncasecmp ( interface , " Local/ " , 6 ) & & ( slash_pos = strchr ( slash_pos + 1 , ' / ' ) ) )
2008-09-09 17:15:29 +00:00
* slash_pos = ' \0 ' ;
if ( ! strcasecmp ( interface , sc - > dev ) ) {
found = 1 ;
update_status ( q , m , sc - > state ) ;
ao2_ref ( m , - 1 ) ;
break ;
}
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & miter ) ;
2008-09-09 17:15:29 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2008-03-18 20:59:02 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & qiter ) ;
2008-03-18 20:59:02 +00:00
2008-09-09 17:15:29 +00:00
if ( found )
2008-11-04 18:47:20 +00:00
ast_debug ( 1 , " Device '%s' changed to state '%d' (%s) \n " , sc - > dev , sc - > state , ast_devstate2str ( sc - > state ) ) ;
2008-09-09 17:15:29 +00:00
else
2008-11-04 18:47:20 +00:00
ast_debug ( 3 , " Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue. \n " , sc - > dev , sc - > state , ast_devstate2str ( sc - > state ) ) ;
2008-03-18 20:59:02 +00:00
2008-05-03 03:57:42 +00:00
ast_free ( sc ) ;
2004-11-13 22:44:33 +00:00
return 0 ;
}
2008-03-10 15:45:13 +00:00
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
static void device_state_cb ( const struct ast_event * event , void * unused )
{
enum ast_device_state state ;
const char * device ;
2008-05-03 03:57:42 +00:00
struct statechange * sc ;
size_t datapsize ;
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
state = ast_event_get_ie_uint ( event , AST_EVENT_IE_STATE ) ;
device = ast_event_get_ie_str ( event , AST_EVENT_IE_DEVICE ) ;
if ( ast_strlen_zero ( device ) ) {
ast_log ( LOG_ERROR , " Received invalid event that had no device IE \n " ) ;
return ;
}
2008-05-03 03:57:42 +00:00
datapsize = sizeof ( * sc ) + strlen ( device ) + 1 ;
if ( ! ( sc = ast_calloc ( 1 , datapsize ) ) ) {
ast_log ( LOG_ERROR , " failed to calloc a state change struct \n " ) ;
return ;
}
sc - > state = state ;
strcpy ( sc - > dev , device ) ;
if ( ast_taskprocessor_push ( devicestate_tps , handle_statechange , sc ) < 0 ) {
ast_free ( sc ) ;
}
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
}
2009-11-03 21:16:14 +00:00
/*! \brief Helper function which converts from extension state to device state values */
static int extensionstate2devicestate ( int state )
{
switch ( state ) {
case AST_EXTENSION_NOT_INUSE :
state = AST_DEVICE_NOT_INUSE ;
break ;
case AST_EXTENSION_INUSE :
state = AST_DEVICE_INUSE ;
break ;
case AST_EXTENSION_BUSY :
state = AST_DEVICE_BUSY ;
break ;
case AST_EXTENSION_RINGING :
state = AST_DEVICE_RINGING ;
break ;
case AST_EXTENSION_ONHOLD :
state = AST_DEVICE_ONHOLD ;
break ;
case AST_EXTENSION_UNAVAILABLE :
state = AST_DEVICE_UNAVAILABLE ;
break ;
case AST_EXTENSION_REMOVED :
case AST_EXTENSION_DEACTIVATED :
default :
state = AST_DEVICE_INVALID ;
break ;
}
return state ;
}
2011-07-15 00:23:14 +00:00
static int extension_state_cb ( const char * context , const char * exten , enum ast_extension_states state , void * data )
2009-11-03 21:16:14 +00:00
{
struct ao2_iterator miter , qiter ;
struct member * m ;
struct call_queue * q ;
int found = 0 , device_state = extensionstate2devicestate ( state ) ;
qiter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & qiter , " Iterate through queues " ) ) ) {
2009-11-03 21:16:14 +00:00
ao2_lock ( q ) ;
miter = ao2_iterator_init ( q - > members , 0 ) ;
for ( ; ( m = ao2_iterator_next ( & miter ) ) ; ao2_ref ( m , - 1 ) ) {
if ( ! strcmp ( m - > state_context , context ) & & ! strcmp ( m - > state_exten , exten ) ) {
update_status ( q , m , device_state ) ;
ao2_ref ( m , - 1 ) ;
found = 1 ;
break ;
}
}
ao2_iterator_destroy ( & miter ) ;
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2009-11-03 21:16:14 +00:00
}
ao2_iterator_destroy ( & qiter ) ;
if ( found ) {
ast_debug ( 1 , " Extension '%s@%s' changed to state '%d' (%s) \n " , exten , context , device_state , ast_devstate2str ( device_state ) ) ;
} else {
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 " ,
exten , context , device_state , ast_devstate2str ( device_state ) ) ;
}
return 0 ;
}
/*! \brief Return the current state of a member */
static int get_queue_member_status ( struct member * cur )
{
return ast_strlen_zero ( cur - > state_exten ) ? ast_device_state ( cur - > state_interface ) : extensionstate2devicestate ( ast_extension_state ( NULL , cur - > state_context , cur - > state_exten ) ) ;
}
2008-01-09 18:53:29 +00:00
/*! \brief allocate space for new queue member and set fields based on parameters passed */
2008-01-08 21:18:32 +00:00
static struct member * create_queue_member ( const char * interface , const char * membername , int penalty , int paused , const char * state_interface )
2005-06-02 21:42:49 +00:00
{
struct member * cur ;
2011-10-23 11:37:50 +00:00
2007-09-10 18:32:59 +00:00
if ( ( cur = ao2_alloc ( sizeof ( * cur ) , NULL ) ) ) {
2005-06-02 21:42:49 +00:00
cur - > penalty = penalty ;
cur - > paused = paused ;
ast_copy_string ( cur - > interface , interface , sizeof ( cur - > interface ) ) ;
2011-10-23 11:37:50 +00:00
if ( ! ast_strlen_zero ( state_interface ) ) {
2008-01-08 21:18:32 +00:00
ast_copy_string ( cur - > state_interface , state_interface , sizeof ( cur - > state_interface ) ) ;
2011-10-23 11:37:50 +00:00
} else {
2008-01-08 21:18:32 +00:00
ast_copy_string ( cur - > state_interface , interface , sizeof ( cur - > state_interface ) ) ;
2011-10-23 11:37:50 +00:00
}
if ( ! ast_strlen_zero ( membername ) ) {
2007-09-18 20:10:59 +00:00
ast_copy_string ( cur - > membername , membername , sizeof ( cur - > membername ) ) ;
2011-10-23 11:37:50 +00:00
} else {
2007-09-18 20:10:59 +00:00
ast_copy_string ( cur - > membername , interface , sizeof ( cur - > membername ) ) ;
2011-10-23 11:37:50 +00:00
}
if ( ! strchr ( cur - > interface , ' / ' ) ) {
2005-06-02 21:42:49 +00:00
ast_log ( LOG_WARNING , " No location at interface '%s' \n " , interface ) ;
2011-10-23 11:37:50 +00:00
}
2009-11-04 03:15:10 +00:00
if ( ! strncmp ( cur - > state_interface , " hint: " , 5 ) ) {
char * tmp = ast_strdupa ( cur - > state_interface ) , * context = tmp ;
2009-11-03 21:16:14 +00:00
char * exten = strsep ( & context , " @ " ) + 5 ;
ast_copy_string ( cur - > state_exten , exten , sizeof ( cur - > state_exten ) ) ;
ast_copy_string ( cur - > state_context , S_OR ( context , " default " ) , sizeof ( cur - > state_context ) ) ;
}
cur - > status = get_queue_member_status ( cur ) ;
2005-06-02 21:42:49 +00:00
}
return cur ;
}
2007-09-10 18:32:59 +00:00
static int compress_char ( const char c )
{
if ( c < 32 )
return 0 ;
else if ( c > 96 )
return c - 64 ;
else
return c - 32 ;
}
static int member_hash_fn ( const void * obj , const int flags )
{
const struct member * mem = obj ;
2011-10-24 21:01:58 +00:00
const char * interface = ( flags & OBJ_KEY ) ? obj : mem - > interface ;
const char * chname = strchr ( interface , ' / ' ) ;
2007-09-10 18:32:59 +00:00
int ret = 0 , i ;
2011-10-24 21:01:58 +00:00
if ( ! chname ) {
chname = interface ;
}
for ( i = 0 ; i < 5 & & chname [ i ] ; i + + ) {
2007-09-10 18:32:59 +00:00
ret + = compress_char ( chname [ i ] ) < < ( i * 6 ) ;
2011-10-24 21:01:58 +00:00
}
2007-09-10 18:32:59 +00:00
return ret ;
}
2008-11-25 01:01:49 +00:00
static int member_cmp_fn ( void * obj1 , void * obj2 , int flags )
2007-09-10 18:32:59 +00:00
{
2011-10-24 21:01:58 +00:00
struct member * mem1 = obj1 ;
struct member * mem2 = obj2 ;
const char * interface = ( flags & OBJ_KEY ) ? obj2 : mem2 - > interface ;
return strcasecmp ( mem1 - > interface , interface ) ? 0 : CMP_MATCH | CMP_STOP ;
2007-09-10 18:32:59 +00:00
}
2011-10-23 11:37:50 +00:00
/*!
2008-03-10 15:45:13 +00:00
* \ brief Initialize Queue default values .
* \ note the queue ' s lock must be held before executing this function
*/
2006-06-15 13:35:04 +00:00
static void init_queue ( struct call_queue * q )
2005-06-02 21:42:49 +00:00
{
2006-01-17 20:58:56 +00:00
int i ;
2007-12-21 00:44:17 +00:00
struct penalty_rule * pr_iter ;
2006-06-14 23:20:08 +00:00
2005-06-02 21:42:49 +00:00
q - > dead = 0 ;
q - > retry = DEFAULT_RETRY ;
2009-01-20 21:57:24 +00:00
q - > timeout = DEFAULT_TIMEOUT ;
2005-06-02 21:42:49 +00:00
q - > maxlen = 0 ;
q - > announcefrequency = 0 ;
2007-04-27 22:08:54 +00:00
q - > minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY ;
2007-10-12 20:06:37 +00:00
q - > announceholdtime = 1 ;
2008-04-30 19:30:41 +00:00
q - > announcepositionlimit = 10 ; /* Default 10 positions */
q - > announceposition = ANNOUNCEPOSITION_YES ; /* Default yes */
2005-06-02 21:42:49 +00:00
q - > roundingseconds = 0 ; /* Default - don't announce seconds */
q - > servicelevel = 0 ;
2006-02-15 02:52:19 +00:00
q - > ringinuse = 1 ;
2006-05-05 21:22:45 +00:00
q - > setinterfacevar = 0 ;
2006-10-27 18:59:16 +00:00
q - > setqueuevar = 0 ;
q - > setqueueentryvar = 0 ;
2006-05-03 20:01:30 +00:00
q - > autofill = autofill_default ;
2006-05-05 22:02:38 +00:00
q - > montype = montype_default ;
2005-06-02 21:42:49 +00:00
q - > monfmt [ 0 ] = ' \0 ' ;
2008-01-14 16:33:06 +00:00
q - > reportholdtime = 0 ;
q - > wrapuptime = 0 ;
2009-12-04 18:48:31 +00:00
q - > penaltymemberslimit = 0 ;
2008-01-14 16:33:06 +00:00
q - > joinempty = 0 ;
q - > leavewhenempty = 0 ;
q - > memberdelay = 0 ;
q - > maskmemberstatus = 0 ;
q - > eventwhencalled = 0 ;
q - > weight = 0 ;
q - > timeoutrestart = 0 ;
2005-07-31 22:07:58 +00:00
q - > periodicannouncefrequency = 0 ;
2008-03-18 18:58:42 +00:00
q - > randomperiodicannounce = 0 ;
q - > numperiodicannounce = 0 ;
2010-05-04 22:46:42 +00:00
q - > autopause = QUEUE_AUTOPAUSE_OFF ;
2008-07-03 14:34:25 +00:00
q - > timeoutpriority = TIMEOUT_PRIORITY_APP ;
2011-06-29 06:39:26 +00:00
q - > autopausedelay = 0 ;
2007-12-21 00:44:17 +00:00
if ( ! q - > members ) {
2011-01-20 17:14:01 +00:00
if ( q - > strategy = = QUEUE_STRATEGY_LINEAR | | q - > strategy = = QUEUE_STRATEGY_RRORDERED )
2007-10-26 15:19:46 +00:00
/* linear strategy depends on order, so we have to place all members in a single bucket */
q - > members = ao2_container_alloc ( 1 , member_hash_fn , member_cmp_fn ) ;
else
q - > members = ao2_container_alloc ( 37 , member_hash_fn , member_cmp_fn ) ;
}
2007-09-13 16:27:42 +00:00
q - > found = 1 ;
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_next , " queue-youarenext " ) ;
ast_string_field_set ( q , sound_thereare , " queue-thereare " ) ;
ast_string_field_set ( q , sound_calls , " queue-callswaiting " ) ;
2008-04-30 19:30:41 +00:00
ast_string_field_set ( q , queue_quantity1 , " queue-quantity1 " ) ;
ast_string_field_set ( q , queue_quantity2 , " queue-quantity2 " ) ;
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_holdtime , " queue-holdtime " ) ;
ast_string_field_set ( q , sound_minutes , " queue-minutes " ) ;
2008-02-14 20:46:00 +00:00
ast_string_field_set ( q , sound_minute , " queue-minute " ) ;
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_seconds , " queue-seconds " ) ;
ast_string_field_set ( q , sound_thanks , " queue-thankyou " ) ;
ast_string_field_set ( q , sound_reporthold , " queue-reporthold " ) ;
2011-08-23 18:17:52 +00:00
if ( ! q - > sound_periodicannounce [ 0 ] ) {
q - > sound_periodicannounce [ 0 ] = ast_str_create ( 32 ) ;
}
if ( q - > sound_periodicannounce [ 0 ] ) {
2007-11-14 01:35:28 +00:00
ast_str_set ( & q - > sound_periodicannounce [ 0 ] , 0 , " queue-periodic-announce " ) ;
2011-08-23 18:17:52 +00:00
}
2007-11-14 01:35:28 +00:00
for ( i = 1 ; i < MAX_PERIODIC_ANNOUNCEMENTS ; i + + ) {
if ( q - > sound_periodicannounce [ i ] )
ast_str_set ( & q - > sound_periodicannounce [ i ] , 0 , " %s " , " " ) ;
}
2007-12-21 00:44:17 +00:00
while ( ( pr_iter = AST_LIST_REMOVE_HEAD ( & q - > rules , list ) ) )
ast_free ( pr_iter ) ;
2005-06-02 21:42:49 +00:00
}
2006-06-15 13:35:04 +00:00
static void clear_queue ( struct call_queue * q )
2005-06-02 21:42:49 +00:00
{
q - > holdtime = 0 ;
q - > callscompleted = 0 ;
q - > callsabandoned = 0 ;
q - > callscompletedinsl = 0 ;
2009-05-08 19:50:44 +00:00
q - > talktime = 0 ;
if ( q - > members ) {
struct member * mem ;
struct ao2_iterator mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( mem = ao2_iterator_next ( & mem_iter ) ) ) {
mem - > calls = 0 ;
2010-05-05 15:42:07 +00:00
mem - > lastcall = 0 ;
2009-05-08 19:50:44 +00:00
ao2_ref ( mem , - 1 ) ;
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2009-05-08 19:50:44 +00:00
}
2005-06-02 21:42:49 +00:00
}
2011-10-23 11:37:50 +00:00
/*!
2008-03-10 15:45:13 +00:00
* \ brief Change queue penalty by adding rule .
*
2011-10-23 11:37:50 +00:00
* Check rule for errors with time or fomatting , see if rule is relative to rest
2008-03-10 15:45:13 +00:00
* of queue , iterate list of rules to find correct insertion point , insert and return .
* \ retval - 1 on failure
2011-10-23 11:37:50 +00:00
* \ retval 0 on success
* \ note Call this with the rule_lists locked
2008-03-10 15:45:13 +00:00
*/
2011-06-29 18:18:00 +00:00
static int insert_penaltychange ( const char * list_name , const char * content , const int linenum )
2007-12-21 00:44:17 +00:00
{
char * timestr , * maxstr , * minstr , * contentdup ;
struct penalty_rule * rule = NULL , * rule_iter ;
struct rule_list * rl_iter ;
2008-08-10 20:23:50 +00:00
int penaltychangetime , inserted = 0 ;
2007-12-21 00:44:17 +00:00
if ( ! ( rule = ast_calloc ( 1 , sizeof ( * rule ) ) ) ) {
return - 1 ;
}
contentdup = ast_strdupa ( content ) ;
if ( ! ( maxstr = strchr ( contentdup , ' , ' ) ) ) {
ast_log ( LOG_WARNING , " Improperly formatted penaltychange rule at line %d. Ignoring. \n " , linenum ) ;
ast_free ( rule ) ;
return - 1 ;
}
* maxstr + + = ' \0 ' ;
timestr = contentdup ;
2008-08-10 20:23:50 +00:00
if ( ( penaltychangetime = atoi ( timestr ) ) < 0 ) {
2007-12-21 00:44:17 +00:00
ast_log ( LOG_WARNING , " Improper time parameter specified for penaltychange rule at line %d. Ignoring. \n " , linenum ) ;
ast_free ( rule ) ;
return - 1 ;
}
2008-08-10 20:23:50 +00:00
rule - > time = penaltychangetime ;
2007-12-21 00:44:17 +00:00
if ( ( minstr = strchr ( maxstr , ' , ' ) ) )
* minstr + + = ' \0 ' ;
/* The last check will evaluate true if either no penalty change is indicated for a given rule
* OR if a min penalty change is indicated but no max penalty change is */
if ( * maxstr = = ' + ' | | * maxstr = = ' - ' | | * maxstr = = ' \0 ' ) {
rule - > max_relative = 1 ;
}
rule - > max_value = atoi ( maxstr ) ;
if ( ! ast_strlen_zero ( minstr ) ) {
if ( * minstr = = ' + ' | | * minstr = = ' - ' )
rule - > min_relative = 1 ;
rule - > min_value = atoi ( minstr ) ;
} else /*there was no minimum specified, so assume this means no change*/
rule - > min_relative = 1 ;
/*We have the rule made, now we need to insert it where it belongs*/
AST_LIST_TRAVERSE ( & rule_lists , rl_iter , list ) {
if ( strcasecmp ( rl_iter - > name , list_name ) )
continue ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & rl_iter - > rules , rule_iter , list ) {
if ( rule - > time < rule_iter - > time ) {
AST_LIST_INSERT_BEFORE_CURRENT ( rule , list ) ;
inserted = 1 ;
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( ! inserted ) {
AST_LIST_INSERT_TAIL ( & rl_iter - > rules , rule , list ) ;
}
}
return 0 ;
}
2009-04-23 17:45:35 +00:00
static void parse_empty_options ( const char * value , enum empty_conditions * empty , int joinempty )
2008-10-06 15:29:56 +00:00
{
char * value_copy = ast_strdupa ( value ) ;
char * option = NULL ;
while ( ( option = strsep ( & value_copy , " , " ) ) ) {
if ( ! strcasecmp ( option , " paused " ) ) {
* empty | = QUEUE_EMPTY_PAUSED ;
} else if ( ! strcasecmp ( option , " penalty " ) ) {
* empty | = QUEUE_EMPTY_PENALTY ;
} else if ( ! strcasecmp ( option , " inuse " ) ) {
* empty | = QUEUE_EMPTY_INUSE ;
} else if ( ! strcasecmp ( option , " ringing " ) ) {
* empty | = QUEUE_EMPTY_RINGING ;
} else if ( ! strcasecmp ( option , " invalid " ) ) {
* empty | = QUEUE_EMPTY_INVALID ;
} else if ( ! strcasecmp ( option , " wrapup " ) ) {
* empty | = QUEUE_EMPTY_WRAPUP ;
} else if ( ! strcasecmp ( option , " unavailable " ) ) {
* empty | = QUEUE_EMPTY_UNAVAILABLE ;
} else if ( ! strcasecmp ( option , " unknown " ) ) {
* empty | = QUEUE_EMPTY_UNKNOWN ;
} else if ( ! strcasecmp ( option , " loose " ) ) {
* empty = ( QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID ) ;
} else if ( ! strcasecmp ( option , " strict " ) ) {
* empty = ( QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE ) ;
2009-04-23 17:45:35 +00:00
} else if ( ( ast_false ( option ) & & joinempty ) | | ( ast_true ( option ) & & ! joinempty ) ) {
2008-10-06 15:29:56 +00:00
* empty = ( QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED ) ;
2009-04-23 17:45:35 +00:00
} else if ( ( ast_false ( option ) & & ! joinempty ) | | ( ast_true ( option ) & & joinempty ) ) {
2008-10-06 15:29:56 +00:00
* empty = 0 ;
2009-04-23 17:45:35 +00:00
} else {
ast_log ( LOG_WARNING , " Unknown option %s for '%s' \n " , option , joinempty ? " joinempty " : " leavewhenempty " ) ;
2008-10-06 15:29:56 +00:00
}
}
}
2005-11-06 15:09:47 +00:00
/*! \brief Configure a queue parameter.
2008-03-10 15:45:13 +00:00
*
* The failunknown flag is set for config files ( and static realtime ) to show
* errors for unknown parameters . It is cleared for dynamic realtime to allow
* extra fields in the tables .
* \ note For error reporting , line number is passed for . conf static configuration ,
* for Realtime queues , linenum is - 1.
*/
2006-06-15 13:35:04 +00:00
static void queue_set_param ( struct call_queue * q , const char * param , const char * val , int linenum , int failunknown )
2005-06-02 21:42:49 +00:00
{
2006-07-19 20:44:39 +00:00
if ( ! strcasecmp ( param , " musicclass " ) | |
! strcasecmp ( param , " music " ) | | ! strcasecmp ( param , " musiconhold " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , moh , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " announce " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , announce , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " context " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , context , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " timeout " ) ) {
q - > timeout = atoi ( val ) ;
if ( q - > timeout < 0 )
q - > timeout = DEFAULT_TIMEOUT ;
2006-02-15 02:52:19 +00:00
} else if ( ! strcasecmp ( param , " ringinuse " ) ) {
q - > ringinuse = ast_true ( val ) ;
2006-05-05 21:22:45 +00:00
} else if ( ! strcasecmp ( param , " setinterfacevar " ) ) {
q - > setinterfacevar = ast_true ( val ) ;
2006-10-27 18:59:16 +00:00
} else if ( ! strcasecmp ( param , " setqueuevar " ) ) {
q - > setqueuevar = ast_true ( val ) ;
} else if ( ! strcasecmp ( param , " setqueueentryvar " ) ) {
q - > setqueueentryvar = ast_true ( val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " monitor-format " ) ) {
ast_copy_string ( q - > monfmt , val , sizeof ( q - > monfmt ) ) ;
2006-10-27 18:59:16 +00:00
} else if ( ! strcasecmp ( param , " membermacro " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , membermacro , val ) ;
2007-06-19 23:36:34 +00:00
} else if ( ! strcasecmp ( param , " membergosub " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , membergosub , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-youarenext " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_next , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-thereare " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_thereare , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-callswaiting " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_calls , val ) ;
2008-04-30 19:30:41 +00:00
} else if ( ! strcasecmp ( param , " queue-quantity1 " ) ) {
ast_string_field_set ( q , queue_quantity1 , val ) ;
} else if ( ! strcasecmp ( param , " queue-quantity2 " ) ) {
ast_string_field_set ( q , queue_quantity2 , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-holdtime " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_holdtime , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-minutes " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_minutes , val ) ;
2008-02-14 20:46:00 +00:00
} else if ( ! strcasecmp ( param , " queue-minute " ) ) {
ast_string_field_set ( q , sound_minute , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-seconds " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_seconds , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-thankyou " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_thanks , val ) ;
2007-07-05 22:23:23 +00:00
} else if ( ! strcasecmp ( param , " queue-callerannounce " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_callerannounce , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-reporthold " ) ) {
2007-11-14 01:15:26 +00:00
ast_string_field_set ( q , sound_reporthold , val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " announce-frequency " ) ) {
q - > announcefrequency = atoi ( val ) ;
2007-04-27 22:08:54 +00:00
} else if ( ! strcasecmp ( param , " min-announce-frequency " ) ) {
q - > minannouncefrequency = atoi ( val ) ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " %s=%s for queue '%s' \n " , param , val , q - > name ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " announce-round-seconds " ) ) {
q - > roundingseconds = atoi ( val ) ;
2007-05-14 18:08:54 +00:00
/* Rounding to any other values just doesn't make sense... */
2008-02-14 20:46:00 +00:00
if ( ! ( q - > roundingseconds = = 0 | | q - > roundingseconds = = 5 | | q - > roundingseconds = = 10
2007-05-14 18:08:54 +00:00
| | q - > roundingseconds = = 15 | | q - > roundingseconds = = 20 | | q - > roundingseconds = = 30 ) ) {
2005-06-02 21:42:49 +00:00
if ( linenum > = 0 ) {
ast_log ( LOG_WARNING , " '%s' isn't a valid value for %s "
" using 0 instead for queue '%s' at line %d of queues.conf \n " ,
val , param , q - > name , linenum ) ;
} else {
ast_log ( LOG_WARNING , " '%s' isn't a valid value for %s "
" using 0 instead for queue '%s' \n " , val , param , q - > name ) ;
}
q - > roundingseconds = 0 ;
}
} else if ( ! strcasecmp ( param , " announce-holdtime " ) ) {
if ( ! strcasecmp ( val , " once " ) )
q - > announceholdtime = ANNOUNCEHOLDTIME_ONCE ;
else if ( ast_true ( val ) )
q - > announceholdtime = ANNOUNCEHOLDTIME_ALWAYS ;
else
q - > announceholdtime = 0 ;
2007-10-12 20:06:37 +00:00
} else if ( ! strcasecmp ( param , " announce-position " ) ) {
2008-04-30 19:30:41 +00:00
if ( ! strcasecmp ( val , " limit " ) )
q - > announceposition = ANNOUNCEPOSITION_LIMIT ;
else if ( ! strcasecmp ( val , " more " ) )
q - > announceposition = ANNOUNCEPOSITION_MORE_THAN ;
else if ( ast_true ( val ) )
q - > announceposition = ANNOUNCEPOSITION_YES ;
else
q - > announceposition = ANNOUNCEPOSITION_NO ;
} else if ( ! strcasecmp ( param , " announce-position-limit " ) ) {
q - > announcepositionlimit = atoi ( val ) ;
2006-01-17 20:58:56 +00:00
} else if ( ! strcasecmp ( param , " periodic-announce " ) ) {
2007-07-23 19:51:41 +00:00
if ( strchr ( val , ' , ' ) ) {
2006-10-25 14:44:50 +00:00
char * s , * buf = ast_strdupa ( val ) ;
unsigned int i = 0 ;
2007-07-23 19:51:41 +00:00
while ( ( s = strsep ( & buf , " ,| " ) ) ) {
2007-11-14 01:35:28 +00:00
if ( ! q - > sound_periodicannounce [ i ] )
q - > sound_periodicannounce [ i ] = ast_str_create ( 16 ) ;
2008-03-18 15:43:34 +00:00
ast_str_set ( & q - > sound_periodicannounce [ i ] , 0 , " %s " , s ) ;
2006-01-17 20:58:56 +00:00
i + + ;
2006-10-25 14:44:50 +00:00
if ( i = = MAX_PERIODIC_ANNOUNCEMENTS )
break ;
2006-01-17 20:58:56 +00:00
}
2008-03-18 18:58:42 +00:00
q - > numperiodicannounce = i ;
2006-01-17 20:58:56 +00:00
} else {
2008-03-18 15:43:34 +00:00
ast_str_set ( & q - > sound_periodicannounce [ 0 ] , 0 , " %s " , val ) ;
2008-03-18 18:58:42 +00:00
q - > numperiodicannounce = 1 ;
2006-01-17 20:58:56 +00:00
}
2005-07-31 22:07:58 +00:00
} else if ( ! strcasecmp ( param , " periodic-announce-frequency " ) ) {
q - > periodicannouncefrequency = atoi ( val ) ;
2009-11-09 16:28:31 +00:00
} else if ( ! strcasecmp ( param , " relative-periodic-announce " ) ) {
q - > relativeperiodicannounce = ast_true ( val ) ;
2008-03-18 18:58:42 +00:00
} else if ( ! strcasecmp ( param , " random-periodic-announce " ) ) {
q - > randomperiodicannounce = ast_true ( val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " retry " ) ) {
q - > retry = atoi ( val ) ;
2006-09-03 14:28:25 +00:00
if ( q - > retry < = 0 )
2005-06-02 21:42:49 +00:00
q - > retry = DEFAULT_RETRY ;
} else if ( ! strcasecmp ( param , " wrapuptime " ) ) {
q - > wrapuptime = atoi ( val ) ;
2009-12-04 18:48:31 +00:00
} else if ( ! strcasecmp ( param , " penaltymemberslimit " ) ) {
2009-12-04 18:55:21 +00:00
if ( ( sscanf ( val , " %10d " , & q - > penaltymemberslimit ) ! = 1 ) ) {
q - > penaltymemberslimit = 0 ;
}
2006-01-13 17:39:56 +00:00
} else if ( ! strcasecmp ( param , " autofill " ) ) {
q - > autofill = ast_true ( val ) ;
2006-05-05 22:02:38 +00:00
} else if ( ! strcasecmp ( param , " monitor-type " ) ) {
if ( ! strcasecmp ( val , " mixmonitor " ) )
q - > montype = 1 ;
2006-01-13 17:39:56 +00:00
} else if ( ! strcasecmp ( param , " autopause " ) ) {
2010-05-04 22:46:42 +00:00
q - > autopause = autopause2int ( val ) ;
2011-06-29 06:39:26 +00:00
} else if ( ! strcasecmp ( param , " autopausedelay " ) ) {
q - > autopausedelay = atoi ( val ) ;
2011-09-28 16:59:11 +00:00
} else if ( ! strcasecmp ( param , " autopausebusy " ) ) {
q - > autopausebusy = ast_true ( val ) ;
} else if ( ! strcasecmp ( param , " autopauseunavail " ) ) {
q - > autopauseunavail = ast_true ( val ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " maxlen " ) ) {
q - > maxlen = atoi ( val ) ;
if ( q - > maxlen < 0 )
q - > maxlen = 0 ;
} else if ( ! strcasecmp ( param , " servicelevel " ) ) {
q - > servicelevel = atoi ( val ) ;
} else if ( ! strcasecmp ( param , " strategy " ) ) {
2008-12-12 23:48:26 +00:00
int strategy ;
/* We are a static queue and already have set this, no need to do it again */
if ( failunknown ) {
return ;
}
strategy = strat2int ( val ) ;
if ( strategy < 0 ) {
ast_log ( LOG_WARNING , " '%s' isn't a valid strategy for queue '%s', using ringall instead \n " ,
val , q - > name ) ;
q - > strategy = QUEUE_STRATEGY_RINGALL ;
}
if ( strategy = = q - > strategy ) {
return ;
}
if ( strategy = = QUEUE_STRATEGY_LINEAR ) {
ast_log ( LOG_WARNING , " Changing to the linear strategy currently requires asterisk to be restarted. \n " ) ;
return ;
}
q - > strategy = strategy ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " joinempty " ) ) {
2009-04-23 17:45:35 +00:00
parse_empty_options ( val , & q - > joinempty , 1 ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " leavewhenempty " ) ) {
2009-04-23 17:45:35 +00:00
parse_empty_options ( val , & q - > leavewhenempty , 0 ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " eventmemberstatus " ) ) {
q - > maskmemberstatus = ! ast_true ( val ) ;
} else if ( ! strcasecmp ( param , " eventwhencalled " ) ) {
2006-07-26 19:59:25 +00:00
if ( ! strcasecmp ( val , " vars " ) ) {
2006-06-22 15:43:02 +00:00
q - > eventwhencalled = QUEUE_EVENT_VARIABLES ;
} else {
2007-11-27 23:21:38 +00:00
q - > eventwhencalled = ast_true ( val ) ? 1 : 0 ;
2006-06-22 15:43:02 +00:00
}
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " reportholdtime " ) ) {
q - > reportholdtime = ast_true ( val ) ;
} else if ( ! strcasecmp ( param , " memberdelay " ) ) {
q - > memberdelay = atoi ( val ) ;
} else if ( ! strcasecmp ( param , " weight " ) ) {
q - > weight = atoi ( val ) ;
} else if ( ! strcasecmp ( param , " timeoutrestart " ) ) {
q - > timeoutrestart = ast_true ( val ) ;
2007-12-21 00:44:17 +00:00
} else if ( ! strcasecmp ( param , " defaultrule " ) ) {
ast_string_field_set ( q , defaultrule , val ) ;
2008-07-03 14:34:25 +00:00
} else if ( ! strcasecmp ( param , " timeoutpriority " ) ) {
if ( ! strcasecmp ( val , " conf " ) ) {
q - > timeoutpriority = TIMEOUT_PRIORITY_CONF ;
} else {
q - > timeoutpriority = TIMEOUT_PRIORITY_APP ;
}
2006-07-14 05:42:06 +00:00
} else if ( failunknown ) {
2005-06-02 21:42:49 +00:00
if ( linenum > = 0 ) {
ast_log ( LOG_WARNING , " Unknown keyword in queue '%s': %s at line %d of queues.conf \n " ,
q - > name , param , linenum ) ;
} else {
ast_log ( LOG_WARNING , " Unknown keyword in queue '%s': %s \n " , q - > name , param ) ;
}
}
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief Find rt member record to update otherwise create one .
*
* Search for member in queue , if found update penalty / paused state ,
2011-06-06 19:30:56 +00:00
* if no member exists create one flag it as a RT member and add to queue member list .
2008-03-10 15:45:13 +00:00
*/
2011-06-06 19:30:56 +00:00
static void rt_handle_member_record ( struct call_queue * q , char * interface , struct ast_config * member_config )
2005-06-02 21:42:49 +00:00
{
2008-05-21 18:31:05 +00:00
struct member * m ;
struct ao2_iterator mem_iter ;
2005-06-02 21:42:49 +00:00
int penalty = 0 ;
2007-01-31 17:35:14 +00:00
int paused = 0 ;
2008-05-21 18:31:05 +00:00
int found = 0 ;
2011-06-29 06:39:26 +00:00
int ignorebusy = 0 ;
2005-06-02 21:42:49 +00:00
2011-06-29 06:39:26 +00:00
const char * config_val ;
2011-06-06 19:30:56 +00:00
const char * rt_uniqueid = ast_variable_retrieve ( member_config , interface , " uniqueid " ) ;
const char * membername = S_OR ( ast_variable_retrieve ( member_config , interface , " membername " ) , interface ) ;
const char * state_interface = S_OR ( ast_variable_retrieve ( member_config , interface , " state_interface " ) , interface ) ;
const char * penalty_str = ast_variable_retrieve ( member_config , interface , " penalty " ) ;
const char * paused_str = ast_variable_retrieve ( member_config , interface , " paused " ) ;
2010-01-04 16:39:11 +00:00
if ( ast_strlen_zero ( rt_uniqueid ) ) {
2010-01-18 15:52:55 +00:00
ast_log ( LOG_WARNING , " Realtime field uniqueid is empty for member %s \n " , S_OR ( membername , " NULL " ) ) ;
2010-01-04 16:39:11 +00:00
return ;
}
2006-06-14 23:20:08 +00:00
if ( penalty_str ) {
2005-06-02 21:42:49 +00:00
penalty = atoi ( penalty_str ) ;
2011-06-29 06:39:26 +00:00
if ( ( penalty < 0 ) & & negative_penalty_invalid ) {
return ;
} else if ( penalty < 0 ) {
2005-06-02 21:42:49 +00:00
penalty = 0 ;
2011-06-29 06:39:26 +00:00
}
2005-06-02 21:42:49 +00:00
}
2007-01-31 17:35:14 +00:00
if ( paused_str ) {
paused = atoi ( paused_str ) ;
2011-10-23 11:37:50 +00:00
if ( paused < 0 ) {
2007-01-31 17:35:14 +00:00
paused = 0 ;
2011-10-23 11:37:50 +00:00
}
2007-01-31 17:35:14 +00:00
}
2011-06-29 06:39:26 +00:00
if ( ( config_val = ast_variable_retrieve ( member_config , interface , " ignorebusy " ) ) ) {
ignorebusy = ast_true ( config_val ) ;
} else {
ignorebusy = 1 ;
}
/* Find member by realtime uniqueid and update */
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
if ( ! strcasecmp ( m - > rt_uniqueid , rt_uniqueid ) ) {
m - > dead = 0 ; /* Do not delete this one. */
ast_copy_string ( m - > rt_uniqueid , rt_uniqueid , sizeof ( m - > rt_uniqueid ) ) ;
2011-10-23 11:37:50 +00:00
if ( paused_str ) {
2011-06-29 06:39:26 +00:00
m - > paused = paused ;
2011-10-23 11:37:50 +00:00
}
2011-06-29 06:39:26 +00:00
if ( strcasecmp ( state_interface , m - > state_interface ) ) {
ast_copy_string ( m - > state_interface , state_interface , sizeof ( m - > state_interface ) ) ;
}
m - > penalty = penalty ;
m - > ignorebusy = ignorebusy ;
found = 1 ;
ao2_ref ( m , - 1 ) ;
break ;
}
ao2_ref ( m , - 1 ) ;
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2008-05-21 18:31:05 +00:00
2011-06-29 06:39:26 +00:00
/* Create a new member */
if ( ! found ) {
2008-01-08 21:18:32 +00:00
if ( ( m = create_queue_member ( interface , membername , penalty , paused , state_interface ) ) ) {
2005-06-02 21:42:49 +00:00
m - > dead = 0 ;
2007-08-29 15:57:27 +00:00
m - > realtime = 1 ;
2011-06-29 06:39:26 +00:00
m - > ignorebusy = ignorebusy ;
2008-05-21 18:31:05 +00:00
ast_copy_string ( m - > rt_uniqueid , rt_uniqueid , sizeof ( m - > rt_uniqueid ) ) ;
2011-08-08 20:28:20 +00:00
if ( ! log_membername_as_agent ) {
2011-11-14 22:27:42 +00:00
ast_queue_log ( q - > name , " REALTIME " , m - > interface , " ADDMEMBER " , " %s " , paused ? " PAUSED " : " " ) ;
2011-08-08 20:28:20 +00:00
} else {
2011-11-14 22:27:42 +00:00
ast_queue_log ( q - > name , " REALTIME " , m - > membername , " ADDMEMBER " , " %s " , paused ? " PAUSED " : " " ) ;
2011-08-08 20:28:20 +00:00
}
2007-09-10 18:32:59 +00:00
ao2_link ( q - > members , m ) ;
2007-11-30 19:34:47 +00:00
ao2_ref ( m , - 1 ) ;
m = NULL ;
2005-06-02 21:42:49 +00:00
}
}
}
2008-03-10 15:45:13 +00:00
/*! \brief Iterate through queue's member list and delete them */
2006-06-15 13:35:04 +00:00
static void free_members ( struct call_queue * q , int all )
2006-01-22 19:09:50 +00:00
{
/* Free non-dynamic members */
2007-09-10 18:32:59 +00:00
struct member * cur ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
2006-01-22 19:09:50 +00:00
2007-09-11 16:12:09 +00:00
while ( ( cur = ao2_iterator_next ( & mem_iter ) ) ) {
2007-09-10 18:32:59 +00:00
if ( all | | ! cur - > dynamic ) {
ao2_unlink ( q - > members , cur ) ;
}
ao2_ref ( cur , - 1 ) ;
2006-01-22 19:09:50 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2006-01-22 19:09:50 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Free queue's member list then its string fields */
2007-09-20 21:21:28 +00:00
static void destroy_queue ( void * obj )
2006-01-22 19:09:50 +00:00
{
2007-09-20 21:21:28 +00:00
struct call_queue * q = obj ;
2007-11-14 01:35:28 +00:00
int i ;
2006-01-22 19:09:50 +00:00
free_members ( q , 1 ) ;
2007-11-14 01:15:26 +00:00
ast_string_field_free_memory ( q ) ;
2007-11-14 01:35:28 +00:00
for ( i = 0 ; i < MAX_PERIODIC_ANNOUNCEMENTS ; i + + ) {
if ( q - > sound_periodicannounce [ i ] )
free ( q - > sound_periodicannounce [ i ] ) ;
}
2007-09-10 18:32:59 +00:00
ao2_ref ( q - > members , - 1 ) ;
2007-09-20 21:21:28 +00:00
}
static struct call_queue * alloc_queue ( const char * queuename )
{
struct call_queue * q ;
2009-11-24 20:31:28 +00:00
if ( ( q = ao2_t_alloc ( sizeof ( * q ) , destroy_queue , " Allocate queue " ) ) ) {
2007-11-14 01:15:26 +00:00
if ( ast_string_field_init ( q , 64 ) ) {
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " String field allocation failed " ) ;
2007-11-14 01:15:26 +00:00
return NULL ;
}
ast_string_field_set ( q , name , queuename ) ;
2007-09-20 21:21:28 +00:00
}
return q ;
2006-01-22 19:09:50 +00:00
}
2005-06-02 21:42:49 +00:00
2008-03-10 15:45:13 +00:00
/*!
* \ brief Reload a single queue via realtime .
*
* Check for statically defined queue first , check if deleted RT queue ,
* check for new RT queue , if queue vars are not defined init them with defaults .
* reload RT queue vars , set RT queue members dead and reload them , return finished queue .
2011-10-23 11:37:50 +00:00
* \ retval the queue ,
2008-03-10 15:45:13 +00:00
* \ retval NULL if it doesn ' t exist .
2011-10-23 11:37:50 +00:00
* \ note Should be called with the " queues " container locked .
2008-03-10 15:45:13 +00:00
*/
2006-06-15 13:35:04 +00:00
static struct call_queue * find_queue_by_name_rt ( const char * queuename , struct ast_variable * queue_vars , struct ast_config * member_config )
2005-06-02 21:42:49 +00:00
{
struct ast_variable * v ;
2007-11-14 01:15:26 +00:00
struct call_queue * q , tmpq = {
2011-10-23 11:37:50 +00:00
. name = queuename ,
2007-11-14 01:15:26 +00:00
} ;
2007-09-10 18:32:59 +00:00
struct member * m ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2006-06-21 18:39:56 +00:00
char * interface = NULL ;
2007-11-14 13:18:40 +00:00
const char * tmp_name ;
char * tmp ;
2005-06-02 21:42:49 +00:00
char tmpbuf [ 64 ] ; /* Must be longer than the longest queue param name. */
/* Static queues override realtime. */
2009-11-24 20:31:28 +00:00
if ( ( q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Check if static queue exists " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2005-06-02 21:42:49 +00:00
if ( ! q - > realtime ) {
if ( q - > dead ) {
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Queue is dead; can't return it " ) ;
2005-06-02 21:42:49 +00:00
return NULL ;
} else {
2007-09-13 16:27:42 +00:00
ast_log ( LOG_WARNING , " Static queue '%s' already exists. Not loading from realtime \n " , q - > name ) ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2005-06-02 21:42:49 +00:00
return q ;
}
}
2011-10-23 11:37:50 +00:00
} else if ( ! member_config ) {
2005-09-30 14:01:56 +00:00
/* Not found in the list, and it's not realtime ... */
return NULL ;
2011-10-23 11:37:50 +00:00
}
2005-06-02 21:42:49 +00:00
/* Check if queue is defined in realtime. */
if ( ! queue_vars ) {
/* Delete queue from in-core list if it has been deleted in realtime. */
if ( q ) {
2005-11-06 15:09:47 +00:00
/*! \note Hmm, can't seem to distinguish a DB failure from a not
2005-06-02 21:42:49 +00:00
found condition . . . So we might delete an in - core queue
in case of DB failure . */
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Queue %s not found in realtime. \n " , queuename ) ;
2005-06-02 21:42:49 +00:00
q - > dead = 1 ;
/* Delete if unused (else will be deleted when last caller leaves). */
2009-11-24 20:31:28 +00:00
queues_t_unlink ( queues , q , " Unused; removing from container " ) ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Queue is dead; can't return it " ) ;
2005-06-02 21:42:49 +00:00
}
return NULL ;
}
/* Create a new queue if an in-core entry does not exist yet. */
if ( ! q ) {
2008-01-31 22:04:52 +00:00
struct ast_variable * tmpvar = NULL ;
2011-10-23 11:37:50 +00:00
if ( ! ( q = alloc_queue ( queuename ) ) ) {
2005-06-02 21:42:49 +00:00
return NULL ;
2011-10-23 11:37:50 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2005-06-02 21:42:49 +00:00
clear_queue ( q ) ;
q - > realtime = 1 ;
2008-01-31 22:04:52 +00:00
/*Before we initialize the queue, we need to set the strategy, so that linear strategy
* will allocate the members properly
*/
for ( tmpvar = queue_vars ; tmpvar ; tmpvar = tmpvar - > next ) {
2008-01-31 22:12:50 +00:00
if ( ! strcasecmp ( tmpvar - > name , " strategy " ) ) {
2008-01-31 22:04:52 +00:00
q - > strategy = strat2int ( tmpvar - > value ) ;
if ( q - > strategy < 0 ) {
ast_log ( LOG_WARNING , " '%s' isn't a valid strategy for queue '%s', using ringall instead \n " ,
tmpvar - > value , q - > name ) ;
q - > strategy = QUEUE_STRATEGY_RINGALL ;
}
break ;
}
}
/* We traversed all variables and didn't find a strategy */
2011-10-23 11:37:50 +00:00
if ( ! tmpvar ) {
2008-01-31 22:04:52 +00:00
q - > strategy = QUEUE_STRATEGY_RINGALL ;
2011-10-23 11:37:50 +00:00
}
2009-11-24 20:31:28 +00:00
queues_t_link ( queues , q , " Add queue to container " ) ;
2005-06-02 21:42:49 +00:00
}
2008-07-16 20:24:12 +00:00
init_queue ( q ) ; /* Ensure defaults for all parameters not set explicitly. */
2005-06-02 21:42:49 +00:00
memset ( tmpbuf , 0 , sizeof ( tmpbuf ) ) ;
2006-06-14 23:20:08 +00:00
for ( v = queue_vars ; v ; v = v - > next ) {
2005-06-02 21:42:49 +00:00
/* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
2006-06-14 23:20:08 +00:00
if ( ( tmp = strchr ( v - > name , ' _ ' ) ) ) {
2005-06-02 21:42:49 +00:00
ast_copy_string ( tmpbuf , v - > name , sizeof ( tmpbuf ) ) ;
tmp_name = tmpbuf ;
2007-11-14 13:18:40 +00:00
tmp = tmpbuf ;
2006-06-14 23:20:08 +00:00
while ( ( tmp = strchr ( tmp , ' _ ' ) ) )
2005-06-02 21:42:49 +00:00
* tmp + + = ' - ' ;
} else
tmp_name = v - > name ;
2008-04-15 16:18:38 +00:00
2010-09-17 21:10:02 +00:00
/* NULL values don't get returned from realtime; blank values should
* still get set . If someone doesn ' t want a value to be set , they
* should set the realtime column to NULL , not blank . */
queue_set_param ( q , tmp_name , v - > value , - 1 , 0 ) ;
2005-06-02 21:42:49 +00:00
}
Simplify queue membercount code
Despite an ominous sounding comment stating that membercount was for "logged
in" members only and thus we couldn't use ao2_container_count(), I could not
find a single place in the code where that seemed to be accurate. The only time
we decremented membercount was when we were marking something dead or actually
removing it. The only places we incremented it were either after ao2_link(), or
trying to correct for having set it to 0 during a reload. In every case where
we were correcting the value, it seemed that we were trying to make the count
actually match what ao2_container_count() would return. The only place I could
find where we made a determination about something being "logged in" or not, we
didn't trust the membercount, but instead looked at devicestate, paused, etc.
This patch removes membercount, replaces its use with ao2_container_count, and
manually adds the results of ao2_container_count to a "membercount" field for
ast_data queue query results. This patch also would fix AST-676, but as it is
slightly riskier than the previously committed fix, the two commits have been
made separately.
Reivew: https://reviewboard.asterisk.org/r/1541/
........
Merged revisions 342383 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 342384 from http://svn.asterisk.org/svn/asterisk/branches/10
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@342385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2011-10-25 20:07:59 +00:00
/* Temporarily set realtime members dead so we can detect deleted ones. */
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
2011-10-23 11:37:50 +00:00
if ( m - > realtime ) {
2005-12-05 00:11:00 +00:00
m - > dead = 1 ;
2011-10-23 11:37:50 +00:00
}
2007-09-10 18:32:59 +00:00
ao2_ref ( m , - 1 ) ;
2005-06-02 21:42:49 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2005-06-02 21:42:49 +00:00
2006-09-20 05:59:53 +00:00
while ( ( interface = ast_category_browse ( member_config , interface ) ) ) {
2011-06-06 19:30:56 +00:00
rt_handle_member_record ( q , interface , member_config ) ;
2006-09-20 05:59:53 +00:00
}
2005-06-02 21:42:49 +00:00
/* Delete all realtime members that have been deleted in DB. */
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
2005-06-02 21:42:49 +00:00
if ( m - > dead ) {
2011-08-08 20:28:20 +00:00
if ( ast_strlen_zero ( m - > membername ) | | ! log_membername_as_agent ) {
ast_queue_log ( q - > name , " REALTIME " , m - > interface , " REMOVEMEMBER " , " %s " , " " ) ;
} else {
ast_queue_log ( q - > name , " REALTIME " , m - > membername , " REMOVEMEMBER " , " %s " , " " ) ;
}
2007-09-10 18:32:59 +00:00
ao2_unlink ( q - > members , m ) ;
2005-06-02 21:42:49 +00:00
}
2007-09-10 18:32:59 +00:00
ao2_ref ( m , - 1 ) ;
2005-06-02 21:42:49 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2005-06-02 21:42:49 +00:00
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2006-01-21 20:32:17 +00:00
2005-06-02 21:42:49 +00:00
return q ;
}
2011-12-08 20:55:19 +00:00
/*!
* note */
/*!
* \ internal
* \ brief Returns reference to the named queue . If the queue is realtime , it will load the queue as well .
* \ param queuename - name of the desired queue
*
* \ retval the queue
* \ retval NULL if it doesn ' t exist
*/
static struct call_queue * find_load_queue_rt_friendly ( const char * queuename )
2002-09-02 23:15:40 +00:00
{
2006-06-14 23:20:08 +00:00
struct ast_variable * queue_vars ;
2005-06-02 21:42:49 +00:00
struct ast_config * member_config = NULL ;
2007-11-14 01:15:26 +00:00
struct call_queue * q = NULL , tmpq = {
2011-10-23 11:37:50 +00:00
. name = queuename ,
2007-11-14 01:15:26 +00:00
} ;
2009-02-13 20:57:37 +00:00
int prev_weight = 0 ;
2006-01-21 20:32:17 +00:00
/* Find the queue in the in-core list first. */
2009-11-24 20:31:28 +00:00
q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Look for queue in memory first " ) ;
2006-01-21 20:32:17 +00:00
2006-03-14 18:30:52 +00:00
if ( ! q | | q - > realtime ) {
2008-03-10 15:45:13 +00:00
/*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
2006-01-21 20:32:17 +00:00
queue operations while waiting for the DB .
This will be two separate database transactions , so we might
see queue parameters as they were before another process
changed the queue and member list as it was after the change .
Thus we might see an empty member list when a queue is
deleted . In practise , this is unlikely to cause a problem . */
2008-06-19 20:48:33 +00:00
queue_vars = ast_load_realtime ( " queues " , " name " , queuename , SENTINEL ) ;
2006-01-21 20:32:17 +00:00
if ( queue_vars ) {
2008-06-19 20:48:33 +00:00
member_config = ast_load_realtime_multientry ( " queue_members " , " interface LIKE " , " % " , " queue_name " , queuename , SENTINEL ) ;
2006-01-21 20:32:17 +00:00
if ( ! member_config ) {
2012-01-13 17:00:12 +00:00
ast_debug ( 1 , " No queue_members defined in config extconfig.conf \n " ) ;
member_config = ast_config_new ( ) ;
2006-01-21 20:32:17 +00:00
}
}
2009-02-13 20:57:37 +00:00
if ( q ) {
prev_weight = q - > weight ? 1 : 0 ;
2011-08-22 20:01:30 +00:00
queue_t_unref ( q , " Need to find realtime queue " ) ;
2009-02-13 20:57:37 +00:00
}
2006-01-21 20:32:17 +00:00
q = find_queue_by_name_rt ( queuename , queue_vars , member_config ) ;
2011-08-22 20:01:30 +00:00
ast_config_destroy ( member_config ) ;
ast_variables_destroy ( queue_vars ) ;
2011-09-15 15:59:24 +00:00
/* update the use_weight value if the queue's has gained or lost a weight */
2009-03-11 14:28:40 +00:00
if ( q ) {
if ( ! q - > weight & & prev_weight ) {
ast_atomic_fetchadd_int ( & use_weight , - 1 ) ;
}
if ( q - > weight & & ! prev_weight ) {
ast_atomic_fetchadd_int ( & use_weight , + 1 ) ;
}
2009-02-13 20:57:37 +00:00
}
/* Other cases will end up with the proper value for use_weight */
2007-08-13 15:39:48 +00:00
} else {
update_realtime_members ( q ) ;
2006-01-21 20:32:17 +00:00
}
return q ;
}
2007-08-29 16:39:40 +00:00
static int update_realtime_member_field ( struct member * mem , const char * queue_name , const char * field , const char * value )
{
int ret = - 1 ;
2008-05-21 18:31:05 +00:00
if ( ast_strlen_zero ( mem - > rt_uniqueid ) )
return ret ;
2008-06-19 20:48:33 +00:00
if ( ( ast_update_realtime ( " queue_members " , " uniqueid " , mem - > rt_uniqueid , field , value , SENTINEL ) ) > 0 )
2008-05-21 18:31:05 +00:00
ret = 0 ;
2007-08-29 16:39:40 +00:00
return ret ;
}
2008-05-21 18:31:05 +00:00
2007-08-01 14:02:19 +00:00
static void update_realtime_members ( struct call_queue * q )
{
struct ast_config * member_config = NULL ;
2007-09-10 18:32:59 +00:00
struct member * m ;
2007-08-01 14:02:19 +00:00
char * interface = NULL ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2007-08-01 14:02:19 +00:00
2008-06-19 20:48:33 +00:00
if ( ! ( member_config = ast_load_realtime_multientry ( " queue_members " , " interface LIKE " , " % " , " queue_name " , q - > name , SENTINEL ) ) ) {
2007-08-01 14:02:19 +00:00
/*This queue doesn't have realtime members*/
ast_debug ( 3 , " Queue %s has no realtime members defined. No need for update \n " , q - > name ) ;
return ;
}
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2011-09-15 15:59:24 +00:00
/* Temporarily set realtime members dead so we can detect deleted ones.*/
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
2007-08-29 15:57:27 +00:00
if ( m - > realtime )
2007-08-01 14:02:19 +00:00
m - > dead = 1 ;
2007-09-10 18:32:59 +00:00
ao2_ref ( m , - 1 ) ;
2007-08-01 14:02:19 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2007-08-01 14:02:19 +00:00
while ( ( interface = ast_category_browse ( member_config , interface ) ) ) {
2011-06-06 19:30:56 +00:00
rt_handle_member_record ( q , interface , member_config ) ;
2007-08-01 14:02:19 +00:00
}
/* Delete all realtime members that have been deleted in DB. */
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
2007-08-01 14:02:19 +00:00
if ( m - > dead ) {
2011-08-08 20:28:20 +00:00
if ( ast_strlen_zero ( m - > membername ) | | ! log_membername_as_agent ) {
ast_queue_log ( q - > name , " REALTIME " , m - > interface , " REMOVEMEMBER " , " %s " , " " ) ;
} else {
ast_queue_log ( q - > name , " REALTIME " , m - > membername , " REMOVEMEMBER " , " %s " , " " ) ;
}
2007-09-10 18:32:59 +00:00
ao2_unlink ( q - > members , m ) ;
2007-08-01 14:02:19 +00:00
}
2007-09-10 18:32:59 +00:00
ao2_ref ( m , - 1 ) ;
2007-08-01 14:02:19 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2008-01-29 23:02:48 +00:00
ast_config_destroy ( member_config ) ;
2007-08-01 14:02:19 +00:00
}
2009-04-27 16:37:51 +00:00
static int join_queue ( char * queuename , struct queue_ent * qe , enum queue_result * reason , int position )
2006-01-21 20:32:17 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2002-09-02 23:15:40 +00:00
struct queue_ent * cur , * prev = NULL ;
int res = - 1 ;
int pos = 0 ;
2004-06-23 20:05:18 +00:00
int inserted = 0 ;
2005-06-02 21:42:49 +00:00
2011-12-08 20:55:19 +00:00
if ( ! ( q = find_load_queue_rt_friendly ( queuename ) ) ) {
2006-01-21 20:32:17 +00:00
return res ;
2011-10-23 11:37:50 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2005-06-02 21:42:49 +00:00
/* This is our one */
2008-10-06 15:29:56 +00:00
if ( q - > joinempty ) {
int status = 0 ;
if ( ( status = get_member_status ( q , qe - > max_penalty , qe - > min_penalty , q - > joinempty ) ) ) {
2008-09-08 23:00:06 +00:00
* reason = QUEUE_JOINEMPTY ;
2008-10-06 15:29:56 +00:00
ao2_unlock ( q ) ;
2011-08-22 20:01:30 +00:00
queue_t_unref ( q , " Done with realtime queue " ) ;
2008-10-06 15:29:56 +00:00
return res ;
}
2008-09-08 23:00:06 +00:00
}
if ( * reason = = QUEUE_UNKNOWN & & q - > maxlen & & ( q - > count > = q - > maxlen ) )
2005-06-02 21:42:49 +00:00
* reason = QUEUE_FULL ;
2008-09-08 23:00:06 +00:00
else if ( * reason = = QUEUE_UNKNOWN ) {
2005-06-02 21:42:49 +00:00
/* There's space for us, put us at the right position inside
2006-07-14 05:42:06 +00:00
* the queue .
2005-06-02 21:42:49 +00:00
* Take into account the priority of the calling user */
inserted = 0 ;
prev = NULL ;
cur = q - > head ;
2006-06-14 23:20:08 +00:00
while ( cur ) {
2005-06-02 21:42:49 +00:00
/* We have higher priority than the current user, enter
* before him , after all the other users with priority
* higher or equal to our priority . */
if ( ( ! inserted ) & & ( qe - > prio > cur - > prio ) ) {
insert_entry ( q , prev , qe , & pos ) ;
inserted = 1 ;
}
2009-04-27 16:37:51 +00:00
/* <= is necessary for the position comparison because it may not be possible to enter
* at our desired position since higher - priority callers may have taken the position we want
*/
2010-04-30 19:53:36 +00:00
if ( ! inserted & & ( qe - > prio > = cur - > prio ) & & position & & ( position < = pos + 1 ) ) {
2009-04-27 16:37:51 +00:00
insert_entry ( q , prev , qe , & pos ) ;
/*pos is incremented inside insert_entry, so don't need to add 1 here*/
if ( position < pos ) {
ast_log ( LOG_NOTICE , " Asked to be inserted at position %d but forced into position %d due to higher priority callers \n " , position , pos ) ;
}
inserted = 1 ;
}
2005-06-02 21:42:49 +00:00
cur - > pos = + + pos ;
prev = cur ;
cur = cur - > next ;
}
/* No luck, join at the end of the queue */
if ( ! inserted )
insert_entry ( q , prev , qe , & pos ) ;
ast_copy_string ( qe - > moh , q - > moh , sizeof ( qe - > moh ) ) ;
ast_copy_string ( qe - > announce , q - > announce , sizeof ( qe - > announce ) ) ;
ast_copy_string ( qe - > context , q - > context , sizeof ( qe - > context ) ) ;
q - > count + + ;
res = 0 ;
2009-11-13 20:42:03 +00:00
ast_manager_event ( qe - > chan , EVENT_FLAG_CALL , " Join " ,
2011-05-25 17:14:11 +00:00
" Channel: %s \r \n "
" CallerIDNum: %s \r \n "
" CallerIDName: %s \r \n "
" ConnectedLineNum: %s \r \n "
" ConnectedLineName: %s \r \n "
" Queue: %s \r \n "
" Position: %d \r \n "
" Count: %d \r \n "
" Uniqueid: %s \r \n " ,
2012-01-09 22:15:50 +00:00
ast_channel_name ( qe - > chan ) ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( qe - > chan ) - > id . number . valid , ast_channel_caller ( qe - > chan ) - > id . number . str , " unknown " ) , /* XXX somewhere else it is <unknown> */
S_COR ( ast_channel_caller ( qe - > chan ) - > id . name . valid , ast_channel_caller ( qe - > chan ) - > id . name . str , " unknown " ) ,
S_COR ( ast_channel_connected ( qe - > chan ) - > id . number . valid , ast_channel_connected ( qe - > chan ) - > id . number . str , " unknown " ) , /* XXX somewhere else it is <unknown> */
S_COR ( ast_channel_connected ( qe - > chan ) - > id . name . valid , ast_channel_connected ( qe - > chan ) - > id . name . str , " unknown " ) ,
2012-01-24 20:12:09 +00:00
q - > name , qe - > pos , q - > count , ast_channel_uniqueid ( qe - > chan ) ) ;
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " Queue '%s' Join, Channel '%s', Position '%d' \n " , q - > name , ast_channel_name ( qe - > chan ) , qe - > pos ) ;
2002-09-02 23:15:40 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2011-08-22 20:01:30 +00:00
queue_t_unref ( q , " Done with realtime queue " ) ;
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
return res ;
}
2007-11-14 01:15:26 +00:00
static int play_file ( struct ast_channel * chan , const char * filename )
2004-03-13 06:00:41 +00:00
{
int res ;
2009-03-03 22:49:07 +00:00
if ( ast_strlen_zero ( filename ) ) {
return 0 ;
}
2012-01-24 20:12:09 +00:00
if ( ! ast_fileexists ( filename , NULL , ast_channel_language ( chan ) ) ) {
2010-05-21 21:08:51 +00:00
return 0 ;
}
2004-03-13 06:00:41 +00:00
ast_stopstream ( chan ) ;
2007-07-19 16:29:51 +00:00
2012-01-24 20:12:09 +00:00
res = ast_streamfile ( chan , filename , ast_channel_language ( chan ) ) ;
2004-03-13 06:00:41 +00:00
if ( ! res )
2005-05-15 05:42:51 +00:00
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
2007-07-19 16:29:51 +00:00
2004-03-13 06:00:41 +00:00
ast_stopstream ( chan ) ;
return res ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief Check for valid exit from queue via goto
* \ retval 0 if failure
* \ retval 1 if successful
*/
2005-05-15 05:42:51 +00:00
static int valid_exit ( struct queue_ent * qe , char digit )
{
2005-07-12 22:28:51 +00:00
int digitlen = strlen ( qe - > digits ) ;
2005-05-15 05:42:51 +00:00
2005-07-12 22:28:51 +00:00
/* Prevent possible buffer overflow */
if ( digitlen < sizeof ( qe - > digits ) - 2 ) {
qe - > digits [ digitlen ] = digit ;
qe - > digits [ digitlen + 1 ] = ' \0 ' ;
} else {
qe - > digits [ 0 ] = ' \0 ' ;
return 0 ;
}
2006-07-14 05:42:06 +00:00
/* If there's no context to goto, short-circuit */
2005-05-15 05:42:51 +00:00
if ( ast_strlen_zero ( qe - > context ) )
return 0 ;
2005-07-12 22:28:51 +00:00
/* If the extension is bad, then reset the digits to blank */
2010-07-14 15:48:36 +00:00
if ( ! ast_canmatch_extension ( qe - > chan , qe - > context , qe - > digits , 1 ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( qe - > chan ) - > id . number . valid , ast_channel_caller ( qe - > chan ) - > id . number . str , NULL ) ) ) {
2005-07-12 22:28:51 +00:00
qe - > digits [ 0 ] = ' \0 ' ;
return 0 ;
}
/* We have an exact match */
2005-09-28 19:33:00 +00:00
if ( ! ast_goto_if_exists ( qe - > chan , qe - > context , qe - > digits , 1 ) ) {
2007-07-19 16:29:51 +00:00
qe - > valid_digits = 1 ;
2005-09-28 19:33:00 +00:00
/* Return 1 on a successful goto */
2005-05-15 05:42:51 +00:00
return 1 ;
}
2006-06-14 23:20:08 +00:00
2005-05-15 05:42:51 +00:00
return 0 ;
}
2007-07-21 00:57:00 +00:00
static int say_position ( struct queue_ent * qe , int ringing )
2004-03-13 06:00:41 +00:00
{
2008-04-30 19:30:41 +00:00
int res = 0 , avgholdmins , avgholdsecs , announceposition = 0 ;
2009-02-11 23:03:08 +00:00
int say_thanks = 1 ;
2004-03-13 06:00:41 +00:00
time_t now ;
2007-04-27 22:08:54 +00:00
/* Let minannouncefrequency seconds pass between the start of each position announcement */
2004-03-13 06:00:41 +00:00
time ( & now ) ;
2007-04-27 22:08:54 +00:00
if ( ( now - qe - > last_pos ) < qe - > parent - > minannouncefrequency )
2005-05-15 05:42:51 +00:00
return 0 ;
2004-03-13 06:00:41 +00:00
/* If either our position has changed, or we are over the freq timer, say position */
2006-06-14 23:20:08 +00:00
if ( ( qe - > last_pos_said = = qe - > pos ) & & ( ( now - qe - > last_pos ) < qe - > parent - > announcefrequency ) )
2005-05-15 05:42:51 +00:00
return 0 ;
2004-03-13 06:00:41 +00:00
2007-07-21 00:57:00 +00:00
if ( ringing ) {
ast_indicate ( qe - > chan , - 1 ) ;
} else {
ast_moh_stop ( qe - > chan ) ;
}
2008-04-30 19:30:41 +00:00
if ( qe - > parent - > announceposition = = ANNOUNCEPOSITION_YES | |
qe - > parent - > announceposition = = ANNOUNCEPOSITION_MORE_THAN | |
( qe - > parent - > announceposition = = ANNOUNCEPOSITION_LIMIT & &
qe - > pos < = qe - > parent - > announcepositionlimit ) )
announceposition = 1 ;
if ( announceposition = = 1 ) {
2007-10-12 20:06:37 +00:00
/* Say we're next, if we are */
if ( qe - > pos = = 1 ) {
res = play_file ( qe - > chan , qe - > parent - > sound_next ) ;
if ( res )
goto playout ;
else
goto posout ;
} else {
2008-04-30 19:30:41 +00:00
if ( qe - > parent - > announceposition = = ANNOUNCEPOSITION_MORE_THAN & & qe - > pos > qe - > parent - > announcepositionlimit ) {
/* More than Case*/
res = play_file ( qe - > chan , qe - > parent - > queue_quantity1 ) ;
if ( res )
goto playout ;
2012-01-24 20:12:09 +00:00
res = ast_say_number ( qe - > chan , qe - > parent - > announcepositionlimit , AST_DIGIT_ANY , ast_channel_language ( qe - > chan ) , NULL ) ; /* Needs gender */
2008-04-30 19:30:41 +00:00
if ( res )
goto playout ;
} else {
/* Normal Case */
res = play_file ( qe - > chan , qe - > parent - > sound_thereare ) ;
if ( res )
goto playout ;
2012-01-24 20:12:09 +00:00
res = ast_say_number ( qe - > chan , qe - > pos , AST_DIGIT_ANY , ast_channel_language ( qe - > chan ) , NULL ) ; /* Needs gender */
2008-04-30 19:30:41 +00:00
if ( res )
goto playout ;
}
if ( qe - > parent - > announceposition = = ANNOUNCEPOSITION_MORE_THAN & & qe - > pos > qe - > parent - > announcepositionlimit ) {
/* More than Case*/
res = play_file ( qe - > chan , qe - > parent - > queue_quantity2 ) ;
if ( res )
goto playout ;
} else {
res = play_file ( qe - > chan , qe - > parent - > sound_calls ) ;
if ( res )
goto playout ;
}
2007-10-12 20:06:37 +00:00
}
2004-03-13 06:00:41 +00:00
}
/* Round hold time to nearest minute */
2006-06-14 23:20:08 +00:00
avgholdmins = abs ( ( ( qe - > parent - > holdtime + 30 ) - ( now - qe - > start ) ) / 60 ) ;
2004-06-28 20:17:20 +00:00
/* If they have specified a rounding then round the seconds as well */
2006-06-14 23:20:08 +00:00
if ( qe - > parent - > roundingseconds ) {
avgholdsecs = ( abs ( ( ( qe - > parent - > holdtime + 30 ) - ( now - qe - > start ) ) ) - 60 * avgholdmins ) / qe - > parent - > roundingseconds ;
avgholdsecs * = qe - > parent - > roundingseconds ;
2004-06-28 20:17:20 +00:00
} else {
2006-06-14 23:20:08 +00:00
avgholdsecs = 0 ;
2004-06-28 20:17:20 +00:00
}
2008-02-14 20:46:00 +00:00
ast_verb ( 3 , " Hold time for %s is %d minute(s) %d seconds \n " , qe - > parent - > name , avgholdmins , avgholdsecs ) ;
2004-03-13 06:00:41 +00:00
/* If the hold time is >1 min, if it's enabled, and if it's not
supposed to be only once and we have already said it , say it */
2008-06-12 18:53:17 +00:00
if ( ( avgholdmins + avgholdsecs ) > 0 & & qe - > parent - > announceholdtime & &
( ( qe - > parent - > announceholdtime = = ANNOUNCEHOLDTIME_ONCE & & ! qe - > last_pos ) | |
! ( qe - > parent - > announceholdtime = = ANNOUNCEHOLDTIME_ONCE ) ) ) {
2005-05-15 05:42:51 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_holdtime ) ;
2007-07-19 16:29:51 +00:00
if ( res )
2005-05-15 05:42:51 +00:00
goto playout ;
2009-03-13 21:26:20 +00:00
if ( avgholdmins > = 1 ) {
2012-01-24 20:12:09 +00:00
res = ast_say_number ( qe - > chan , avgholdmins , AST_DIGIT_ANY , ast_channel_language ( qe - > chan ) , NULL ) ;
2008-02-14 20:46:00 +00:00
if ( res )
goto playout ;
2005-05-15 05:42:51 +00:00
2008-02-14 20:46:00 +00:00
if ( avgholdmins = = 1 ) {
res = play_file ( qe - > chan , qe - > parent - > sound_minute ) ;
2007-07-19 16:29:51 +00:00
if ( res )
2005-05-15 05:42:51 +00:00
goto playout ;
} else {
2008-02-14 20:46:00 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_minutes ) ;
2007-07-19 16:29:51 +00:00
if ( res )
2005-05-15 05:42:51 +00:00
goto playout ;
}
2004-06-28 20:17:20 +00:00
}
2009-03-13 21:26:20 +00:00
if ( avgholdsecs > = 1 ) {
2012-01-24 20:12:09 +00:00
res = ast_say_number ( qe - > chan , avgholdsecs , AST_DIGIT_ANY , ast_channel_language ( qe - > chan ) , NULL ) ;
2007-07-19 16:29:51 +00:00
if ( res )
2005-05-15 05:42:51 +00:00
goto playout ;
res = play_file ( qe - > chan , qe - > parent - > sound_seconds ) ;
2007-07-19 16:29:51 +00:00
if ( res )
2005-05-15 05:42:51 +00:00
goto playout ;
2004-06-28 20:17:20 +00:00
}
2009-02-11 23:03:08 +00:00
} else if ( qe - > parent - > announceholdtime & & ! qe - > parent - > announceposition ) {
say_thanks = 0 ;
2004-03-13 06:00:41 +00:00
}
2006-07-14 05:42:06 +00:00
posout :
2009-02-11 23:12:57 +00:00
if ( qe - > parent - > announceposition ) {
ast_verb ( 3 , " Told %s in %s their queue position (which was %d) \n " ,
2012-01-09 22:15:50 +00:00
ast_channel_name ( qe - > chan ) , qe - > parent - > name , qe - > pos ) ;
2009-02-11 23:12:57 +00:00
}
if ( say_thanks ) {
2008-04-30 19:30:41 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_thanks ) ;
2007-10-12 20:06:37 +00:00
}
2006-07-14 05:42:06 +00:00
playout :
2009-07-08 19:26:55 +00:00
if ( ( res > 0 & & ! valid_exit ( qe , res ) ) )
2007-07-19 16:29:51 +00:00
res = 0 ;
2004-03-13 06:00:41 +00:00
/* Set our last_pos indicators */
2006-07-14 05:42:06 +00:00
qe - > last_pos = now ;
2004-03-13 06:00:41 +00:00
qe - > last_pos_said = qe - > pos ;
2006-05-24 16:54:10 +00:00
2006-05-08 13:12:39 +00:00
/* Don't restart music on hold if we're about to exit the caller from the queue */
2007-07-21 00:57:00 +00:00
if ( ! res ) {
2008-04-25 20:20:10 +00:00
if ( ringing ) {
ast_indicate ( qe - > chan , AST_CONTROL_RINGING ) ;
} else {
ast_moh_start ( qe - > chan , qe - > moh , NULL ) ;
}
2007-07-21 00:57:00 +00:00
}
2005-05-15 05:42:51 +00:00
return res ;
2004-03-13 06:00:41 +00:00
}
2007-07-10 20:02:05 +00:00
static void recalc_holdtime ( struct queue_ent * qe , int newholdtime )
2004-03-13 06:00:41 +00:00
{
2007-07-10 20:02:05 +00:00
int oldvalue ;
2004-03-13 06:00:41 +00:00
2008-06-17 15:57:43 +00:00
/* Calculate holdtime using an exponential average */
2004-03-13 06:00:41 +00:00
/* Thanks to SRT for this contribution */
/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
2007-09-20 21:21:28 +00:00
ao2_lock ( qe - > parent ) ;
2004-03-13 06:00:41 +00:00
oldvalue = qe - > parent - > holdtime ;
2007-07-10 20:02:05 +00:00
qe - > parent - > holdtime = ( ( ( oldvalue < < 2 ) - oldvalue ) + newholdtime ) > > 2 ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( qe - > parent ) ;
2004-03-13 06:00:41 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Caller leaving queue.
*
* Search the queue to find the leaving client , if found remove from queue
* create manager event , move others up the queue .
*/
2002-09-02 23:15:40 +00:00
static void leave_queue ( struct queue_ent * qe )
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2008-08-10 20:23:50 +00:00
struct queue_ent * current , * prev = NULL ;
2007-12-21 00:44:17 +00:00
struct penalty_rule * pr_iter ;
2002-09-02 23:15:40 +00:00
int pos = 0 ;
2005-03-28 20:48:24 +00:00
2011-10-23 11:37:50 +00:00
if ( ! ( q = qe - > parent ) ) {
2002-09-02 23:15:40 +00:00
return ;
2011-10-23 11:37:50 +00:00
}
2009-11-24 20:31:28 +00:00
queue_t_ref ( q , " Copy queue pointer from queue entry " ) ;
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2003-06-23 23:40:56 +00:00
2002-09-02 23:15:40 +00:00
prev = NULL ;
2008-08-10 20:23:50 +00:00
for ( current = q - > head ; current ; current = current - > next ) {
if ( current = = qe ) {
2009-04-27 16:37:51 +00:00
char posstr [ 20 ] ;
2002-09-02 23:15:40 +00:00
q - > count - - ;
2003-06-23 23:40:56 +00:00
/* Take us out of the queue */
2009-11-13 20:42:03 +00:00
ast_manager_event ( qe - > chan , EVENT_FLAG_CALL , " Leave " ,
2009-04-27 16:37:51 +00:00
" Channel: %s \r \n Queue: %s \r \n Count: %d \r \n Position: %d \r \n Uniqueid: %s \r \n " ,
2012-01-24 20:12:09 +00:00
ast_channel_name ( qe - > chan ) , q - > name , q - > count , qe - > pos , ast_channel_uniqueid ( qe - > chan ) ) ;
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " Queue '%s' Leave, Channel '%s' \n " , q - > name , ast_channel_name ( qe - > chan ) ) ;
2002-09-02 23:15:40 +00:00
/* Take us out of the queue */
if ( prev )
2008-08-10 20:23:50 +00:00
prev - > next = current - > next ;
2002-09-02 23:15:40 +00:00
else
2008-08-10 20:23:50 +00:00
q - > head = current - > next ;
2007-12-21 00:44:17 +00:00
/* Free penalty rules */
while ( ( pr_iter = AST_LIST_REMOVE_HEAD ( & qe - > qe_rules , list ) ) )
ast_free ( pr_iter ) ;
2009-04-27 16:37:51 +00:00
snprintf ( posstr , sizeof ( posstr ) , " %d " , qe - > pos ) ;
pbx_builtin_setvar_helper ( qe - > chan , " QUEUEPOSITION " , posstr ) ;
2002-09-02 23:15:40 +00:00
} else {
2004-03-13 06:00:41 +00:00
/* Renumber the people after us in the queue based on a new count */
2008-08-10 20:23:50 +00:00
current - > pos = + + pos ;
prev = current ;
2002-09-02 23:15:40 +00:00
}
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
/*If the queue is a realtime queue, check to see if it's still defined in real time*/
2007-12-21 00:44:17 +00:00
if ( q - > realtime ) {
2008-12-17 21:17:20 +00:00
struct ast_variable * var ;
if ( ! ( var = ast_load_realtime ( " queues " , " name " , q - > name , SENTINEL ) ) ) {
2007-09-20 21:21:28 +00:00
q - > dead = 1 ;
2008-12-17 21:17:20 +00:00
} else {
ast_variables_destroy ( var ) ;
}
2007-09-20 21:21:28 +00:00
}
2006-06-14 23:20:08 +00:00
2011-10-23 11:37:50 +00:00
if ( q - > dead ) {
2002-09-02 23:15:40 +00:00
/* It's dead and nobody is in it, so kill it */
2009-11-24 20:31:28 +00:00
queues_t_unlink ( queues , q , " Queue is now dead; remove it from the container " ) ;
2002-09-02 23:15:40 +00:00
}
2008-02-11 02:59:14 +00:00
/* unref the explicit ref earlier in the function */
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Expire copied reference " ) ;
2002-09-02 23:15:40 +00:00
}
2010-05-20 19:40:03 +00:00
/*!
* \ internal
* \ brief Destroy the given callattempt structure and free it .
* \ since 1.8
*
* \ param doomed callattempt structure to destroy .
*
* \ return Nothing
*/
static void callattempt_free ( struct callattempt * doomed )
{
if ( doomed - > member ) {
ao2_ref ( doomed - > member , - 1 ) ;
}
ast_party_connected_line_free ( & doomed - > connected ) ;
ast_free ( doomed ) ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Hang up a list of outgoing calls */
2009-01-28 14:37:16 +00:00
static void hangupcalls ( struct callattempt * outgoing , struct ast_channel * exception , int cancel_answered_elsewhere )
2002-09-02 23:15:40 +00:00
{
2006-02-14 21:50:35 +00:00
struct callattempt * oo ;
2005-03-28 20:48:24 +00:00
2006-06-14 23:20:08 +00:00
while ( outgoing ) {
2009-01-15 13:01:52 +00:00
/* If someone else answered the call we should indicate this in the CANCEL */
2002-09-02 23:15:40 +00:00
/* Hangup any existing lines we have open */
2010-02-18 20:58:41 +00:00
if ( outgoing - > chan & & ( outgoing - > chan ! = exception ) ) {
2009-01-28 14:37:16 +00:00
if ( exception | | cancel_answered_elsewhere )
2012-03-13 18:20:34 +00:00
ast_set_flag ( ast_channel_flags ( outgoing - > chan ) , AST_FLAG_ANSWERED_ELSEWHERE ) ;
2002-09-02 23:15:40 +00:00
ast_hangup ( outgoing - > chan ) ;
2009-01-15 13:01:52 +00:00
}
2002-09-02 23:15:40 +00:00
oo = outgoing ;
2006-06-14 23:20:08 +00:00
outgoing = outgoing - > q_next ;
Generic Advice of Charge.
Asterisk Generic AOC Representation
- Generic AOC encode/decode routines.
(Generic AOC must be encoded to be passed on the wire in the AST_CONTROL_AOC frame)
- AST_CONTROL_AOC frame type to represent generic encoded AOC data
- Manager events for AOC-S, AOC-D, and AOC-E messages
Asterisk App Support
- app_dial AOC-S pass-through support on call setup
- app_queue AOC-S pass-through support on call setup
AOC Unit Tests
- AOC Unit Tests for encode/decode routines
- AOC Unit Test for manager event representation.
SIP AOC Support
- Pass-through of generic AOC-D and AOC-E messages to snom phones via the
snom AOC specification.
- Creation of chan_sip page3 flags for the addition of the new
'snom_aoc_enabled' sip.conf option.
IAX AOC Support
- Natively supports AOC pass-through through the use of the new
AST_CONTROL_AOC frame type
DAHDI AOC Support
- ETSI PRI full AOC Pass-through support
- 'aoc_enable' chan_dahdi.conf option for independently enabling
pass-through of AOC-S, AOC-D, AOC-E.
- 'aoce_delayhangup' option for retrieving AOC-E on disconnect.
- DAHDI A() dial string option for requesting AOC services.
example usage:
;requests AOC-S, AOC-D, and AOC-E on call setup
exten=>1111,1,Dial(DAHDI/g1/1112/A(s,d,e))
Review: https://reviewboard.asterisk.org/r/552/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@267096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-06-02 18:10:15 +00:00
ast_aoc_destroy_decoded ( oo - > aoc_s_rate_list ) ;
2010-05-20 19:40:03 +00:00
callattempt_free ( oo ) ;
2002-09-02 23:15:40 +00:00
}
}
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
/*!
* \ brief Get the number of members available to accept a call .
*
* \ note The queue passed in should be locked prior to this function call
*
* \ param [ in ] q The queue for which we are couting the number of available members
* \ return Return the number of available members in queue q
*/
static int num_available_members ( struct call_queue * q )
{
struct member * mem ;
int avl = 0 ;
struct ao2_iterator mem_iter ;
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( mem = ao2_iterator_next ( & mem_iter ) ) ) {
switch ( mem - > status ) {
2011-06-29 06:39:26 +00:00
case AST_DEVICE_INVALID :
case AST_DEVICE_UNAVAILABLE :
break ;
case AST_DEVICE_INUSE :
case AST_DEVICE_BUSY :
case AST_DEVICE_RINGING :
case AST_DEVICE_RINGINUSE :
case AST_DEVICE_ONHOLD :
if ( ( ! q - > ringinuse ) | | ( ! mem - > ignorebusy ) ) {
break ;
}
/* else fall through */
case AST_DEVICE_NOT_INUSE :
case AST_DEVICE_UNKNOWN :
if ( ! mem - > paused ) {
avl + + ;
}
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
break ;
}
ao2_ref ( mem , - 1 ) ;
/* If autofill is not enabled or if the queue's strategy is ringall, then
* we really don ' t care about the number of available members so much as we
* do that there is at least one available .
*
* In fact , we purposely will return from this function stating that only
* one member is available if either of those conditions hold . That way ,
* functions which determine what action to take based on the number of available
* members will operate properly . The reasoning is that even if multiple
* members are available , only the head caller can actually be serviced .
*/
if ( ( ! q - > autofill | | q - > strategy = = QUEUE_STRATEGY_RINGALL ) & & avl ) {
break ;
}
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
return avl ;
}
/* traverse all defined queues which have calls waiting and contain this member
return 0 if no other queue has precedence ( higher weight ) or 1 if found */
2006-06-15 13:35:04 +00:00
static int compare_weight ( struct call_queue * rq , struct member * member )
2005-03-28 20:48:24 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2005-01-07 04:05:22 +00:00
struct member * mem ;
2005-03-19 17:35:59 +00:00
int found = 0 ;
2007-09-20 21:21:28 +00:00
struct ao2_iterator queue_iter ;
2011-09-15 15:59:24 +00:00
2007-09-20 21:21:28 +00:00
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2007-09-20 21:21:28 +00:00
if ( q = = rq ) { /* don't check myself, could deadlock */
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2006-07-14 05:42:06 +00:00
continue ;
2007-09-20 21:21:28 +00:00
}
ao2_lock ( q ) ;
2005-03-19 17:35:59 +00:00
if ( q - > count & & q - > members ) {
2008-11-25 01:01:49 +00:00
if ( ( mem = ao2_find ( q - > members , member , OBJ_POINTER ) ) ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Found matching member %s in queue '%s' \n " , mem - > interface , q - > name ) ;
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
if ( q - > weight > rq - > weight & & q - > count > = num_available_members ( q ) ) {
2007-06-14 19:39:12 +00:00
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 ) ;
2006-06-14 23:20:08 +00:00
found = 1 ;
2005-01-07 04:05:22 +00:00
}
2007-09-10 18:32:59 +00:00
ao2_ref ( mem , - 1 ) ;
2005-01-07 04:05:22 +00:00
}
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2007-09-20 21:21:28 +00:00
if ( found ) {
2005-01-07 04:05:22 +00:00
break ;
2007-09-20 21:21:28 +00:00
}
2005-01-07 04:05:22 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2005-01-07 04:05:22 +00:00
return found ;
}
2006-02-14 21:50:35 +00:00
/*! \brief common hangup actions */
static void do_hang ( struct callattempt * o )
{
o - > stillgoing = 0 ;
ast_hangup ( o - > chan ) ;
o - > chan = NULL ;
}
2008-03-10 15:45:13 +00:00
/*! \brief convert "\n" to "\nVariable: " ready for manager to use */
2006-06-22 15:43:02 +00:00
static char * vars2manager ( struct ast_channel * chan , char * vars , size_t len )
{
2009-03-27 16:21:10 +00:00
struct ast_str * buf = ast_str_thread_get ( & ast_str_thread_global_buf , len + 1 ) ;
2009-05-21 21:13:09 +00:00
const char * tmp ;
2006-06-22 15:43:02 +00:00
2006-12-18 15:38:50 +00:00
if ( pbx_builtin_serialize_variables ( chan , & buf ) ) {
2006-06-22 15:43:02 +00:00
int i , j ;
/* convert "\n" to "\nVariable: " */
strcpy ( vars , " Variable: " ) ;
2008-12-13 08:36:35 +00:00
tmp = ast_str_buffer ( buf ) ;
2006-06-22 15:43:02 +00:00
for ( i = 0 , j = 10 ; ( i < len - 1 ) & & ( j < len - 1 ) ; i + + , j + + ) {
vars [ j ] = tmp [ i ] ;
if ( tmp [ i + 1 ] = = ' \0 ' )
break ;
if ( tmp [ i ] = = ' \n ' ) {
2007-11-27 23:11:12 +00:00
vars [ j + + ] = ' \r ' ;
vars [ j + + ] = ' \n ' ;
2006-06-22 15:43:02 +00:00
ast_copy_string ( & ( vars [ j ] ) , " Variable: " , len - j ) ;
j + = 9 ;
}
}
2008-04-24 20:43:52 +00:00
if ( j > len - 3 )
j = len - 3 ;
vars [ j + + ] = ' \r ' ;
vars [ j + + ] = ' \n ' ;
2006-06-22 15:43:02 +00:00
vars [ j ] = ' \0 ' ;
} else {
/* there are no channel variables; leave it blank */
* vars = ' \0 ' ;
}
return vars ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief Part 2 of ring_one
*
* Does error checking before attempting to request a channel and call a member .
* This function is only called from ring_one ( ) .
* Failure can occur if :
* - Agent on call
* - Agent is paused
* - Wrapup time not expired
* - Priority by another queue
2008-01-08 23:56:24 +00:00
*
2008-03-10 15:45:13 +00:00
* \ retval 1 on success to reach a free agent
* \ retval 0 on failure to get agent .
2008-01-08 23:56:24 +00:00
*/
2006-02-14 21:50:35 +00:00
static int ring_entry ( struct queue_ent * qe , struct callattempt * tmp , int * busies )
2003-07-30 16:10:51 +00:00
{
int res ;
2004-10-26 22:25:43 +00:00
int status ;
2004-12-19 21:30:55 +00:00
char tech [ 256 ] ;
char * location ;
2008-04-29 19:42:04 +00:00
const char * macrocontext , * macroexten ;
2011-10-20 17:34:54 +00:00
enum ast_device_state newstate ;
2005-03-19 17:35:59 +00:00
2006-02-14 21:50:35 +00:00
/* on entry here, we know that tmp->chan == NULL */
2011-06-29 06:39:26 +00:00
if ( tmp - > member - > paused ) {
ast_debug ( 1 , " %s paused, can't receive call \n " , tmp - > interface ) ;
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_cdr_busy ( ast_channel_cdr ( qe - > chan ) ) ;
2011-06-29 06:39:26 +00:00
}
2004-06-26 16:26:39 +00:00
tmp - > stillgoing = 0 ;
return 0 ;
}
2006-02-15 02:52:19 +00:00
2011-06-29 06:39:26 +00:00
if ( ( tmp - > lastqueue & & tmp - > lastqueue - > wrapuptime & & ( time ( NULL ) - tmp - > lastcall < tmp - > lastqueue - > wrapuptime ) ) | |
( ! tmp - > lastqueue & & qe - > parent - > wrapuptime & & ( time ( NULL ) - tmp - > lastcall < qe - > parent - > wrapuptime ) ) ) {
ast_debug ( 1 , " Wrapuptime not yet expired on queue %s for %s \n " ,
( tmp - > lastqueue ? tmp - > lastqueue - > name : qe - > parent - > name ) , tmp - > interface ) ;
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_cdr_busy ( ast_channel_cdr ( qe - > chan ) ) ;
2011-06-29 06:39:26 +00:00
}
2006-02-15 02:52:19 +00:00
tmp - > stillgoing = 0 ;
2011-06-29 06:39:26 +00:00
( * busies ) + + ;
2006-02-15 02:52:19 +00:00
return 0 ;
}
2011-06-29 06:39:26 +00:00
if ( ! qe - > parent - > ringinuse | | ! tmp - > member - > ignorebusy ) {
2011-10-20 17:34:54 +00:00
if ( check_state_unknown & & ( tmp - > member - > status = = AST_DEVICE_UNKNOWN ) ) {
newstate = ast_device_state ( tmp - > member - > interface ) ;
if ( newstate ! = tmp - > member - > status ) {
ast_log ( LOG_WARNING , " Found a channel matching iterface %s while status was %s changed to %s \n " ,
tmp - > member - > interface , ast_devstate2str ( tmp - > member - > status ) , ast_devstate2str ( newstate ) ) ;
ast_devstate_changed_literal ( newstate , tmp - > member - > interface ) ;
}
}
2011-06-29 06:39:26 +00:00
if ( ( tmp - > member - > status ! = AST_DEVICE_NOT_INUSE ) & & ( tmp - > member - > status ! = AST_DEVICE_UNKNOWN ) ) {
ast_debug ( 1 , " %s in use, can't receive call \n " , tmp - > interface ) ;
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_cdr_busy ( ast_channel_cdr ( qe - > chan ) ) ;
2011-06-29 06:39:26 +00:00
}
tmp - > stillgoing = 0 ;
return 0 ;
}
2005-01-21 04:14:26 +00:00
}
2011-06-29 06:39:26 +00:00
2005-03-19 17:35:59 +00:00
if ( use_weight & & compare_weight ( qe - > parent , tmp - > member ) ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Priority queue delaying call to %s:%s \n " , qe - > parent - > name , tmp - > interface ) ;
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_cdr_busy ( ast_channel_cdr ( qe - > chan ) ) ;
2011-06-29 06:39:26 +00:00
}
2005-03-19 17:35:59 +00:00
tmp - > stillgoing = 0 ;
( * busies ) + + ;
return 0 ;
}
2005-01-21 04:14:26 +00:00
2005-05-15 05:42:51 +00:00
ast_copy_string ( tech , tmp - > interface , sizeof ( tech ) ) ;
2004-12-19 21:30:55 +00:00
if ( ( location = strchr ( tech , ' / ' ) ) )
* location + + = ' \0 ' ;
else
location = " " ;
2003-07-30 16:10:51 +00:00
/* Request the peer */
2012-02-20 23:43:27 +00:00
tmp - > chan = ast_request ( tech , ast_channel_nativeformats ( qe - > chan ) , qe - > chan , location , & status ) ;
2003-07-30 16:10:51 +00:00
if ( ! tmp - > chan ) { /* If we can't, just go on to the next call */
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_cdr_busy ( ast_channel_cdr ( qe - > chan ) ) ;
2011-06-29 06:39:26 +00:00
}
2011-10-23 11:37:50 +00:00
tmp - > stillgoing = 0 ;
2006-09-03 17:12:30 +00:00
2007-09-20 21:21:28 +00:00
ao2_lock ( qe - > parent ) ;
2009-11-03 21:16:14 +00:00
update_status ( qe - > parent , tmp - > member , get_queue_member_status ( tmp - > member ) ) ;
2006-09-03 17:12:30 +00:00
qe - > parent - > rrpos + + ;
2007-10-26 15:19:46 +00:00
qe - > linpos + + ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( qe - > parent ) ;
2006-09-03 17:12:30 +00:00
2005-01-18 16:40:56 +00:00
( * busies ) + + ;
2003-07-30 16:10:51 +00:00
return 0 ;
2008-03-18 20:59:02 +00:00
}
2009-04-03 22:41:46 +00:00
2011-11-10 23:02:46 +00:00
ast_channel_lock_both ( tmp - > chan , qe - > chan ) ;
2009-04-03 22:41:46 +00:00
2009-01-29 17:08:22 +00:00
if ( qe - > cancel_answered_elsewhere ) {
2012-03-13 18:20:34 +00:00
ast_set_flag ( ast_channel_flags ( tmp - > chan ) , AST_FLAG_ANSWERED_ELSEWHERE ) ;
2009-01-29 17:08:22 +00:00
}
2012-02-13 17:27:06 +00:00
ast_channel_appl_set ( tmp - > chan , " AppQueue " ) ;
ast_channel_data_set ( tmp - > chan , " (Outgoing Line) " ) ;
2012-02-29 16:52:47 +00:00
memset ( ast_channel_whentohangup ( tmp - > chan ) , 0 , sizeof ( * ast_channel_whentohangup ( tmp - > chan ) ) ) ;
2009-04-03 22:41:46 +00:00
/* If the new channel has no callerid, try to guess what it should be */
2012-02-29 16:52:47 +00:00
if ( ! ast_channel_caller ( tmp - > chan ) - > id . number . valid ) {
if ( ast_channel_connected ( qe - > chan ) - > id . number . valid ) {
2010-07-14 15:48:36 +00:00
struct ast_party_caller caller ;
2012-02-29 16:52:47 +00:00
ast_party_caller_set_init ( & caller , ast_channel_caller ( tmp - > chan ) ) ;
caller . id = ast_channel_connected ( qe - > chan ) - > id ;
caller . ani = ast_channel_connected ( qe - > chan ) - > ani ;
2010-07-14 15:48:36 +00:00
ast_channel_set_caller_event ( tmp - > chan , & caller , NULL ) ;
2012-02-29 16:52:47 +00:00
} else if ( ! ast_strlen_zero ( ast_channel_dialed ( qe - > chan ) - > number . str ) ) {
ast_set_callerid ( tmp - > chan , ast_channel_dialed ( qe - > chan ) - > number . str , NULL , NULL ) ;
2012-02-13 17:27:06 +00:00
} else if ( ! ast_strlen_zero ( S_OR ( ast_channel_macroexten ( qe - > chan ) , ast_channel_exten ( qe - > chan ) ) ) ) {
ast_set_callerid ( tmp - > chan , S_OR ( ast_channel_macroexten ( qe - > chan ) , ast_channel_exten ( qe - > chan ) ) , NULL , NULL ) ;
2009-04-03 22:41:46 +00:00
}
2010-05-20 19:40:03 +00:00
tmp - > dial_callerid_absent = 1 ;
2009-04-03 22:41:46 +00:00
}
2012-02-29 16:52:47 +00:00
ast_party_redirecting_copy ( ast_channel_redirecting ( tmp - > chan ) , ast_channel_redirecting ( qe - > chan ) ) ;
2009-04-03 22:41:46 +00:00
2012-02-29 16:52:47 +00:00
ast_channel_dialed ( tmp - > chan ) - > transit_network_select = ast_channel_dialed ( qe - > chan ) - > transit_network_select ;
2009-04-03 22:41:46 +00:00
2012-02-29 16:52:47 +00:00
ast_connected_line_copy_from_caller ( ast_channel_connected ( tmp - > chan ) , ast_channel_caller ( qe - > chan ) ) ;
2005-01-08 17:23:29 +00:00
/* Inherit specially named variables from parent channel */
ast_channel_inherit_variables ( qe - > chan , tmp - > chan ) ;
2009-09-10 23:54:51 +00:00
ast_channel_datastore_inherit ( qe - > chan , tmp - > chan ) ;
2005-01-08 17:23:29 +00:00
2003-07-30 16:10:51 +00:00
/* Presense of ADSI CPE on outgoing channel follows ours */
2012-02-20 23:43:27 +00:00
ast_channel_adsicpe_set ( tmp - > chan , ast_channel_adsicpe ( qe - > chan ) ) ;
2005-01-08 17:23:29 +00:00
2008-01-08 18:42:39 +00:00
/* Inherit context and extension */
2008-04-29 19:42:04 +00:00
macrocontext = pbx_builtin_getvar_helper ( qe - > chan , " MACRO_CONTEXT " ) ;
2012-02-13 17:27:06 +00:00
ast_channel_dialcontext_set ( tmp - > chan , ast_strlen_zero ( macrocontext ) ? ast_channel_context ( qe - > chan ) : macrocontext ) ;
2008-04-29 19:42:04 +00:00
macroexten = pbx_builtin_getvar_helper ( qe - > chan , " MACRO_EXTEN " ) ;
if ( ! ast_strlen_zero ( macroexten ) )
2012-02-13 17:27:06 +00:00
ast_channel_exten_set ( tmp - > chan , macroexten ) ;
2008-01-08 18:42:39 +00:00
else
2012-02-13 17:27:06 +00:00
ast_channel_exten_set ( tmp - > chan , ast_channel_exten ( qe - > chan ) ) ;
2009-05-12 22:32:13 +00:00
if ( ast_cdr_isset_unanswered ( ) ) {
/* they want to see the unanswered dial attempts! */
/* set up the CDR fields on all the CDRs to give sensical information */
2012-02-20 23:43:27 +00:00
ast_cdr_setdestchan ( ast_channel_cdr ( tmp - > chan ) , ast_channel_name ( tmp - > chan ) ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > clid , ast_channel_cdr ( qe - > chan ) - > clid ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > channel , ast_channel_cdr ( qe - > chan ) - > channel ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > src , ast_channel_cdr ( qe - > chan ) - > src ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > dst , ast_channel_exten ( qe - > chan ) ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > dcontext , ast_channel_context ( qe - > chan ) ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > lastapp , ast_channel_cdr ( qe - > chan ) - > lastapp ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > lastdata , ast_channel_cdr ( qe - > chan ) - > lastdata ) ;
ast_channel_cdr ( tmp - > chan ) - > amaflags = ast_channel_cdr ( qe - > chan ) - > amaflags ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > accountcode , ast_channel_cdr ( qe - > chan ) - > accountcode ) ;
strcpy ( ast_channel_cdr ( tmp - > chan ) - > userfield , ast_channel_cdr ( qe - > chan ) - > userfield ) ;
2009-05-12 22:32:13 +00:00
}
2008-01-08 18:42:39 +00:00
2011-11-10 23:02:46 +00:00
ast_channel_unlock ( tmp - > chan ) ;
ast_channel_unlock ( qe - > chan ) ;
2003-07-30 16:10:51 +00:00
/* Place the call, but don't wait on the answer */
2006-06-14 23:20:08 +00:00
if ( ( res = ast_call ( tmp - > chan , location , 0 ) ) ) {
2003-07-30 16:10:51 +00:00
/* Again, keep going even if there's an error */
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " ast call on peer returned %d \n " , res ) ;
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Couldn't call %s \n " , tmp - > interface ) ;
2006-02-14 21:50:35 +00:00
do_hang ( tmp ) ;
2005-01-18 16:40:56 +00:00
( * busies ) + + ;
2009-11-03 21:16:14 +00:00
update_status ( qe - > parent , tmp - > member , get_queue_member_status ( tmp - > member ) ) ;
2003-07-30 16:10:51 +00:00
return 0 ;
2006-06-22 15:43:02 +00:00
} else if ( qe - > parent - > eventwhencalled ) {
char vars [ 2048 ] ;
2011-11-10 23:02:46 +00:00
ast_channel_lock_both ( tmp - > chan , qe - > chan ) ;
2006-06-22 15:43:02 +00:00
manager_event ( EVENT_FLAG_AGENT , " AgentCalled " ,
2010-07-14 15:48:36 +00:00
" Queue: %s \r \n "
" AgentCalled: %s \r \n "
" AgentName: %s \r \n "
" ChannelCalling: %s \r \n "
" DestinationChannel: %s \r \n "
" CallerIDNum: %s \r \n "
" CallerIDName: %s \r \n "
2011-05-25 17:14:11 +00:00
" ConnectedLineNum: %s \r \n "
" ConnectedLineName: %s \r \n "
2010-07-14 15:48:36 +00:00
" Context: %s \r \n "
" Extension: %s \r \n "
" Priority: %d \r \n "
" Uniqueid: %s \r \n "
" %s " ,
2012-01-09 22:15:50 +00:00
qe - > parent - > name , tmp - > interface , tmp - > member - > membername , ast_channel_name ( qe - > chan ) , ast_channel_name ( tmp - > chan ) ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( qe - > chan ) - > id . number . valid , ast_channel_caller ( qe - > chan ) - > id . number . str , " unknown " ) ,
S_COR ( ast_channel_caller ( qe - > chan ) - > id . name . valid , ast_channel_caller ( qe - > chan ) - > id . name . str , " unknown " ) ,
S_COR ( ast_channel_connected ( qe - > chan ) - > id . number . valid , ast_channel_connected ( qe - > chan ) - > id . number . str , " unknown " ) ,
S_COR ( ast_channel_connected ( qe - > chan ) - > id . name . valid , ast_channel_connected ( qe - > chan ) - > id . name . str , " unknown " ) ,
2012-02-20 23:43:27 +00:00
ast_channel_context ( qe - > chan ) , ast_channel_exten ( qe - > chan ) , ast_channel_priority ( qe - > chan ) , ast_channel_uniqueid ( qe - > chan ) ,
2010-07-14 15:48:36 +00:00
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
2011-11-10 23:02:46 +00:00
ast_channel_unlock ( tmp - > chan ) ;
ast_channel_unlock ( qe - > chan ) ;
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Called %s \n " , tmp - > interface ) ;
2004-07-27 04:13:04 +00:00
}
2006-06-14 23:20:08 +00:00
2009-11-03 21:16:14 +00:00
update_status ( qe - > parent , tmp - > member , get_queue_member_status ( tmp - > member ) ) ;
2005-01-18 16:40:56 +00:00
return 1 ;
2003-07-30 16:10:51 +00:00
}
2006-02-14 21:50:35 +00:00
/*! \brief find the entry with the best metric, or NULL */
static struct callattempt * find_best ( struct callattempt * outgoing )
2003-07-30 16:10:51 +00:00
{
2006-02-14 21:50:35 +00:00
struct callattempt * best = NULL , * cur ;
2005-03-28 20:48:24 +00:00
2006-02-14 21:50:35 +00:00
for ( cur = outgoing ; cur ; cur = cur - > q_next ) {
if ( cur - > stillgoing & & /* Not already done */
2006-07-14 05:42:06 +00:00
! cur - > chan & & /* Isn't already going */
( ! best | | cur - > metric < best - > metric ) ) { /* We haven't found one yet, or it's better */
2006-06-14 23:20:08 +00:00
best = cur ;
2003-07-30 16:10:51 +00:00
}
2006-02-14 21:50:35 +00:00
}
2006-06-14 23:20:08 +00:00
2006-02-14 21:50:35 +00:00
return best ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief Place a call to a queue member .
2008-01-08 23:56:24 +00:00
*
* Once metrics have been calculated for each member , this function is used
* to place a call to the appropriate member ( or members ) . The low - level
* channel - handling and error detection is handled in ring_entry
*
2008-03-10 15:45:13 +00:00
* \ retval 1 if a member was called successfully
* \ retval 0 otherwise
2008-01-08 23:56:24 +00:00
*/
2006-02-14 21:50:35 +00:00
static int ring_one ( struct queue_ent * qe , struct callattempt * outgoing , int * busies )
{
int ret = 0 ;
while ( ret = = 0 ) {
struct callattempt * best = find_best ( outgoing ) ;
if ( ! best ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Nobody left to try ringing in queue \n " ) ;
2006-02-14 21:50:35 +00:00
break ;
}
2006-05-09 07:27:03 +00:00
if ( qe - > parent - > strategy = = QUEUE_STRATEGY_RINGALL ) {
2006-02-14 21:50:35 +00:00
struct callattempt * cur ;
/* Ring everyone who shares this best metric (for ringall) */
for ( cur = outgoing ; cur ; cur = cur - > q_next ) {
if ( cur - > stillgoing & & ! cur - > chan & & cur - > metric < = best - > metric ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " (Parallel) Trying '%s' with metric %d \n " , cur - > interface , cur - > metric ) ;
2007-12-28 00:17:41 +00:00
ret | = ring_entry ( qe , cur , busies ) ;
2003-08-13 18:58:01 +00:00
}
}
2006-02-14 21:50:35 +00:00
} else {
/* Ring just the best channel */
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Trying '%s' with metric %d \n " , best - > interface , best - > metric ) ;
2007-12-28 00:17:41 +00:00
ret = ring_entry ( qe , best , busies ) ;
2003-07-30 16:10:51 +00:00
}
2008-08-18 20:23:11 +00:00
/* If we have timed out, break out */
if ( qe - > expire & & ( time ( NULL ) > = qe - > expire ) ) {
2008-09-09 12:32:38 +00:00
ast_debug ( 1 , " Queue timed out while ringing members. \n " ) ;
2008-08-18 20:23:11 +00:00
ret = 0 ;
break ;
}
2003-07-30 16:10:51 +00:00
}
2006-06-14 23:20:08 +00:00
2006-02-14 21:50:35 +00:00
return ret ;
2003-07-30 16:10:51 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Search for best metric and add to Round Robbin queue */
2007-10-26 15:19:46 +00:00
static int store_next_rr ( struct queue_ent * qe , struct callattempt * outgoing )
2004-05-18 05:41:53 +00:00
{
2006-02-14 21:50:35 +00:00
struct callattempt * best = find_best ( outgoing ) ;
2005-03-28 20:48:24 +00:00
2004-05-18 05:41:53 +00:00
if ( best ) {
/* Ring just the best channel */
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Next is '%s' with metric %d \n " , best - > interface , best - > metric ) ;
2004-05-18 05:41:53 +00:00
qe - > parent - > rrpos = best - > metric % 1000 ;
} else {
/* Just increment rrpos */
2005-03-28 20:48:24 +00:00
if ( qe - > parent - > wrapped ) {
2004-05-18 05:56:46 +00:00
/* No more channels, start over */
qe - > parent - > rrpos = 0 ;
} else {
/* Prioritize next entry */
qe - > parent - > rrpos + + ;
}
2004-05-18 05:41:53 +00:00
}
2005-03-28 20:48:24 +00:00
qe - > parent - > wrapped = 0 ;
2006-06-14 23:20:08 +00:00
2004-05-18 05:41:53 +00:00
return 0 ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Search for best metric and add to Linear queue */
2007-10-26 15:19:46 +00:00
static int store_next_lin ( struct queue_ent * qe , struct callattempt * outgoing )
{
struct callattempt * best = find_best ( outgoing ) ;
if ( best ) {
/* Ring just the best channel */
ast_debug ( 1 , " Next is '%s' with metric %d \n " , best - > interface , best - > metric ) ;
qe - > linpos = best - > metric % 1000 ;
} else {
/* Just increment rrpos */
if ( qe - > linwrapped ) {
/* No more channels, start over */
qe - > linpos = 0 ;
} else {
/* Prioritize next entry */
qe - > linpos + + ;
}
}
qe - > linwrapped = 0 ;
return 0 ;
}
2011-09-07 08:17:24 +00:00
/*! \brief Playback announcement to queued members if period has elapsed */
2007-07-21 00:57:00 +00:00
static int say_periodic_announcement ( struct queue_ent * qe , int ringing )
2005-07-31 22:07:58 +00:00
{
int res = 0 ;
time_t now ;
/* Get the current time */
time ( & now ) ;
/* Check to see if it is time to announce */
2005-09-26 00:41:28 +00:00
if ( ( now - qe - > last_periodic_announce_time ) < qe - > parent - > periodicannouncefrequency )
return 0 ;
2005-07-31 22:07:58 +00:00
/* Stop the music on hold so we can play our own file */
2007-07-21 00:57:00 +00:00
if ( ringing )
ast_indicate ( qe - > chan , - 1 ) ;
else
ast_moh_stop ( qe - > chan ) ;
2005-07-31 22:07:58 +00:00
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Playing periodic announcement \n " ) ;
2008-03-18 18:58:42 +00:00
2009-09-30 21:15:01 +00:00
if ( qe - > parent - > randomperiodicannounce & & qe - > parent - > numperiodicannounce ) {
2008-03-18 18:58:42 +00:00
qe - > last_periodic_announce_sound = ( ( unsigned long ) ast_random ( ) ) % qe - > parent - > numperiodicannounce ;
} else if ( qe - > last_periodic_announce_sound > = qe - > parent - > numperiodicannounce | |
2008-12-13 08:36:35 +00:00
ast_str_strlen ( qe - > parent - > sound_periodicannounce [ qe - > last_periodic_announce_sound ] ) = = 0 ) {
2006-01-17 20:58:56 +00:00
qe - > last_periodic_announce_sound = 0 ;
}
2005-07-31 22:07:58 +00:00
/* play the announcement */
2008-12-13 08:36:35 +00:00
res = play_file ( qe - > chan , ast_str_buffer ( qe - > parent - > sound_periodicannounce [ qe - > last_periodic_announce_sound ] ) ) ;
2007-07-19 16:29:51 +00:00
2009-07-08 19:26:55 +00:00
if ( res > 0 & & ! valid_exit ( qe , res ) )
2007-07-19 16:29:51 +00:00
res = 0 ;
2005-07-31 22:07:58 +00:00
2006-05-24 16:54:10 +00:00
/* Resume Music on Hold if the caller is going to stay in the queue */
2007-07-21 00:57:00 +00:00
if ( ! res ) {
2007-10-18 16:16:51 +00:00
if ( ringing )
ast_indicate ( qe - > chan , AST_CONTROL_RINGING ) ;
else
ast_moh_start ( qe - > chan , qe - > moh , NULL ) ;
2007-07-21 00:57:00 +00:00
}
2005-07-31 22:07:58 +00:00
/* update last_periodic_announce_time */
2009-11-09 16:28:31 +00:00
if ( qe - > parent - > relativeperiodicannounce )
time ( & qe - > last_periodic_announce_time ) ;
else
qe - > last_periodic_announce_time = now ;
2005-07-31 22:07:58 +00:00
2006-01-17 20:58:56 +00:00
/* Update the current periodic announcement to the next announcement */
2008-03-18 18:58:42 +00:00
if ( ! qe - > parent - > randomperiodicannounce ) {
qe - > last_periodic_announce_sound + + ;
}
2006-01-17 20:58:56 +00:00
2005-09-26 00:41:28 +00:00
return res ;
2005-07-31 22:07:58 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Record that a caller gave up on waiting in queue */
2005-07-31 22:07:58 +00:00
static void record_abandoned ( struct queue_ent * qe )
{
2009-01-27 19:30:54 +00:00
set_queue_variables ( qe - > parent , qe - > chan ) ;
2010-11-19 21:42:10 +00:00
ao2_lock ( qe - > parent ) ;
2006-03-11 15:17:50 +00:00
manager_event ( EVENT_FLAG_AGENT , " QueueCallerAbandon " ,
2006-07-14 05:42:06 +00:00
" Queue: %s \r \n "
" Uniqueid: %s \r \n "
" Position: %d \r \n "
" OriginalPosition: %d \r \n "
" HoldTime: %d \r \n " ,
2012-01-24 20:12:09 +00:00
qe - > parent - > name , ast_channel_uniqueid ( qe - > chan ) , qe - > pos , qe - > opos , ( int ) ( time ( NULL ) - qe - > start ) ) ;
2006-03-11 15:17:50 +00:00
2005-07-31 22:07:58 +00:00
qe - > parent - > callsabandoned + + ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( qe - > parent ) ;
2005-07-31 22:07:58 +00:00
}
2006-05-25 01:40:20 +00:00
/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2012-03-08 17:48:14 +00:00
static void rna ( int rnatime , struct queue_ent * qe , char * interface , char * membername , int autopause )
2006-05-25 00:11:30 +00:00
{
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Nobody picked up in %d ms \n " , rnatime ) ;
2009-12-23 18:39:37 +00:00
/* Stop ringing, and resume MOH if specified */
if ( qe - > ring_when_ringing ) {
ast_indicate ( qe - > chan , - 1 ) ;
ast_moh_start ( qe - > chan , qe - > moh , NULL ) ;
}
2008-08-25 17:24:02 +00:00
if ( qe - > parent - > eventwhencalled ) {
char vars [ 2048 ] ;
2008-05-21 20:27:45 +00:00
manager_event ( EVENT_FLAG_AGENT , " AgentRingNoAnswer " ,
" Queue: %s \r \n "
" Uniqueid: %s \r \n "
" Channel: %s \r \n "
" Member: %s \r \n "
" MemberName: %s \r \n "
2008-08-25 17:24:02 +00:00
" Ringtime: %d \r \n "
" %s " ,
2008-05-21 20:27:45 +00:00
qe - > parent - > name ,
2012-01-24 20:12:09 +00:00
ast_channel_uniqueid ( qe - > chan ) ,
2012-01-09 22:15:50 +00:00
ast_channel_name ( qe - > chan ) ,
2008-05-21 20:27:45 +00:00
interface ,
membername ,
2008-08-25 17:24:02 +00:00
rnatime ,
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
}
2012-01-24 20:12:09 +00:00
ast_queue_log ( qe - > parent - > name , ast_channel_uniqueid ( qe - > chan ) , membername , " RINGNOANSWER " , " %d " , rnatime ) ;
2012-03-08 17:48:14 +00:00
if ( qe - > parent - > autopause ! = QUEUE_AUTOPAUSE_OFF & & autopause ) {
2011-06-29 06:39:26 +00:00
if ( qe - > parent - > autopausedelay > 0 ) {
struct member * mem ;
ao2_lock ( qe - > parent ) ;
if ( ( mem = interface_exists ( qe - > parent , interface ) ) ) {
time_t idletime = time ( & idletime ) - mem - > lastcall ;
if ( ( mem - > lastcall ! = 0 ) & & ( qe - > parent - > autopausedelay > idletime ) ) {
ao2_unlock ( qe - > parent ) ;
2011-10-23 14:35:26 +00:00
ao2_ref ( mem , - 1 ) ;
2011-06-29 06:39:26 +00:00
return ;
}
2011-10-23 14:35:26 +00:00
ao2_ref ( mem , - 1 ) ;
2011-06-29 06:39:26 +00:00
}
ao2_unlock ( qe - > parent ) ;
}
2010-05-04 22:46:42 +00:00
if ( qe - > parent - > autopause = = QUEUE_AUTOPAUSE_ON ) {
if ( ! set_member_paused ( qe - > parent - > name , interface , " Auto-Pause " , 1 ) ) {
ast_verb ( 3 , " Auto-Pausing Queue Member %s in queue %s since they failed to answer. \n " ,
interface , qe - > parent - > name ) ;
} else {
ast_verb ( 3 , " Failed to pause Queue Member %s in queue %s! \n " , interface , qe - > parent - > name ) ;
}
2006-05-25 00:11:30 +00:00
} else {
2010-05-04 22:46:42 +00:00
/* If queue autopause is mode all, just don't send any queue to stop.
* the function will stop in all queues */
if ( ! set_member_paused ( " " , interface , " Auto-Pause " , 1 ) ) {
ast_verb ( 3 , " Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s. \n " ,
interface , qe - > parent - > name ) ;
} else {
ast_verb ( 3 , " Failed to pause Queue Member %s in all queues! \n " , interface ) ;
}
2006-05-25 00:11:30 +00:00
}
}
2006-07-14 05:42:06 +00:00
return ;
}
2005-07-31 22:07:58 +00:00
2004-06-22 17:42:14 +00:00
# define AST_MAX_WATCHERS 256
2010-05-20 19:40:03 +00:00
/*!
* \ brief Wait for a member to answer the call
2008-01-08 23:56:24 +00:00
*
* \ param [ in ] qe the queue_ent corresponding to the caller in the queue
* \ param [ in ] outgoing the list of callattempts . Relevant ones will have their chan and stillgoing parameters non - zero
* \ param [ in ] to the amount of time ( in milliseconds ) to wait for a response
* \ param [ out ] digit if a user presses a digit to exit the queue , this is the digit the caller pressed
* \ param [ in ] prebusies number of busy members calculated prior to calling wait_for_answer
* \ 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
* \ param [ in ] forwardsallowed used to detect if we should allow call forwarding , based on the ' i ' option to Queue ( )
2010-05-20 19:40:03 +00:00
* \ param [ in ] update_connectedline Allow connected line and redirecting updates to pass through .
2009-06-03 20:30:10 +00:00
*
* \ todo eventually all call forward logic should be intergerated into and replaced by ast_call_forward ( )
2008-01-08 23:56:24 +00:00
*/
2009-04-03 22:41:46 +00:00
static struct callattempt * wait_for_answer ( struct queue_ent * qe , struct callattempt * outgoing , int * to , char * digit , int prebusies , int caller_disconnect , int forwardsallowed , int update_connectedline )
2003-08-01 23:42:49 +00:00
{
2007-11-14 01:15:26 +00:00
const char * queue = qe - > parent - > name ;
2008-01-02 18:05:57 +00:00
struct callattempt * o , * start = NULL , * prev = NULL ;
Enhancements to connected line and redirecting work.
From reviewboard:
Digium has a commercial customer who has made extensive use of the connected party and
redirecting information present in later versions of Asterisk Business Edition and which
is to be in the upcoming 1.8 release. Through their use of the feature, new problems and solutions
have come about. This patch adds several enhancements to maximize usage of the connected party
and redirecting information functionality.
First, Asterisk trunk already had connected line interception macros. These macros allow you to
manipulate connected line information before it was sent out to its target. This patch adds the
same feature except for redirecting information instead.
Second, the ast_callerid and ast_party_id structures have been enhanced to provide a "tag." This
tag can be set with func_callerid, func_connectedline, func_redirecting, and in the case of DAHDI,
mISDN, and SIP channels, can be set in a configuration file. The idea behind the callerid tag is
that it can be set to whatever value the administrator likes. Later, when running connected line
and redirecting macros, the admin can read the tag off the appropriate structure to determine what
action to take. You can think of this sort of like a channel variable, except that instead of having
the variable associated with a channel, the variable is associated with a specific identity within
Asterisk.
Third, app_dial has two new options, s and u. The s option lets a dialplan writer force a specific
caller ID tag to be placed on the outgoing channel. The u option allows the dialplan writer to force
a specific calling presentation value on the outgoing channel.
Fourth, there is a new control frame subclass called AST_CONTROL_READ_ACTION added. This was added
to correct a very specific situation. In the case of SIP semi-attended (blond) transfers, the party
being transferred would not have the opportunity to run a connected line interception macro to
possibly alter the transfer target's connected line information. The issue here was that during a
blond transfer, the SIP transfer code has no bridged channel on which to queue the connected line
update. The way this was corrected was to add this new control frame subclass. Now, we queue an
AST_CONTROL_READ_ACTION frame on the channel on which the connected line interception macro should
be run. When ast_read is called to read the frame, ast_read responds by calling a callback function
associated with the specific read action the control frame describes. In this case, the action taken
is to run the connected line interception macro on the transferee's channel.
Review: https://reviewboard.asterisk.org/r/652/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@263541 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-05-17 15:36:31 +00:00
int res ;
2004-10-26 22:25:43 +00:00
int status ;
2005-01-18 16:40:56 +00:00
int numbusies = prebusies ;
2004-10-05 06:46:11 +00:00
int numnochan = 0 ;
2005-01-18 16:40:56 +00:00
int stillgoing = 0 ;
2002-09-02 23:15:40 +00:00
int orig = * to ;
struct ast_frame * f ;
2006-02-14 21:50:35 +00:00
struct callattempt * peer = NULL ;
2002-09-02 23:15:40 +00:00
struct ast_channel * winner ;
2003-07-30 16:10:51 +00:00
struct ast_channel * in = qe - > chan ;
2006-09-20 05:59:53 +00:00
char on [ 80 ] = " " ;
char membername [ 80 ] = " " ;
2006-05-25 00:11:30 +00:00
long starttime = 0 ;
2007-09-05 21:59:52 +00:00
long endtime = 0 ;
# ifdef HAVE_EPOLL
struct callattempt * epollo ;
# endif
2009-04-03 22:41:46 +00:00
struct ast_party_connected_line connected_caller ;
char * inchan_name ;
2009-05-05 20:54:07 +00:00
ast_party_connected_line_init ( & connected_caller ) ;
2009-04-03 22:41:46 +00:00
ast_channel_lock ( qe - > chan ) ;
2012-01-09 22:15:50 +00:00
inchan_name = ast_strdupa ( ast_channel_name ( qe - > chan ) ) ;
2009-04-03 22:41:46 +00:00
ast_channel_unlock ( qe - > chan ) ;
2006-05-25 00:11:30 +00:00
2006-06-14 23:20:08 +00:00
starttime = ( long ) time ( NULL ) ;
2007-09-05 21:59:52 +00:00
# ifdef HAVE_EPOLL
2007-09-10 19:10:46 +00:00
for ( epollo = outgoing ; epollo ; epollo = epollo - > q_next ) {
2007-12-21 00:44:17 +00:00
if ( epollo - > chan )
2007-09-10 19:10:46 +00:00
ast_poll_channel_add ( in , epollo - > chan ) ;
}
2007-09-05 21:59:52 +00:00
# endif
2003-07-30 16:10:51 +00:00
2006-07-14 05:42:06 +00:00
while ( * to & & ! peer ) {
2006-02-14 21:50:35 +00:00
int numlines , retry , pos = 1 ;
struct ast_channel * watchers [ AST_MAX_WATCHERS ] ;
watchers [ 0 ] = in ;
2008-01-02 18:05:57 +00:00
start = NULL ;
2006-02-14 21:50:35 +00:00
for ( retry = 0 ; retry < 2 ; retry + + ) {
numlines = 0 ;
for ( o = outgoing ; o ; o = o - > q_next ) { /* Keep track of important channels */
if ( o - > stillgoing ) { /* Keep track of important channels */
stillgoing = 1 ;
2008-01-02 18:05:57 +00:00
if ( o - > chan ) {
2011-06-08 20:48:03 +00:00
if ( pos < AST_MAX_WATCHERS ) {
watchers [ pos + + ] = o - > chan ;
}
2008-01-02 18:05:57 +00:00
if ( ! start )
start = o ;
else
prev - > call_next = o ;
prev = o ;
}
2006-02-14 21:50:35 +00:00
}
numlines + + ;
}
if ( pos > 1 /* found */ | | ! stillgoing /* nobody listening */ | |
2006-07-14 05:42:06 +00:00
( qe - > parent - > strategy ! = QUEUE_STRATEGY_RINGALL ) /* ring would not be delivered */ )
2006-02-14 21:50:35 +00:00
break ;
2005-01-18 16:40:56 +00:00
/* On "ringall" strategy we only move to the next penalty level
when * all * ringing phones are done in the current penalty level */
ring_one ( qe , outgoing , & numbusies ) ;
2006-02-14 21:50:35 +00:00
/* and retry... */
2002-09-02 23:15:40 +00:00
}
2006-02-14 21:50:35 +00:00
if ( pos = = 1 /* not found */ ) {
2004-10-05 06:46:11 +00:00
if ( numlines = = ( numbusies + numnochan ) ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Everyone is busy at this time \n " ) ;
2002-09-02 23:15:40 +00:00
} else {
2009-01-13 22:30:59 +00:00
ast_debug ( 3 , " No one is answering queue '%s' (%d numlines / %d busies / %d failed channels) \n " , queue , numlines , numbusies , numnochan ) ;
2002-09-02 23:15:40 +00:00
}
* to = 0 ;
return NULL ;
}
2009-09-10 23:54:51 +00:00
/* Poll for events from both the incoming channel as well as any outgoing channels */
2002-09-02 23:15:40 +00:00
winner = ast_waitfor_n ( watchers , pos , to ) ;
2009-09-10 23:54:51 +00:00
/* Service all of the outgoing channels */
2008-01-02 18:05:57 +00:00
for ( o = start ; o ; o = o - > call_next ) {
2009-04-03 22:41:46 +00:00
/* We go with a static buffer here instead of using ast_strdupa. Using
* ast_strdupa in a loop like this one can cause a stack overflow
*/
char ochan_name [ AST_CHANNEL_NAME ] ;
2009-06-12 14:55:07 +00:00
if ( o - > chan ) {
ast_channel_lock ( o - > chan ) ;
2012-01-09 22:15:50 +00:00
ast_copy_string ( ochan_name , ast_channel_name ( o - > chan ) , sizeof ( ochan_name ) ) ;
2009-06-12 14:55:07 +00:00
ast_channel_unlock ( o - > chan ) ;
}
2012-02-20 23:43:27 +00:00
if ( o - > stillgoing & & ( o - > chan ) & & ( ast_channel_state ( o - > chan ) = = AST_STATE_UP ) ) {
2002-09-02 23:15:40 +00:00
if ( ! peer ) {
2009-04-03 22:41:46 +00:00
ast_verb ( 3 , " %s answered %s \n " , ochan_name , inchan_name ) ;
if ( update_connectedline ) {
2010-05-20 19:40:03 +00:00
if ( o - > pending_connected_update ) {
2012-02-27 16:50:19 +00:00
if ( ast_channel_connected_line_sub ( o - > chan , in , & o - > connected , 0 ) & &
ast_channel_connected_line_macro ( o - > chan , in , & o - > connected , 1 , 0 ) ) {
2010-07-14 15:48:36 +00:00
ast_channel_update_connected_line ( in , & o - > connected , NULL ) ;
2009-06-01 20:57:31 +00:00
}
2010-05-20 19:40:03 +00:00
} else if ( ! o - > dial_callerid_absent ) {
2009-04-03 22:41:46 +00:00
ast_channel_lock ( o - > chan ) ;
2012-02-29 16:52:47 +00:00
ast_connected_line_copy_from_caller ( & connected_caller , ast_channel_caller ( o - > chan ) ) ;
2009-04-03 22:41:46 +00:00
ast_channel_unlock ( o - > chan ) ;
connected_caller . source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER ;
2010-07-14 15:48:36 +00:00
ast_channel_update_connected_line ( in , & connected_caller , NULL ) ;
2009-04-03 22:41:46 +00:00
ast_party_connected_line_free ( & connected_caller ) ;
}
}
Generic Advice of Charge.
Asterisk Generic AOC Representation
- Generic AOC encode/decode routines.
(Generic AOC must be encoded to be passed on the wire in the AST_CONTROL_AOC frame)
- AST_CONTROL_AOC frame type to represent generic encoded AOC data
- Manager events for AOC-S, AOC-D, and AOC-E messages
Asterisk App Support
- app_dial AOC-S pass-through support on call setup
- app_queue AOC-S pass-through support on call setup
AOC Unit Tests
- AOC Unit Tests for encode/decode routines
- AOC Unit Test for manager event representation.
SIP AOC Support
- Pass-through of generic AOC-D and AOC-E messages to snom phones via the
snom AOC specification.
- Creation of chan_sip page3 flags for the addition of the new
'snom_aoc_enabled' sip.conf option.
IAX AOC Support
- Natively supports AOC pass-through through the use of the new
AST_CONTROL_AOC frame type
DAHDI AOC Support
- ETSI PRI full AOC Pass-through support
- 'aoc_enable' chan_dahdi.conf option for independently enabling
pass-through of AOC-S, AOC-D, AOC-E.
- 'aoce_delayhangup' option for retrieving AOC-E on disconnect.
- DAHDI A() dial string option for requesting AOC services.
example usage:
;requests AOC-S, AOC-D, and AOC-E on call setup
exten=>1111,1,Dial(DAHDI/g1/1112/A(s,d,e))
Review: https://reviewboard.asterisk.org/r/552/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@267096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-06-02 18:10:15 +00:00
if ( o - > aoc_s_rate_list ) {
size_t encoded_size ;
struct ast_aoc_encoded * encoded ;
if ( ( encoded = ast_aoc_encode ( o - > aoc_s_rate_list , & encoded_size , o - > chan ) ) ) {
ast_indicate_data ( in , AST_CONTROL_AOC , encoded , encoded_size ) ;
ast_aoc_destroy_encoded ( encoded ) ;
}
}
2003-08-02 21:10:06 +00:00
peer = o ;
2002-09-02 23:15:40 +00:00
}
2003-07-28 14:24:10 +00:00
} else if ( o - > chan & & ( o - > chan = = winner ) ) {
2006-09-28 13:35:04 +00:00
ast_copy_string ( on , o - > member - > interface , sizeof ( on ) ) ;
ast_copy_string ( membername , o - > member - > membername , sizeof ( membername ) ) ;
2012-01-24 20:12:09 +00:00
if ( ! ast_strlen_zero ( ast_channel_call_forward ( o - > chan ) ) & & ! forwardsallowed ) {
ast_verb ( 3 , " Forwarding %s to '%s' prevented. \n " , inchan_name , ast_channel_call_forward ( o - > chan ) ) ;
2007-02-17 03:01:38 +00:00
numnochan + + ;
do_hang ( o ) ;
winner = NULL ;
continue ;
2012-01-24 20:12:09 +00:00
} else if ( ! ast_strlen_zero ( ast_channel_call_forward ( o - > chan ) ) ) {
2009-04-03 22:41:46 +00:00
struct ast_channel * original = o - > chan ;
2006-06-14 23:20:08 +00:00
char tmpchan [ 256 ] ;
2004-10-05 06:46:11 +00:00
char * stuff ;
char * tech ;
2006-06-14 23:20:08 +00:00
2012-01-24 20:12:09 +00:00
ast_copy_string ( tmpchan , ast_channel_call_forward ( o - > chan ) , sizeof ( tmpchan ) ) ;
2004-10-05 06:46:11 +00:00
if ( ( stuff = strchr ( tmpchan , ' / ' ) ) ) {
2006-02-14 21:50:35 +00:00
* stuff + + = ' \0 ' ;
2004-10-05 06:46:11 +00:00
tech = tmpchan ;
} else {
2012-02-13 17:27:06 +00:00
snprintf ( tmpchan , sizeof ( tmpchan ) , " %s@%s " , ast_channel_call_forward ( o - > chan ) , ast_channel_context ( o - > chan ) ) ;
2004-10-05 06:46:11 +00:00
stuff = tmpchan ;
tech = " Local " ;
}
2009-06-26 15:28:53 +00:00
2012-01-24 20:12:09 +00:00
ast_cel_report_event ( in , AST_CEL_FORWARD , NULL , ast_channel_call_forward ( o - > chan ) , NULL ) ;
2009-06-26 15:28:53 +00:00
2004-10-05 06:46:11 +00:00
/* Before processing channel, go ahead and check for forwarding */
2009-04-03 22:41:46 +00:00
ast_verb ( 3 , " Now forwarding %s to '%s/%s' (thanks to %s) \n " , inchan_name , tech , stuff , ochan_name ) ;
2004-10-05 06:46:11 +00:00
/* Setup parameters */
2012-02-20 23:43:27 +00:00
o - > chan = ast_request ( tech , ast_channel_nativeformats ( in ) , in , stuff , & status ) ;
2004-10-05 06:46:11 +00:00
if ( ! o - > chan ) {
2010-07-23 22:24:52 +00:00
ast_log ( LOG_NOTICE ,
" Forwarding failed to create channel to dial '%s/%s' \n " ,
tech , stuff ) ;
2004-10-05 06:46:11 +00:00
o - > stillgoing = 0 ;
numnochan + + ;
} else {
2010-09-21 20:33:20 +00:00
struct ast_party_redirecting redirecting ;
2011-11-10 23:02:46 +00:00
ast_channel_lock_both ( o - > chan , in ) ;
2006-09-03 17:40:08 +00:00
ast_channel_inherit_variables ( in , o - > chan ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
ast_channel_datastore_inherit ( in , o - > chan ) ;
2004-10-05 06:46:11 +00:00
2012-01-24 20:12:09 +00:00
ast_channel_accountcode_set ( o - > chan , ast_channel_accountcode ( in ) ) ;
2004-10-05 06:46:11 +00:00
2012-02-29 16:52:47 +00:00
ast_channel_set_redirecting ( o - > chan , ast_channel_redirecting ( original ) , NULL ) ;
if ( ! ast_channel_redirecting ( o - > chan ) - > from . number . valid
| | ast_strlen_zero ( ast_channel_redirecting ( o - > chan ) - > from . number . str ) ) {
2010-04-03 02:12:33 +00:00
/*
* The call was not previously redirected so it is
* now redirected from this number .
*/
2012-02-29 16:52:47 +00:00
ast_party_number_free ( & ast_channel_redirecting ( o - > chan ) - > from . number ) ;
ast_party_number_init ( & ast_channel_redirecting ( o - > chan ) - > from . number ) ;
ast_channel_redirecting ( o - > chan ) - > from . number . valid = 1 ;
ast_channel_redirecting ( o - > chan ) - > from . number . str =
2012-02-13 17:27:06 +00:00
ast_strdup ( S_OR ( ast_channel_macroexten ( in ) , ast_channel_exten ( in ) ) ) ;
2010-04-03 02:12:33 +00:00
}
2009-04-03 22:41:46 +00:00
2012-02-29 16:52:47 +00:00
ast_channel_dialed ( o - > chan ) - > transit_network_select = ast_channel_dialed ( in ) - > transit_network_select ;
2009-04-03 22:41:46 +00:00
2012-02-29 16:52:47 +00:00
ast_party_caller_copy ( ast_channel_caller ( o - > chan ) , ast_channel_caller ( in ) ) ;
ast_party_connected_line_copy ( ast_channel_connected ( o - > chan ) , ast_channel_connected ( original ) ) ;
2009-04-03 22:41:46 +00:00
Enhancements to connected line and redirecting work.
From reviewboard:
Digium has a commercial customer who has made extensive use of the connected party and
redirecting information present in later versions of Asterisk Business Edition and which
is to be in the upcoming 1.8 release. Through their use of the feature, new problems and solutions
have come about. This patch adds several enhancements to maximize usage of the connected party
and redirecting information functionality.
First, Asterisk trunk already had connected line interception macros. These macros allow you to
manipulate connected line information before it was sent out to its target. This patch adds the
same feature except for redirecting information instead.
Second, the ast_callerid and ast_party_id structures have been enhanced to provide a "tag." This
tag can be set with func_callerid, func_connectedline, func_redirecting, and in the case of DAHDI,
mISDN, and SIP channels, can be set in a configuration file. The idea behind the callerid tag is
that it can be set to whatever value the administrator likes. Later, when running connected line
and redirecting macros, the admin can read the tag off the appropriate structure to determine what
action to take. You can think of this sort of like a channel variable, except that instead of having
the variable associated with a channel, the variable is associated with a specific identity within
Asterisk.
Third, app_dial has two new options, s and u. The s option lets a dialplan writer force a specific
caller ID tag to be placed on the outgoing channel. The u option allows the dialplan writer to force
a specific calling presentation value on the outgoing channel.
Fourth, there is a new control frame subclass called AST_CONTROL_READ_ACTION added. This was added
to correct a very specific situation. In the case of SIP semi-attended (blond) transfers, the party
being transferred would not have the opportunity to run a connected line interception macro to
possibly alter the transfer target's connected line information. The issue here was that during a
blond transfer, the SIP transfer code has no bridged channel on which to queue the connected line
update. The way this was corrected was to add this new control frame subclass. Now, we queue an
AST_CONTROL_READ_ACTION frame on the channel on which the connected line interception macro should
be run. When ast_read is called to read the frame, ast_read responds by calling a callback function
associated with the specific read action the control frame describes. In this case, the action taken
is to run the connected line interception macro on the transferee's channel.
Review: https://reviewboard.asterisk.org/r/652/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@263541 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-05-17 15:36:31 +00:00
/*
* We must unlock o - > chan before calling
* ast_channel_redirecting_macro , because we put o - > chan into
* autoservice there . That is pretty much a guaranteed
* deadlock . This is why the handling of o - > chan ' s lock may
* seem a bit unusual here .
*/
2010-09-21 20:33:20 +00:00
ast_party_redirecting_init ( & redirecting ) ;
2012-02-29 16:52:47 +00:00
ast_party_redirecting_copy ( & redirecting , ast_channel_redirecting ( o - > chan ) ) ;
Enhancements to connected line and redirecting work.
From reviewboard:
Digium has a commercial customer who has made extensive use of the connected party and
redirecting information present in later versions of Asterisk Business Edition and which
is to be in the upcoming 1.8 release. Through their use of the feature, new problems and solutions
have come about. This patch adds several enhancements to maximize usage of the connected party
and redirecting information functionality.
First, Asterisk trunk already had connected line interception macros. These macros allow you to
manipulate connected line information before it was sent out to its target. This patch adds the
same feature except for redirecting information instead.
Second, the ast_callerid and ast_party_id structures have been enhanced to provide a "tag." This
tag can be set with func_callerid, func_connectedline, func_redirecting, and in the case of DAHDI,
mISDN, and SIP channels, can be set in a configuration file. The idea behind the callerid tag is
that it can be set to whatever value the administrator likes. Later, when running connected line
and redirecting macros, the admin can read the tag off the appropriate structure to determine what
action to take. You can think of this sort of like a channel variable, except that instead of having
the variable associated with a channel, the variable is associated with a specific identity within
Asterisk.
Third, app_dial has two new options, s and u. The s option lets a dialplan writer force a specific
caller ID tag to be placed on the outgoing channel. The u option allows the dialplan writer to force
a specific calling presentation value on the outgoing channel.
Fourth, there is a new control frame subclass called AST_CONTROL_READ_ACTION added. This was added
to correct a very specific situation. In the case of SIP semi-attended (blond) transfers, the party
being transferred would not have the opportunity to run a connected line interception macro to
possibly alter the transfer target's connected line information. The issue here was that during a
blond transfer, the SIP transfer code has no bridged channel on which to queue the connected line
update. The way this was corrected was to add this new control frame subclass. Now, we queue an
AST_CONTROL_READ_ACTION frame on the channel on which the connected line interception macro should
be run. When ast_read is called to read the frame, ast_read responds by calling a callback function
associated with the specific read action the control frame describes. In this case, the action taken
is to run the connected line interception macro on the transferee's channel.
Review: https://reviewboard.asterisk.org/r/652/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@263541 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-05-17 15:36:31 +00:00
ast_channel_unlock ( o - > chan ) ;
2012-02-27 16:50:19 +00:00
if ( ( res = ast_channel_redirecting_sub ( o - > chan , in , & redirecting , 0 ) ) & &
( res = ast_channel_redirecting_macro ( o - > chan , in , & redirecting , 1 , 0 ) ) ) {
2010-09-21 20:33:20 +00:00
ast_channel_update_redirecting ( in , & redirecting , NULL ) ;
Enhancements to connected line and redirecting work.
From reviewboard:
Digium has a commercial customer who has made extensive use of the connected party and
redirecting information present in later versions of Asterisk Business Edition and which
is to be in the upcoming 1.8 release. Through their use of the feature, new problems and solutions
have come about. This patch adds several enhancements to maximize usage of the connected party
and redirecting information functionality.
First, Asterisk trunk already had connected line interception macros. These macros allow you to
manipulate connected line information before it was sent out to its target. This patch adds the
same feature except for redirecting information instead.
Second, the ast_callerid and ast_party_id structures have been enhanced to provide a "tag." This
tag can be set with func_callerid, func_connectedline, func_redirecting, and in the case of DAHDI,
mISDN, and SIP channels, can be set in a configuration file. The idea behind the callerid tag is
that it can be set to whatever value the administrator likes. Later, when running connected line
and redirecting macros, the admin can read the tag off the appropriate structure to determine what
action to take. You can think of this sort of like a channel variable, except that instead of having
the variable associated with a channel, the variable is associated with a specific identity within
Asterisk.
Third, app_dial has two new options, s and u. The s option lets a dialplan writer force a specific
caller ID tag to be placed on the outgoing channel. The u option allows the dialplan writer to force
a specific calling presentation value on the outgoing channel.
Fourth, there is a new control frame subclass called AST_CONTROL_READ_ACTION added. This was added
to correct a very specific situation. In the case of SIP semi-attended (blond) transfers, the party
being transferred would not have the opportunity to run a connected line interception macro to
possibly alter the transfer target's connected line information. The issue here was that during a
blond transfer, the SIP transfer code has no bridged channel on which to queue the connected line
update. The way this was corrected was to add this new control frame subclass. Now, we queue an
AST_CONTROL_READ_ACTION frame on the channel on which the connected line interception macro should
be run. When ast_read is called to read the frame, ast_read responds by calling a callback function
associated with the specific read action the control frame describes. In this case, the action taken
is to run the connected line interception macro on the transferee's channel.
Review: https://reviewboard.asterisk.org/r/652/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@263541 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-05-17 15:36:31 +00:00
}
2010-09-21 20:33:20 +00:00
ast_party_redirecting_free ( & redirecting ) ;
ast_channel_unlock ( in ) ;
2009-04-03 22:41:46 +00:00
update_connectedline = 1 ;
2010-07-23 22:24:52 +00:00
if ( ast_call ( o - > chan , stuff , 0 ) ) {
ast_log ( LOG_NOTICE , " Forwarding failed to dial '%s/%s' \n " ,
tech , stuff ) ;
2006-02-14 21:50:35 +00:00
do_hang ( o ) ;
2004-10-05 06:46:11 +00:00
numnochan + + ;
}
}
/* Hangup the original channel now, in case we needed it */
ast_hangup ( winner ) ;
continue ;
}
2002-09-02 23:15:40 +00:00
f = ast_read ( winner ) ;
if ( f ) {
if ( f - > frametype = = AST_FRAME_CONTROL ) {
2009-11-04 14:05:12 +00:00
switch ( f - > subclass . integer ) {
2006-04-04 18:02:40 +00:00
case AST_CONTROL_ANSWER :
2002-09-02 23:15:40 +00:00
/* This is our guy if someone answered. */
if ( ! peer ) {
2009-04-03 22:41:46 +00:00
ast_verb ( 3 , " %s answered %s \n " , ochan_name , inchan_name ) ;
if ( update_connectedline ) {
2010-05-20 19:40:03 +00:00
if ( o - > pending_connected_update ) {
2012-02-27 16:50:19 +00:00
if ( ast_channel_connected_line_sub ( o - > chan , in , & o - > connected , 0 ) & &
ast_channel_connected_line_macro ( o - > chan , in , & o - > connected , 1 , 0 ) ) {
2010-07-14 15:48:36 +00:00
ast_channel_update_connected_line ( in , & o - > connected , NULL ) ;
2009-06-01 20:57:31 +00:00
}
2010-05-20 19:40:03 +00:00
} else if ( ! o - > dial_callerid_absent ) {
2009-04-03 22:41:46 +00:00
ast_channel_lock ( o - > chan ) ;
2012-02-29 16:52:47 +00:00
ast_connected_line_copy_from_caller ( & connected_caller , ast_channel_caller ( o - > chan ) ) ;
2009-04-03 22:41:46 +00:00
ast_channel_unlock ( o - > chan ) ;
connected_caller . source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER ;
2010-07-14 15:48:36 +00:00
ast_channel_update_connected_line ( in , & connected_caller , NULL ) ;
2009-04-03 22:41:46 +00:00
ast_party_connected_line_free ( & connected_caller ) ;
}
}
Generic Advice of Charge.
Asterisk Generic AOC Representation
- Generic AOC encode/decode routines.
(Generic AOC must be encoded to be passed on the wire in the AST_CONTROL_AOC frame)
- AST_CONTROL_AOC frame type to represent generic encoded AOC data
- Manager events for AOC-S, AOC-D, and AOC-E messages
Asterisk App Support
- app_dial AOC-S pass-through support on call setup
- app_queue AOC-S pass-through support on call setup
AOC Unit Tests
- AOC Unit Tests for encode/decode routines
- AOC Unit Test for manager event representation.
SIP AOC Support
- Pass-through of generic AOC-D and AOC-E messages to snom phones via the
snom AOC specification.
- Creation of chan_sip page3 flags for the addition of the new
'snom_aoc_enabled' sip.conf option.
IAX AOC Support
- Natively supports AOC pass-through through the use of the new
AST_CONTROL_AOC frame type
DAHDI AOC Support
- ETSI PRI full AOC Pass-through support
- 'aoc_enable' chan_dahdi.conf option for independently enabling
pass-through of AOC-S, AOC-D, AOC-E.
- 'aoce_delayhangup' option for retrieving AOC-E on disconnect.
- DAHDI A() dial string option for requesting AOC services.
example usage:
;requests AOC-S, AOC-D, and AOC-E on call setup
exten=>1111,1,Dial(DAHDI/g1/1112/A(s,d,e))
Review: https://reviewboard.asterisk.org/r/552/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@267096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-06-02 18:10:15 +00:00
if ( o - > aoc_s_rate_list ) {
size_t encoded_size ;
struct ast_aoc_encoded * encoded ;
if ( ( encoded = ast_aoc_encode ( o - > aoc_s_rate_list , & encoded_size , o - > chan ) ) ) {
ast_indicate_data ( in , AST_CONTROL_AOC , encoded , encoded_size ) ;
ast_aoc_destroy_encoded ( encoded ) ;
}
}
2003-08-02 21:10:06 +00:00
peer = o ;
2002-09-02 23:15:40 +00:00
}
break ;
case AST_CONTROL_BUSY :
2009-04-03 22:41:46 +00:00
ast_verb ( 3 , " %s is busy \n " , ochan_name ) ;
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( in ) )
ast_cdr_busy ( ast_channel_cdr ( in ) ) ;
2006-02-14 21:50:35 +00:00
do_hang ( o ) ;
2007-04-30 16:46:49 +00:00
endtime = ( long ) time ( NULL ) ;
2006-05-25 00:11:30 +00:00
endtime - = starttime ;
2011-09-28 16:59:11 +00:00
rna ( endtime * 1000 , qe , on , membername , qe - > parent - > autopausebusy ) ;
2006-05-09 07:27:03 +00:00
if ( qe - > parent - > strategy ! = QUEUE_STRATEGY_RINGALL ) {
2005-03-28 20:48:24 +00:00
if ( qe - > parent - > timeoutrestart )
2005-03-04 00:59:58 +00:00
* to = orig ;
2009-09-10 23:54:51 +00:00
/* Have enough time for a queue member to answer? */
if ( * to > 500 ) {
ring_one ( qe , outgoing , & numbusies ) ;
starttime = ( long ) time ( NULL ) ;
}
2005-03-04 00:59:58 +00:00
}
2002-09-02 23:15:40 +00:00
numbusies + + ;
break ;
case AST_CONTROL_CONGESTION :
2009-04-03 22:41:46 +00:00
ast_verb ( 3 , " %s is circuit-busy \n " , ochan_name ) ;
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( in ) )
ast_cdr_busy ( ast_channel_cdr ( in ) ) ;
2007-04-30 16:46:49 +00:00
endtime = ( long ) time ( NULL ) ;
2006-05-25 00:11:30 +00:00
endtime - = starttime ;
2011-09-28 16:59:11 +00:00
rna ( endtime * 1000 , qe , on , membername , qe - > parent - > autopauseunavail ) ;
2006-02-14 21:50:35 +00:00
do_hang ( o ) ;
2006-05-09 07:27:03 +00:00
if ( qe - > parent - > strategy ! = QUEUE_STRATEGY_RINGALL ) {
2005-03-28 20:48:24 +00:00
if ( qe - > parent - > timeoutrestart )
2005-03-04 00:59:58 +00:00
* to = orig ;
2009-09-10 23:54:51 +00:00
if ( * to > 500 ) {
ring_one ( qe , outgoing , & numbusies ) ;
starttime = ( long ) time ( NULL ) ;
}
2005-03-04 00:59:58 +00:00
}
2002-09-02 23:15:40 +00:00
numbusies + + ;
break ;
case AST_CONTROL_RINGING :
2009-04-03 22:41:46 +00:00
ast_verb ( 3 , " %s is ringing \n " , ochan_name ) ;
2009-12-23 18:39:37 +00:00
/* Start ring indication when the channel is ringing, if specified */
if ( qe - > ring_when_ringing ) {
ast_moh_stop ( qe - > chan ) ;
ast_indicate ( qe - > chan , AST_CONTROL_RINGING ) ;
}
2002-09-02 23:15:40 +00:00
break ;
case AST_CONTROL_OFFHOOK :
/* Ignore going off hook */
break ;
2009-04-03 22:41:46 +00:00
case AST_CONTROL_CONNECTED_LINE :
if ( ! update_connectedline ) {
ast_verb ( 3 , " Connected line update to %s prevented. \n " , inchan_name ) ;
} else if ( qe - > parent - > strategy = = QUEUE_STRATEGY_RINGALL ) {
struct ast_party_connected_line connected ;
ast_verb ( 3 , " %s connected line has changed. Saving it until answer for %s \n " , ochan_name , inchan_name ) ;
ast_party_connected_line_set_init ( & connected , & o - > connected ) ;
ast_connected_line_parse_data ( f - > data . ptr , f - > datalen , & connected ) ;
2010-07-14 15:48:36 +00:00
ast_party_connected_line_set ( & o - > connected , & connected , NULL ) ;
2009-04-03 22:41:46 +00:00
ast_party_connected_line_free ( & connected ) ;
2010-05-20 19:40:03 +00:00
o - > pending_connected_update = 1 ;
2009-04-03 22:41:46 +00:00
} else {
2012-02-27 16:50:19 +00:00
if ( ast_channel_connected_line_sub ( o - > chan , in , f , 1 ) & &
ast_channel_connected_line_macro ( o - > chan , in , f , 1 , 1 ) ) {
2009-06-01 20:57:31 +00:00
ast_indicate_data ( in , AST_CONTROL_CONNECTED_LINE , f - > data . ptr , f - > datalen ) ;
}
2009-04-03 22:41:46 +00:00
}
break ;
Generic Advice of Charge.
Asterisk Generic AOC Representation
- Generic AOC encode/decode routines.
(Generic AOC must be encoded to be passed on the wire in the AST_CONTROL_AOC frame)
- AST_CONTROL_AOC frame type to represent generic encoded AOC data
- Manager events for AOC-S, AOC-D, and AOC-E messages
Asterisk App Support
- app_dial AOC-S pass-through support on call setup
- app_queue AOC-S pass-through support on call setup
AOC Unit Tests
- AOC Unit Tests for encode/decode routines
- AOC Unit Test for manager event representation.
SIP AOC Support
- Pass-through of generic AOC-D and AOC-E messages to snom phones via the
snom AOC specification.
- Creation of chan_sip page3 flags for the addition of the new
'snom_aoc_enabled' sip.conf option.
IAX AOC Support
- Natively supports AOC pass-through through the use of the new
AST_CONTROL_AOC frame type
DAHDI AOC Support
- ETSI PRI full AOC Pass-through support
- 'aoc_enable' chan_dahdi.conf option for independently enabling
pass-through of AOC-S, AOC-D, AOC-E.
- 'aoce_delayhangup' option for retrieving AOC-E on disconnect.
- DAHDI A() dial string option for requesting AOC services.
example usage:
;requests AOC-S, AOC-D, and AOC-E on call setup
exten=>1111,1,Dial(DAHDI/g1/1112/A(s,d,e))
Review: https://reviewboard.asterisk.org/r/552/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@267096 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-06-02 18:10:15 +00:00
case AST_CONTROL_AOC :
{
struct ast_aoc_decoded * decoded = ast_aoc_decode ( f - > data . ptr , f - > datalen , o - > chan ) ;
if ( decoded & & ( ast_aoc_get_msg_type ( decoded ) = = AST_AOC_S ) ) {
ast_aoc_destroy_decoded ( o - > aoc_s_rate_list ) ;
o - > aoc_s_rate_list = decoded ;
} else {
ast_aoc_destroy_decoded ( decoded ) ;
}
}
break ;
2009-04-03 22:41:46 +00:00
case AST_CONTROL_REDIRECTING :
if ( ! update_connectedline ) {
ast_verb ( 3 , " Redirecting update to %s prevented \n " , inchan_name ) ;
2011-02-04 18:57:39 +00:00
} else if ( qe - > parent - > strategy ! = QUEUE_STRATEGY_RINGALL ) {
2009-04-03 22:41:46 +00:00
ast_verb ( 3 , " %s redirecting info has changed, passing it to %s \n " , ochan_name , inchan_name ) ;
2012-02-27 16:50:19 +00:00
if ( ast_channel_redirecting_sub ( o - > chan , in , f , 1 ) & &
ast_channel_redirecting_macro ( o - > chan , in , f , 1 , 1 ) ) {
Enhancements to connected line and redirecting work.
From reviewboard:
Digium has a commercial customer who has made extensive use of the connected party and
redirecting information present in later versions of Asterisk Business Edition and which
is to be in the upcoming 1.8 release. Through their use of the feature, new problems and solutions
have come about. This patch adds several enhancements to maximize usage of the connected party
and redirecting information functionality.
First, Asterisk trunk already had connected line interception macros. These macros allow you to
manipulate connected line information before it was sent out to its target. This patch adds the
same feature except for redirecting information instead.
Second, the ast_callerid and ast_party_id structures have been enhanced to provide a "tag." This
tag can be set with func_callerid, func_connectedline, func_redirecting, and in the case of DAHDI,
mISDN, and SIP channels, can be set in a configuration file. The idea behind the callerid tag is
that it can be set to whatever value the administrator likes. Later, when running connected line
and redirecting macros, the admin can read the tag off the appropriate structure to determine what
action to take. You can think of this sort of like a channel variable, except that instead of having
the variable associated with a channel, the variable is associated with a specific identity within
Asterisk.
Third, app_dial has two new options, s and u. The s option lets a dialplan writer force a specific
caller ID tag to be placed on the outgoing channel. The u option allows the dialplan writer to force
a specific calling presentation value on the outgoing channel.
Fourth, there is a new control frame subclass called AST_CONTROL_READ_ACTION added. This was added
to correct a very specific situation. In the case of SIP semi-attended (blond) transfers, the party
being transferred would not have the opportunity to run a connected line interception macro to
possibly alter the transfer target's connected line information. The issue here was that during a
blond transfer, the SIP transfer code has no bridged channel on which to queue the connected line
update. The way this was corrected was to add this new control frame subclass. Now, we queue an
AST_CONTROL_READ_ACTION frame on the channel on which the connected line interception macro should
be run. When ast_read is called to read the frame, ast_read responds by calling a callback function
associated with the specific read action the control frame describes. In this case, the action taken
is to run the connected line interception macro on the transferee's channel.
Review: https://reviewboard.asterisk.org/r/652/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@263541 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2010-05-17 15:36:31 +00:00
ast_indicate_data ( in , AST_CONTROL_REDIRECTING , f - > data . ptr , f - > datalen ) ;
}
2009-04-03 22:41:46 +00:00
}
break ;
2002-09-02 23:15:40 +00:00
default :
2009-11-04 14:05:12 +00:00
ast_debug ( 1 , " Dunno what to do with control type %d \n " , f - > subclass . integer ) ;
2009-04-03 22:41:46 +00:00
break ;
2002-09-02 23:15:40 +00:00
}
}
ast_frfree ( f ) ;
2009-09-10 23:54:51 +00:00
} else { /* ast_read() returned NULL */
2006-06-14 23:20:08 +00:00
endtime = ( long ) time ( NULL ) - starttime ;
2009-02-05 20:30:45 +00:00
rna ( endtime * 1000 , qe , on , membername , 1 ) ;
2006-02-14 21:50:35 +00:00
do_hang ( o ) ;
2006-05-09 07:27:03 +00:00
if ( qe - > parent - > strategy ! = QUEUE_STRATEGY_RINGALL ) {
2005-03-28 20:48:24 +00:00
if ( qe - > parent - > timeoutrestart )
2005-03-04 00:59:58 +00:00
* to = orig ;
2009-09-10 23:54:51 +00:00
if ( * to > 500 ) {
ring_one ( qe , outgoing , & numbusies ) ;
starttime = ( long ) time ( NULL ) ;
}
2005-03-04 00:59:58 +00:00
}
2002-09-02 23:15:40 +00:00
}
}
}
2009-09-10 23:54:51 +00:00
/* If we received an event from the caller, deal with it. */
2002-09-02 23:15:40 +00:00
if ( winner = = in ) {
f = ast_read ( in ) ;
2009-11-04 14:05:12 +00:00
if ( ! f | | ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass . integer = = AST_CONTROL_HANGUP ) ) ) {
2002-09-02 23:15:40 +00:00
/* Got hung up */
2006-06-14 23:20:08 +00:00
* to = - 1 ;
2007-09-20 21:21:28 +00:00
if ( f ) {
2008-05-22 16:29:54 +00:00
if ( f - > data . uint32 ) {
2012-02-20 23:43:27 +00:00
ast_channel_hangupcause_set ( in , f - > data . uint32 ) ;
2008-05-22 16:29:54 +00:00
}
2005-05-30 13:34:23 +00:00
ast_frfree ( f ) ;
2007-09-20 21:21:28 +00:00
}
2002-09-02 23:15:40 +00:00
return NULL ;
}
2009-11-04 14:05:12 +00:00
if ( ( f - > frametype = = AST_FRAME_DTMF ) & & caller_disconnect & & ( f - > subclass . integer = = ' * ' ) ) {
ast_verb ( 3 , " User hit %c to disconnect call. \n " , f - > subclass . integer ) ;
2006-06-14 23:20:08 +00:00
* to = 0 ;
2005-05-30 13:34:23 +00:00
ast_frfree ( f ) ;
2002-09-02 23:15:40 +00:00
return NULL ;
}
2009-11-04 14:05:12 +00:00
if ( ( f - > frametype = = AST_FRAME_DTMF ) & & valid_exit ( qe , f - > subclass . integer ) ) {
ast_verb ( 3 , " User pressed digit: %c \n " , f - > subclass . integer ) ;
2006-06-14 23:20:08 +00:00
* to = 0 ;
2009-11-04 14:05:12 +00:00
* digit = f - > subclass . integer ;
2005-05-30 13:34:23 +00:00
ast_frfree ( f ) ;
2003-08-01 23:42:49 +00:00
return NULL ;
}
2005-05-30 13:34:23 +00:00
ast_frfree ( f ) ;
2002-09-02 23:15:40 +00:00
}
2008-01-02 18:05:57 +00:00
if ( ! * to ) {
for ( o = start ; o ; o = o - > call_next )
2009-02-05 20:30:45 +00:00
rna ( orig , qe , o - > interface , o - > member - > membername , 1 ) ;
2008-01-02 18:05:57 +00:00
}
2002-09-02 23:15:40 +00:00
}
2007-09-05 21:59:52 +00:00
# ifdef HAVE_EPOLL
2007-12-21 00:44:17 +00:00
for ( epollo = outgoing ; epollo ; epollo = epollo - > q_next ) {
if ( epollo - > chan )
2007-09-10 19:10:46 +00:00
ast_poll_channel_del ( in , epollo - > chan ) ;
}
2007-09-05 21:59:52 +00:00
# endif
2002-09-02 23:15:40 +00:00
return peer ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief Check if we should start attempting to call queue members .
2008-01-08 23:56:24 +00:00
*
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
* A simple process , really . Count the number of members who are available
* to take our call and then see if we are in a position in the queue at
* which a member could accept our call .
*
* \ param [ in ] qe The caller who wants to know if it is his turn
* \ retval 0 It is not our turn
* \ retval 1 It is our turn
2008-01-08 23:56:24 +00:00
*/
2004-06-23 20:05:18 +00:00
static int is_our_turn ( struct queue_ent * qe )
{
struct queue_ent * ch ;
int res ;
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
int avl ;
int idx = 0 ;
/* This needs a lock. How many members are available to be served? */
ao2_lock ( qe - > parent ) ;
2004-06-23 20:05:18 +00:00
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
avl = num_available_members ( qe - > parent ) ;
2006-05-03 20:01:30 +00:00
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
ch = qe - > parent - > head ;
2006-06-14 23:20:08 +00:00
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
ast_debug ( 1 , " There %s %d available %s. \n " , avl ! = 1 ? " are " : " is " , avl , avl ! = 1 ? " members " : " member " ) ;
while ( ( idx < avl ) & & ( ch ) & & ( ch ! = qe ) ) {
if ( ! ch - > pending )
idx + + ;
ch = ch - > next ;
}
ao2_unlock ( qe - > parent ) ;
2010-02-17 16:24:54 +00:00
/* If the queue entry is within avl [the number of available members] calls from the top ...
* Autofill and position check added to support autofill = no ( as only calls
* from the front of the queue are valid when autofill is disabled )
*/
if ( ch & & idx < avl & & ( qe - > parent - > autofill | | qe - > pos = = 1 ) ) {
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " It's our turn (%s). \n " , ast_channel_name ( qe - > chan ) ) ;
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
res = 1 ;
} else {
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " It's not our turn (%s). \n " , ast_channel_name ( qe - > chan ) ) ;
Merged revisions 185031 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r185031 | mmichelson | 2009-03-30 11:17:35 -0500 (Mon, 30 Mar 2009) | 39 lines
Fix queue weight behavior so that calls in low-weight queues are not inappropriately blocked.
(This is copied and pasted from the review request I made for this patch)
Asterisk has some odd behavior when queue weights are used. The current logic used when
potentially calling a queue member is:
If the member we are going to call is part of another queue and _that other queue has any
callers in it_ and has a higher weight than the queue we are calling from, then don't try
to contact that member. The issue here is what I have marked with underscores. If the
higher-weighted queue has any callers in it at all, then the queue member will be unreachable
from the lower-weighted queue. This has the potential to be really really bad if using a
queue strategy, such as leastrecent or fewestcalls, with the potential to call the same
member repeatedly.
The fix proposed by garychen on issue 13220 is very simple and, as far as I can see, works
well for this situation. With this set of changes, the logic used becomes:
If the member we are going to call is part of another queue, the other queue has a higher
weight than the queue we are calling from, and the higher weight queue has at least as many
callers as available members, then do not try to contact the queue member. If the higher
weighted queue has fewer callers than available members, then there is no reason to deny
the call to this member since the other queue can afford to spare a member.
Since the fix involved writing a generic function for determining the number of available
members in the queue, I also modified the is_our_turn function to make use of the new
num_available_members function to determine if it is our turn to try calling a member. There
is one small behavior change. Before writing this patch, if you had autofill disabled, then
if you were the head caller in a queue, you would automatically be told that it was your
turn to try calling a member. This did not take into account whether there were actually any
queue members available to take the call. Now we actually make sure there is at least one
member available to take the call if autofill is disabled.
(closes issue #13220)
Reported by: garychen
Review: http://reviewboard.digium.com/r/202/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@185072 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-03-30 16:26:48 +00:00
res = 0 ;
2004-06-23 20:05:18 +00:00
}
2006-05-03 20:01:30 +00:00
2004-06-23 20:05:18 +00:00
return res ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief update rules for queues
*
* Calculate min / max penalties making sure if relative they stay within bounds .
* Update queues penalty and set dialplan vars , goto next list entry .
*/
2007-12-21 00:44:17 +00:00
static void update_qe_rule ( struct queue_ent * qe )
{
int max_penalty = qe - > pr - > max_relative ? qe - > max_penalty + qe - > pr - > max_value : qe - > pr - > max_value ;
int min_penalty = qe - > pr - > min_relative ? qe - > min_penalty + qe - > pr - > min_value : qe - > pr - > min_value ;
char max_penalty_str [ 20 ] , min_penalty_str [ 20 ] ;
/* a relative change to the penalty could put it below 0 */
if ( max_penalty < 0 )
max_penalty = 0 ;
if ( min_penalty < 0 )
min_penalty = 0 ;
if ( min_penalty > max_penalty )
min_penalty = max_penalty ;
2008-03-04 23:10:45 +00:00
snprintf ( max_penalty_str , sizeof ( max_penalty_str ) , " %d " , max_penalty ) ;
snprintf ( min_penalty_str , sizeof ( min_penalty_str ) , " %d " , min_penalty ) ;
2007-12-21 00:44:17 +00:00
pbx_builtin_setvar_helper ( qe - > chan , " QUEUE_MAX_PENALTY " , max_penalty_str ) ;
pbx_builtin_setvar_helper ( qe - > chan , " QUEUE_MIN_PENALTY " , min_penalty_str ) ;
qe - > max_penalty = max_penalty ;
qe - > min_penalty = min_penalty ;
2012-01-09 22:15:50 +00:00
ast_debug ( 3 , " Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed \n " , qe - > max_penalty , qe - > min_penalty , ast_channel_name ( qe - > chan ) , qe - > pr - > time ) ;
2007-12-21 00:44:17 +00:00
qe - > pr = AST_LIST_NEXT ( qe - > pr , list ) ;
}
2008-01-08 23:56:24 +00:00
/*! \brief The waiting areas for callers who are not actively calling members
*
* This function is one large loop . This function will return if a caller
* either exits the queue or it becomes that caller ' s turn to attempt calling
* queue members . Inside the loop , we service the caller with periodic announcements ,
* holdtime announcements , etc . as configured in queues . conf
*
2008-01-09 00:18:15 +00:00
* \ retval 0 if the caller ' s turn has arrived
* \ retval - 1 if the caller should exit the queue .
2008-01-08 23:56:24 +00:00
*/
2005-03-28 20:48:24 +00:00
static int wait_our_turn ( struct queue_ent * qe , int ringing , enum queue_result * reason )
2002-09-02 23:15:40 +00:00
{
int res = 0 ;
2004-03-13 06:00:41 +00:00
/* This is the holding pen for callers 2 through maxlen */
2002-09-02 23:15:40 +00:00
for ( ; ; ) {
2005-03-28 20:48:24 +00:00
2004-12-19 21:44:24 +00:00
if ( is_our_turn ( qe ) )
2004-03-13 06:00:41 +00:00
break ;
/* If we have timed out, break out */
2008-08-18 20:04:10 +00:00
if ( qe - > expire & & ( time ( NULL ) > = qe - > expire ) ) {
2005-03-28 20:48:24 +00:00
* reason = QUEUE_TIMEOUT ;
2002-09-02 23:15:40 +00:00
break ;
2005-03-28 20:48:24 +00:00
}
2008-09-08 23:00:06 +00:00
if ( qe - > parent - > leavewhenempty ) {
2008-10-06 15:29:56 +00:00
int status = 0 ;
2004-03-13 06:00:41 +00:00
2008-10-06 15:29:56 +00:00
if ( ( status = get_member_status ( qe - > parent , qe - > max_penalty , qe - > min_penalty , qe - > parent - > leavewhenempty ) ) ) {
2008-09-08 23:00:06 +00:00
* reason = QUEUE_LEAVEEMPTY ;
2012-01-24 20:12:09 +00:00
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 ) ;
2008-09-08 23:00:06 +00:00
leave_queue ( qe ) ;
break ;
}
2004-09-28 03:32:21 +00:00
}
2004-03-13 06:00:41 +00:00
/* Make a position announcement, if enabled */
2007-07-21 00:57:00 +00:00
if ( qe - > parent - > announcefrequency & &
( res = say_position ( qe , ringing ) ) )
2005-05-15 05:42:51 +00:00
break ;
2004-03-13 06:00:41 +00:00
2008-07-31 16:05:12 +00:00
/* If we have timed out, break out */
2008-08-18 20:04:10 +00:00
if ( qe - > expire & & ( time ( NULL ) > = qe - > expire ) ) {
2008-07-31 16:05:12 +00:00
* reason = QUEUE_TIMEOUT ;
break ;
}
2005-07-31 22:07:58 +00:00
/* Make a periodic announcement, if enabled */
2007-07-21 00:57:00 +00:00
if ( qe - > parent - > periodicannouncefrequency & &
( res = say_periodic_announcement ( qe , ringing ) ) )
2006-05-24 16:54:10 +00:00
break ;
2007-12-21 00:44:17 +00:00
/* see if we need to move to the next penalty level for this queue */
2008-08-18 20:04:10 +00:00
while ( qe - > pr & & ( ( time ( NULL ) - qe - > start ) > = qe - > pr - > time ) ) {
2007-12-21 00:44:17 +00:00
update_qe_rule ( qe ) ;
}
2005-07-31 22:07:58 +00:00
2008-07-31 16:05:12 +00:00
/* If we have timed out, break out */
2008-08-18 20:04:10 +00:00
if ( qe - > expire & & ( time ( NULL ) > = qe - > expire ) ) {
2008-07-31 16:05:12 +00:00
* reason = QUEUE_TIMEOUT ;
break ;
}
2002-09-02 23:15:40 +00:00
/* Wait a second before checking again */
2007-07-19 16:29:51 +00:00
if ( ( res = ast_waitfordigit ( qe - > chan , RECHECK * 1000 ) ) ) {
if ( res > 0 & & ! valid_exit ( qe , res ) )
res = 0 ;
else
break ;
}
2008-07-31 16:05:12 +00:00
/* If we have timed out, break out */
2008-08-18 20:04:10 +00:00
if ( qe - > expire & & ( time ( NULL ) > = qe - > expire ) ) {
2008-07-31 16:05:12 +00:00
* reason = QUEUE_TIMEOUT ;
break ;
}
2002-09-02 23:15:40 +00:00
}
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
return res ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief update the queue status
* \ retval Always 0
*/
2009-01-08 19:48:42 +00:00
static int update_queue ( struct call_queue * q , struct member * member , int callcompletedinsl , int newtalktime )
2003-08-02 21:10:06 +00:00
{
2009-01-08 19:48:42 +00:00
int oldtalktime ;
2007-10-24 21:26:27 +00:00
struct member * mem ;
struct call_queue * qtmp ;
2011-10-23 11:37:50 +00:00
struct ao2_iterator queue_iter ;
2007-10-24 21:26:27 +00:00
if ( shared_lastcall ) {
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( qtmp = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2007-10-24 21:26:27 +00:00
ao2_lock ( qtmp ) ;
2008-11-25 01:01:49 +00:00
if ( ( mem = ao2_find ( qtmp - > members , member , OBJ_POINTER ) ) ) {
2007-10-24 21:26:27 +00:00
time ( & mem - > lastcall ) ;
mem - > calls + + ;
mem - > lastqueue = q ;
ao2_ref ( mem , - 1 ) ;
}
ao2_unlock ( qtmp ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( qtmp , " Done with iterator " ) ;
2007-10-24 21:26:27 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2007-10-24 21:26:27 +00:00
} else {
ao2_lock ( q ) ;
time ( & member - > lastcall ) ;
member - > calls + + ;
member - > lastqueue = q ;
ao2_unlock ( q ) ;
}
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2004-03-13 06:00:41 +00:00
q - > callscompleted + + ;
2007-07-10 20:02:05 +00:00
if ( callcompletedinsl )
q - > callscompletedinsl + + ;
2009-01-08 19:48:42 +00:00
/* Calculate talktime using the same exponential average as holdtime code*/
oldtalktime = q - > talktime ;
q - > talktime = ( ( ( oldtalktime < < 2 ) - oldtalktime ) + newtalktime ) > > 2 ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2003-08-02 21:10:06 +00:00
return 0 ;
}
2008-01-08 23:56:24 +00:00
/*! \brief Calculate the metric of each member in the outgoing callattempts
*
* A numeric metric is given to each member depending on the ring strategy used
* by the queue . Members with lower metrics will be called before members with
* higher metrics
2008-03-10 15:45:13 +00:00
* \ retval - 1 if penalties are exceeded
* \ retval 0 otherwise
2008-01-08 23:56:24 +00:00
*/
2006-06-15 13:35:04 +00:00
static int calc_metric ( struct call_queue * q , struct member * mem , int pos , struct queue_ent * qe , struct callattempt * tmp )
2003-07-27 01:55:22 +00:00
{
2009-12-04 18:48:31 +00:00
/* disregarding penalty on too few members? */
2011-10-25 21:11:14 +00:00
int membercount = ao2_container_count ( q - > members ) ;
Simplify queue membercount code
Despite an ominous sounding comment stating that membercount was for "logged
in" members only and thus we couldn't use ao2_container_count(), I could not
find a single place in the code where that seemed to be accurate. The only time
we decremented membercount was when we were marking something dead or actually
removing it. The only places we incremented it were either after ao2_link(), or
trying to correct for having set it to 0 during a reload. In every case where
we were correcting the value, it seemed that we were trying to make the count
actually match what ao2_container_count() would return. The only place I could
find where we made a determination about something being "logged in" or not, we
didn't trust the membercount, but instead looked at devicestate, paused, etc.
This patch removes membercount, replaces its use with ao2_container_count, and
manually adds the results of ao2_container_count to a "membercount" field for
ast_data queue query results. This patch also would fix AST-676, but as it is
slightly riskier than the previously committed fix, the two commits have been
made separately.
Reivew: https://reviewboard.asterisk.org/r/1541/
........
Merged revisions 342383 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 342384 from http://svn.asterisk.org/svn/asterisk/branches/10
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@342385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2011-10-25 20:07:59 +00:00
unsigned char usepenalty = ( membercount < = q - > penaltymemberslimit ) ? 0 : 1 ;
2009-12-04 18:48:31 +00:00
if ( usepenalty ) {
if ( ( qe - > max_penalty & & ( mem - > penalty > qe - > max_penalty ) ) | |
( qe - > min_penalty & & ( mem - > penalty < qe - > min_penalty ) ) ) {
return - 1 ;
}
} else {
ast_debug ( 1 , " Disregarding penalty, %d members and %d in penaltymemberslimit. \n " ,
Simplify queue membercount code
Despite an ominous sounding comment stating that membercount was for "logged
in" members only and thus we couldn't use ao2_container_count(), I could not
find a single place in the code where that seemed to be accurate. The only time
we decremented membercount was when we were marking something dead or actually
removing it. The only places we incremented it were either after ao2_link(), or
trying to correct for having set it to 0 during a reload. In every case where
we were correcting the value, it seemed that we were trying to make the count
actually match what ao2_container_count() would return. The only place I could
find where we made a determination about something being "logged in" or not, we
didn't trust the membercount, but instead looked at devicestate, paused, etc.
This patch removes membercount, replaces its use with ao2_container_count, and
manually adds the results of ao2_container_count to a "membercount" field for
ast_data queue query results. This patch also would fix AST-676, but as it is
slightly riskier than the previously committed fix, the two commits have been
made separately.
Reivew: https://reviewboard.asterisk.org/r/1541/
........
Merged revisions 342383 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 342384 from http://svn.asterisk.org/svn/asterisk/branches/10
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@342385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2011-10-25 20:07:59 +00:00
membercount , q - > penaltymemberslimit ) ;
2009-12-04 18:48:31 +00:00
}
2006-01-06 03:40:12 +00:00
2003-07-27 01:55:22 +00:00
switch ( q - > strategy ) {
case QUEUE_STRATEGY_RINGALL :
2003-08-13 18:58:01 +00:00
/* Everyone equal, except for penalty */
2009-12-04 18:48:31 +00:00
tmp - > metric = mem - > penalty * 1000000 * usepenalty ;
2003-07-27 01:55:22 +00:00
break ;
2007-10-26 15:19:46 +00:00
case QUEUE_STRATEGY_LINEAR :
if ( pos < qe - > linpos ) {
tmp - > metric = 1000 + pos ;
} else {
if ( pos > qe - > linpos )
/* Indicate there is another priority */
qe - > linwrapped = 1 ;
tmp - > metric = pos ;
}
2009-12-04 18:48:31 +00:00
tmp - > metric + = mem - > penalty * 1000000 * usepenalty ;
2007-10-26 15:19:46 +00:00
break ;
2011-01-20 17:14:01 +00:00
case QUEUE_STRATEGY_RRORDERED :
2004-05-18 05:41:53 +00:00
case QUEUE_STRATEGY_RRMEMORY :
2003-07-30 16:10:51 +00:00
if ( pos < q - > rrpos ) {
tmp - > metric = 1000 + pos ;
} else {
2005-03-28 20:48:24 +00:00
if ( pos > q - > rrpos )
2003-08-09 19:04:03 +00:00
/* Indicate there is another priority */
2005-03-28 20:48:24 +00:00
q - > wrapped = 1 ;
2003-07-30 16:10:51 +00:00
tmp - > metric = pos ;
}
2009-12-04 18:48:31 +00:00
tmp - > metric + = mem - > penalty * 1000000 * usepenalty ;
2003-07-30 16:10:51 +00:00
break ;
case QUEUE_STRATEGY_RANDOM :
2006-04-05 17:44:44 +00:00
tmp - > metric = ast_random ( ) % 1000 ;
2009-12-04 18:48:31 +00:00
tmp - > metric + = mem - > penalty * 1000000 * usepenalty ;
2003-08-02 21:10:06 +00:00
break ;
2007-11-06 22:36:55 +00:00
case QUEUE_STRATEGY_WRANDOM :
tmp - > metric = ast_random ( ) % ( ( 1 + mem - > penalty ) * 1000 ) ;
break ;
2003-08-02 21:10:06 +00:00
case QUEUE_STRATEGY_FEWESTCALLS :
tmp - > metric = mem - > calls ;
2009-12-04 18:48:31 +00:00
tmp - > metric + = mem - > penalty * 1000000 * usepenalty ;
2003-08-02 21:10:06 +00:00
break ;
case QUEUE_STRATEGY_LEASTRECENT :
if ( ! mem - > lastcall )
tmp - > metric = 0 ;
else
tmp - > metric = 1000000 - ( time ( NULL ) - mem - > lastcall ) ;
2009-12-04 18:48:31 +00:00
tmp - > metric + = mem - > penalty * 1000000 * usepenalty ;
2003-07-30 16:10:51 +00:00
break ;
default :
ast_log ( LOG_WARNING , " Can't calculate metric for unknown strategy %d \n " , q - > strategy ) ;
break ;
2003-07-27 01:55:22 +00:00
}
return 0 ;
}
2007-03-06 23:20:57 +00:00
enum agent_complete_reason {
CALLER ,
AGENT ,
TRANSFER
} ;
2008-03-10 15:45:13 +00:00
/*! \brief Send out AMI message with member call completion status information */
2007-03-06 23:20:57 +00:00
static void send_agent_complete ( const struct queue_ent * qe , const char * queuename ,
const struct ast_channel * peer , const struct member * member , time_t callstart ,
char * vars , size_t vars_len , enum agent_complete_reason rsn )
{
2007-11-14 13:18:40 +00:00
const char * reason = NULL ; /* silence dumb compilers */
2007-03-06 23:20:57 +00:00
if ( ! qe - > parent - > eventwhencalled )
return ;
switch ( rsn ) {
case CALLER :
reason = " caller " ;
break ;
case AGENT :
reason = " agent " ;
break ;
case TRANSFER :
reason = " transfer " ;
break ;
}
manager_event ( EVENT_FLAG_AGENT , " AgentComplete " ,
" Queue: %s \r \n "
" Uniqueid: %s \r \n "
" Channel: %s \r \n "
" Member: %s \r \n "
" MemberName: %s \r \n "
" HoldTime: %ld \r \n "
" TalkTime: %ld \r \n "
" Reason: %s \r \n "
" %s " ,
2012-01-24 20:12:09 +00:00
queuename , ast_channel_uniqueid ( qe - > chan ) , ast_channel_name ( peer ) , member - > interface , member - > membername ,
2007-03-06 23:20:57 +00:00
( long ) ( callstart - qe - > start ) , ( long ) ( time ( NULL ) - callstart ) , reason ,
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , vars_len ) : " " ) ;
}
2008-03-10 15:45:13 +00:00
2008-06-12 21:23:02 +00:00
struct queue_transfer_ds {
struct queue_ent * qe ;
struct member * member ;
2008-12-11 16:33:16 +00:00
time_t starttime ;
2008-10-14 22:41:13 +00:00
int callcompletedinsl ;
2008-06-12 21:23:02 +00:00
} ;
2008-06-12 23:58:01 +00:00
static void queue_transfer_destroy ( void * data )
{
struct queue_transfer_ds * qtds = data ;
ast_free ( qtds ) ;
}
2008-06-12 21:23:02 +00:00
/*! \brief a datastore used to help correctly log attended transfers of queue callers
*/
static const struct ast_datastore_info queue_transfer_info = {
. type = " queue_transfer " ,
. chan_fixup = queue_transfer_fixup ,
2008-06-12 23:58:01 +00:00
. destroy = queue_transfer_destroy ,
2008-06-12 21:23:02 +00:00
} ;
/*! \brief Log an attended transfer when a queue caller channel is masqueraded
*
* When a caller is masqueraded , we want to log a transfer . Fixup time is the closest we can come to when
* the actual transfer occurs . This happens during the masquerade after datastores are moved from old_chan
* to new_chan . This is why new_chan is referenced for exten , context , and datastore information .
*
* At the end of this , we want to remove the datastore so that this fixup function is not called on any
* future masquerades of the caller during the current call .
*/
2008-12-11 16:33:16 +00:00
static void queue_transfer_fixup ( void * data , struct ast_channel * old_chan , struct ast_channel * new_chan )
2008-06-12 21:23:02 +00:00
{
struct queue_transfer_ds * qtds = data ;
struct queue_ent * qe = qtds - > qe ;
struct member * member = qtds - > member ;
2008-12-11 16:33:16 +00:00
time_t callstart = qtds - > starttime ;
2008-10-14 22:41:13 +00:00
int callcompletedinsl = qtds - > callcompletedinsl ;
2008-06-12 21:23:02 +00:00
struct ast_datastore * datastore ;
2008-07-16 18:59:27 +00:00
2012-01-24 20:12:09 +00:00
ast_queue_log ( qe - > parent - > name , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " TRANSFER " , " %s|%s|%ld|%ld|%d " ,
2012-02-13 17:27:06 +00:00
ast_channel_exten ( new_chan ) , ast_channel_context ( new_chan ) , ( long ) ( callstart - qe - > start ) ,
2008-12-11 16:47:34 +00:00
( long ) ( time ( NULL ) - callstart ) , qe - > opos ) ;
2008-10-14 22:41:13 +00:00
2009-01-08 19:48:42 +00:00
update_queue ( qe - > parent , member , callcompletedinsl , ( time ( NULL ) - callstart ) ) ;
2008-06-12 21:23:02 +00:00
2009-01-15 00:14:17 +00:00
/* No need to lock the channels because they are already locked in ast_do_masquerade */
if ( ( datastore = ast_channel_datastore_find ( old_chan , & queue_transfer_info , NULL ) ) ) {
ast_channel_datastore_remove ( old_chan , datastore ) ;
2008-12-11 16:33:16 +00:00
} else {
2008-06-12 21:23:02 +00:00
ast_log ( LOG_WARNING , " Can't find the queue_transfer datastore. \n " ) ;
}
}
/*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
*
* When a caller is atxferred , then the queue_transfer_info datastore
* is removed from the channel . If it ' s still there after the bridge is
* broken , then the caller was not atxferred .
2009-01-15 00:14:17 +00:00
*
* \ note Only call this with chan locked
2008-06-12 21:23:02 +00:00
*/
static int attended_transfer_occurred ( struct ast_channel * chan )
{
return ast_channel_datastore_find ( chan , & queue_transfer_info , NULL ) ? 0 : 1 ;
}
/*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
*/
2008-12-11 16:33:16 +00:00
static struct ast_datastore * setup_transfer_datastore ( struct queue_ent * qe , struct member * member , time_t starttime , int callcompletedinsl )
2008-06-12 21:23:02 +00:00
{
struct ast_datastore * ds ;
2008-06-18 14:54:47 +00:00
struct queue_transfer_ds * qtds = ast_calloc ( 1 , sizeof ( * qtds ) ) ;
2008-06-12 23:58:01 +00:00
if ( ! qtds ) {
ast_log ( LOG_WARNING , " Memory allocation error! \n " ) ;
2008-12-11 16:33:16 +00:00
return NULL ;
2008-06-12 23:58:01 +00:00
}
2008-06-12 21:23:02 +00:00
ast_channel_lock ( qe - > chan ) ;
2008-08-05 16:56:11 +00:00
if ( ! ( ds = ast_datastore_alloc ( & queue_transfer_info , NULL ) ) ) {
2008-06-12 21:23:02 +00:00
ast_channel_unlock ( qe - > chan ) ;
ast_log ( LOG_WARNING , " Unable to create transfer datastore. queue_log will not show attended transfer \n " ) ;
2008-12-11 16:33:16 +00:00
return NULL ;
2008-06-12 21:23:02 +00:00
}
2008-06-12 23:58:01 +00:00
qtds - > qe = qe ;
2008-06-12 21:23:02 +00:00
/* This member is refcounted in try_calling, so no need to add it here, too */
2008-06-12 23:58:01 +00:00
qtds - > member = member ;
qtds - > starttime = starttime ;
2008-10-14 22:41:13 +00:00
qtds - > callcompletedinsl = callcompletedinsl ;
2008-06-12 23:58:01 +00:00
ds - > data = qtds ;
2008-06-12 21:23:02 +00:00
ast_channel_datastore_add ( qe - > chan , ds ) ;
ast_channel_unlock ( qe - > chan ) ;
2008-12-11 16:33:16 +00:00
return ds ;
2008-06-12 21:23:02 +00:00
}
2009-01-27 19:30:54 +00:00
struct queue_end_bridge {
struct call_queue * q ;
struct ast_channel * chan ;
} ;
static void end_bridge_callback_data_fixup ( struct ast_bridge_config * bconfig , struct ast_channel * originator , struct ast_channel * terminator )
{
struct queue_end_bridge * qeb = bconfig - > end_bridge_callback_data ;
ao2_ref ( qeb , + 1 ) ;
qeb - > chan = originator ;
}
2008-11-09 01:27:00 +00:00
static void end_bridge_callback ( void * data )
{
2009-01-27 19:30:54 +00:00
struct queue_end_bridge * qeb = data ;
struct call_queue * q = qeb - > q ;
struct ast_channel * chan = qeb - > chan ;
2008-11-09 01:27:00 +00:00
2009-01-27 19:30:54 +00:00
if ( ao2_ref ( qeb , - 1 ) = = 1 ) {
set_queue_variables ( q , chan ) ;
/* This unrefs the reference we made in try_calling when we allocated qeb */
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Expire bridge_config reference " ) ;
2009-01-27 19:30:54 +00:00
}
2008-11-09 01:27:00 +00:00
}
2012-03-02 16:57:12 +00:00
/*!
* \ internal
* \ brief A large function which calls members , updates statistics , and bridges the caller and a member
2011-10-23 11:37:50 +00:00
*
2008-01-08 23:56:24 +00:00
* Here is the process of this function
* 1. Process any options passed to the Queue ( ) application . Options here mean the third argument to Queue ( )
* 2. Iterate trough the members of the queue , creating a callattempt corresponding to each member . During this
* iteration , we also check the dialed_interfaces datastore to see if we have already attempted calling this
* member . If we have , we do not create a callattempt . This is in place to prevent call forwarding loops . Also
* during each iteration , we call calc_metric to determine which members should be rung when .
* 3. Call ring_one to place a call to the appropriate member ( s )
* 4. Call wait_for_answer to wait for an answer . If no one answers , return .
* 5. Take care of any holdtime announcements , member delays , or other options which occur after a call has been answered .
* 6. Start the monitor or mixmonitor if the option is set
* 7. Remove the caller from the queue to allow other callers to advance
* 8. Bridge the call .
* 9. Do any post processing after the call has disconnected .
*
* \ param [ in ] qe the queue_ent structure which corresponds to the caller attempting to reach members
2012-03-02 16:57:12 +00:00
* \ param [ in ] opts the options passed as the third parameter to the Queue ( ) application
* \ param [ in ] opt_args the options passed as the third parameter to the Queue ( ) application
2008-02-15 17:29:08 +00:00
* \ param [ in ] announceoverride filename to play to user when waiting
2008-01-08 23:56:24 +00:00
* \ param [ in ] url the url passed as the fourth parameter to the Queue ( ) application
* \ param [ in , out ] tries the number of times we have tried calling queue members
* \ param [ out ] noption set if the call to Queue ( ) has the ' n ' option set .
* \ param [ in ] agi the agi passed as the fifth parameter to the Queue ( ) application
* \ param [ in ] macro the macro passed as the sixth parameter to the Queue ( ) application
* \ param [ in ] gosub the gosub passed as the seventh parameter to the Queue ( ) application
* \ param [ in ] ringing 1 if the ' r ' option is set , otherwise 0
*/
2012-03-02 16:57:12 +00:00
static int try_calling ( struct queue_ent * qe , const struct ast_flags opts , char * * opt_args , char * announceoverride , const char * url , int * tries , int * noption , const char * agi , const char * macro , const char * gosub , int ringing )
2002-09-02 23:15:40 +00:00
{
struct member * cur ;
2006-06-14 23:20:08 +00:00
struct callattempt * outgoing = NULL ; /* the list of calls we are building */
2007-08-01 20:05:13 +00:00
int to , orig ;
2004-02-12 22:11:02 +00:00
char oldexten [ AST_MAX_EXTENSION ] = " " ;
2005-07-10 23:49:57 +00:00
char oldcontext [ AST_MAX_CONTEXT ] = " " ;
2004-02-12 22:11:02 +00:00
char queuename [ 256 ] = " " ;
2006-10-27 18:59:16 +00:00
char interfacevar [ 256 ] = " " ;
2002-09-02 23:15:40 +00:00
struct ast_channel * peer ;
2005-02-01 03:49:35 +00:00
struct ast_channel * which ;
2006-02-14 21:50:35 +00:00
struct callattempt * lpeer ;
2004-05-30 22:10:09 +00:00
struct member * member ;
2008-08-10 20:23:50 +00:00
struct ast_app * application ;
2002-12-11 00:15:13 +00:00
int res = 0 , bridge = 0 ;
2005-01-18 16:40:56 +00:00
int numbusies = 0 ;
2003-07-30 16:10:51 +00:00
int x = 0 ;
2002-12-11 00:15:13 +00:00
char * announce = NULL ;
2003-08-01 23:42:49 +00:00
char digit = 0 ;
2004-02-12 22:11:02 +00:00
time_t callstart ;
2005-07-27 22:55:48 +00:00
time_t now = time ( NULL ) ;
2005-03-28 20:48:24 +00:00
struct ast_bridge_config bridge_config ;
char nondataquality = 1 ;
2006-05-05 21:22:45 +00:00
char * agiexec = NULL ;
2006-10-27 18:59:16 +00:00
char * macroexec = NULL ;
2007-06-19 23:36:34 +00:00
char * gosubexec = NULL ;
2006-05-05 22:02:38 +00:00
const char * monitorfilename ;
const char * monitor_exec ;
const char * monitor_options ;
char tmpid [ 256 ] , tmpid2 [ 256 ] ;
char meid [ 1024 ] , meid2 [ 1024 ] ;
char mixmonargs [ 1512 ] ;
struct ast_app * mixmonapp = NULL ;
char * p ;
2006-06-22 15:43:02 +00:00
char vars [ 2048 ] ;
2006-09-03 19:32:51 +00:00
int forwardsallowed = 1 ;
2009-04-03 22:41:46 +00:00
int update_connectedline = 1 ;
2007-07-10 20:02:05 +00:00
int callcompletedinsl ;
2007-09-10 18:32:59 +00:00
struct ao2_iterator memi ;
2008-12-11 16:33:16 +00:00
struct ast_datastore * datastore , * transfer_ds ;
2009-01-27 19:30:54 +00:00
struct queue_end_bridge * queue_end_bridge = NULL ;
2007-12-07 16:28:36 +00:00
ast_channel_lock ( qe - > chan ) ;
datastore = ast_channel_datastore_find ( qe - > chan , & dialed_interface_info , NULL ) ;
ast_channel_unlock ( qe - > chan ) ;
2007-07-10 20:02:05 +00:00
2005-03-28 20:48:24 +00:00
memset ( & bridge_config , 0 , sizeof ( bridge_config ) ) ;
2007-11-12 18:44:36 +00:00
tmpid [ 0 ] = 0 ;
meid [ 0 ] = 0 ;
2005-08-14 03:08:08 +00:00
time ( & now ) ;
2008-07-31 16:05:12 +00:00
/* If we've already exceeded our timeout, then just stop
* This should be extremely rare . queue_exec will take care
* of removing the caller and reporting the timeout as the reason .
*/
2008-08-18 20:04:10 +00:00
if ( qe - > expire & & now > = qe - > expire ) {
2008-07-31 16:05:12 +00:00
res = 0 ;
goto out ;
}
2012-03-02 16:57:12 +00:00
if ( ast_test_flag ( & opts , OPT_CALLEE_TRANSFER ) ) {
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_REDIRECT ) ;
}
if ( ast_test_flag ( & opts , OPT_CALLER_TRANSFER ) ) {
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_REDIRECT ) ;
}
if ( ast_test_flag ( & opts , OPT_CALLEE_AUTOMON ) ) {
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_AUTOMON ) ;
}
if ( ast_test_flag ( & opts , OPT_CALLER_AUTOMON ) ) {
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_AUTOMON ) ;
}
if ( ast_test_flag ( & opts , OPT_GO_ON ) ) {
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_NO_H_EXTEN ) ;
}
if ( ast_test_flag ( & opts , OPT_DATA_QUALITY ) ) {
nondataquality = 0 ;
}
if ( ast_test_flag ( & opts , OPT_CALLEE_HANGUP ) ) {
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_DISCONNECT ) ;
}
if ( ast_test_flag ( & opts , OPT_CALLER_HANGUP ) ) {
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_DISCONNECT ) ;
}
if ( ast_test_flag ( & opts , OPT_CALLEE_PARK ) ) {
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_PARKCALL ) ;
}
if ( ast_test_flag ( & opts , OPT_CALLER_PARK ) ) {
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_PARKCALL ) ;
}
if ( ast_test_flag ( & opts , OPT_NO_RETRY ) ) {
if ( qe - > parent - > strategy = = QUEUE_STRATEGY_RRMEMORY | | qe - > parent - > strategy = = QUEUE_STRATEGY_LINEAR | | qe - > parent - > strategy = = QUEUE_STRATEGY_RRORDERED )
( * tries ) + + ;
else
* tries = ao2_container_count ( qe - > parent - > members ) ;
* noption = 1 ;
}
if ( ast_test_flag ( & opts , OPT_IGNORE_CALL_FW ) ) {
forwardsallowed = 0 ;
}
if ( ast_test_flag ( & opts , OPT_UPDATE_CONNECTED ) ) {
update_connectedline = 0 ;
}
if ( ast_test_flag ( & opts , OPT_CALLEE_AUTOMIXMON ) ) {
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_AUTOMIXMON ) ;
}
if ( ast_test_flag ( & opts , OPT_CALLER_AUTOMIXMON ) ) {
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_AUTOMIXMON ) ;
}
if ( ast_test_flag ( & opts , OPT_MARK_AS_ANSWERED ) ) {
qe - > cancel_answered_elsewhere = 1 ;
}
2005-03-28 20:48:24 +00:00
2009-01-29 17:08:22 +00:00
/* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited.
( this is mainly to support chan_local )
*/
2012-03-13 18:20:34 +00:00
if ( ast_test_flag ( ast_channel_flags ( qe - > chan ) , AST_FLAG_ANSWERED_ELSEWHERE ) ) {
2009-01-29 17:08:22 +00:00
qe - > cancel_answered_elsewhere = 1 ;
}
2007-09-20 21:21:28 +00:00
ao2_lock ( qe - > parent ) ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " %s is trying to call a queue member. \n " ,
2012-01-09 22:15:50 +00:00
ast_channel_name ( qe - > chan ) ) ;
2005-05-15 05:42:51 +00:00
ast_copy_string ( queuename , qe - > parent - > name , sizeof ( queuename ) ) ;
2004-05-09 07:51:44 +00:00
if ( ! ast_strlen_zero ( qe - > announce ) )
2002-12-11 00:15:13 +00:00
announce = qe - > announce ;
2005-11-08 01:55:31 +00:00
if ( ! ast_strlen_zero ( announceoverride ) )
2002-12-11 00:15:13 +00:00
announce = announceoverride ;
2005-03-28 20:48:24 +00:00
2007-09-10 18:32:59 +00:00
memi = ao2_iterator_init ( qe - > parent - > members , 0 ) ;
while ( ( cur = ao2_iterator_next ( & memi ) ) ) {
2006-03-29 00:30:29 +00:00
struct callattempt * tmp = ast_calloc ( 1 , sizeof ( * tmp ) ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
struct ast_dialed_interface * di ;
2012-02-29 16:52:47 +00:00
AST_LIST_HEAD ( , ast_dialed_interface ) * dialed_interfaces ;
2006-02-14 21:50:35 +00:00
if ( ! tmp ) {
2007-09-11 14:25:20 +00:00
ao2_ref ( cur , - 1 ) ;
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & memi ) ;
2012-01-27 18:47:16 +00:00
ao2_unlock ( qe - > parent ) ;
2002-09-02 23:15:40 +00:00
goto out ;
}
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
if ( ! datastore ) {
2008-08-05 16:56:11 +00:00
if ( ! ( datastore = ast_datastore_alloc ( & dialed_interface_info , NULL ) ) ) {
2012-01-27 18:47:16 +00:00
callattempt_free ( tmp ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
ao2_ref ( cur , - 1 ) ;
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & memi ) ;
2012-01-27 18:47:16 +00:00
ao2_unlock ( qe - > parent ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
goto out ;
}
datastore - > inheritance = DATASTORE_INHERIT_FOREVER ;
2007-12-07 16:28:36 +00:00
if ( ! ( dialed_interfaces = ast_calloc ( 1 , sizeof ( * dialed_interfaces ) ) ) ) {
2012-01-27 18:47:16 +00:00
callattempt_free ( tmp ) ;
2007-12-07 16:28:36 +00:00
ao2_ref ( cur , - 1 ) ;
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & memi ) ;
2012-01-27 18:47:16 +00:00
ao2_unlock ( & qe - > parent ) ;
2007-12-07 16:28:36 +00:00
goto out ;
}
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
datastore - > data = dialed_interfaces ;
AST_LIST_HEAD_INIT ( dialed_interfaces ) ;
2007-12-07 16:28:36 +00:00
ast_channel_lock ( qe - > chan ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
ast_channel_datastore_add ( qe - > chan , datastore ) ;
2007-12-07 16:28:36 +00:00
ast_channel_unlock ( qe - > chan ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
} else
dialed_interfaces = datastore - > data ;
2007-12-07 16:28:36 +00:00
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
AST_LIST_LOCK ( dialed_interfaces ) ;
AST_LIST_TRAVERSE ( dialed_interfaces , di , list ) {
2007-12-07 16:28:36 +00:00
if ( ! strcasecmp ( cur - > interface , di - > interface ) ) {
2008-09-09 12:32:38 +00:00
ast_debug ( 1 , " Skipping dialing interface '%s' since it has already been dialed \n " ,
2007-12-07 16:28:36 +00:00
di - > interface ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
break ;
}
}
2007-12-07 16:28:36 +00:00
AST_LIST_UNLOCK ( dialed_interfaces ) ;
2009-04-03 22:41:46 +00:00
2007-12-07 16:28:36 +00:00
if ( di ) {
2010-05-20 19:40:03 +00:00
callattempt_free ( tmp ) ;
2012-01-27 18:47:16 +00:00
ao2_ref ( cur , - 1 ) ;
2007-12-07 16:28:36 +00:00
continue ;
}
/* It is always ok to dial a Local interface. We only keep track of
* which " real " interfaces have been dialed . The Local channel will
* inherit this list so that if it ends up dialing a real interface ,
* it won ' t call one that has already been called . */
if ( strncasecmp ( cur - > interface , " Local/ " , 6 ) ) {
if ( ! ( di = ast_calloc ( 1 , sizeof ( * di ) + strlen ( cur - > interface ) ) ) ) {
2012-01-27 18:47:16 +00:00
callattempt_free ( tmp ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
ao2_ref ( cur , - 1 ) ;
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & memi ) ;
2012-01-27 18:47:16 +00:00
ao2_unlock ( qe - > parent ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
goto out ;
}
strcpy ( di - > interface , cur - > interface ) ;
2007-12-07 16:28:36 +00:00
AST_LIST_LOCK ( dialed_interfaces ) ;
Merged revisions 90735 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r90735 | mmichelson | 2007-12-03 17:12:17 -0600 (Mon, 03 Dec 2007) | 22 lines
A big one...
This is the merge of the forward-loop branch. The main change here is that call-forwards can no longer loop.
This is accomplished by creating a datastore on the calling channel which has a linked list of all devices
dialed. If a forward happens, then the local channel which is created inherits the datastore. If, through this
progression of forwards and datastore inheritance, a device is attempted to be dialed a second time, it will simply
be skipped and a warning message will be printed to the CLI. After the dialing has been completed, the datastore
is detached from the channel and destroyed.
This change also introduces some side effects to the code which I shall enumerate here:
1. Datastore inheritance has been backported from trunk into 1.4
2. A large chunk of code has been removed from app_dial. This chunk is the section of code
which handles the call forward case after the channel has been requested but before it has
been called. This was removed because call-forwarding still works fine without it, it makes the
code less error-prone should it need changing, and it made this set of changes much less painful
to just have the forwarding handled in one place in each module.
3. Two new files, global_datastores.h and .c have been added. These are necessary since the datastore
which is attached to the channel may be created and attached in either app_dial or app_queue, so they
need a common place to find the datastore info. This approach was taken in case similar datastores are
needed in the future, there will be a common place to add them.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@90873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-12-04 17:08:36 +00:00
AST_LIST_INSERT_TAIL ( dialed_interfaces , di , list ) ;
AST_LIST_UNLOCK ( dialed_interfaces ) ;
}
2007-12-07 16:28:36 +00:00
2010-05-20 19:40:03 +00:00
ast_channel_lock ( qe - > chan ) ;
/*
* Seed the callattempt ' s connected line information with previously
* acquired connected line info from the queued channel . The
* previously acquired connected line info could have been set
* through the CONNECTED_LINE dialplan function .
*/
2012-02-29 16:52:47 +00:00
ast_party_connected_line_copy ( & tmp - > connected , ast_channel_connected ( qe - > chan ) ) ;
2010-05-20 19:40:03 +00:00
ast_channel_unlock ( qe - > chan ) ;
2003-07-27 01:55:22 +00:00
tmp - > stillgoing = - 1 ;
2010-05-20 19:40:03 +00:00
tmp - > member = cur ; /* Place the reference for cur into callattempt. */
2004-06-26 16:26:39 +00:00
tmp - > lastcall = cur - > lastcall ;
2007-10-24 21:26:27 +00:00
tmp - > lastqueue = cur - > lastqueue ;
2005-07-11 21:05:35 +00:00
ast_copy_string ( tmp - > interface , cur - > interface , sizeof ( tmp - > interface ) ) ;
2003-07-27 01:55:22 +00:00
/* Special case: If we ring everyone, go ahead and ring them, otherwise
just calculate their metric for the appropriate strategy */
2006-01-06 03:40:12 +00:00
if ( ! calc_metric ( qe - > parent , cur , x + + , qe , tmp ) ) {
2006-07-14 05:42:06 +00:00
/* Put them in the list of outgoing thingies... We're ready now.
2006-01-06 03:40:12 +00:00
XXX If we ' re forcibly removed , these outgoing calls won ' t get
hung up XXX */
2006-02-14 21:50:35 +00:00
tmp - > q_next = outgoing ;
2006-01-06 03:40:12 +00:00
outgoing = tmp ;
/* If this line is up, don't try anybody else */
2012-02-20 23:43:27 +00:00
if ( outgoing - > chan & & ( ast_channel_state ( outgoing - > chan ) = = AST_STATE_UP ) )
2006-01-06 03:40:12 +00:00
break ;
} else {
2010-05-20 19:40:03 +00:00
callattempt_free ( tmp ) ;
2006-01-06 03:40:12 +00:00
}
2002-09-02 23:15:40 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & memi ) ;
2008-07-03 14:34:25 +00:00
2008-08-18 20:23:11 +00:00
if ( qe - > parent - > timeoutpriority = = TIMEOUT_PRIORITY_APP ) {
/* Application arguments have higher timeout priority (behaviour for <=1.6) */
if ( qe - > expire & & ( ! qe - > parent - > timeout | | ( qe - > expire - now ) < = qe - > parent - > timeout ) )
to = ( qe - > expire - now ) * 1000 ;
else
to = ( qe - > parent - > timeout ) ? qe - > parent - > timeout * 1000 : - 1 ;
} else {
/* Config timeout is higher priority thatn application timeout */
if ( qe - > expire & & qe - > expire < = now ) {
to = 0 ;
} else if ( qe - > parent - > timeout ) {
to = qe - > parent - > timeout * 1000 ;
} else {
to = - 1 ;
}
}
2007-08-01 20:05:13 +00:00
orig = to ;
2007-12-11 17:44:42 +00:00
+ + qe - > pending ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( qe - > parent ) ;
2008-03-18 20:59:02 +00:00
ring_one ( qe , outgoing , & numbusies ) ;
2009-04-03 22:41:46 +00:00
lpeer = wait_for_answer ( qe , outgoing , & to , & digit , numbusies , ast_test_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_DISCONNECT ) , forwardsallowed , update_connectedline ) ;
2011-05-06 08:21:22 +00:00
/* The ast_channel_datastore_remove() function could fail here if the
* datastore was moved to another channel during a masquerade . If this is
* the case , don ' t free the datastore here because later , when the channel
* to which the datastore was moved hangs up , it will attempt to free this
* datastore again , causing a crash
*/
ast_channel_lock ( qe - > chan ) ;
if ( datastore & & ! ast_channel_datastore_remove ( qe - > chan , datastore ) ) {
ast_datastore_free ( datastore ) ;
}
ast_channel_unlock ( qe - > chan ) ;
2007-09-20 21:21:28 +00:00
ao2_lock ( qe - > parent ) ;
2011-01-20 17:14:01 +00:00
if ( qe - > parent - > strategy = = QUEUE_STRATEGY_RRMEMORY | | qe - > parent - > strategy = = QUEUE_STRATEGY_RRORDERED ) {
2007-10-26 15:19:46 +00:00
store_next_rr ( qe , outgoing ) ;
2011-01-20 17:14:01 +00:00
2007-10-26 15:19:46 +00:00
}
if ( qe - > parent - > strategy = = QUEUE_STRATEGY_LINEAR ) {
store_next_lin ( qe , outgoing ) ;
2004-05-18 05:41:53 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( qe - > parent ) ;
2006-02-14 21:50:35 +00:00
peer = lpeer ? lpeer - > chan : NULL ;
2002-09-02 23:15:40 +00:00
if ( ! peer ) {
2007-12-11 17:44:42 +00:00
qe - > pending = 0 ;
2003-08-01 23:42:49 +00:00
if ( to ) {
2006-01-16 17:37:44 +00:00
/* Must gotten hung up */
2002-09-02 23:15:40 +00:00
res = - 1 ;
2003-08-01 23:42:49 +00:00
} else {
2007-09-06 16:31:30 +00:00
/* User exited by pressing a digit */
2005-07-12 22:28:51 +00:00
res = digit ;
2003-08-01 23:42:49 +00:00
}
2007-09-06 16:31:30 +00:00
if ( res = = - 1 )
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " %s: Nobody answered. \n " , ast_channel_name ( qe - > chan ) ) ;
2009-05-12 22:32:13 +00:00
if ( ast_cdr_isset_unanswered ( ) ) {
/* channel contains the name of one of the outgoing channels
in its CDR ; zero out this CDR to avoid a dual - posting */
struct callattempt * o ;
for ( o = outgoing ; o ; o = o - > q_next ) {
if ( ! o - > chan ) {
continue ;
}
2012-02-20 23:43:27 +00:00
if ( strcmp ( ast_channel_cdr ( o - > chan ) - > dstchannel , ast_channel_cdr ( qe - > chan ) - > dstchannel ) = = 0 ) {
ast_set_flag ( ast_channel_cdr ( o - > chan ) , AST_CDR_FLAG_POST_DISABLED ) ;
2009-05-12 22:32:13 +00:00
break ;
}
}
}
2006-02-14 21:50:35 +00:00
} else { /* peer is valid */
2012-03-02 16:57:12 +00:00
/* These variables are used with the F option without arguments (callee jumps to next priority after queue) */
char * caller_context ;
char * caller_extension ;
int caller_priority ;
2002-09-02 23:15:40 +00:00
/* Ah ha! Someone answered within the desired timeframe. Of course after this
2006-07-14 05:42:06 +00:00
we will always return with - 1 so that it is hung up properly after the
2002-09-02 23:15:40 +00:00
conversation . */
2012-02-20 23:43:27 +00:00
if ( ! strcmp ( ast_channel_tech ( qe - > chan ) - > type , " DAHDI " ) )
2005-03-28 20:48:24 +00:00
ast_channel_setoption ( qe - > chan , AST_OPTION_TONE_VERIFY , & nondataquality , sizeof ( nondataquality ) , 0 ) ;
2012-02-20 23:43:27 +00:00
if ( ! strcmp ( ast_channel_tech ( peer ) - > type , " DAHDI " ) )
2005-03-28 20:48:24 +00:00
ast_channel_setoption ( peer , AST_OPTION_TONE_VERIFY , & nondataquality , sizeof ( nondataquality ) , 0 ) ;
2003-08-02 21:10:06 +00:00
/* Update parameters for the queue */
2007-07-10 20:02:05 +00:00
time ( & now ) ;
recalc_holdtime ( qe , ( now - qe - > start ) ) ;
2007-09-20 21:21:28 +00:00
ao2_lock ( qe - > parent ) ;
2007-07-10 20:02:05 +00:00
callcompletedinsl = ( ( now - qe - > start ) < = qe - > parent - > servicelevel ) ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( qe - > parent ) ;
2004-05-30 22:10:09 +00:00
member = lpeer - > member ;
2007-11-07 23:47:45 +00:00
/* Increment the refcount for this member, since we're going to be using it for awhile in here. */
ao2_ref ( member , 1 ) ;
2009-01-29 17:08:22 +00:00
hangupcalls ( outgoing , peer , qe - > cancel_answered_elsewhere ) ;
2002-09-02 23:15:40 +00:00
outgoing = NULL ;
2005-03-28 20:48:24 +00:00
if ( announce | | qe - > parent - > reportholdtime | | qe - > parent - > memberdelay ) {
2002-09-02 23:15:40 +00:00
int res2 ;
2006-06-14 23:20:08 +00:00
2003-07-30 16:10:51 +00:00
res2 = ast_autoservice_start ( qe - > chan ) ;
2004-04-10 21:10:22 +00:00
if ( ! res2 ) {
2004-10-03 20:37:09 +00:00
if ( qe - > parent - > memberdelay ) {
ast_log ( LOG_NOTICE , " Delaying member connect for %d seconds \n " , qe - > parent - > memberdelay ) ;
res2 | = ast_safe_sleep ( peer , qe - > parent - > memberdelay * 1000 ) ;
}
if ( ! res2 & & announce ) {
2007-08-30 22:08:51 +00:00
play_file ( peer , announce ) ;
2004-10-03 16:46:06 +00:00
}
2005-03-28 20:48:24 +00:00
if ( ! res2 & & qe - > parent - > reportholdtime ) {
2004-10-03 16:46:06 +00:00
if ( ! play_file ( peer , qe - > parent - > sound_reporthold ) ) {
2008-02-14 20:46:00 +00:00
int holdtime , holdtimesecs ;
2004-10-03 16:46:06 +00:00
time ( & now ) ;
holdtime = abs ( ( now - qe - > start ) / 60 ) ;
2010-01-05 23:08:50 +00:00
holdtimesecs = abs ( ( now - qe - > start ) % 60 ) ;
if ( holdtime > 0 ) {
2012-01-24 20:12:09 +00:00
ast_say_number ( peer , holdtime , AST_DIGIT_ANY , ast_channel_language ( peer ) , NULL ) ;
2008-02-14 20:46:00 +00:00
play_file ( peer , qe - > parent - > sound_minutes ) ;
}
if ( holdtimesecs > 1 ) {
2012-01-24 20:12:09 +00:00
ast_say_number ( peer , holdtimesecs , AST_DIGIT_ANY , ast_channel_language ( peer ) , NULL ) ;
2008-02-14 20:46:00 +00:00
play_file ( peer , qe - > parent - > sound_seconds ) ;
}
2004-10-03 16:46:06 +00:00
}
2004-04-10 21:10:22 +00:00
}
}
2003-07-30 16:10:51 +00:00
res2 | = ast_autoservice_stop ( qe - > chan ) ;
2007-08-01 15:39:54 +00:00
if ( ast_check_hangup ( peer ) ) {
2002-09-02 23:15:40 +00:00
/* Agent must have hung up */
2012-01-09 22:15:50 +00:00
ast_log ( LOG_WARNING , " Agent on %s hungup on the customer. \n " , ast_channel_name ( peer ) ) ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( queuename , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " AGENTDUMP " , " %s " , " " ) ;
2006-06-22 15:43:02 +00:00
if ( qe - > parent - > eventwhencalled )
2005-01-29 22:42:40 +00:00
manager_event ( EVENT_FLAG_AGENT , " AgentDump " ,
2006-06-22 15:43:02 +00:00
" Queue: %s \r \n "
" Uniqueid: %s \r \n "
" Channel: %s \r \n "
" Member: %s \r \n "
2006-09-20 05:59:53 +00:00
" MemberName: %s \r \n "
2006-06-22 15:43:02 +00:00
" %s " ,
2012-01-24 20:12:09 +00:00
queuename , ast_channel_uniqueid ( qe - > chan ) , ast_channel_name ( peer ) , member - > interface , member - > membername ,
2006-06-22 15:43:02 +00:00
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
2002-09-02 23:15:40 +00:00
ast_hangup ( peer ) ;
2007-11-07 23:47:45 +00:00
ao2_ref ( member , - 1 ) ;
2005-02-13 16:53:29 +00:00
goto out ;
} else if ( res2 ) {
/* Caller must have hung up just before being connected*/
2012-01-09 22:15:50 +00:00
ast_log ( LOG_NOTICE , " Caller was about to talk to agent on %s but the caller hungup. \n " , ast_channel_name ( peer ) ) ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( queuename , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " ABANDON " , " %d|%d|%ld " , qe - > pos , qe - > opos , ( long ) time ( NULL ) - qe - > start ) ;
2005-02-13 16:53:29 +00:00
record_abandoned ( qe ) ;
ast_hangup ( peer ) ;
2007-11-07 23:47:45 +00:00
ao2_ref ( member , - 1 ) ;
2002-09-02 23:15:40 +00:00
return - 1 ;
}
}
2004-04-08 05:03:47 +00:00
/* Stop music on hold */
2007-07-21 00:57:00 +00:00
if ( ringing )
ast_indicate ( qe - > chan , - 1 ) ;
else
ast_moh_stop ( qe - > chan ) ;
2002-09-02 23:15:40 +00:00
/* If appropriate, log that we have a destination channel */
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_cdr_setdestchan ( ast_channel_cdr ( qe - > chan ) , ast_channel_name ( peer ) ) ;
2011-06-29 06:39:26 +00:00
}
2002-09-02 23:15:40 +00:00
/* Make sure channels are compatible */
res = ast_channel_make_compatible ( qe - > chan , peer ) ;
if ( res < 0 ) {
2012-01-24 20:12:09 +00:00
ast_queue_log ( queuename , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " SYSCOMPAT " , " %s " , " " ) ;
2012-01-09 22:15:50 +00:00
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 ) ) ;
2006-05-24 16:54:10 +00:00
record_abandoned ( qe ) ;
2012-02-20 23:43:27 +00:00
ast_cdr_failed ( ast_channel_cdr ( qe - > chan ) ) ;
2002-09-02 23:15:40 +00:00
ast_hangup ( peer ) ;
2007-11-07 23:47:45 +00:00
ao2_ref ( member , - 1 ) ;
2002-09-02 23:15:40 +00:00
return - 1 ;
}
2007-07-05 22:23:23 +00:00
/* Play announcement to the caller telling it's his turn if defined */
if ( ! ast_strlen_zero ( qe - > parent - > sound_callerannounce ) ) {
if ( play_file ( qe - > chan , qe - > parent - > sound_callerannounce ) )
ast_log ( LOG_WARNING , " Announcement file '%s' is unavailable, continuing anyway... \n " , qe - > parent - > sound_callerannounce ) ;
}
2007-09-20 21:21:28 +00:00
ao2_lock ( qe - > parent ) ;
2007-09-07 19:53:30 +00:00
/* if setinterfacevar is defined, make member variables available to the channel */
/* use pbx_builtin_setvar to set a load of variables with one call */
if ( qe - > parent - > setinterfacevar ) {
2008-04-22 18:14:09 +00:00
snprintf ( interfacevar , sizeof ( interfacevar ) , " MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d " ,
2007-09-07 19:53:30 +00:00
member - > interface , member - > membername , member - > calls , ( long ) member - > lastcall , member - > penalty , member - > dynamic , member - > realtime ) ;
2008-04-22 18:14:09 +00:00
pbx_builtin_setvar_multiple ( qe - > chan , interfacevar ) ;
2009-04-13 14:17:56 +00:00
pbx_builtin_setvar_multiple ( peer , interfacevar ) ;
2007-09-07 19:53:30 +00:00
}
/* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
/* use pbx_builtin_setvar to set a load of variables with one call */
if ( qe - > parent - > setqueueentryvar ) {
2008-04-22 18:14:09 +00:00
snprintf ( interfacevar , sizeof ( interfacevar ) , " QEHOLDTIME=%ld,QEORIGINALPOS=%d " ,
2007-09-07 19:53:30 +00:00
( long ) time ( NULL ) - qe - > start , qe - > opos ) ;
2008-04-22 18:14:09 +00:00
pbx_builtin_setvar_multiple ( qe - > chan , interfacevar ) ;
2009-04-13 14:17:56 +00:00
pbx_builtin_setvar_multiple ( peer , interfacevar ) ;
2007-09-07 19:53:30 +00:00
}
2010-11-19 21:42:10 +00:00
ao2_unlock ( qe - > parent ) ;
2007-09-07 19:53:30 +00:00
/* try to set queue variables if configured to do so*/
2009-01-27 19:30:54 +00:00
set_queue_variables ( qe - > parent , qe - > chan ) ;
2009-04-13 14:17:56 +00:00
set_queue_variables ( qe - > parent , peer ) ;
2012-03-02 16:57:12 +00:00
2008-05-28 19:56:18 +00:00
ast_channel_lock ( qe - > chan ) ;
2012-03-02 16:57:12 +00:00
/* Copy next destination data for 'F' option (no args) */
caller_context = ast_strdupa ( ast_channel_context ( qe - > chan ) ) ;
caller_extension = ast_strdupa ( ast_channel_exten ( qe - > chan ) ) ;
caller_priority = ast_channel_priority ( qe - > chan ) ;
2008-05-28 19:56:18 +00:00
if ( ( monitorfilename = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_FILENAME " ) ) ) {
monitorfilename = ast_strdupa ( monitorfilename ) ;
}
ast_channel_unlock ( qe - > chan ) ;
2012-03-02 16:57:12 +00:00
2004-03-13 06:00:41 +00:00
/* Begin Monitoring */
if ( qe - > parent - > monfmt & & * qe - > parent - > monfmt ) {
2006-05-05 22:02:38 +00:00
if ( ! qe - > parent - > montype ) {
2012-02-08 20:49:48 +00:00
const char * monexec ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Starting Monitor as requested. \n " ) ;
2008-06-27 16:16:13 +00:00
ast_channel_lock ( qe - > chan ) ;
2012-02-08 20:49:48 +00:00
if ( ( monexec = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_EXEC " ) ) | | pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_EXEC_ARGS " ) ) {
2006-05-05 22:02:38 +00:00
which = qe - > chan ;
2008-06-27 16:16:13 +00:00
monexec = monexec ? ast_strdupa ( monexec ) : NULL ;
}
2006-05-05 22:02:38 +00:00
else
which = peer ;
2008-06-27 16:16:13 +00:00
ast_channel_unlock ( qe - > chan ) ;
2009-06-15 16:07:23 +00:00
if ( monitorfilename ) {
ast_monitor_start ( which , qe - > parent - > monfmt , monitorfilename , 1 , X_REC_IN | X_REC_OUT ) ;
2012-02-20 23:43:27 +00:00
} else if ( ast_channel_cdr ( qe - > chan ) ) {
ast_monitor_start ( which , qe - > parent - > monfmt , ast_channel_cdr ( qe - > chan ) - > uniqueid , 1 , X_REC_IN | X_REC_OUT ) ;
2009-06-15 16:07:23 +00:00
} else {
/* Last ditch effort -- no CDR, make up something */
snprintf ( tmpid , sizeof ( tmpid ) , " chan-%lx " , ast_random ( ) ) ;
ast_monitor_start ( which , qe - > parent - > monfmt , tmpid , 1 , X_REC_IN | X_REC_OUT ) ;
2006-05-05 22:02:38 +00:00
}
2009-06-15 16:07:23 +00:00
if ( ! ast_strlen_zero ( monexec ) ) {
2008-06-27 00:15:54 +00:00
ast_monitor_setjoinfiles ( which , 1 ) ;
}
2006-05-05 22:02:38 +00:00
} else {
2008-05-28 19:56:18 +00:00
mixmonapp = pbx_findapp ( " MixMonitor " ) ;
if ( mixmonapp ) {
ast_debug ( 1 , " Starting MixMonitor as requested. \n " ) ;
if ( ! monitorfilename ) {
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_copy_string ( tmpid , ast_channel_cdr ( qe - > chan ) - > uniqueid , sizeof ( tmpid ) ) ;
2011-06-29 06:39:26 +00:00
} else {
2008-05-28 19:56:18 +00:00
snprintf ( tmpid , sizeof ( tmpid ) , " chan-%lx " , ast_random ( ) ) ;
2011-06-29 06:39:26 +00:00
}
2008-05-28 19:56:18 +00:00
} else {
const char * m = monitorfilename ;
for ( p = tmpid2 ; p < tmpid2 + sizeof ( tmpid2 ) - 1 ; p + + , m + + ) {
switch ( * m ) {
case ' ^ ' :
if ( * ( m + 1 ) = = ' { ' )
* p = ' $ ' ;
break ;
case ' , ' :
* p + + = ' \\ ' ;
/* Fall through */
default :
* p = * m ;
}
if ( * m = = ' \0 ' )
break ;
2006-05-05 22:02:38 +00:00
}
2008-05-28 19:56:18 +00:00
if ( p = = tmpid2 + sizeof ( tmpid2 ) )
tmpid2 [ sizeof ( tmpid2 ) - 1 ] = ' \0 ' ;
2006-05-05 22:02:38 +00:00
2008-05-28 19:56:18 +00:00
pbx_substitute_variables_helper ( qe - > chan , tmpid2 , tmpid , sizeof ( tmpid ) - 1 ) ;
}
2006-05-05 22:02:38 +00:00
2008-05-28 19:56:18 +00:00
ast_channel_lock ( qe - > chan ) ;
if ( ( monitor_exec = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_EXEC " ) ) ) {
monitor_exec = ast_strdupa ( monitor_exec ) ;
}
if ( ( monitor_options = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_OPTIONS " ) ) ) {
monitor_options = ast_strdupa ( monitor_options ) ;
} else {
monitor_options = " " ;
2006-05-05 22:02:38 +00:00
}
2008-05-28 19:56:18 +00:00
ast_channel_unlock ( qe - > chan ) ;
if ( monitor_exec ) {
const char * m = monitor_exec ;
for ( p = meid2 ; p < meid2 + sizeof ( meid2 ) - 1 ; p + + , m + + ) {
switch ( * m ) {
case ' ^ ' :
if ( * ( m + 1 ) = = ' { ' )
* p = ' $ ' ;
break ;
case ' , ' :
* p + + = ' \\ ' ;
/* Fall through */
default :
* p = * m ;
}
if ( * m = = ' \0 ' )
break ;
}
if ( p = = meid2 + sizeof ( meid2 ) )
meid2 [ sizeof ( meid2 ) - 1 ] = ' \0 ' ;
2006-05-25 14:32:15 +00:00
2008-05-28 19:56:18 +00:00
pbx_substitute_variables_helper ( qe - > chan , meid2 , meid , sizeof ( meid ) - 1 ) ;
}
2006-05-05 22:02:38 +00:00
2008-05-28 19:56:18 +00:00
snprintf ( tmpid2 , sizeof ( tmpid2 ) , " %s.%s " , tmpid , qe - > parent - > monfmt ) ;
2006-05-05 22:02:38 +00:00
2007-04-08 17:13:39 +00:00
if ( ! ast_strlen_zero ( monitor_exec ) )
2007-09-21 18:41:36 +00:00
snprintf ( mixmonargs , sizeof ( mixmonargs ) , " %s,b%s,%s " , tmpid2 , monitor_options , monitor_exec ) ;
2006-07-14 05:42:06 +00:00
else
2007-09-21 18:41:36 +00:00
snprintf ( mixmonargs , sizeof ( mixmonargs ) , " %s,b%s " , tmpid2 , monitor_options ) ;
2008-05-28 19:56:18 +00:00
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Arguments being passed to MixMonitor: %s \n " , mixmonargs ) ;
2007-09-19 13:49:47 +00:00
/* We purposely lock the CDR so that pbx_exec does not update the application data */
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_set_flag ( ast_channel_cdr ( qe - > chan ) , AST_CDR_FLAG_LOCKED ) ;
2011-06-29 06:39:26 +00:00
}
2011-05-03 20:45:32 +00:00
pbx_exec ( qe - > chan , mixmonapp , mixmonargs ) ;
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
ast_clear_flag ( ast_channel_cdr ( qe - > chan ) , AST_CDR_FLAG_LOCKED ) ;
2011-06-29 06:39:26 +00:00
}
2008-05-28 19:56:18 +00:00
} else {
2006-05-05 22:02:38 +00:00
ast_log ( LOG_WARNING , " Asked to run MixMonitor on this call, but cannot find the MixMonitor app! \n " ) ;
2008-05-28 19:56:18 +00:00
}
2005-10-18 20:58:16 +00:00
}
2004-03-13 06:00:41 +00:00
}
2002-09-02 23:15:40 +00:00
/* Drop out of the queue at this point, to prepare for next caller */
leave_queue ( qe ) ;
2006-07-14 05:42:06 +00:00
if ( ! ast_strlen_zero ( url ) & & ast_channel_supports_html ( peer ) ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " app_queue: sendurl=%s. \n " , url ) ;
2006-07-14 05:42:06 +00:00
ast_channel_sendurl ( peer , url ) ;
}
2006-10-27 18:59:16 +00:00
/* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
/* use macro from dialplan if passed as a option, otherwise use the default queue macro */
if ( ! ast_strlen_zero ( macro ) ) {
macroexec = ast_strdupa ( macro ) ;
} else {
if ( qe - > parent - > membermacro )
macroexec = ast_strdupa ( qe - > parent - > membermacro ) ;
}
if ( ! ast_strlen_zero ( macroexec ) ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " app_queue: macro=%s. \n " , macroexec ) ;
2006-10-27 18:59:16 +00:00
res = ast_autoservice_start ( qe - > chan ) ;
if ( res ) {
ast_log ( LOG_ERROR , " Unable to start autoservice on calling channel \n " ) ;
res = - 1 ;
}
2008-08-10 20:23:50 +00:00
application = pbx_findapp ( " Macro " ) ;
2007-09-21 18:41:36 +00:00
2008-08-10 20:23:50 +00:00
if ( application ) {
2009-03-12 21:43:51 +00:00
res = pbx_exec ( peer , application , macroexec ) ;
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Macro exited with status %d \n " , res ) ;
2006-10-27 18:59:16 +00:00
res = 0 ;
} else {
ast_log ( LOG_ERROR , " Could not find application Macro \n " ) ;
res = - 1 ;
}
2007-09-21 18:41:36 +00:00
2006-10-27 18:59:16 +00:00
if ( ast_autoservice_stop ( qe - > chan ) < 0 ) {
ast_log ( LOG_ERROR , " Could not stop autoservice on calling channel \n " ) ;
res = - 1 ;
}
}
2007-06-19 23:36:34 +00:00
/* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
/* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
if ( ! ast_strlen_zero ( gosub ) ) {
gosubexec = ast_strdupa ( gosub ) ;
} else {
if ( qe - > parent - > membergosub )
gosubexec = ast_strdupa ( qe - > parent - > membergosub ) ;
}
if ( ! ast_strlen_zero ( gosubexec ) ) {
2008-09-09 12:32:38 +00:00
ast_debug ( 1 , " app_queue: gosub=%s. \n " , gosubexec ) ;
2007-06-19 23:36:34 +00:00
res = ast_autoservice_start ( qe - > chan ) ;
if ( res ) {
ast_log ( LOG_ERROR , " Unable to start autoservice on calling channel \n " ) ;
res = - 1 ;
}
2008-08-10 20:23:50 +00:00
application = pbx_findapp ( " Gosub " ) ;
2007-06-19 23:36:34 +00:00
2008-08-10 20:23:50 +00:00
if ( application ) {
2007-06-20 21:38:49 +00:00
char * gosub_args , * gosub_argstart ;
/* Set where we came from */
2012-02-13 17:27:06 +00:00
ast_channel_context_set ( peer , " app_queue_gosub_virtual_context " ) ;
ast_channel_exten_set ( peer , " s " ) ;
2012-02-20 23:43:27 +00:00
ast_channel_priority_set ( peer , 0 ) ;
2007-06-20 21:38:49 +00:00
2007-09-21 18:41:36 +00:00
gosub_argstart = strchr ( gosubexec , ' , ' ) ;
2007-06-20 21:38:49 +00:00
if ( gosub_argstart ) {
2011-02-14 06:54:08 +00:00
const char * what_is_s = " s " ;
2007-06-20 21:38:49 +00:00
* gosub_argstart = 0 ;
2012-02-29 16:52:47 +00:00
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 ) ) & &
ast_exists_extension ( peer , gosubexec , " ~~s~~ " , 1 , S_COR ( ast_channel_caller ( peer ) - > id . number . valid , ast_channel_caller ( peer ) - > id . number . str , NULL ) ) ) {
2011-02-14 06:54:08 +00:00
what_is_s = " ~~s~~ " ;
}
if ( asprintf ( & gosub_args , " %s,%s,1(%s) " , gosubexec , what_is_s , gosub_argstart + 1 ) < 0 ) {
2008-11-02 18:52:13 +00:00
ast_log ( LOG_WARNING , " asprintf() failed: %s \n " , strerror ( errno ) ) ;
gosub_args = NULL ;
}
2008-12-03 18:37:46 +00:00
* gosub_argstart = ' , ' ;
2007-06-20 21:38:49 +00:00
} else {
2011-02-14 06:54:08 +00:00
const char * what_is_s = " s " ;
2012-02-29 16:52:47 +00:00
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 ) ) & &
ast_exists_extension ( peer , gosubexec , " ~~s~~ " , 1 , S_COR ( ast_channel_caller ( peer ) - > id . number . valid , ast_channel_caller ( peer ) - > id . number . str , NULL ) ) ) {
2011-02-14 06:54:08 +00:00
what_is_s = " ~~s~~ " ;
}
if ( asprintf ( & gosub_args , " %s,%s,1 " , gosubexec , what_is_s ) < 0 ) {
2008-11-02 18:52:13 +00:00
ast_log ( LOG_WARNING , " asprintf() failed: %s \n " , strerror ( errno ) ) ;
gosub_args = NULL ;
}
2007-06-20 21:38:49 +00:00
}
if ( gosub_args ) {
2008-12-03 17:07:09 +00:00
res = pbx_exec ( peer , application , gosub_args ) ;
2008-12-03 18:37:46 +00:00
if ( ! res ) {
2008-12-29 18:04:52 +00:00
struct ast_pbx_args args ;
memset ( & args , 0 , sizeof ( args ) ) ;
args . no_hangup_chan = 1 ;
ast_pbx_run_args ( peer , & args ) ;
2008-12-03 18:37:46 +00:00
}
2008-12-29 18:04:52 +00:00
ast_free ( gosub_args ) ;
2008-09-09 12:32:38 +00:00
ast_debug ( 1 , " Gosub exited with status %d \n " , res ) ;
2008-12-03 18:37:46 +00:00
} else {
2007-06-20 21:38:49 +00:00
ast_log ( LOG_ERROR , " Could not Allocate string for Gosub arguments -- Gosub Call Aborted! \n " ) ;
2008-12-03 18:37:46 +00:00
}
2007-06-19 23:36:34 +00:00
} else {
ast_log ( LOG_ERROR , " Could not find application Gosub \n " ) ;
res = - 1 ;
}
if ( ast_autoservice_stop ( qe - > chan ) < 0 ) {
ast_log ( LOG_ERROR , " Could not stop autoservice on calling channel \n " ) ;
res = - 1 ;
}
}
2006-05-05 21:22:45 +00:00
if ( ! ast_strlen_zero ( agi ) ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " app_queue: agi=%s. \n " , agi ) ;
2008-08-10 20:23:50 +00:00
application = pbx_findapp ( " agi " ) ;
if ( application ) {
2006-05-05 21:22:45 +00:00
agiexec = ast_strdupa ( agi ) ;
2011-05-03 20:45:32 +00:00
pbx_exec ( qe - > chan , application , agiexec ) ;
2006-07-14 05:42:06 +00:00
} else
2006-05-05 21:22:45 +00:00
ast_log ( LOG_WARNING , " Asked to execute an AGI on this channel, but could not find application (agi)! \n " ) ;
}
2008-05-05 21:43:21 +00:00
qe - > handled + + ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( queuename , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " CONNECT " , " %ld|%s|%ld " , ( long ) time ( NULL ) - qe - > start , ast_channel_uniqueid ( peer ) ,
2007-08-01 20:05:13 +00:00
( long ) ( orig - to > 0 ? ( orig - to ) / 1000 : 0 ) ) ;
2011-09-23 09:35:32 +00:00
2012-02-20 23:43:27 +00:00
if ( ast_channel_cdr ( qe - > chan ) ) {
2011-09-23 09:35:32 +00:00
struct ast_cdr * cdr ;
struct ast_cdr * newcdr ;
/* Only work with the last CDR in the stack*/
2012-02-20 23:43:27 +00:00
cdr = ast_channel_cdr ( qe - > chan ) ;
2011-09-23 09:35:32 +00:00
while ( cdr - > next ) {
cdr = cdr - > next ;
}
/* If this CDR is not related to us add new one*/
2012-01-24 20:12:09 +00:00
if ( ( strcasecmp ( cdr - > uniqueid , ast_channel_uniqueid ( qe - > chan ) ) ) & &
( strcasecmp ( cdr - > linkedid , ast_channel_uniqueid ( qe - > chan ) ) ) & &
2011-09-23 09:35:32 +00:00
( newcdr = ast_cdr_dup ( cdr ) ) ) {
2011-12-16 21:10:19 +00:00
ast_channel_lock ( qe - > chan ) ;
2011-09-23 09:35:32 +00:00
ast_cdr_init ( newcdr , qe - > chan ) ;
ast_cdr_reset ( newcdr , 0 ) ;
cdr = ast_cdr_append ( cdr , newcdr ) ;
cdr = cdr - > next ;
ast_channel_unlock ( qe - > chan ) ;
}
if ( update_cdr ) {
ast_copy_string ( cdr - > dstchannel , member - > membername , sizeof ( cdr - > dstchannel ) ) ;
}
}
2005-03-28 20:48:24 +00:00
if ( qe - > parent - > eventwhencalled )
2005-01-29 22:42:40 +00:00
manager_event ( EVENT_FLAG_AGENT , " AgentConnect " ,
2006-06-22 15:43:02 +00:00
" Queue: %s \r \n "
" Uniqueid: %s \r \n "
" Channel: %s \r \n "
" Member: %s \r \n "
2006-09-20 05:59:53 +00:00
" MemberName: %s \r \n "
2006-06-22 15:43:02 +00:00
" Holdtime: %ld \r \n "
" BridgedChannel: %s \r \n "
2007-08-01 20:05:13 +00:00
" Ringtime: %ld \r \n "
2006-06-22 15:43:02 +00:00
" %s " ,
2012-01-24 20:12:09 +00:00
queuename , ast_channel_uniqueid ( qe - > chan ) , ast_channel_name ( peer ) , member - > interface , member - > membername ,
( long ) time ( NULL ) - qe - > start , ast_channel_uniqueid ( peer ) , ( long ) ( orig - to > 0 ? ( orig - to ) / 1000 : 0 ) ,
2006-06-22 15:43:02 +00:00
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
2012-02-13 17:27:06 +00:00
ast_copy_string ( oldcontext , ast_channel_context ( qe - > chan ) , sizeof ( oldcontext ) ) ;
ast_copy_string ( oldexten , ast_channel_exten ( qe - > chan ) , sizeof ( oldexten ) ) ;
2009-03-19 18:10:34 +00:00
if ( ( queue_end_bridge = ao2_alloc ( sizeof ( * queue_end_bridge ) , NULL ) ) ) {
queue_end_bridge - > q = qe - > parent ;
queue_end_bridge - > chan = qe - > chan ;
bridge_config . end_bridge_callback = end_bridge_callback ;
bridge_config . end_bridge_callback_data = queue_end_bridge ;
bridge_config . end_bridge_callback_data_fixup = end_bridge_callback_data_fixup ;
/* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
* to make sure to increase the refcount of this queue so it cannot be freed until we
* are done with it . We remove this reference in end_bridge_callback .
*/
2009-11-24 20:31:28 +00:00
queue_t_ref ( qe - > parent , " For bridge_config reference " ) ;
2009-03-19 18:10:34 +00:00
}
2004-02-12 22:11:02 +00:00
time ( & callstart ) ;
2008-12-11 16:33:16 +00:00
transfer_ds = setup_transfer_datastore ( qe , member , callstart , callcompletedinsl ) ;
2005-03-28 20:48:24 +00:00
bridge = ast_bridge_call ( qe - > chan , peer , & bridge_config ) ;
2004-04-26 23:22:34 +00:00
2008-06-12 21:23:02 +00:00
/* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
2010-06-23 22:36:49 +00:00
* when the masquerade occurred . These other " ending " queue_log messages are unnecessary , except for
* the AgentComplete manager event
2008-06-12 21:23:02 +00:00
*/
2009-01-15 00:14:17 +00:00
ast_channel_lock ( qe - > chan ) ;
Merged revisions 166093 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
In order to merge this 1.4 patch into trunk,
I had to resolve some conflicts and wait for
Russell to make some changes to res_agi.
I re-ran all the tests; 39 calls in all, and
made fairly careful notes and comparisons: I
don't want this to blow up some aspect of
asterisk; I completely removed the KEEPALIVE
from the pbx.h decls. The first 3 scenarios
involving feature park; feature xfer to 700;
hookflash park to Park() app call all behave
the same, don't appear to leave hung channels,
and no crashes.
........
r166093 | murf | 2008-12-19 15:30:32 -0700 (Fri, 19 Dec 2008) | 131 lines
This merges the masqpark branch into 1.4
These changes eliminate the need for (and use of)
the KEEPALIVE return code in res_features.c;
There are other places that use this result code
for similar purposes at a higher level, these appear
to be left alone in 1.4, but attacked in trunk.
The reason these changes are being made in 1.4, is
that parking ends a channel's life, in some situations,
and the code in the bridge (and some other places),
was not checking the result code properly, and dereferencing
the channel pointer, which could lead to memory corruption
and crashes.
Calling the masq_park function eliminates this danger
in higher levels.
A series of previous commits have replaced some parking calls
with masq_park, but this patch puts them ALL to rest,
(except one, purposely left alone because a masquerade
is done anyway), and gets rid of the code that tests
the KEEPALIVE result, and the NOHANGUP_PEER result codes.
While bug 13820 inspired this work, this patch does
not solve all the problems mentioned there.
I have tested this patch (again) to make sure I have
not introduced regressions.
Crashes that occurred when a parked party hung up
while the parking party was listening to the numbers
of the parking stall being assigned, is eliminated.
These are the cases where parking code may be activated:
1. Feature one touch (eg. *3)
2. Feature blind xfer to parking lot (eg ##700)
3. Run Park() app from dialplan (eg sip xfer to 700)
(eg. dahdi hookflash xfer to 700)
4. Run Park via manager.
The interesting testing cases for parking are:
I. A calls B, A parks B
a. B hangs up while A is getting the numbers announced.
b. B hangs up after A gets the announcement, but
before the parking time expires
c. B waits, time expires, A is redialed,
A answers, B and A are connected, after
which, B hangs up.
d. C picks up B while still in parking lot.
II. A calls B, B parks A
a. A hangs up while B is getting the numbers announced.
b. A hangs up after B gets the announcement, but
before the parking time expires
c. A waits, time expires, B is redialed,
B answers, A and B are connected, after
which, A hangs up.
d. C picks up A while still in parking lot.
Testing this throroughly involves acting all the permutations
of I and II, in situations 1,2,3, and 4.
Since I added a few more changes (ALL references to KEEPALIVE in the bridge
code eliimated (I missed one earlier), I retested
most of the above cases, and no crashes.
H-extension weirdness.
Current h-extension execution is not completely
correct for several of the cases.
For the case where A calls B, and A parks B, the
'h' exten is run on A's channel as soon as the park
is accomplished. This is expected behavior.
But when A calls B, and B parks A, this will be
current behavior:
After B parks A, B is hung up by the system, and
the 'h' (hangup) exten gets run, but the channel
mentioned will be a derivative of A's...
Thus, if A is DAHDI/1, and B is DAHDI/2,
the h-extension will be run on channel
Parked/DAHDI/1-1<ZOMBIE>, and the
start/answer/end info will be those
relating to Channel A.
And, in the case where A is reconnected to
B after the park time expires, when both parties
hang up after the joyful reunion, no h-exten
will be run at all.
In the case where C picks up A from the
parking lot, when either A or C hang up,
the h-exten will be run for the C channel.
CDR's are a separate issue, and not addressed
here.
As to WHY this strange behavior occurs,
the answer lies in the procedure followed
to accomplish handing over the channel
to the parking manager thread. This procedure
is called masquerading. In the process,
a duplicate copy of the channel is created,
and most of the active data is given to the
new copy. The original channel gets its name
changed to XXX<ZOMBIE> and keeps the PBX
information for the sake of the original
thread (preserving its role as a call
originator, if it had this role to begin
with), while the new channel is without
this info and becomes a call target (a
"peer").
In this case, the parking lot manager
thread is handed the new (masqueraded)
channel. It will not run an h-exten
on the channel if it hangs up while
in the parking lot. The h exten will
be run on the original channel instead,
in the original thread, after the bridge
completes.
See bug 13820 for our intentions as
to how to clean up the h exten behavior.
Review: http://reviewboard.digium.com/r/29/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@166665 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-12-23 18:13:49 +00:00
if ( ! attended_transfer_occurred ( qe - > chan ) ) {
2008-12-11 16:33:16 +00:00
struct ast_datastore * tds ;
2009-08-12 23:14:36 +00:00
/* detect a blind transfer */
2012-03-01 22:09:18 +00:00
if ( ! ( ast_channel_softhangup_internal_flag ( qe - > chan ) | ast_channel_softhangup_internal_flag ( peer ) ) & & ( strcasecmp ( oldcontext , ast_channel_context ( qe - > chan ) ) | | strcasecmp ( oldexten , ast_channel_exten ( qe - > chan ) ) ) ) {
2012-01-24 20:12:09 +00:00
ast_queue_log ( queuename , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " TRANSFER " , " %s|%s|%ld|%ld|%d " ,
2012-02-13 17:27:06 +00:00
ast_channel_exten ( qe - > chan ) , ast_channel_context ( qe - > chan ) , ( long ) ( callstart - qe - > start ) ,
2008-06-19 14:24:43 +00:00
( long ) ( time ( NULL ) - callstart ) , qe - > opos ) ;
Merged revisions 166093 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
In order to merge this 1.4 patch into trunk,
I had to resolve some conflicts and wait for
Russell to make some changes to res_agi.
I re-ran all the tests; 39 calls in all, and
made fairly careful notes and comparisons: I
don't want this to blow up some aspect of
asterisk; I completely removed the KEEPALIVE
from the pbx.h decls. The first 3 scenarios
involving feature park; feature xfer to 700;
hookflash park to Park() app call all behave
the same, don't appear to leave hung channels,
and no crashes.
........
r166093 | murf | 2008-12-19 15:30:32 -0700 (Fri, 19 Dec 2008) | 131 lines
This merges the masqpark branch into 1.4
These changes eliminate the need for (and use of)
the KEEPALIVE return code in res_features.c;
There are other places that use this result code
for similar purposes at a higher level, these appear
to be left alone in 1.4, but attacked in trunk.
The reason these changes are being made in 1.4, is
that parking ends a channel's life, in some situations,
and the code in the bridge (and some other places),
was not checking the result code properly, and dereferencing
the channel pointer, which could lead to memory corruption
and crashes.
Calling the masq_park function eliminates this danger
in higher levels.
A series of previous commits have replaced some parking calls
with masq_park, but this patch puts them ALL to rest,
(except one, purposely left alone because a masquerade
is done anyway), and gets rid of the code that tests
the KEEPALIVE result, and the NOHANGUP_PEER result codes.
While bug 13820 inspired this work, this patch does
not solve all the problems mentioned there.
I have tested this patch (again) to make sure I have
not introduced regressions.
Crashes that occurred when a parked party hung up
while the parking party was listening to the numbers
of the parking stall being assigned, is eliminated.
These are the cases where parking code may be activated:
1. Feature one touch (eg. *3)
2. Feature blind xfer to parking lot (eg ##700)
3. Run Park() app from dialplan (eg sip xfer to 700)
(eg. dahdi hookflash xfer to 700)
4. Run Park via manager.
The interesting testing cases for parking are:
I. A calls B, A parks B
a. B hangs up while A is getting the numbers announced.
b. B hangs up after A gets the announcement, but
before the parking time expires
c. B waits, time expires, A is redialed,
A answers, B and A are connected, after
which, B hangs up.
d. C picks up B while still in parking lot.
II. A calls B, B parks A
a. A hangs up while B is getting the numbers announced.
b. A hangs up after B gets the announcement, but
before the parking time expires
c. A waits, time expires, B is redialed,
B answers, A and B are connected, after
which, A hangs up.
d. C picks up A while still in parking lot.
Testing this throroughly involves acting all the permutations
of I and II, in situations 1,2,3, and 4.
Since I added a few more changes (ALL references to KEEPALIVE in the bridge
code eliimated (I missed one earlier), I retested
most of the above cases, and no crashes.
H-extension weirdness.
Current h-extension execution is not completely
correct for several of the cases.
For the case where A calls B, and A parks B, the
'h' exten is run on A's channel as soon as the park
is accomplished. This is expected behavior.
But when A calls B, and B parks A, this will be
current behavior:
After B parks A, B is hung up by the system, and
the 'h' (hangup) exten gets run, but the channel
mentioned will be a derivative of A's...
Thus, if A is DAHDI/1, and B is DAHDI/2,
the h-extension will be run on channel
Parked/DAHDI/1-1<ZOMBIE>, and the
start/answer/end info will be those
relating to Channel A.
And, in the case where A is reconnected to
B after the park time expires, when both parties
hang up after the joyful reunion, no h-exten
will be run at all.
In the case where C picks up A from the
parking lot, when either A or C hang up,
the h-exten will be run for the C channel.
CDR's are a separate issue, and not addressed
here.
As to WHY this strange behavior occurs,
the answer lies in the procedure followed
to accomplish handing over the channel
to the parking manager thread. This procedure
is called masquerading. In the process,
a duplicate copy of the channel is created,
and most of the active data is given to the
new copy. The original channel gets its name
changed to XXX<ZOMBIE> and keeps the PBX
information for the sake of the original
thread (preserving its role as a call
originator, if it had this role to begin
with), while the new channel is without
this info and becomes a call target (a
"peer").
In this case, the parking lot manager
thread is handed the new (masqueraded)
channel. It will not run an h-exten
on the channel if it hangs up while
in the parking lot. The h exten will
be run on the original channel instead,
in the original thread, after the bridge
completes.
See bug 13820 for our intentions as
to how to clean up the h exten behavior.
Review: http://reviewboard.digium.com/r/29/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@166665 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-12-23 18:13:49 +00:00
send_agent_complete ( qe , queuename , peer , member , callstart , vars , sizeof ( vars ) , TRANSFER ) ;
2008-06-12 21:23:02 +00:00
} else if ( ast_check_hangup ( qe - > chan ) ) {
2012-01-24 20:12:09 +00:00
ast_queue_log ( queuename , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " COMPLETECALLER " , " %ld|%ld|%d " ,
2008-06-12 21:23:02 +00:00
( long ) ( callstart - qe - > start ) , ( long ) ( time ( NULL ) - callstart ) , qe - > opos ) ;
Merged revisions 166093 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
In order to merge this 1.4 patch into trunk,
I had to resolve some conflicts and wait for
Russell to make some changes to res_agi.
I re-ran all the tests; 39 calls in all, and
made fairly careful notes and comparisons: I
don't want this to blow up some aspect of
asterisk; I completely removed the KEEPALIVE
from the pbx.h decls. The first 3 scenarios
involving feature park; feature xfer to 700;
hookflash park to Park() app call all behave
the same, don't appear to leave hung channels,
and no crashes.
........
r166093 | murf | 2008-12-19 15:30:32 -0700 (Fri, 19 Dec 2008) | 131 lines
This merges the masqpark branch into 1.4
These changes eliminate the need for (and use of)
the KEEPALIVE return code in res_features.c;
There are other places that use this result code
for similar purposes at a higher level, these appear
to be left alone in 1.4, but attacked in trunk.
The reason these changes are being made in 1.4, is
that parking ends a channel's life, in some situations,
and the code in the bridge (and some other places),
was not checking the result code properly, and dereferencing
the channel pointer, which could lead to memory corruption
and crashes.
Calling the masq_park function eliminates this danger
in higher levels.
A series of previous commits have replaced some parking calls
with masq_park, but this patch puts them ALL to rest,
(except one, purposely left alone because a masquerade
is done anyway), and gets rid of the code that tests
the KEEPALIVE result, and the NOHANGUP_PEER result codes.
While bug 13820 inspired this work, this patch does
not solve all the problems mentioned there.
I have tested this patch (again) to make sure I have
not introduced regressions.
Crashes that occurred when a parked party hung up
while the parking party was listening to the numbers
of the parking stall being assigned, is eliminated.
These are the cases where parking code may be activated:
1. Feature one touch (eg. *3)
2. Feature blind xfer to parking lot (eg ##700)
3. Run Park() app from dialplan (eg sip xfer to 700)
(eg. dahdi hookflash xfer to 700)
4. Run Park via manager.
The interesting testing cases for parking are:
I. A calls B, A parks B
a. B hangs up while A is getting the numbers announced.
b. B hangs up after A gets the announcement, but
before the parking time expires
c. B waits, time expires, A is redialed,
A answers, B and A are connected, after
which, B hangs up.
d. C picks up B while still in parking lot.
II. A calls B, B parks A
a. A hangs up while B is getting the numbers announced.
b. A hangs up after B gets the announcement, but
before the parking time expires
c. A waits, time expires, B is redialed,
B answers, A and B are connected, after
which, A hangs up.
d. C picks up A while still in parking lot.
Testing this throroughly involves acting all the permutations
of I and II, in situations 1,2,3, and 4.
Since I added a few more changes (ALL references to KEEPALIVE in the bridge
code eliimated (I missed one earlier), I retested
most of the above cases, and no crashes.
H-extension weirdness.
Current h-extension execution is not completely
correct for several of the cases.
For the case where A calls B, and A parks B, the
'h' exten is run on A's channel as soon as the park
is accomplished. This is expected behavior.
But when A calls B, and B parks A, this will be
current behavior:
After B parks A, B is hung up by the system, and
the 'h' (hangup) exten gets run, but the channel
mentioned will be a derivative of A's...
Thus, if A is DAHDI/1, and B is DAHDI/2,
the h-extension will be run on channel
Parked/DAHDI/1-1<ZOMBIE>, and the
start/answer/end info will be those
relating to Channel A.
And, in the case where A is reconnected to
B after the park time expires, when both parties
hang up after the joyful reunion, no h-exten
will be run at all.
In the case where C picks up A from the
parking lot, when either A or C hang up,
the h-exten will be run for the C channel.
CDR's are a separate issue, and not addressed
here.
As to WHY this strange behavior occurs,
the answer lies in the procedure followed
to accomplish handing over the channel
to the parking manager thread. This procedure
is called masquerading. In the process,
a duplicate copy of the channel is created,
and most of the active data is given to the
new copy. The original channel gets its name
changed to XXX<ZOMBIE> and keeps the PBX
information for the sake of the original
thread (preserving its role as a call
originator, if it had this role to begin
with), while the new channel is without
this info and becomes a call target (a
"peer").
In this case, the parking lot manager
thread is handed the new (masqueraded)
channel. It will not run an h-exten
on the channel if it hangs up while
in the parking lot. The h exten will
be run on the original channel instead,
in the original thread, after the bridge
completes.
See bug 13820 for our intentions as
to how to clean up the h exten behavior.
Review: http://reviewboard.digium.com/r/29/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@166665 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-12-23 18:13:49 +00:00
send_agent_complete ( qe , queuename , peer , member , callstart , vars , sizeof ( vars ) , CALLER ) ;
2008-06-12 21:23:02 +00:00
} else {
2012-01-24 20:12:09 +00:00
ast_queue_log ( queuename , ast_channel_uniqueid ( qe - > chan ) , member - > membername , " COMPLETEAGENT " , " %ld|%ld|%d " ,
2008-06-12 21:23:02 +00:00
( long ) ( callstart - qe - > start ) , ( long ) ( time ( NULL ) - callstart ) , qe - > opos ) ;
Merged revisions 166093 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
In order to merge this 1.4 patch into trunk,
I had to resolve some conflicts and wait for
Russell to make some changes to res_agi.
I re-ran all the tests; 39 calls in all, and
made fairly careful notes and comparisons: I
don't want this to blow up some aspect of
asterisk; I completely removed the KEEPALIVE
from the pbx.h decls. The first 3 scenarios
involving feature park; feature xfer to 700;
hookflash park to Park() app call all behave
the same, don't appear to leave hung channels,
and no crashes.
........
r166093 | murf | 2008-12-19 15:30:32 -0700 (Fri, 19 Dec 2008) | 131 lines
This merges the masqpark branch into 1.4
These changes eliminate the need for (and use of)
the KEEPALIVE return code in res_features.c;
There are other places that use this result code
for similar purposes at a higher level, these appear
to be left alone in 1.4, but attacked in trunk.
The reason these changes are being made in 1.4, is
that parking ends a channel's life, in some situations,
and the code in the bridge (and some other places),
was not checking the result code properly, and dereferencing
the channel pointer, which could lead to memory corruption
and crashes.
Calling the masq_park function eliminates this danger
in higher levels.
A series of previous commits have replaced some parking calls
with masq_park, but this patch puts them ALL to rest,
(except one, purposely left alone because a masquerade
is done anyway), and gets rid of the code that tests
the KEEPALIVE result, and the NOHANGUP_PEER result codes.
While bug 13820 inspired this work, this patch does
not solve all the problems mentioned there.
I have tested this patch (again) to make sure I have
not introduced regressions.
Crashes that occurred when a parked party hung up
while the parking party was listening to the numbers
of the parking stall being assigned, is eliminated.
These are the cases where parking code may be activated:
1. Feature one touch (eg. *3)
2. Feature blind xfer to parking lot (eg ##700)
3. Run Park() app from dialplan (eg sip xfer to 700)
(eg. dahdi hookflash xfer to 700)
4. Run Park via manager.
The interesting testing cases for parking are:
I. A calls B, A parks B
a. B hangs up while A is getting the numbers announced.
b. B hangs up after A gets the announcement, but
before the parking time expires
c. B waits, time expires, A is redialed,
A answers, B and A are connected, after
which, B hangs up.
d. C picks up B while still in parking lot.
II. A calls B, B parks A
a. A hangs up while B is getting the numbers announced.
b. A hangs up after B gets the announcement, but
before the parking time expires
c. A waits, time expires, B is redialed,
B answers, A and B are connected, after
which, A hangs up.
d. C picks up A while still in parking lot.
Testing this throroughly involves acting all the permutations
of I and II, in situations 1,2,3, and 4.
Since I added a few more changes (ALL references to KEEPALIVE in the bridge
code eliimated (I missed one earlier), I retested
most of the above cases, and no crashes.
H-extension weirdness.
Current h-extension execution is not completely
correct for several of the cases.
For the case where A calls B, and A parks B, the
'h' exten is run on A's channel as soon as the park
is accomplished. This is expected behavior.
But when A calls B, and B parks A, this will be
current behavior:
After B parks A, B is hung up by the system, and
the 'h' (hangup) exten gets run, but the channel
mentioned will be a derivative of A's...
Thus, if A is DAHDI/1, and B is DAHDI/2,
the h-extension will be run on channel
Parked/DAHDI/1-1<ZOMBIE>, and the
start/answer/end info will be those
relating to Channel A.
And, in the case where A is reconnected to
B after the park time expires, when both parties
hang up after the joyful reunion, no h-exten
will be run at all.
In the case where C picks up A from the
parking lot, when either A or C hang up,
the h-exten will be run for the C channel.
CDR's are a separate issue, and not addressed
here.
As to WHY this strange behavior occurs,
the answer lies in the procedure followed
to accomplish handing over the channel
to the parking manager thread. This procedure
is called masquerading. In the process,
a duplicate copy of the channel is created,
and most of the active data is given to the
new copy. The original channel gets its name
changed to XXX<ZOMBIE> and keeps the PBX
information for the sake of the original
thread (preserving its role as a call
originator, if it had this role to begin
with), while the new channel is without
this info and becomes a call target (a
"peer").
In this case, the parking lot manager
thread is handed the new (masqueraded)
channel. It will not run an h-exten
on the channel if it hangs up while
in the parking lot. The h exten will
be run on the original channel instead,
in the original thread, after the bridge
completes.
See bug 13820 for our intentions as
to how to clean up the h exten behavior.
Review: http://reviewboard.digium.com/r/29/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@166665 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-12-23 18:13:49 +00:00
send_agent_complete ( qe , queuename , peer , member , callstart , vars , sizeof ( vars ) , AGENT ) ;
2008-06-12 21:23:02 +00:00
}
2009-01-15 00:14:17 +00:00
if ( ( tds = ast_channel_datastore_find ( qe - > chan , & queue_transfer_info , NULL ) ) ) {
2008-12-11 16:33:16 +00:00
ast_channel_datastore_remove ( qe - > chan , tds ) ;
2008-07-16 18:59:27 +00:00
}
2011-08-12 19:06:10 +00:00
ast_channel_unlock ( qe - > chan ) ;
2009-01-08 19:48:42 +00:00
update_queue ( qe - > parent , member , callcompletedinsl , ( time ( NULL ) - callstart ) ) ;
2010-06-23 22:36:49 +00:00
} else {
2011-08-12 19:06:10 +00:00
ast_channel_unlock ( qe - > chan ) ;
2010-06-23 22:36:49 +00:00
/* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
send_agent_complete ( qe , queuename , peer , member , callstart , vars , sizeof ( vars ) , TRANSFER ) ;
2004-02-12 22:11:02 +00:00
}
2003-07-02 14:06:12 +00:00
2008-12-11 16:33:16 +00:00
if ( transfer_ds ) {
ast_datastore_free ( transfer_ds ) ;
}
2012-03-02 16:57:12 +00:00
if ( ! ast_check_hangup ( peer ) & & ast_test_flag ( & opts , OPT_CALLEE_GO_ON ) ) {
if ( ! ast_strlen_zero ( opt_args [ OPT_ARG_CALLEE_GO_ON ] ) ) {
replace_macro_delimiter ( opt_args [ OPT_ARG_CALLEE_GO_ON ] ) ;
2012-03-08 17:48:14 +00:00
if ( ast_parseable_goto ( peer , opt_args [ OPT_ARG_CALLEE_GO_ON ] ) = = AST_PBX_SUCCESS ) {
2012-03-02 16:57:12 +00:00
ast_pbx_start ( peer ) ;
} else {
ast_hangup ( peer ) ;
}
} else { /* F() */
2012-03-08 17:48:14 +00:00
if ( ast_goto_if_exists ( peer , caller_context , caller_extension , caller_priority + 1 ) = = AST_PBX_GOTO_FAILED ) {
2012-03-02 16:57:12 +00:00
ast_hangup ( peer ) ;
} else {
ast_pbx_start ( peer ) ;
}
}
} else {
ast_hangup ( peer ) ;
}
2006-06-04 03:45:54 +00:00
res = bridge ? bridge : 1 ;
2007-11-07 23:47:45 +00:00
ao2_ref ( member , - 1 ) ;
2006-02-14 21:50:35 +00:00
}
2002-09-02 23:15:40 +00:00
out :
2009-01-29 17:08:22 +00:00
hangupcalls ( outgoing , NULL , qe - > cancel_answered_elsewhere ) ;
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
return res ;
}
static int wait_a_bit ( struct queue_ent * qe )
{
2003-11-08 04:35:57 +00:00
/* Don't need to hold the lock while we setup the outgoing calls */
int retrywait = qe - > parent - > retry * 1000 ;
2005-03-28 20:48:24 +00:00
2007-07-19 16:29:51 +00:00
int res = ast_waitfordigit ( qe - > chan , retrywait ) ;
if ( res > 0 & & ! valid_exit ( qe , res ) )
res = 0 ;
return res ;
2002-09-02 23:15:40 +00:00
}
2007-01-05 22:43:18 +00:00
static struct member * interface_exists ( struct call_queue * q , const char * interface )
2003-06-27 23:07:59 +00:00
{
struct member * mem ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2003-06-27 23:07:59 +00:00
2011-10-23 11:37:50 +00:00
if ( ! q ) {
2006-06-14 23:20:08 +00:00
return NULL ;
2011-10-23 11:37:50 +00:00
}
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( mem = ao2_iterator_next ( & mem_iter ) ) ) {
2009-10-06 01:24:24 +00:00
if ( ! strcasecmp ( interface , mem - > interface ) ) {
ao2_iterator_destroy ( & mem_iter ) ;
2006-06-14 23:20:08 +00:00
return mem ;
2009-10-06 01:24:24 +00:00
}
2007-09-10 18:32:59 +00:00
ao2_ref ( mem , - 1 ) ;
2006-06-14 23:20:08 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2003-06-27 23:07:59 +00:00
2004-12-19 21:30:55 +00:00
return NULL ;
2003-06-27 23:07:59 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Dump all members in a specific queue to the database
2004-12-06 05:54:16 +00:00
*
2008-07-10 19:13:12 +00:00
* < pm_family > / < queuename > = < interface > ; < penalty > ; < paused > ; < state_interface > [ | . . . ]
2004-12-06 05:54:16 +00:00
*/
2006-06-15 13:35:04 +00:00
static void dump_queue_members ( struct call_queue * pm_queue )
2004-12-06 05:54:16 +00:00
{
2005-03-03 20:31:21 +00:00
struct member * cur_member ;
2004-12-06 05:54:16 +00:00
char value [ PM_MAX_LEN ] ;
int value_len = 0 ;
int res ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2004-12-06 05:54:16 +00:00
memset ( value , 0 , sizeof ( value ) ) ;
2005-03-03 20:31:21 +00:00
if ( ! pm_queue )
return ;
2004-12-06 05:54:16 +00:00
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( pm_queue - > members , 0 ) ;
while ( ( cur_member = ao2_iterator_next ( & mem_iter ) ) ) {
2007-09-10 18:32:59 +00:00
if ( ! cur_member - > dynamic ) {
ao2_ref ( cur_member , - 1 ) ;
2005-03-03 20:31:21 +00:00
continue ;
2007-09-10 18:32:59 +00:00
}
2008-07-10 19:13:12 +00:00
res = snprintf ( value + value_len , sizeof ( value ) - value_len , " %s%s;%d;%d;%s;%s " ,
value_len ? " | " : " " , cur_member - > interface , cur_member - > penalty , cur_member - > paused , cur_member - > membername , cur_member - > state_interface ) ;
2007-09-10 18:32:59 +00:00
ao2_ref ( cur_member , - 1 ) ;
2004-12-06 05:54:16 +00:00
2005-03-03 20:31:21 +00:00
if ( res ! = strlen ( value + value_len ) ) {
ast_log ( LOG_WARNING , " Could not create persistent member string, out of space \n " ) ;
break ;
}
value_len + = res ;
2004-12-06 05:54:16 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2005-03-03 20:31:21 +00:00
if ( value_len & & ! cur_member ) {
if ( ast_db_put ( pm_family , pm_queue - > name , value ) )
ast_log ( LOG_WARNING , " failed to create persistent dynamic entry! \n " ) ;
} else
/* Delete the entry if the queue is empty or there is an error */
ast_db_del ( pm_family , pm_queue - > name ) ;
2004-12-06 05:54:16 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Remove member from queue
* \ retval RES_NOT_DYNAMIC when they aren ' t a RT member
* \ retval RES_NOSUCHQUEUE queue does not exist
* \ retval RES_OKAY removed member from queue
* \ retval RES_EXISTS queue exists but no members
*/
2007-01-05 22:43:18 +00:00
static int remove_from_queue ( const char * queuename , const char * interface )
2004-07-28 02:55:22 +00:00
{
2007-11-14 01:15:26 +00:00
struct call_queue * q , tmpq = {
2011-10-23 11:37:50 +00:00
. name = queuename ,
2007-11-14 01:15:26 +00:00
} ;
2007-09-11 22:38:02 +00:00
struct member * mem , tmpmem ;
2004-07-28 02:55:22 +00:00
int res = RES_NOSUCHQUEUE ;
2007-09-11 22:38:02 +00:00
ast_copy_string ( tmpmem . interface , interface , sizeof ( tmpmem . interface ) ) ;
2009-11-24 20:31:28 +00:00
if ( ( q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Temporary reference for interface removal " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2008-11-25 01:01:49 +00:00
if ( ( mem = ao2_find ( q - > members , & tmpmem , OBJ_POINTER ) ) ) {
2007-10-16 21:16:00 +00:00
/* XXX future changes should beware of this assumption!! */
2011-06-29 06:39:26 +00:00
/*Change Penalty on realtime users*/
if ( mem - > realtime & & ! ast_strlen_zero ( mem - > rt_uniqueid ) & & negative_penalty_invalid ) {
update_realtime_member_field ( mem , q - > name , " penalty " , " -1 " ) ;
} else if ( ! mem - > dynamic ) {
2007-10-16 21:16:00 +00:00
ao2_ref ( mem , - 1 ) ;
2007-10-16 21:51:26 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Interface wasn't dynamic, expiring temporary reference " ) ;
2007-10-16 21:51:26 +00:00
return RES_NOT_DYNAMIC ;
2007-10-16 21:16:00 +00:00
}
2006-06-14 23:20:08 +00:00
manager_event ( EVENT_FLAG_AGENT , " QueueMemberRemoved " ,
2006-07-14 05:42:06 +00:00
" Queue: %s \r \n "
2006-09-20 05:59:53 +00:00
" Location: %s \r \n "
" MemberName: %s \r \n " ,
2007-09-10 18:32:59 +00:00
q - > name , mem - > interface , mem - > membername ) ;
2007-10-16 21:16:00 +00:00
ao2_unlink ( q - > members , mem ) ;
2007-09-10 18:32:59 +00:00
ao2_ref ( mem , - 1 ) ;
2006-06-14 23:20:08 +00:00
if ( queue_persistent_members )
dump_queue_members ( q ) ;
res = RES_OKAY ;
} else {
res = RES_EXISTS ;
2004-07-28 02:55:22 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Expiring temporary reference " ) ;
2004-07-28 02:55:22 +00:00
}
2006-06-14 23:20:08 +00:00
2004-07-28 02:55:22 +00:00
return res ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Add member to queue
* \ retval RES_NOT_DYNAMIC when they aren ' t a RT member
* \ retval RES_NOSUCHQUEUE queue does not exist
* \ retval RES_OKAY added member from queue
* \ retval RES_EXISTS queue exists but no members
* \ retval RES_OUT_OF_MEMORY queue exists but not enough memory to create member
*/
2008-01-08 21:18:32 +00:00
static int add_to_queue ( const char * queuename , const char * interface , const char * membername , int penalty , int paused , int dump , const char * state_interface )
2004-07-28 02:55:22 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2007-09-10 18:32:59 +00:00
struct member * new_member , * old_member ;
2004-07-28 02:55:22 +00:00
int res = RES_NOSUCHQUEUE ;
2008-03-10 15:45:13 +00:00
/*! \note Ensure the appropriate realtime queue is loaded. Note that this
2006-01-21 20:32:17 +00:00
* short - circuits if the queue is already in memory . */
2011-12-08 20:55:19 +00:00
if ( ! ( q = find_load_queue_rt_friendly ( queuename ) ) ) {
2006-06-14 23:20:08 +00:00
return res ;
2011-10-23 11:37:50 +00:00
}
2006-01-21 20:32:17 +00:00
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2007-09-10 18:32:59 +00:00
if ( ( old_member = interface_exists ( q , interface ) ) = = NULL ) {
2008-01-08 21:18:32 +00:00
if ( ( new_member = create_queue_member ( interface , membername , penalty , paused , state_interface ) ) ) {
2006-06-14 23:20:08 +00:00
new_member - > dynamic = 1 ;
2007-09-10 18:32:59 +00:00
ao2_link ( q - > members , new_member ) ;
2006-06-14 23:20:08 +00:00
manager_event ( EVENT_FLAG_AGENT , " QueueMemberAdded " ,
2006-07-14 05:42:06 +00:00
" Queue: %s \r \n "
" Location: %s \r \n "
2006-09-20 05:59:53 +00:00
" MemberName: %s \r \n "
2011-08-08 15:00:26 +00:00
" StateInterface: %s \r \n "
2006-07-14 05:42:06 +00:00
" Membership: %s \r \n "
" Penalty: %d \r \n "
" CallsTaken: %d \r \n "
" LastCall: %d \r \n "
" Status: %d \r \n "
" Paused: %d \r \n " ,
2011-08-08 15:00:26 +00:00
q - > name , new_member - > interface , new_member - > membername , state_interface ,
2007-08-20 16:18:55 +00:00
" dynamic " ,
2006-07-14 05:42:06 +00:00
new_member - > penalty , new_member - > calls , ( int ) new_member - > lastcall ,
new_member - > status , new_member - > paused ) ;
2011-10-23 11:37:50 +00:00
2007-12-02 18:20:13 +00:00
ao2_ref ( new_member , - 1 ) ;
new_member = NULL ;
2011-10-23 11:37:50 +00:00
if ( dump ) {
2006-06-14 23:20:08 +00:00
dump_queue_members ( q ) ;
2011-10-23 11:37:50 +00:00
}
2006-06-14 23:20:08 +00:00
res = RES_OKAY ;
2006-01-21 20:32:17 +00:00
} else {
2006-06-14 23:20:08 +00:00
res = RES_OUTOFMEMORY ;
2004-07-28 02:55:22 +00:00
}
2006-06-14 23:20:08 +00:00
} else {
2007-09-10 18:32:59 +00:00
ao2_ref ( old_member , - 1 ) ;
2006-06-14 23:20:08 +00:00
res = RES_EXISTS ;
2004-07-28 02:55:22 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2010-07-28 16:53:14 +00:00
queue_t_unref ( q , " Expiring temporary reference " ) ;
2006-06-14 23:20:08 +00:00
2004-07-28 02:55:22 +00:00
return res ;
}
2003-06-27 23:07:59 +00:00
2007-09-07 16:13:57 +00:00
static int set_member_paused ( const char * queuename , const char * interface , const char * reason , int paused )
2005-01-21 04:14:26 +00:00
{
int found = 0 ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2005-01-21 04:14:26 +00:00
struct member * mem ;
2007-09-20 21:21:28 +00:00
struct ao2_iterator queue_iter ;
2008-05-21 18:31:05 +00:00
int failed ;
2005-01-21 04:14:26 +00:00
/* Special event for when all queues are paused - individual events still generated */
2006-09-20 05:59:53 +00:00
/* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
2005-01-21 04:14:26 +00:00
if ( ast_strlen_zero ( queuename ) )
ast_queue_log ( " NONE " , " NONE " , interface , ( paused ? " PAUSEALL " : " UNPAUSEALL " ) , " %s " , " " ) ;
2007-09-20 21:21:28 +00:00
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate over queues " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2005-07-26 21:53:56 +00:00
if ( ast_strlen_zero ( queuename ) | | ! strcasecmp ( q - > name , queuename ) ) {
2005-01-21 04:14:26 +00:00
if ( ( mem = interface_exists ( q , interface ) ) ) {
2006-10-03 15:53:07 +00:00
if ( mem - > paused = = paused ) {
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " %spausing already-%spaused queue member %s:%s \n " , ( paused ? " " : " un " ) , ( paused ? " " : " un " ) , q - > name , interface ) ;
2006-10-03 15:53:07 +00:00
}
2008-05-21 18:31:05 +00:00
failed = 0 ;
if ( mem - > realtime ) {
failed = update_realtime_member_field ( mem , q - > name , " paused " , paused ? " 1 " : " 0 " ) ;
}
2011-10-23 11:37:50 +00:00
2008-05-21 18:31:05 +00:00
if ( failed ) {
ast_log ( LOG_WARNING , " Failed %spausing realtime queue member %s:%s \n " , ( paused ? " " : " un " ) , q - > name , interface ) ;
ao2_ref ( mem , - 1 ) ;
2008-07-25 13:52:52 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2008-05-21 18:31:05 +00:00
continue ;
2011-10-23 11:37:50 +00:00
}
2008-05-21 18:31:05 +00:00
found + + ;
2005-01-21 04:14:26 +00:00
mem - > paused = paused ;
if ( queue_persistent_members )
2006-04-04 18:02:40 +00:00
dump_queue_members ( q ) ;
2005-01-21 04:14:26 +00:00
2007-09-07 16:13:57 +00:00
ast_queue_log ( q - > name , " NONE " , mem - > membername , ( paused ? " PAUSE " : " UNPAUSE " ) , " %s " , S_OR ( reason , " " ) ) ;
2011-10-23 11:37:50 +00:00
2007-09-07 16:13:57 +00:00
if ( ! ast_strlen_zero ( reason ) ) {
manager_event ( EVENT_FLAG_AGENT , " QueueMemberPaused " ,
" Queue: %s \r \n "
" Location: %s \r \n "
" MemberName: %s \r \n "
" Paused: %d \r \n "
" Reason: %s \r \n " ,
q - > name , mem - > interface , mem - > membername , paused , reason ) ;
} else {
manager_event ( EVENT_FLAG_AGENT , " QueueMemberPaused " ,
" Queue: %s \r \n "
" Location: %s \r \n "
" MemberName: %s \r \n "
" Paused: %d \r \n " ,
q - > name , mem - > interface , mem - > membername , paused ) ;
}
2007-09-10 18:32:59 +00:00
ao2_ref ( mem , - 1 ) ;
2005-01-21 04:14:26 +00:00
}
}
2011-10-23 11:37:50 +00:00
2008-05-22 16:05:18 +00:00
if ( ! ast_strlen_zero ( queuename ) & & ! strcasecmp ( queuename , q - > name ) ) {
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2008-05-21 18:31:05 +00:00
break ;
}
2011-10-23 11:37:50 +00:00
2008-05-22 16:05:18 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2005-01-21 04:14:26 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2005-01-21 04:14:26 +00:00
2006-06-14 23:20:08 +00:00
return found ? RESULT_SUCCESS : RESULT_FAILURE ;
2005-01-21 04:14:26 +00:00
}
2011-12-08 20:55:19 +00:00
/*!
* \ internal
* \ brief helper function for set_member_penalty - given a queue , sets all member penalties with the interface
* \ param [ in ] q queue which is having its member ' s penalty changed - must be unlocked prior to calling
* \ param [ in ] interface String of interface used to search for queue members being changed
* \ param [ in ] penalty Value penalty is being changed to for the member .
* \ retval 0 if the there is no member with interface belonging to q and no change is made
* \ retval 1 if the there is a member with interface belonging to q and changes are made
*/
static int set_member_penalty_help_members ( struct call_queue * q , const char * interface , int penalty )
{
struct member * mem ;
int foundinterface = 0 ;
char rtpenalty [ 80 ] ;
ao2_lock ( q ) ;
if ( ( mem = interface_exists ( q , interface ) ) ) {
foundinterface + + ;
if ( ! mem - > realtime ) {
mem - > penalty = penalty ;
} else {
sprintf ( rtpenalty , " %i " , penalty ) ;
update_realtime_member_field ( mem , q - > name , " penalty " , rtpenalty ) ;
}
ast_queue_log ( q - > name , " NONE " , interface , " PENALTY " , " %d " , penalty ) ;
manager_event ( EVENT_FLAG_AGENT , " QueueMemberPenalty " ,
" Queue: %s \r \n "
" Location: %s \r \n "
" Penalty: %d \r \n " ,
q - > name , mem - > interface , penalty ) ;
ao2_ref ( mem , - 1 ) ;
}
ao2_unlock ( q ) ;
return foundinterface ;
}
/*!
* \ internal
* \ brief Sets members penalty , if queuename = NULL we set member penalty in all the queues .
* \ param [ in ] queuename If specified , only act on a member if it belongs to this queue
* \ param [ in ] interface Interface of queue member ( s ) having priority set .
* \ param [ in ] penalty Value penalty is being changed to for each member
*/
2009-05-21 21:13:09 +00:00
static int set_member_penalty ( const char * queuename , const char * interface , int penalty )
2007-11-06 22:05:56 +00:00
{
int foundinterface = 0 , foundqueue = 0 ;
struct call_queue * q ;
2011-12-08 20:55:19 +00:00
struct ast_config * queue_config = NULL ;
struct ao2_iterator queue_iter ;
2007-11-06 22:05:56 +00:00
2011-06-29 06:39:26 +00:00
if ( penalty < 0 & & ! negative_penalty_invalid ) {
2007-12-19 23:17:42 +00:00
ast_log ( LOG_ERROR , " Invalid penalty (%d) \n " , penalty ) ;
return RESULT_FAILURE ;
}
2011-12-08 20:55:19 +00:00
if ( ast_strlen_zero ( queuename ) ) { /* This means we need to iterate through all the queues. */
if ( ast_check_realtime ( " queues " ) ) {
2012-03-08 17:48:14 +00:00
char * name ;
2011-12-08 20:55:19 +00:00
queue_config = ast_load_realtime_multientry ( " queues " , " name LIKE " , " % " , SENTINEL ) ;
if ( queue_config ) {
2012-03-08 17:48:14 +00:00
for ( name = ast_category_browse ( queue_config , NULL ) ;
! ast_strlen_zero ( name ) ;
name = ast_category_browse ( queue_config , name ) ) {
if ( ( q = find_load_queue_rt_friendly ( name ) ) ) {
2011-12-08 20:55:19 +00:00
foundqueue + + ;
foundinterface + = set_member_penalty_help_members ( q , interface , penalty ) ;
}
}
2007-11-06 22:05:56 +00:00
}
}
2011-12-08 20:55:19 +00:00
/* After hitting realtime queues, go back and get the regular ones. */
queue_iter = ao2_iterator_init ( queues , 0 ) ;
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
foundqueue + + ;
foundinterface + = set_member_penalty_help_members ( q , interface , penalty ) ;
}
2012-01-27 21:38:54 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2011-12-08 20:55:19 +00:00
} else { /* We actually have a queuename, so we can just act on the single queue. */
if ( ( q = find_load_queue_rt_friendly ( queuename ) ) ) {
foundqueue + + ;
foundinterface + = set_member_penalty_help_members ( q , interface , penalty ) ;
}
2007-11-06 22:05:56 +00:00
}
if ( foundinterface ) {
return RESULT_SUCCESS ;
2007-12-19 23:17:42 +00:00
} else if ( ! foundqueue ) {
2011-12-08 20:55:19 +00:00
ast_log ( LOG_ERROR , " Invalid queuename \n " ) ;
2007-11-06 22:05:56 +00:00
} else {
ast_log ( LOG_ERROR , " Invalid interface \n " ) ;
2011-10-23 11:37:50 +00:00
}
2007-11-06 22:05:56 +00:00
return RESULT_FAILURE ;
}
2011-10-23 11:37:50 +00:00
/* \brief Gets members penalty.
* \ return Return the members penalty or RESULT_FAILURE on error .
2008-03-10 15:45:13 +00:00
*/
2007-11-06 22:05:56 +00:00
static int get_member_penalty ( char * queuename , char * interface )
{
int foundqueue = 0 , penalty ;
2007-11-14 01:15:26 +00:00
struct call_queue * q , tmpq = {
2011-10-23 11:37:50 +00:00
. name = queuename ,
2007-11-14 01:15:26 +00:00
} ;
2007-11-06 22:05:56 +00:00
struct member * mem ;
2011-10-23 11:37:50 +00:00
2009-11-24 20:31:28 +00:00
if ( ( q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Search for queue " ) ) ) {
2007-11-06 22:05:56 +00:00
foundqueue = 1 ;
ao2_lock ( q ) ;
if ( ( mem = interface_exists ( q , interface ) ) ) {
penalty = mem - > penalty ;
2009-04-14 23:28:13 +00:00
ao2_ref ( mem , - 1 ) ;
2007-11-06 22:05:56 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Search complete " ) ;
2007-11-06 22:05:56 +00:00
return penalty ;
}
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Search complete " ) ;
2007-11-06 22:05:56 +00:00
}
/* some useful debuging */
2011-10-23 11:37:50 +00:00
if ( foundqueue ) {
2007-11-06 22:05:56 +00:00
ast_log ( LOG_ERROR , " Invalid queuename \n " ) ;
2011-10-23 11:37:50 +00:00
} else {
2007-11-06 22:05:56 +00:00
ast_log ( LOG_ERROR , " Invalid interface \n " ) ;
2011-10-23 11:37:50 +00:00
}
2007-11-06 22:05:56 +00:00
return RESULT_FAILURE ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Reload dynamic queue members persisted into the astdb */
2004-12-06 05:54:16 +00:00
static void reload_queue_members ( void )
{
2008-12-11 16:33:16 +00:00
char * cur_ptr ;
2007-11-14 01:15:26 +00:00
const char * queue_name ;
2005-03-03 20:31:21 +00:00
char * member ;
char * interface ;
2007-09-18 20:10:59 +00:00
char * membername = NULL ;
2008-01-08 21:18:32 +00:00
char * state_interface ;
2005-03-03 20:31:21 +00:00
char * penalty_tok ;
int penalty = 0 ;
char * paused_tok ;
int paused = 0 ;
struct ast_db_entry * db_tree ;
struct ast_db_entry * entry ;
2007-11-14 01:15:26 +00:00
struct call_queue * cur_queue ;
2004-12-06 05:54:16 +00:00
char queue_data [ PM_MAX_LEN ] ;
2005-03-03 20:31:21 +00:00
/* Each key in 'pm_family' is the name of a queue */
db_tree = ast_db_gettree ( pm_family , NULL ) ;
for ( entry = db_tree ; entry ; entry = entry - > next ) {
queue_name = entry - > key + strlen ( pm_family ) + 2 ;
2004-12-06 05:54:16 +00:00
2007-11-14 01:15:26 +00:00
{
struct call_queue tmpq = {
. name = queue_name ,
} ;
2009-11-24 20:31:28 +00:00
cur_queue = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Reload queue members " ) ;
2011-10-23 11:37:50 +00:00
}
2007-11-14 01:15:26 +00:00
2011-10-23 11:37:50 +00:00
if ( ! cur_queue ) {
2011-12-08 20:55:19 +00:00
cur_queue = find_load_queue_rt_friendly ( queue_name ) ;
2011-10-23 11:37:50 +00:00
}
2004-12-06 05:54:16 +00:00
if ( ! cur_queue ) {
/* If the queue no longer exists, remove it from the
* database */
2007-06-07 21:17:52 +00:00
ast_log ( LOG_WARNING , " Error loading persistent queue: '%s': it does not exist \n " , queue_name ) ;
2005-03-03 20:31:21 +00:00
ast_db_del ( pm_family , queue_name ) ;
2004-12-06 05:54:16 +00:00
continue ;
2011-10-23 11:37:50 +00:00
}
2004-12-06 05:54:16 +00:00
2007-09-20 21:21:28 +00:00
if ( ast_db_get ( pm_family , queue_name , queue_data , PM_MAX_LEN ) ) {
2009-11-24 20:31:28 +00:00
queue_t_unref ( cur_queue , " Expire reload reference " ) ;
2005-03-03 20:31:21 +00:00
continue ;
2007-09-20 21:21:28 +00:00
}
2005-01-21 04:14:26 +00:00
2005-03-03 20:31:21 +00:00
cur_ptr = queue_data ;
2007-07-23 19:51:41 +00:00
while ( ( member = strsep ( & cur_ptr , " ,| " ) ) ) {
2005-03-03 20:31:21 +00:00
if ( ast_strlen_zero ( member ) )
continue ;
2005-01-21 04:14:26 +00:00
2005-03-03 20:31:21 +00:00
interface = strsep ( & member , " ; " ) ;
penalty_tok = strsep ( & member , " ; " ) ;
paused_tok = strsep ( & member , " ; " ) ;
2006-09-20 05:59:53 +00:00
membername = strsep ( & member , " ; " ) ;
2008-01-08 21:18:32 +00:00
state_interface = strsep ( & member , " ; " ) ;
2005-03-03 20:31:21 +00:00
if ( ! penalty_tok ) {
2006-01-16 17:37:44 +00:00
ast_log ( LOG_WARNING , " Error parsing persistent member string for '%s' (penalty) \n " , queue_name ) ;
2005-03-03 20:31:21 +00:00
break ;
}
penalty = strtol ( penalty_tok , NULL , 10 ) ;
if ( errno = = ERANGE ) {
ast_log ( LOG_WARNING , " Error converting penalty: %s: Out of range. \n " , penalty_tok ) ;
break ;
}
if ( ! paused_tok ) {
ast_log ( LOG_WARNING , " Error parsing persistent member string for '%s' (paused) \n " , queue_name ) ;
break ;
}
paused = strtol ( paused_tok , NULL , 10 ) ;
if ( ( errno = = ERANGE ) | | paused < 0 | | paused > 1 ) {
ast_log ( LOG_WARNING , " Error converting paused: %s: Expected 0 or 1. \n " , paused_tok ) ;
break ;
}
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d \n " , queue_name , interface , membername , penalty , paused ) ;
2005-03-03 20:31:21 +00:00
2008-01-08 21:18:32 +00:00
if ( add_to_queue ( queue_name , interface , membername , penalty , paused , 0 , state_interface ) = = RES_OUTOFMEMORY ) {
2005-03-03 20:31:21 +00:00
ast_log ( LOG_ERROR , " Out of Memory when reloading persistent queue member \n " ) ;
break ;
2004-12-06 05:54:16 +00:00
}
}
2009-11-24 20:31:28 +00:00
queue_t_unref ( cur_queue , " Expire reload reference " ) ;
2004-12-06 05:54:16 +00:00
}
2005-03-03 20:31:21 +00:00
if ( db_tree ) {
2006-01-16 17:37:44 +00:00
ast_log ( LOG_NOTICE , " Queue members successfully reloaded from database. \n " ) ;
2005-03-03 20:31:21 +00:00
ast_db_freetree ( db_tree ) ;
2004-12-06 05:54:16 +00:00
}
}
2008-03-10 15:45:13 +00:00
/*! \brief PauseQueueMember application */
2009-05-21 21:13:09 +00:00
static int pqm_exec ( struct ast_channel * chan , const char * data )
2005-01-21 04:14:26 +00:00
{
2005-11-08 04:48:00 +00:00
char * parse ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
AST_APP_ARG ( options ) ;
2007-09-07 16:13:57 +00:00
AST_APP_ARG ( reason ) ;
2005-11-08 04:48:00 +00:00
) ;
2005-01-21 04:14:26 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_WARNING , " PauseQueueMember requires an argument ([queuename],interface[,options][,reason]) \n " ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2006-05-10 13:22:15 +00:00
parse = ast_strdupa ( data ) ;
2005-01-21 04:14:26 +00:00
2005-11-08 04:48:00 +00:00
AST_STANDARD_APP_ARGS ( args , parse ) ;
if ( ast_strlen_zero ( args . interface ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_WARNING , " Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]]) \n " ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2007-09-07 16:13:57 +00:00
if ( set_member_paused ( args . queuename , args . interface , args . reason , 1 ) ) {
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " Attempt to pause interface %s, not found \n " , args . interface ) ;
pbx_builtin_setvar_helper ( chan , " PQMSTATUS " , " NOTFOUND " ) ;
2008-01-29 19:41:42 +00:00
return 0 ;
2005-01-21 04:14:26 +00:00
}
2005-11-08 04:48:00 +00:00
pbx_builtin_setvar_helper ( chan , " PQMSTATUS " , " PAUSED " ) ;
2006-06-14 23:20:08 +00:00
2005-01-21 04:14:26 +00:00
return 0 ;
}
2008-03-10 15:45:13 +00:00
/*! \brief UnPauseQueueMember application */
2009-05-21 21:13:09 +00:00
static int upqm_exec ( struct ast_channel * chan , const char * data )
2005-01-21 04:14:26 +00:00
{
2005-11-08 04:48:00 +00:00
char * parse ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
AST_APP_ARG ( options ) ;
2007-09-07 16:13:57 +00:00
AST_APP_ARG ( reason ) ;
2005-11-08 04:48:00 +00:00
) ;
2005-01-21 04:14:26 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_WARNING , " UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]]) \n " ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2006-05-10 13:22:15 +00:00
parse = ast_strdupa ( data ) ;
2005-01-21 04:14:26 +00:00
2005-11-08 04:48:00 +00:00
AST_STANDARD_APP_ARGS ( args , parse ) ;
if ( ast_strlen_zero ( args . interface ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_WARNING , " Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]]) \n " ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2007-09-07 16:13:57 +00:00
if ( set_member_paused ( args . queuename , args . interface , args . reason , 0 ) ) {
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " Attempt to unpause interface %s, not found \n " , args . interface ) ;
pbx_builtin_setvar_helper ( chan , " UPQMSTATUS " , " NOTFOUND " ) ;
2008-01-29 19:41:42 +00:00
return 0 ;
2005-01-21 04:14:26 +00:00
}
2005-11-08 04:48:00 +00:00
pbx_builtin_setvar_helper ( chan , " UPQMSTATUS " , " UNPAUSED " ) ;
2006-06-14 23:20:08 +00:00
2005-01-21 04:14:26 +00:00
return 0 ;
}
2008-03-10 15:45:13 +00:00
/*! \brief RemoveQueueMember application */
2009-05-21 21:13:09 +00:00
static int rqm_exec ( struct ast_channel * chan , const char * data )
2003-06-27 23:07:59 +00:00
{
int res = - 1 ;
2005-11-08 04:48:00 +00:00
char * parse , * temppos = NULL ;
2011-08-08 20:28:20 +00:00
struct member * mem = NULL ;
2005-11-08 04:48:00 +00:00
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
AST_APP_ARG ( options ) ;
) ;
2003-06-27 23:07:59 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_WARNING , " RemoveQueueMember requires an argument (queuename[,interface[,options]]) \n " ) ;
2003-06-27 23:07:59 +00:00
return - 1 ;
}
2004-07-28 02:55:22 +00:00
2006-05-10 13:22:15 +00:00
parse = ast_strdupa ( data ) ;
2004-07-28 02:55:22 +00:00
2005-11-08 04:48:00 +00:00
AST_STANDARD_APP_ARGS ( args , parse ) ;
if ( ast_strlen_zero ( args . interface ) ) {
2012-01-09 22:15:50 +00:00
args . interface = ast_strdupa ( ast_channel_name ( chan ) ) ;
2005-11-08 04:48:00 +00:00
temppos = strrchr ( args . interface , ' - ' ) ;
if ( temppos )
* temppos = ' \0 ' ;
2003-06-27 23:07:59 +00:00
}
2008-09-09 17:15:29 +00:00
ast_debug ( 1 , " queue: %s, member: %s \n " , args . queuename , args . interface ) ;
2011-08-08 20:28:20 +00:00
if ( log_membername_as_agent ) {
mem = find_member_by_queuename_and_interface ( args . queuename , args . interface ) ;
}
2005-11-08 04:48:00 +00:00
switch ( remove_from_queue ( args . queuename , args . interface ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2011-10-23 14:35:26 +00:00
if ( ! mem | | ast_strlen_zero ( mem - > membername ) ) {
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , args . interface , " REMOVEMEMBER " , " %s " , " " ) ;
2011-08-08 20:28:20 +00:00
} else {
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , mem - > membername , " REMOVEMEMBER " , " %s " , " " ) ;
2011-08-08 20:28:20 +00:00
}
2006-11-10 16:38:33 +00:00
ast_log ( LOG_NOTICE , " Removed interface '%s' from queue '%s' \n " , args . interface , args . queuename ) ;
2005-11-08 04:48:00 +00:00
pbx_builtin_setvar_helper ( chan , " RQMSTATUS " , " REMOVED " ) ;
2004-07-28 02:55:22 +00:00
res = 0 ;
break ;
case RES_EXISTS :
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " Unable to remove interface '%s' from queue '%s': Not there \n " , args . interface , args . queuename ) ;
2005-11-08 04:48:00 +00:00
pbx_builtin_setvar_helper ( chan , " RQMSTATUS " , " NOTINQUEUE " ) ;
2004-07-28 02:55:22 +00:00
res = 0 ;
break ;
case RES_NOSUCHQUEUE :
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " Unable to remove interface from queue '%s': No such queue \n " , args . queuename ) ;
pbx_builtin_setvar_helper ( chan , " RQMSTATUS " , " NOSUCHQUEUE " ) ;
2004-07-28 02:55:22 +00:00
res = 0 ;
break ;
2007-10-16 21:16:00 +00:00
case RES_NOT_DYNAMIC :
ast_log ( LOG_WARNING , " Unable to remove interface from queue '%s': '%s' is not a dynamic member \n " , args . queuename , args . interface ) ;
pbx_builtin_setvar_helper ( chan , " RQMSTATUS " , " NOTDYNAMIC " ) ;
res = 0 ;
break ;
2004-07-28 02:55:22 +00:00
}
2003-06-27 23:07:59 +00:00
2011-10-23 14:35:26 +00:00
if ( mem ) {
ao2_ref ( mem , - 1 ) ;
}
2003-06-27 23:07:59 +00:00
return res ;
}
2008-03-10 15:45:13 +00:00
/*! \brief AddQueueMember application */
2009-05-21 21:13:09 +00:00
static int aqm_exec ( struct ast_channel * chan , const char * data )
2003-06-27 23:07:59 +00:00
{
int res = - 1 ;
2005-11-08 04:48:00 +00:00
char * parse , * temppos = NULL ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
AST_APP_ARG ( penalty ) ;
AST_APP_ARG ( options ) ;
2006-09-20 05:59:53 +00:00
AST_APP_ARG ( membername ) ;
2008-01-08 21:18:32 +00:00
AST_APP_ARG ( state_interface ) ;
2005-11-08 04:48:00 +00:00
) ;
2004-06-19 16:00:50 +00:00
int penalty = 0 ;
2003-06-27 23:07:59 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2008-05-12 16:35:50 +00:00
ast_log ( LOG_WARNING , " AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]]) \n " ) ;
2003-06-27 23:07:59 +00:00
return - 1 ;
}
2004-07-28 02:55:22 +00:00
2006-05-10 13:22:15 +00:00
parse = ast_strdupa ( data ) ;
2004-07-28 02:55:22 +00:00
2005-11-08 04:48:00 +00:00
AST_STANDARD_APP_ARGS ( args , parse ) ;
if ( ast_strlen_zero ( args . interface ) ) {
2012-01-09 22:15:50 +00:00
args . interface = ast_strdupa ( ast_channel_name ( chan ) ) ;
2005-11-08 04:48:00 +00:00
temppos = strrchr ( args . interface , ' - ' ) ;
if ( temppos )
* temppos = ' \0 ' ;
}
if ( ! ast_strlen_zero ( args . penalty ) ) {
2009-08-10 19:20:57 +00:00
if ( ( sscanf ( args . penalty , " %30d " , & penalty ) ! = 1 ) | | penalty < 0 ) {
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " Penalty '%s' is invalid, must be an integer >= 0 \n " , args . penalty ) ;
penalty = 0 ;
2004-06-19 16:00:50 +00:00
}
2003-06-27 23:07:59 +00:00
}
2005-11-08 04:48:00 +00:00
2008-01-08 21:18:32 +00:00
switch ( add_to_queue ( args . queuename , args . interface , args . membername , penalty , 0 , queue_persistent_members , args . state_interface ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2011-08-08 20:28:20 +00:00
if ( ast_strlen_zero ( args . membername ) | | ! log_membername_as_agent ) {
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , args . interface , " ADDMEMBER " , " %s " , " " ) ;
2011-08-08 20:28:20 +00:00
} else {
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , args . membername , " ADDMEMBER " , " %s " , " " ) ;
2011-08-08 20:28:20 +00:00
}
2005-11-08 04:48:00 +00:00
ast_log ( LOG_NOTICE , " Added interface '%s' to queue '%s' \n " , args . interface , args . queuename ) ;
pbx_builtin_setvar_helper ( chan , " AQMSTATUS " , " ADDED " ) ;
2004-07-28 02:55:22 +00:00
res = 0 ;
break ;
case RES_EXISTS :
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " Unable to add interface '%s' to queue '%s': Already there \n " , args . interface , args . queuename ) ;
pbx_builtin_setvar_helper ( chan , " AQMSTATUS " , " MEMBERALREADY " ) ;
2004-07-28 02:55:22 +00:00
res = 0 ;
break ;
case RES_NOSUCHQUEUE :
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " Unable to add interface to queue '%s': No such queue \n " , args . queuename ) ;
pbx_builtin_setvar_helper ( chan , " AQMSTATUS " , " NOSUCHQUEUE " ) ;
2004-07-28 02:55:22 +00:00
res = 0 ;
break ;
case RES_OUTOFMEMORY :
2011-08-08 20:28:20 +00:00
ast_log ( LOG_ERROR , " Out of memory adding interface %s to queue %s \n " , args . interface , args . queuename ) ;
2004-07-28 02:55:22 +00:00
break ;
}
2003-06-27 23:07:59 +00:00
return res ;
}
2008-03-10 15:45:13 +00:00
/*! \brief QueueLog application */
2009-05-21 21:13:09 +00:00
static int ql_exec ( struct ast_channel * chan , const char * data )
2006-07-16 19:36:29 +00:00
{
char * parse ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( uniqueid ) ;
2006-09-20 05:59:53 +00:00
AST_APP_ARG ( membername ) ;
2006-07-16 19:36:29 +00:00
AST_APP_ARG ( event ) ;
AST_APP_ARG ( params ) ;
) ;
if ( ast_strlen_zero ( data ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_WARNING , " QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo] \n " ) ;
2006-07-16 19:36:29 +00:00
return - 1 ;
}
parse = ast_strdupa ( data ) ;
AST_STANDARD_APP_ARGS ( args , parse ) ;
if ( ast_strlen_zero ( args . queuename ) | | ast_strlen_zero ( args . uniqueid )
2006-09-20 05:59:53 +00:00
| | ast_strlen_zero ( args . membername ) | | ast_strlen_zero ( args . event ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_WARNING , " QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]) \n " ) ;
2006-07-16 19:36:29 +00:00
return - 1 ;
}
2006-09-20 05:59:53 +00:00
ast_queue_log ( args . queuename , args . uniqueid , args . membername , args . event ,
2006-07-16 19:36:29 +00:00
" %s " , args . params ? args . params : " " ) ;
return 0 ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Copy rule from global list into specified queue */
2007-12-21 00:44:17 +00:00
static void copy_rules ( struct queue_ent * qe , const char * rulename )
{
struct penalty_rule * pr_iter ;
struct rule_list * rl_iter ;
const char * tmp = ast_strlen_zero ( rulename ) ? qe - > parent - > defaultrule : rulename ;
AST_LIST_LOCK ( & rule_lists ) ;
AST_LIST_TRAVERSE ( & rule_lists , rl_iter , list ) {
if ( ! strcasecmp ( rl_iter - > name , tmp ) )
break ;
}
if ( rl_iter ) {
AST_LIST_TRAVERSE ( & rl_iter - > rules , pr_iter , list ) {
struct penalty_rule * new_pr = ast_calloc ( 1 , sizeof ( * new_pr ) ) ;
if ( ! new_pr ) {
ast_log ( LOG_ERROR , " Memory allocation error when copying penalty rules! Aborting! \n " ) ;
break ;
}
new_pr - > time = pr_iter - > time ;
new_pr - > max_value = pr_iter - > max_value ;
new_pr - > min_value = pr_iter - > min_value ;
new_pr - > max_relative = pr_iter - > max_relative ;
new_pr - > min_relative = pr_iter - > min_relative ;
AST_LIST_INSERT_TAIL ( & qe - > qe_rules , new_pr , list ) ;
}
}
AST_LIST_UNLOCK ( & rule_lists ) ;
}
2008-01-08 23:56:24 +00:00
/*!\brief The starting point for all queue calls
*
* The process involved here is to
* 1. Parse the options specified in the call to Queue ( )
* 2. Join the queue
* 3. Wait in a loop until it is our turn to try calling a queue member
* 4. Attempt to call a queue member
* 5. If 4. did not result in a bridged call , then check for between
* call options such as periodic announcements etc .
2008-02-14 20:46:00 +00:00
* 6. Try 4 again unless some condition ( such as an expiration time ) causes us to
2008-01-08 23:56:24 +00:00
* exit the queue .
*/
2009-05-21 21:13:09 +00:00
static int queue_exec ( struct ast_channel * chan , const char * data )
2002-09-02 23:15:40 +00:00
{
int res = - 1 ;
2004-05-09 19:13:43 +00:00
int ringing = 0 ;
2005-12-03 19:25:33 +00:00
const char * user_priority ;
2006-01-06 03:40:12 +00:00
const char * max_penalty_str ;
2007-12-21 00:44:17 +00:00
const char * min_penalty_str ;
2004-06-23 20:05:18 +00:00
int prio ;
2007-05-14 17:38:36 +00:00
int qcontinue = 0 ;
2007-12-21 00:44:17 +00:00
int max_penalty , min_penalty ;
2005-03-28 20:48:24 +00:00
enum queue_result reason = QUEUE_UNKNOWN ;
2004-02-03 00:53:21 +00:00
/* whether to exit Queue application after the timeout hits */
2007-08-08 14:34:10 +00:00
int tries = 0 ;
int noption = 0 ;
2006-01-23 17:23:22 +00:00
char * parse ;
2007-07-19 22:00:59 +00:00
int makeannouncement = 0 ;
2009-04-27 16:37:51 +00:00
int position = 0 ;
2006-01-23 17:23:22 +00:00
AST_DECLARE_APP_ARGS ( args ,
2006-07-14 05:42:06 +00:00
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( options ) ;
AST_APP_ARG ( url ) ;
AST_APP_ARG ( announceoverride ) ;
AST_APP_ARG ( queuetimeoutstr ) ;
AST_APP_ARG ( agi ) ;
2006-10-27 18:59:16 +00:00
AST_APP_ARG ( macro ) ;
2007-06-19 23:36:34 +00:00
AST_APP_ARG ( gosub ) ;
2007-12-21 00:44:17 +00:00
AST_APP_ARG ( rule ) ;
2009-04-27 16:37:51 +00:00
AST_APP_ARG ( position ) ;
2006-01-23 17:23:22 +00:00
) ;
2002-09-02 23:15:40 +00:00
/* Our queue entry */
2009-11-30 18:55:07 +00:00
struct queue_ent qe = { 0 } ;
2012-03-02 16:57:12 +00:00
struct ast_flags opts = { 0 , } ;
char * opt_args [ OPT_ARG_ARRAY_SIZE ] ;
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2009-04-27 16:37:51 +00:00
ast_log ( LOG_WARNING , " Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]] \n " ) ;
2002-09-02 23:15:40 +00:00
return - 1 ;
}
2012-03-02 16:57:12 +00:00
2006-01-23 17:23:22 +00:00
parse = ast_strdupa ( data ) ;
AST_STANDARD_APP_ARGS ( args , parse ) ;
2005-10-19 18:19:02 +00:00
2012-03-02 16:57:12 +00:00
if ( ! ast_strlen_zero ( args . options ) ) {
ast_app_parse_options ( queue_exec_options , & opts , opt_args , args . options ) ;
}
2004-03-13 06:00:41 +00:00
/* Setup our queue entry */
2004-12-19 21:23:36 +00:00
qe . start = time ( NULL ) ;
/* set the expire time based on the supplied timeout; */
2008-04-02 14:32:43 +00:00
if ( ! ast_strlen_zero ( args . queuetimeoutstr ) )
2006-01-23 17:23:22 +00:00
qe . expire = qe . start + atoi ( args . queuetimeoutstr ) ;
2004-12-19 21:23:36 +00:00
else
qe . expire = 0 ;
2004-06-23 20:05:18 +00:00
/* Get the priority from the variable ${QUEUE_PRIO} */
2008-05-28 19:56:18 +00:00
ast_channel_lock ( chan ) ;
2004-06-23 20:05:18 +00:00
user_priority = pbx_builtin_getvar_helper ( chan , " QUEUE_PRIO " ) ;
if ( user_priority ) {
2009-08-10 19:20:57 +00:00
if ( sscanf ( user_priority , " %30d " , & prio ) = = 1 ) {
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " %s: Got priority %d from ${QUEUE_PRIO}. \n " , ast_channel_name ( chan ) , prio ) ;
2004-06-23 20:05:18 +00:00
} else {
ast_log ( LOG_WARNING , " ${QUEUE_PRIO}: Invalid value (%s), channel %s. \n " ,
2012-01-09 22:15:50 +00:00
user_priority , ast_channel_name ( chan ) ) ;
2004-06-23 20:05:18 +00:00
prio = 0 ;
}
} else {
2007-06-14 19:39:12 +00:00
ast_debug ( 3 , " NO QUEUE_PRIO variable found. Using default. \n " ) ;
2004-06-23 20:05:18 +00:00
prio = 0 ;
2004-03-13 06:00:41 +00:00
}
2006-01-06 03:40:12 +00:00
/* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
2007-12-21 00:44:17 +00:00
2006-01-06 03:40:12 +00:00
if ( ( max_penalty_str = pbx_builtin_getvar_helper ( chan , " QUEUE_MAX_PENALTY " ) ) ) {
2009-08-10 19:20:57 +00:00
if ( sscanf ( max_penalty_str , " %30d " , & max_penalty ) = = 1 ) {
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " %s: Got max penalty %d from ${QUEUE_MAX_PENALTY}. \n " , ast_channel_name ( chan ) , max_penalty ) ;
2006-01-06 03:40:12 +00:00
} else {
ast_log ( LOG_WARNING , " ${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s. \n " ,
2012-01-09 22:15:50 +00:00
max_penalty_str , ast_channel_name ( chan ) ) ;
2006-01-06 03:40:12 +00:00
max_penalty = 0 ;
}
} else {
max_penalty = 0 ;
}
2007-12-21 00:44:17 +00:00
if ( ( min_penalty_str = pbx_builtin_getvar_helper ( chan , " QUEUE_MIN_PENALTY " ) ) ) {
2009-08-10 19:20:57 +00:00
if ( sscanf ( min_penalty_str , " %30d " , & min_penalty ) = = 1 ) {
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " %s: Got min penalty %d from ${QUEUE_MIN_PENALTY}. \n " , ast_channel_name ( chan ) , min_penalty ) ;
2007-12-21 00:44:17 +00:00
} else {
ast_log ( LOG_WARNING , " ${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s. \n " ,
2012-01-09 22:15:50 +00:00
min_penalty_str , ast_channel_name ( chan ) ) ;
2007-12-21 00:44:17 +00:00
min_penalty = 0 ;
}
} else {
min_penalty = 0 ;
}
2008-05-28 19:56:18 +00:00
ast_channel_unlock ( chan ) ;
2007-12-21 00:44:17 +00:00
2012-03-02 16:57:12 +00:00
if ( ast_test_flag ( & opts , OPT_RINGING ) ) {
2005-03-28 20:48:24 +00:00
ringing = 1 ;
2012-03-02 16:57:12 +00:00
}
2004-03-13 06:00:41 +00:00
2012-03-02 16:57:12 +00:00
if ( ringing ! = 1 & & ast_test_flag ( & opts , OPT_RING_WHEN_RINGING ) ) {
2009-12-23 18:39:37 +00:00
qe . ring_when_ringing = 1 ;
}
2012-03-02 16:57:12 +00:00
if ( ast_test_flag ( & opts , OPT_GO_ON ) ) {
2007-05-14 17:38:36 +00:00
qcontinue = 1 ;
2012-03-02 16:57:12 +00:00
}
2007-05-14 17:38:36 +00:00
2009-04-27 16:37:51 +00:00
if ( args . position ) {
position = atoi ( args . position ) ;
if ( position < 0 ) {
ast_log ( LOG_WARNING , " Invalid position '%s' given for call to queue '%s'. Assuming no preference for position \n " , args . position , args . queuename ) ;
position = 0 ;
}
}
2007-06-14 19:39:12 +00:00
ast_debug ( 1 , " queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d \n " ,
args . queuename , args . options , args . url , args . announceoverride , ( long ) qe . expire , prio ) ;
2004-03-13 06:00:41 +00:00
2002-09-02 23:15:40 +00:00
qe . chan = chan ;
2006-01-06 03:40:12 +00:00
qe . prio = prio ;
qe . max_penalty = max_penalty ;
2007-12-21 00:44:17 +00:00
qe . min_penalty = min_penalty ;
2004-03-13 06:00:41 +00:00
qe . last_pos_said = 0 ;
qe . last_pos = 0 ;
2005-07-31 22:07:58 +00:00
qe . last_periodic_announce_time = time ( NULL ) ;
2006-01-17 20:58:56 +00:00
qe . last_periodic_announce_sound = 0 ;
2007-07-19 16:29:51 +00:00
qe . valid_digits = 0 ;
2009-04-27 16:37:51 +00:00
if ( join_queue ( args . queuename , & qe , & reason , position ) ) {
2007-12-28 18:39:29 +00:00
ast_log ( LOG_WARNING , " Unable to join queue '%s' \n " , args . queuename ) ;
set_queue_result ( chan , reason ) ;
return 0 ;
}
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " ENTERQUEUE " , " %s|%s|%d " ,
2010-07-14 15:48:36 +00:00
S_OR ( args . url , " " ) ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( chan ) - > id . number . valid , ast_channel_caller ( chan ) - > id . number . str , " " ) ,
2010-07-14 15:48:36 +00:00
qe . opos ) ;
2007-12-28 18:39:29 +00:00
copy_rules ( & qe , args . rule ) ;
qe . pr = AST_LIST_FIRST ( & qe . qe_rules ) ;
2004-06-23 20:05:18 +00:00
check_turns :
2007-12-28 18:39:29 +00:00
if ( ringing ) {
ast_indicate ( chan , AST_CONTROL_RINGING ) ;
} else {
ast_moh_start ( chan , qe . moh , NULL ) ;
}
2007-07-19 16:29:51 +00:00
2007-12-28 18:39:29 +00:00
/* This is the wait loop for callers 2 through maxlen */
res = wait_our_turn ( & qe , ringing , & reason ) ;
if ( res ) {
goto stop ;
}
2006-06-14 23:20:08 +00:00
2007-12-28 18:39:29 +00:00
makeannouncement = 0 ;
2004-03-13 06:00:41 +00:00
2007-12-28 18:39:29 +00:00
for ( ; ; ) {
/* This is the wait loop for the head caller*/
/* To exit, they may get their call answered; */
/* they may dial a digit from the queue context; */
/* or, they may timeout. */
2005-03-28 20:48:24 +00:00
2007-12-28 18:39:29 +00:00
/* Leave if we have exceeded our queuetimeout */
2008-08-18 20:04:10 +00:00
if ( qe . expire & & ( time ( NULL ) > = qe . expire ) ) {
2007-12-28 18:39:29 +00:00
record_abandoned ( & qe ) ;
reason = QUEUE_TIMEOUT ;
res = 0 ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " EXITWITHTIMEOUT " , " %d|%d|%ld " ,
2007-12-28 18:39:29 +00:00
qe . pos , qe . opos , ( long ) time ( NULL ) - qe . start ) ;
break ;
}
2005-07-31 22:07:58 +00:00
2007-12-28 18:39:29 +00:00
if ( makeannouncement ) {
/* Make a position announcement, if enabled */
if ( qe . parent - > announcefrequency )
if ( ( res = say_position ( & qe , ringing ) ) )
2007-07-19 16:29:51 +00:00
goto stop ;
2007-12-28 18:39:29 +00:00
}
makeannouncement = 1 ;
2004-03-13 06:00:41 +00:00
2007-12-28 18:39:29 +00:00
/* Make a periodic announcement, if enabled */
if ( qe . parent - > periodicannouncefrequency )
if ( ( res = say_periodic_announcement ( & qe , ringing ) ) )
2007-07-19 19:02:38 +00:00
goto stop ;
2008-07-31 16:05:12 +00:00
/* Leave if we have exceeded our queuetimeout */
2008-08-18 20:04:10 +00:00
if ( qe . expire & & ( time ( NULL ) > = qe . expire ) ) {
2008-07-31 16:05:12 +00:00
record_abandoned ( & qe ) ;
reason = QUEUE_TIMEOUT ;
res = 0 ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " EXITWITHTIMEOUT " , " %d " , qe . pos ) ;
2008-07-31 16:05:12 +00:00
break ;
}
2005-03-28 20:48:24 +00:00
2007-12-28 18:39:29 +00:00
/* see if we need to move to the next penalty level for this queue */
while ( qe . pr & & ( ( time ( NULL ) - qe . start ) > qe . pr - > time ) ) {
update_qe_rule ( & qe ) ;
}
2007-07-09 18:37:09 +00:00
2007-12-28 18:39:29 +00:00
/* Try calling all queue members for 'timeout' seconds */
2012-03-02 16:57:12 +00:00
res = try_calling ( & qe , opts , opt_args , args . announceoverride , args . url , & tries , & noption , args . agi , args . macro , args . gosub , ringing ) ;
2007-12-28 18:39:29 +00:00
if ( res ) {
goto stop ;
}
2005-03-28 20:48:24 +00:00
2008-09-08 23:00:06 +00:00
if ( qe . parent - > leavewhenempty ) {
2008-10-06 15:29:56 +00:00
int status = 0 ;
if ( ( status = get_member_status ( qe . parent , qe . max_penalty , qe . min_penalty , qe . parent - > leavewhenempty ) ) ) {
2008-09-08 23:00:06 +00:00
record_abandoned ( & qe ) ;
reason = QUEUE_LEAVEEMPTY ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " EXITEMPTY " , " %d|%d|%ld " , qe . pos , qe . opos , ( long ) ( time ( NULL ) - qe . start ) ) ;
2008-09-08 23:00:06 +00:00
res = 0 ;
break ;
}
}
2004-10-26 22:25:43 +00:00
2007-12-28 18:39:29 +00:00
/* exit after 'timeout' cycle if 'n' option enabled */
Simplify queue membercount code
Despite an ominous sounding comment stating that membercount was for "logged
in" members only and thus we couldn't use ao2_container_count(), I could not
find a single place in the code where that seemed to be accurate. The only time
we decremented membercount was when we were marking something dead or actually
removing it. The only places we incremented it were either after ao2_link(), or
trying to correct for having set it to 0 during a reload. In every case where
we were correcting the value, it seemed that we were trying to make the count
actually match what ao2_container_count() would return. The only place I could
find where we made a determination about something being "logged in" or not, we
didn't trust the membercount, but instead looked at devicestate, paused, etc.
This patch removes membercount, replaces its use with ao2_container_count, and
manually adds the results of ao2_container_count to a "membercount" field for
ast_data queue query results. This patch also would fix AST-676, but as it is
slightly riskier than the previously committed fix, the two commits have been
made separately.
Reivew: https://reviewboard.asterisk.org/r/1541/
........
Merged revisions 342383 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 342384 from http://svn.asterisk.org/svn/asterisk/branches/10
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@342385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2011-10-25 20:07:59 +00:00
if ( noption & & tries > = ao2_container_count ( qe . parent - > members ) ) {
2007-12-28 18:39:29 +00:00
ast_verb ( 3 , " Exiting on time-out cycle \n " ) ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " EXITWITHTIMEOUT " , " %d " , qe . pos ) ;
2007-12-28 18:39:29 +00:00
record_abandoned ( & qe ) ;
reason = QUEUE_TIMEOUT ;
res = 0 ;
break ;
}
2004-03-13 06:00:41 +00:00
2008-09-08 23:00:06 +00:00
2007-12-28 18:39:29 +00:00
/* Leave if we have exceeded our queuetimeout */
2008-08-18 20:04:10 +00:00
if ( qe . expire & & ( time ( NULL ) > = qe . expire ) ) {
2007-12-28 18:39:29 +00:00
record_abandoned ( & qe ) ;
reason = QUEUE_TIMEOUT ;
res = 0 ;
2012-01-24 20:12:09 +00:00
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 ) ;
2007-12-28 18:39:29 +00:00
break ;
}
2007-07-19 19:02:38 +00:00
2007-12-28 18:39:29 +00:00
/* If using dynamic realtime members, we should regenerate the member list for this queue */
update_realtime_members ( qe . parent ) ;
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
res = wait_a_bit ( & qe ) ;
if ( res )
goto stop ;
/* Since this is a priority queue and
* it is not sure that we are still at the head
* of the queue , go and check for our turn again .
*/
if ( ! is_our_turn ( & qe ) ) {
2012-01-09 22:15:50 +00:00
ast_debug ( 1 , " Darn priorities, going back in queue (%s)! \n " , ast_channel_name ( qe . chan ) ) ;
2007-12-28 18:39:29 +00:00
goto check_turns ;
2002-09-02 23:15:40 +00:00
}
2007-12-28 18:39:29 +00:00
}
2007-07-19 16:29:51 +00:00
stop :
2007-12-28 18:39:29 +00:00
if ( res ) {
if ( res < 0 ) {
if ( ! qe . handled ) {
record_abandoned ( & qe ) ;
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " ABANDON " ,
2007-12-28 18:39:29 +00:00
" %d|%d|%ld " , qe . pos , qe . opos ,
( long ) time ( NULL ) - qe . start ) ;
2008-06-18 15:08:56 +00:00
res = - 1 ;
} else if ( qcontinue ) {
reason = QUEUE_CONTINUE ;
res = 0 ;
2007-07-19 16:29:51 +00:00
}
2007-12-28 18:39:29 +00:00
} else if ( qe . valid_digits ) {
2012-01-24 20:12:09 +00:00
ast_queue_log ( args . queuename , ast_channel_uniqueid ( chan ) , " NONE " , " EXITWITHKEY " ,
2007-12-28 18:39:29 +00:00
" %s|%d " , qe . digits , qe . pos ) ;
2007-07-19 16:29:51 +00:00
}
2007-12-28 18:39:29 +00:00
}
2007-07-19 16:29:51 +00:00
2007-12-28 18:39:29 +00:00
/* Don't allow return code > 0 */
Merged revisions 166093 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
In order to merge this 1.4 patch into trunk,
I had to resolve some conflicts and wait for
Russell to make some changes to res_agi.
I re-ran all the tests; 39 calls in all, and
made fairly careful notes and comparisons: I
don't want this to blow up some aspect of
asterisk; I completely removed the KEEPALIVE
from the pbx.h decls. The first 3 scenarios
involving feature park; feature xfer to 700;
hookflash park to Park() app call all behave
the same, don't appear to leave hung channels,
and no crashes.
........
r166093 | murf | 2008-12-19 15:30:32 -0700 (Fri, 19 Dec 2008) | 131 lines
This merges the masqpark branch into 1.4
These changes eliminate the need for (and use of)
the KEEPALIVE return code in res_features.c;
There are other places that use this result code
for similar purposes at a higher level, these appear
to be left alone in 1.4, but attacked in trunk.
The reason these changes are being made in 1.4, is
that parking ends a channel's life, in some situations,
and the code in the bridge (and some other places),
was not checking the result code properly, and dereferencing
the channel pointer, which could lead to memory corruption
and crashes.
Calling the masq_park function eliminates this danger
in higher levels.
A series of previous commits have replaced some parking calls
with masq_park, but this patch puts them ALL to rest,
(except one, purposely left alone because a masquerade
is done anyway), and gets rid of the code that tests
the KEEPALIVE result, and the NOHANGUP_PEER result codes.
While bug 13820 inspired this work, this patch does
not solve all the problems mentioned there.
I have tested this patch (again) to make sure I have
not introduced regressions.
Crashes that occurred when a parked party hung up
while the parking party was listening to the numbers
of the parking stall being assigned, is eliminated.
These are the cases where parking code may be activated:
1. Feature one touch (eg. *3)
2. Feature blind xfer to parking lot (eg ##700)
3. Run Park() app from dialplan (eg sip xfer to 700)
(eg. dahdi hookflash xfer to 700)
4. Run Park via manager.
The interesting testing cases for parking are:
I. A calls B, A parks B
a. B hangs up while A is getting the numbers announced.
b. B hangs up after A gets the announcement, but
before the parking time expires
c. B waits, time expires, A is redialed,
A answers, B and A are connected, after
which, B hangs up.
d. C picks up B while still in parking lot.
II. A calls B, B parks A
a. A hangs up while B is getting the numbers announced.
b. A hangs up after B gets the announcement, but
before the parking time expires
c. A waits, time expires, B is redialed,
B answers, A and B are connected, after
which, A hangs up.
d. C picks up A while still in parking lot.
Testing this throroughly involves acting all the permutations
of I and II, in situations 1,2,3, and 4.
Since I added a few more changes (ALL references to KEEPALIVE in the bridge
code eliimated (I missed one earlier), I retested
most of the above cases, and no crashes.
H-extension weirdness.
Current h-extension execution is not completely
correct for several of the cases.
For the case where A calls B, and A parks B, the
'h' exten is run on A's channel as soon as the park
is accomplished. This is expected behavior.
But when A calls B, and B parks A, this will be
current behavior:
After B parks A, B is hung up by the system, and
the 'h' (hangup) exten gets run, but the channel
mentioned will be a derivative of A's...
Thus, if A is DAHDI/1, and B is DAHDI/2,
the h-extension will be run on channel
Parked/DAHDI/1-1<ZOMBIE>, and the
start/answer/end info will be those
relating to Channel A.
And, in the case where A is reconnected to
B after the park time expires, when both parties
hang up after the joyful reunion, no h-exten
will be run at all.
In the case where C picks up A from the
parking lot, when either A or C hang up,
the h-exten will be run for the C channel.
CDR's are a separate issue, and not addressed
here.
As to WHY this strange behavior occurs,
the answer lies in the procedure followed
to accomplish handing over the channel
to the parking manager thread. This procedure
is called masquerading. In the process,
a duplicate copy of the channel is created,
and most of the active data is given to the
new copy. The original channel gets its name
changed to XXX<ZOMBIE> and keeps the PBX
information for the sake of the original
thread (preserving its role as a call
originator, if it had this role to begin
with), while the new channel is without
this info and becomes a call target (a
"peer").
In this case, the parking lot manager
thread is handed the new (masqueraded)
channel. It will not run an h-exten
on the channel if it hangs up while
in the parking lot. The h exten will
be run on the original channel instead,
in the original thread, after the bridge
completes.
See bug 13820 for our intentions as
to how to clean up the h exten behavior.
Review: http://reviewboard.digium.com/r/29/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@166665 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-12-23 18:13:49 +00:00
if ( res > = 0 ) {
2007-12-28 18:39:29 +00:00
res = 0 ;
if ( ringing ) {
ast_indicate ( chan , - 1 ) ;
} else {
ast_moh_stop ( chan ) ;
}
ast_stopstream ( chan ) ;
}
2006-10-27 18:59:16 +00:00
2009-01-27 19:30:54 +00:00
set_queue_variables ( qe . parent , qe . chan ) ;
2006-10-27 18:59:16 +00:00
2007-12-28 18:39:29 +00:00
leave_queue ( & qe ) ;
if ( reason ! = QUEUE_UNKNOWN )
2005-03-28 20:48:24 +00:00
set_queue_result ( chan , reason ) ;
2006-06-14 23:20:08 +00:00
2009-11-30 18:55:07 +00:00
if ( qe . parent ) {
/* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
* This ref must be taken away right before the queue_ent is destroyed . In this case
* the queue_ent is about to be returned on the stack */
qe . parent = queue_unref ( qe . parent ) ;
}
2002-09-02 23:15:40 +00:00
return res ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief create interface var with all queue details .
* \ retval 0 on success
* \ retval - 1 on error
*/
2007-01-05 23:32:42 +00:00
static int queue_function_var ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
2006-10-27 18:59:16 +00:00
{
int res = - 1 ;
2007-11-14 01:15:26 +00:00
struct call_queue * q , tmpq = {
2011-10-23 11:37:50 +00:00
. name = data ,
2007-11-14 01:15:26 +00:00
} ;
2006-10-27 18:59:16 +00:00
2008-04-25 20:20:10 +00:00
char interfacevar [ 256 ] = " " ;
float sl = 0 ;
2006-10-27 18:59:16 +00:00
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_ERROR , " %s requires an argument: queuename \n " , cmd ) ;
return - 1 ;
}
2009-11-24 20:31:28 +00:00
if ( ( q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Find for QUEUE() function " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2008-04-25 20:20:10 +00:00
if ( q - > setqueuevar ) {
sl = 0 ;
2006-10-27 18:59:16 +00:00
res = 0 ;
2008-04-25 20:20:10 +00:00
if ( q - > callscompleted > 0 ) {
sl = 100 * ( ( float ) q - > callscompletedinsl / ( float ) q - > callscompleted ) ;
}
2006-10-27 18:59:16 +00:00
2008-04-25 20:20:10 +00:00
snprintf ( interfacevar , sizeof ( interfacevar ) ,
2009-01-08 19:48:42 +00:00
" QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f " ,
q - > maxlen , int2strat ( q - > strategy ) , q - > count , q - > holdtime , q - > talktime , q - > callscompleted , q - > callsabandoned , q - > servicelevel , sl ) ;
2006-10-27 18:59:16 +00:00
2008-04-25 20:20:10 +00:00
pbx_builtin_setvar_multiple ( chan , interfacevar ) ;
}
2006-10-27 18:59:16 +00:00
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with QUEUE() function " ) ;
2008-04-25 20:20:10 +00:00
} else {
2006-10-27 18:59:16 +00:00
ast_log ( LOG_WARNING , " queue %s was not found \n " , data ) ;
2008-04-25 20:20:10 +00:00
}
2006-10-27 18:59:16 +00:00
snprintf ( buf , len , " %d " , res ) ;
return 0 ;
}
2010-07-16 09:25:48 +00:00
/*!
* \ brief Check if a given queue exists
*
*/
static int queue_function_exists ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
{
struct call_queue * q ;
buf [ 0 ] = ' \0 ' ;
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_ERROR , " %s requires an argument: queuename \n " , cmd ) ;
return - 1 ;
}
2011-12-08 20:55:19 +00:00
q = find_load_queue_rt_friendly ( data ) ;
2010-07-16 09:25:48 +00:00
snprintf ( buf , len , " %d " , q ! = NULL ? 1 : 0 ) ;
if ( q ) {
queue_t_unref ( q , " Done with temporary reference in QUEUE_EXISTS() " ) ;
}
return 0 ;
}
2011-06-29 06:39:26 +00:00
/*!
2009-12-23 19:14:05 +00:00
* \ brief Get number either busy / free / ready or total members of a specific queue
2011-06-29 06:39:26 +00:00
* \ brief Get or set member properties penalty / paused / ignorebusy
* \ retval number of members ( busy / free / ready / total ) or member info ( penalty / paused / ignorebusy )
2008-03-10 15:45:13 +00:00
* \ retval - 1 on error
*/
2011-06-29 06:39:26 +00:00
static int queue_function_mem_read ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
2005-07-12 22:28:51 +00:00
{
int count = 0 ;
2007-09-05 15:32:58 +00:00
struct member * m ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2007-11-16 15:44:27 +00:00
struct call_queue * q ;
2011-06-29 06:39:26 +00:00
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( option ) ;
AST_APP_ARG ( interface ) ;
) ;
/* Make sure the returned value on error is zero length string. */
buf [ 0 ] = ' \0 ' ;
2007-10-29 20:13:23 +00:00
if ( ast_strlen_zero ( data ) ) {
2011-12-14 22:08:55 +00:00
ast_log ( LOG_ERROR , " Missing required argument. %s(<queuename>,<option>[<interface>]) \n " , cmd ) ;
2007-10-29 20:13:23 +00:00
return - 1 ;
}
2011-06-29 06:39:26 +00:00
AST_STANDARD_APP_ARGS ( args , data ) ;
2011-12-14 22:08:55 +00:00
if ( args . argc < 2 ) {
ast_log ( LOG_ERROR , " Missing required argument. %s(<queuename>,<option>[<interface>]) \n " , cmd ) ;
return - 1 ;
}
2011-12-08 20:55:19 +00:00
if ( ( q = find_load_queue_rt_friendly ( args . queuename ) ) ) {
2007-10-29 20:13:23 +00:00
ao2_lock ( q ) ;
2011-06-29 06:39:26 +00:00
if ( ! strcasecmp ( args . option , " logged " ) ) {
2007-10-29 20:13:23 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
/* Count the agents who are logged in and presently answering calls */
if ( ( m - > status ! = AST_DEVICE_UNAVAILABLE ) & & ( m - > status ! = AST_DEVICE_INVALID ) ) {
count + + ;
}
ao2_ref ( m , - 1 ) ;
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2011-06-29 06:39:26 +00:00
} else if ( ! strcasecmp ( args . option , " free " ) ) {
2007-10-29 20:13:23 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
/* Count the agents who are logged in and presently answering calls */
if ( ( m - > status = = AST_DEVICE_NOT_INUSE ) & & ( ! m - > paused ) ) {
count + + ;
}
ao2_ref ( m , - 1 ) ;
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2011-06-29 06:39:26 +00:00
} else if ( ! strcasecmp ( args . option , " ready " ) ) {
2009-12-23 19:14:05 +00:00
time_t now ;
time ( & now ) ;
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
/* Count the agents who are logged in, not paused and not wrapping up */
if ( ( m - > status = = AST_DEVICE_NOT_INUSE ) & & ( ! m - > paused ) & &
! ( m - > lastcall & & q - > wrapuptime & & ( ( now - q - > wrapuptime ) < m - > lastcall ) ) ) {
count + + ;
}
ao2_ref ( m , - 1 ) ;
}
ao2_iterator_destroy ( & mem_iter ) ;
2011-06-29 06:39:26 +00:00
} else if ( ! strcasecmp ( args . option , " count " ) | | ast_strlen_zero ( args . option ) ) {
Simplify queue membercount code
Despite an ominous sounding comment stating that membercount was for "logged
in" members only and thus we couldn't use ao2_container_count(), I could not
find a single place in the code where that seemed to be accurate. The only time
we decremented membercount was when we were marking something dead or actually
removing it. The only places we incremented it were either after ao2_link(), or
trying to correct for having set it to 0 during a reload. In every case where
we were correcting the value, it seemed that we were trying to make the count
actually match what ao2_container_count() would return. The only place I could
find where we made a determination about something being "logged in" or not, we
didn't trust the membercount, but instead looked at devicestate, paused, etc.
This patch removes membercount, replaces its use with ao2_container_count, and
manually adds the results of ao2_container_count to a "membercount" field for
ast_data queue query results. This patch also would fix AST-676, but as it is
slightly riskier than the previously committed fix, the two commits have been
made separately.
Reivew: https://reviewboard.asterisk.org/r/1541/
........
Merged revisions 342383 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 342384 from http://svn.asterisk.org/svn/asterisk/branches/10
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@342385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2011-10-25 20:07:59 +00:00
count = ao2_container_count ( q - > members ) ;
2011-06-29 06:39:26 +00:00
} else if ( ! strcasecmp ( args . option , " penalty " ) & & ! ast_strlen_zero ( args . interface ) & &
( ( m = interface_exists ( q , args . interface ) ) ) ) {
count = m - > penalty ;
2011-10-23 14:35:26 +00:00
ao2_ref ( m , - 1 ) ;
2011-06-29 06:39:26 +00:00
} else if ( ! strcasecmp ( args . option , " paused " ) & & ! ast_strlen_zero ( args . interface ) & &
( ( m = interface_exists ( q , args . interface ) ) ) ) {
count = m - > paused ;
2011-10-23 14:35:26 +00:00
ao2_ref ( m , - 1 ) ;
2011-06-29 06:39:26 +00:00
} else if ( ! strcasecmp ( args . option , " ignorebusy " ) & & ! ast_strlen_zero ( args . interface ) & &
( ( m = interface_exists ( q , args . interface ) ) ) ) {
count = m - > ignorebusy ;
2011-10-23 14:35:26 +00:00
ao2_ref ( m , - 1 ) ;
2011-12-14 22:08:55 +00:00
} else {
ast_log ( LOG_ERROR , " Unknown option %s provided to %s, valid values are: "
" logged, free, ready, count, penalty, paused, ignorebusy \n " , args . option , cmd ) ;
2011-06-29 06:39:26 +00:00
}
2007-10-29 20:13:23 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with temporary reference in QUEUE_MEMBER() " ) ;
2011-06-29 06:39:26 +00:00
} else {
ast_log ( LOG_WARNING , " queue %s was not found \n " , args . queuename ) ;
}
2007-10-29 20:13:23 +00:00
snprintf ( buf , len , " %d " , count ) ;
return 0 ;
}
2011-06-29 06:39:26 +00:00
/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy. */
static int queue_function_mem_write ( struct ast_channel * chan , const char * cmd , char * data , const char * value )
{
int memvalue ;
struct call_queue * q ;
struct member * m ;
char rtvalue [ 80 ] ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( option ) ;
AST_APP_ARG ( interface ) ;
) ;
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_ERROR , " Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>) \n " ) ;
return - 1 ;
}
AST_STANDARD_APP_ARGS ( args , data ) ;
if ( args . argc < 3 ) {
ast_log ( LOG_ERROR , " Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>) \n " ) ;
return - 1 ;
}
if ( ast_strlen_zero ( args . interface ) & & ast_strlen_zero ( args . option ) ) {
ast_log ( LOG_ERROR , " <interface> and <option> parameter's can't be null \n " ) ;
return - 1 ;
}
memvalue = atoi ( value ) ;
if ( ! strcasecmp ( args . option , " penalty " ) ) {
/* if queuename = NULL then penalty will be set for interface in all the queues.*/
if ( set_member_penalty ( args . queuename , args . interface , memvalue ) ) {
ast_log ( LOG_ERROR , " Invalid interface, queue or penalty \n " ) ;
return - 1 ;
}
2011-12-08 20:55:19 +00:00
} else if ( ( q = find_load_queue_rt_friendly ( args . queuename ) ) ) {
2011-06-29 06:39:26 +00:00
ao2_lock ( q ) ;
if ( ( m = interface_exists ( q , args . interface ) ) ) {
sprintf ( rtvalue , " %s " , ( memvalue < = 0 ) ? " 0 " : " 1 " ) ;
if ( ! strcasecmp ( args . option , " paused " ) ) {
if ( m - > realtime ) {
update_realtime_member_field ( m , q - > name , args . option , rtvalue ) ;
} else {
m - > paused = ( memvalue < = 0 ) ? 0 : 1 ;
}
} else if ( ! strcasecmp ( args . option , " ignorebusy " ) ) {
if ( m - > realtime ) {
update_realtime_member_field ( m , q - > name , args . option , rtvalue ) ;
} else {
m - > ignorebusy = ( memvalue < = 0 ) ? 0 : 1 ;
}
} else {
ast_log ( LOG_ERROR , " Invalid option, only penalty , paused or ignorebusy are valid \n " ) ;
2011-10-23 14:35:26 +00:00
ao2_ref ( m , - 1 ) ;
ao2_unlock ( q ) ;
ao2_ref ( q , - 1 ) ;
2011-06-29 06:39:26 +00:00
return - 1 ;
}
2011-10-23 14:35:26 +00:00
ao2_ref ( m , - 1 ) ;
2011-06-29 06:39:26 +00:00
} else {
2011-10-23 14:35:26 +00:00
ao2_unlock ( q ) ;
ao2_ref ( q , - 1 ) ;
ast_log ( LOG_ERROR , " Invalid interface for queue \n " ) ;
2011-06-29 06:39:26 +00:00
return - 1 ;
}
ao2_unlock ( q ) ;
2011-10-23 14:35:26 +00:00
ao2_ref ( q , - 1 ) ;
2011-06-29 06:39:26 +00:00
} else {
ast_log ( LOG_ERROR , " Invalid queue \n " ) ;
return - 1 ;
}
return 0 ;
}
/*!
2008-03-10 15:45:13 +00:00
* \ brief Get the total number of members in a specific queue ( Deprecated )
2011-06-29 06:39:26 +00:00
* \ retval number of members
* \ retval - 1 on error
2008-03-10 15:45:13 +00:00
*/
2007-10-29 20:13:23 +00:00
static int queue_function_qac_dep ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
{
int count = 0 ;
struct member * m ;
2007-11-16 15:44:27 +00:00
struct call_queue * q ;
2007-10-29 20:13:23 +00:00
struct ao2_iterator mem_iter ;
static int depflag = 1 ;
2007-11-14 01:15:26 +00:00
if ( depflag ) {
2007-10-29 20:13:23 +00:00
depflag = 0 ;
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 " ) ;
}
2005-07-12 22:28:51 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2006-01-13 22:59:19 +00:00
ast_log ( LOG_ERROR , " %s requires an argument: queuename \n " , cmd ) ;
2006-02-12 04:28:58 +00:00
return - 1 ;
2005-07-12 22:28:51 +00:00
}
2007-09-20 21:21:28 +00:00
2011-12-08 20:55:19 +00:00
if ( ( q = find_load_queue_rt_friendly ( data ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
2007-09-05 15:04:50 +00:00
/* Count the agents who are logged in and presently answering calls */
if ( ( m - > status ! = AST_DEVICE_UNAVAILABLE ) & & ( m - > status ! = AST_DEVICE_INVALID ) ) {
count + + ;
}
2007-09-10 18:32:59 +00:00
ao2_ref ( m , - 1 ) ;
2007-09-05 15:04:50 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with temporary reference in QUEUE_MEMBER_COUNT " ) ;
2011-10-23 11:37:50 +00:00
} else {
2006-04-26 22:04:42 +00:00
ast_log ( LOG_WARNING , " queue %s was not found \n " , data ) ;
2011-10-23 11:37:50 +00:00
}
2006-04-26 22:04:42 +00:00
snprintf ( buf , len , " %d " , count ) ;
2006-06-14 23:20:08 +00:00
2006-04-26 22:04:42 +00:00
return 0 ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue */
2007-01-05 23:32:42 +00:00
static int queue_function_queuewaitingcount ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
2006-04-26 22:04:42 +00:00
{
int count = 0 ;
2007-11-14 01:15:26 +00:00
struct call_queue * q , tmpq = {
2011-10-23 11:37:50 +00:00
. name = data ,
2007-11-14 01:15:26 +00:00
} ;
2008-02-20 22:34:12 +00:00
struct ast_variable * var = NULL ;
2006-04-26 22:04:42 +00:00
buf [ 0 ] = ' \0 ' ;
2011-10-23 11:37:50 +00:00
2006-04-26 22:04:42 +00:00
if ( ast_strlen_zero ( data ) ) {
2008-03-10 15:45:13 +00:00
ast_log ( LOG_ERROR , " QUEUE_WAITING_COUNT requires an argument: queuename \n " ) ;
2006-04-26 22:04:42 +00:00
return - 1 ;
}
2009-11-24 20:31:28 +00:00
if ( ( q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Find for QUEUE_WAITING_COUNT() " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2006-04-26 22:04:42 +00:00
count = q - > count ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with reference in QUEUE_WAITING_COUNT() " ) ;
2008-06-19 20:48:33 +00:00
} else if ( ( var = ast_load_realtime ( " queues " , " name " , data , SENTINEL ) ) ) {
2008-02-20 22:34:12 +00:00
/* if the queue is realtime but was not found in memory, this
2011-10-23 11:37:50 +00:00
* means that the queue had been deleted from memory since it was
2008-02-20 22:34:12 +00:00
* " dead. " This means it has a 0 waiting count
*/
count = 0 ;
ast_variables_destroy ( var ) ;
2006-04-26 22:04:42 +00:00
} else
ast_log ( LOG_WARNING , " queue %s was not found \n " , data ) ;
2005-07-12 22:28:51 +00:00
snprintf ( buf , len , " %d " , count ) ;
2007-07-16 14:39:29 +00:00
2006-02-12 04:28:58 +00:00
return 0 ;
2005-07-12 22:28:51 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue */
2007-01-05 23:32:42 +00:00
static int queue_function_queuememberlist ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
2006-01-13 22:59:19 +00:00
{
2007-11-14 01:15:26 +00:00
struct call_queue * q , tmpq = {
2011-10-23 11:37:50 +00:00
. name = data ,
2007-11-14 01:15:26 +00:00
} ;
2006-01-13 22:59:19 +00:00
struct member * m ;
/* Ensure an otherwise empty list doesn't return garbage */
buf [ 0 ] = ' \0 ' ;
2006-01-23 17:23:22 +00:00
if ( ast_strlen_zero ( data ) ) {
2006-01-13 22:59:19 +00:00
ast_log ( LOG_ERROR , " QUEUE_MEMBER_LIST requires an argument: queuename \n " ) ;
2006-02-12 04:28:58 +00:00
return - 1 ;
2006-01-13 22:59:19 +00:00
}
2009-11-24 20:31:28 +00:00
if ( ( q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Find for QUEUE_MEMBER_LIST() " ) ) ) {
2006-01-13 22:59:19 +00:00
int buflen = 0 , count = 0 ;
2012-01-27 18:47:16 +00:00
struct ao2_iterator mem_iter ;
2006-06-14 23:20:08 +00:00
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2012-01-27 18:47:16 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
2007-09-11 16:12:09 +00:00
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
2006-01-13 22:59:19 +00:00
/* strcat() is always faster than printf() */
if ( count + + ) {
strncat ( buf + buflen , " , " , len - buflen - 1 ) ;
buflen + + ;
}
Merged revisions 211038 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r211038 | tilghman | 2009-08-07 13:16:28 -0500 (Fri, 07 Aug 2009) | 14 lines
QUEUE_MEMBER_LIST _really_ wants the interface name, not the membername.
This is a partial revert of revision 82590, which was an attempted cleanup,
but in reality, it broke QUEUE_MEMBER_LIST, which has always been intended
as a method by which component interfaces could be queried from the queue.
Membername isn't useful here, because that field cannot be used to obtain
further information about the member. See the documentation on
QUEUE_MEMBER_LIST, RemoveQueueMember, QUEUE_MEMBER_PENALTY, and the various
AMI commands which take a member argument for further justification.
(closes issue #15664)
Reported by: rain
Patches:
app_queue-queue_member_list.diff uploaded by rain (license 327)
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@211040 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2009-08-07 18:17:41 +00:00
strncat ( buf + buflen , m - > interface , len - buflen - 1 ) ;
buflen + = strlen ( m - > interface ) ;
2006-01-13 22:59:19 +00:00
/* Safeguard against overflow (negative length) */
if ( buflen > = len - 2 ) {
2007-09-11 14:35:06 +00:00
ao2_ref ( m , - 1 ) ;
2006-01-13 22:59:19 +00:00
ast_log ( LOG_WARNING , " Truncating list \n " ) ;
break ;
}
2007-09-10 18:32:59 +00:00
ao2_ref ( m , - 1 ) ;
2006-01-13 22:59:19 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with QUEUE_MEMBER_LIST() " ) ;
2006-04-26 22:04:42 +00:00
} else
ast_log ( LOG_WARNING , " queue %s was not found \n " , data ) ;
2006-01-13 22:59:19 +00:00
/* We should already be terminated, but let's make sure. */
buf [ len - 1 ] = ' \0 ' ;
2006-06-14 23:20:08 +00:00
2006-02-12 04:28:58 +00:00
return 0 ;
2006-01-13 22:59:19 +00:00
}
2008-03-10 15:45:13 +00:00
/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. */
2007-11-26 18:11:31 +00:00
static int queue_function_memberpenalty_read ( struct ast_channel * chan , const char * cmd , char * data , char * buf , size_t len )
{
2007-11-06 22:05:56 +00:00
int penalty ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
2008-04-25 20:20:10 +00:00
) ;
2007-11-06 22:05:56 +00:00
/* Make sure the returned value on error is NULL. */
buf [ 0 ] = ' \0 ' ;
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_ERROR , " Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>) \n " ) ;
return - 1 ;
}
AST_STANDARD_APP_ARGS ( args , data ) ;
if ( args . argc < 2 ) {
ast_log ( LOG_ERROR , " Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>) \n " ) ;
return - 1 ;
}
penalty = get_member_penalty ( args . queuename , args . interface ) ;
if ( penalty > = 0 ) /* remember that buf is already '\0' */
snprintf ( buf , len , " %d " , penalty ) ;
2007-11-26 18:11:31 +00:00
2007-11-06 22:05:56 +00:00
return 0 ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. */
2007-11-26 18:11:31 +00:00
static int queue_function_memberpenalty_write ( struct ast_channel * chan , const char * cmd , char * data , const char * value )
{
2007-11-06 22:05:56 +00:00
int penalty ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
) ;
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_ERROR , " Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>) \n " ) ;
return - 1 ;
}
AST_STANDARD_APP_ARGS ( args , data ) ;
if ( args . argc < 2 ) {
ast_log ( LOG_ERROR , " Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>) \n " ) ;
return - 1 ;
}
penalty = atoi ( value ) ;
if ( ast_strlen_zero ( args . interface ) ) {
ast_log ( LOG_ERROR , " <interface> parameter can't be null \n " ) ;
return - 1 ;
}
/* if queuename = NULL then penalty will be set for interface in all the queues. */
2007-12-19 23:17:42 +00:00
if ( set_member_penalty ( args . queuename , args . interface , penalty ) ) {
ast_log ( LOG_ERROR , " Invalid interface, queue or penalty \n " ) ;
return - 1 ;
}
2007-11-06 22:05:56 +00:00
return 0 ;
}
2010-07-16 09:25:48 +00:00
static struct ast_custom_function queueexists_function = {
. name = " QUEUE_EXISTS " ,
. read = queue_function_exists ,
} ;
2006-10-27 18:59:16 +00:00
static struct ast_custom_function queuevar_function = {
. name = " QUEUE_VARIABLES " ,
. read = queue_function_var ,
} ;
2006-01-13 22:59:19 +00:00
static struct ast_custom_function queuemembercount_function = {
2007-10-29 20:13:23 +00:00
. name = " QUEUE_MEMBER " ,
2011-06-29 06:39:26 +00:00
. read = queue_function_mem_read ,
. write = queue_function_mem_write ,
2007-10-29 20:13:23 +00:00
} ;
static struct ast_custom_function queuemembercount_dep = {
2006-01-13 22:59:19 +00:00
. name = " QUEUE_MEMBER_COUNT " ,
2007-10-29 20:13:23 +00:00
. read = queue_function_qac_dep ,
2006-01-13 22:59:19 +00:00
} ;
2006-04-26 22:04:42 +00:00
static struct ast_custom_function queuewaitingcount_function = {
. name = " QUEUE_WAITING_COUNT " ,
. read = queue_function_queuewaitingcount ,
} ;
2006-01-13 22:59:19 +00:00
static struct ast_custom_function queuememberlist_function = {
. name = " QUEUE_MEMBER_LIST " ,
. read = queue_function_queuememberlist ,
} ;
2007-11-06 22:05:56 +00:00
static struct ast_custom_function queuememberpenalty_function = {
. name = " QUEUE_MEMBER_PENALTY " ,
. read = queue_function_memberpenalty_read ,
. write = queue_function_memberpenalty_write ,
} ;
2009-02-13 20:57:37 +00:00
/*! \brief Reload the rules defined in queuerules.conf
*
* \ param reload If 1 , then only process queuerules . conf if the file
* has changed since the last time we inspected it .
* \ return Always returns AST_MODULE_LOAD_SUCCESS
*/
2007-12-21 00:44:17 +00:00
static int reload_queue_rules ( int reload )
{
struct ast_config * cfg ;
struct rule_list * rl_iter , * new_rl ;
struct penalty_rule * pr_iter ;
char * rulecat = NULL ;
struct ast_variable * rulevar = NULL ;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 } ;
if ( ! ( cfg = ast_config_load ( " queuerules.conf " , config_flags ) ) ) {
ast_log ( LOG_NOTICE , " No queuerules.conf file found, queues will not follow penalty rules \n " ) ;
2009-02-13 20:57:37 +00:00
return AST_MODULE_LOAD_SUCCESS ;
2007-12-21 00:44:17 +00:00
} else if ( cfg = = CONFIG_STATUS_FILEUNCHANGED ) {
ast_log ( LOG_NOTICE , " queuerules.conf has not changed since it was last loaded. Not taking any action. \n " ) ;
return AST_MODULE_LOAD_SUCCESS ;
2008-09-12 23:30:03 +00:00
} else if ( cfg = = CONFIG_STATUS_FILEINVALID ) {
ast_log ( LOG_ERROR , " Config file queuerules.conf is in an invalid format. Aborting. \n " ) ;
return AST_MODULE_LOAD_SUCCESS ;
2009-02-13 20:57:37 +00:00
}
AST_LIST_LOCK ( & rule_lists ) ;
while ( ( rl_iter = AST_LIST_REMOVE_HEAD ( & rule_lists , list ) ) ) {
while ( ( pr_iter = AST_LIST_REMOVE_HEAD ( & rl_iter - > rules , list ) ) )
ast_free ( pr_iter ) ;
ast_free ( rl_iter ) ;
}
while ( ( rulecat = ast_category_browse ( cfg , rulecat ) ) ) {
if ( ! ( new_rl = ast_calloc ( 1 , sizeof ( * new_rl ) ) ) ) {
AST_LIST_UNLOCK ( & rule_lists ) ;
2011-06-29 18:18:00 +00:00
ast_config_destroy ( cfg ) ;
2009-02-13 20:57:37 +00:00
return AST_MODULE_LOAD_FAILURE ;
} else {
ast_copy_string ( new_rl - > name , rulecat , sizeof ( new_rl - > name ) ) ;
AST_LIST_INSERT_TAIL ( & rule_lists , new_rl , list ) ;
for ( rulevar = ast_variable_browse ( cfg , rulecat ) ; rulevar ; rulevar = rulevar - > next )
if ( ! strcasecmp ( rulevar - > name , " penaltychange " ) )
insert_penaltychange ( new_rl - > name , rulevar - > value , rulevar - > lineno ) ;
else
ast_log ( LOG_WARNING , " Don't know how to handle rule type '%s' on line %d \n " , rulevar - > name , rulevar - > lineno ) ;
2007-12-21 00:44:17 +00:00
}
}
2009-02-13 20:57:37 +00:00
AST_LIST_UNLOCK ( & rule_lists ) ;
2007-12-21 00:44:17 +00:00
2007-12-21 20:30:05 +00:00
ast_config_destroy ( cfg ) ;
2007-12-21 00:44:17 +00:00
return AST_MODULE_LOAD_SUCCESS ;
}
2009-02-13 20:57:37 +00:00
/*! Set the global queue parameters as defined in the "general" section of queues.conf */
static void queue_set_global_params ( struct ast_config * cfg )
{
const char * general_val = NULL ;
queue_persistent_members = 0 ;
2011-06-29 06:39:26 +00:00
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " persistentmembers " ) ) ) {
2009-02-13 20:57:37 +00:00
queue_persistent_members = ast_true ( general_val ) ;
2011-06-29 06:39:26 +00:00
}
2009-02-13 20:57:37 +00:00
autofill_default = 0 ;
2011-10-20 17:34:54 +00:00
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " autofill " ) ) ) {
2009-02-13 20:57:37 +00:00
autofill_default = ast_true ( general_val ) ;
2011-10-20 17:34:54 +00:00
}
2009-02-13 20:57:37 +00:00
montype_default = 0 ;
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " monitor-type " ) ) ) {
if ( ! strcasecmp ( general_val , " mixmonitor " ) )
montype_default = 1 ;
}
update_cdr = 0 ;
2011-10-20 17:34:54 +00:00
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " updatecdr " ) ) ) {
2009-02-13 20:57:37 +00:00
update_cdr = ast_true ( general_val ) ;
2011-10-20 17:34:54 +00:00
}
2009-02-13 20:57:37 +00:00
shared_lastcall = 0 ;
2011-10-20 17:34:54 +00:00
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " shared_lastcall " ) ) ) {
2009-02-13 20:57:37 +00:00
shared_lastcall = ast_true ( general_val ) ;
2011-10-20 17:34:54 +00:00
}
2011-06-29 06:39:26 +00:00
negative_penalty_invalid = 0 ;
2011-10-20 17:34:54 +00:00
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " negative_penalty_invalid " ) ) ) {
2011-06-29 06:39:26 +00:00
negative_penalty_invalid = ast_true ( general_val ) ;
2011-10-20 17:34:54 +00:00
}
2011-08-08 20:28:20 +00:00
log_membername_as_agent = 0 ;
2011-10-20 17:34:54 +00:00
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " log_membername_as_agent " ) ) ) {
2011-08-08 20:28:20 +00:00
log_membername_as_agent = ast_true ( general_val ) ;
2011-10-20 17:34:54 +00:00
}
check_state_unknown = 0 ;
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " check_state_unknown " ) ) ) {
check_state_unknown = ast_true ( general_val ) ;
}
2009-02-13 20:57:37 +00:00
}
2007-11-06 22:05:56 +00:00
2009-02-13 20:57:37 +00:00
/*! \brief reload information pertaining to a single member
*
* This function is called when a member = line is encountered in
* queues . conf .
*
* \ param memberdata The part after member = in the config file
* \ param q The queue to which this member belongs
*/
static void reload_single_member ( const char * memberdata , struct call_queue * q )
2002-09-02 23:15:40 +00:00
{
2009-02-13 20:57:37 +00:00
char * membername , * interface , * state_interface , * tmp ;
char * parse ;
2007-09-10 18:32:59 +00:00
struct member * cur , * newm ;
2009-02-13 20:57:37 +00:00
struct member tmpmem ;
2005-06-02 21:42:49 +00:00
int penalty ;
2006-09-20 05:59:53 +00:00
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( interface ) ;
AST_APP_ARG ( penalty ) ;
AST_APP_ARG ( membername ) ;
2008-01-08 21:18:32 +00:00
AST_APP_ARG ( state_interface ) ;
2006-09-20 05:59:53 +00:00
) ;
2007-12-21 00:44:17 +00:00
2009-03-31 22:02:48 +00:00
if ( ast_strlen_zero ( memberdata ) ) {
2009-03-31 22:12:52 +00:00
ast_log ( LOG_WARNING , " Empty queue member definition. Moving on! \n " ) ;
2009-03-31 22:02:48 +00:00
return ;
}
2009-02-13 20:57:37 +00:00
/* Add a new member */
parse = ast_strdupa ( memberdata ) ;
AST_STANDARD_APP_ARGS ( args , parse ) ;
interface = args . interface ;
if ( ! ast_strlen_zero ( args . penalty ) ) {
tmp = args . penalty ;
ast_strip ( tmp ) ;
penalty = atoi ( tmp ) ;
if ( penalty < 0 ) {
penalty = 0 ;
}
} else {
penalty = 0 ;
}
if ( ! ast_strlen_zero ( args . membername ) ) {
membername = args . membername ;
ast_strip ( membername ) ;
} else {
membername = interface ;
}
if ( ! ast_strlen_zero ( args . state_interface ) ) {
state_interface = args . state_interface ;
ast_strip ( state_interface ) ;
} else {
state_interface = interface ;
}
/* Find the old position in the list */
ast_copy_string ( tmpmem . interface , interface , sizeof ( tmpmem . interface ) ) ;
cur = ao2_find ( q - > members , & tmpmem , OBJ_POINTER | OBJ_UNLINK ) ;
if ( ( newm = create_queue_member ( interface , membername , penalty , cur ? cur - > paused : 0 , state_interface ) ) ) {
ao2_link ( q - > members , newm ) ;
ao2_ref ( newm , - 1 ) ;
}
newm = NULL ;
if ( cur ) {
ao2_ref ( cur , - 1 ) ;
}
}
static int mark_member_dead ( void * obj , void * arg , int flags )
{
struct member * member = obj ;
if ( ! member - > dynamic ) {
member - > delme = 1 ;
}
return 0 ;
}
static int kill_dead_members ( void * obj , void * arg , int flags )
{
struct member * member = obj ;
if ( ! member - > delme ) {
2009-11-03 21:16:14 +00:00
member - > status = get_queue_member_status ( member ) ;
2009-02-13 20:57:37 +00:00
return 0 ;
} else {
return CMP_MATCH ;
}
}
/*! \brief Reload information pertaining to a particular queue
*
* Once we have isolated a queue within reload_queues , we call this . This will either
* reload information for the queue or if we ' re just reloading member information , we ' ll just
* reload that without touching other settings within the queue
*
* \ param cfg The configuration which we are reading
* \ param mask Tells us what information we need to reload
* \ param queuename The name of the queue we are reloading information from
* \ retval void
*/
static void reload_single_queue ( struct ast_config * cfg , struct ast_flags * mask , const char * queuename )
{
int new ;
struct call_queue * q = NULL ;
/*We're defining a queue*/
struct call_queue tmpq = {
. name = queuename ,
} ;
const char * tmpvar ;
const int queue_reload = ast_test_flag ( mask , QUEUE_RELOAD_PARAMETERS ) ;
const int member_reload = ast_test_flag ( mask , QUEUE_RELOAD_MEMBER ) ;
int prev_weight = 0 ;
struct ast_variable * var ;
2009-11-24 20:31:28 +00:00
if ( ! ( q = ao2_t_find ( queues , & tmpq , OBJ_POINTER , " Find queue for reload " ) ) ) {
2009-02-13 20:57:37 +00:00
if ( queue_reload ) {
/* Make one then */
if ( ! ( q = alloc_queue ( queuename ) ) ) {
return ;
}
} else {
/* Since we're not reloading queues, this means that we found a queue
* in the configuration file which we don ' t know about yet . Just return .
*/
return ;
}
new = 1 ;
} else {
new = 0 ;
}
if ( ! new ) {
ao2_lock ( q ) ;
prev_weight = q - > weight ? 1 : 0 ;
}
/* Check if we already found a queue with this name in the config file */
if ( q - > found ) {
ast_log ( LOG_WARNING , " Queue '%s' already defined! Skipping! \n " , queuename ) ;
if ( ! new ) {
/* It should be impossible to *not* hit this case*/
ao2_unlock ( q ) ;
}
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " We exist! Expiring temporary pointer " ) ;
2009-02-13 20:57:37 +00:00
return ;
}
/* Due to the fact that the "linear" strategy will have a different allocation
* scheme for queue members , we must devise the queue ' s strategy before other initializations .
* To be specific , the linear strategy needs to function like a linked list , meaning the ao2
* container used will have only a single bucket instead of the typical number .
*/
if ( queue_reload ) {
if ( ( tmpvar = ast_variable_retrieve ( cfg , queuename , " strategy " ) ) ) {
q - > strategy = strat2int ( tmpvar ) ;
if ( q - > strategy < 0 ) {
ast_log ( LOG_WARNING , " '%s' isn't a valid strategy for queue '%s', using ringall instead \n " ,
tmpvar , q - > name ) ;
q - > strategy = QUEUE_STRATEGY_RINGALL ;
}
} else {
q - > strategy = QUEUE_STRATEGY_RINGALL ;
}
init_queue ( q ) ;
}
if ( member_reload ) {
ao2_callback ( q - > members , OBJ_NODATA , mark_member_dead , NULL ) ;
}
for ( var = ast_variable_browse ( cfg , queuename ) ; var ; var = var - > next ) {
if ( member_reload & & ! strcasecmp ( var - > name , " member " ) ) {
reload_single_member ( var - > value , q ) ;
} else if ( queue_reload ) {
queue_set_param ( q , var - > name , var - > value , var - > lineno , 1 ) ;
}
}
/* At this point, we've determined if the queue has a weight, so update use_weight
* as appropriate
*/
if ( ! q - > weight & & prev_weight ) {
ast_atomic_fetchadd_int ( & use_weight , - 1 ) ;
}
else if ( q - > weight & & ! prev_weight ) {
ast_atomic_fetchadd_int ( & use_weight , + 1 ) ;
}
/* Free remaining members marked as delme */
if ( member_reload ) {
ao2_callback ( q - > members , OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK , kill_dead_members , q ) ;
}
if ( new ) {
2009-11-24 20:31:28 +00:00
queues_t_link ( queues , q , " Add queue to container " ) ;
2009-02-13 20:57:37 +00:00
} else {
ao2_unlock ( q ) ;
}
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Expiring creation reference " ) ;
2009-02-13 20:57:37 +00:00
}
static int mark_dead_and_unfound ( void * obj , void * arg , int flags )
{
struct call_queue * q = obj ;
char * queuename = arg ;
if ( ! q - > realtime & & ( ast_strlen_zero ( queuename ) | | ! strcasecmp ( queuename , q - > name ) ) ) {
q - > dead = 1 ;
q - > found = 0 ;
}
return 0 ;
}
static int kill_dead_queues ( void * obj , void * arg , int flags )
{
struct call_queue * q = obj ;
char * queuename = arg ;
if ( ( ast_strlen_zero ( queuename ) | | ! strcasecmp ( queuename , q - > name ) ) & & q - > dead ) {
return CMP_MATCH ;
} else {
return 0 ;
}
}
/*! \brief reload the queues.conf file
*
* This function reloads the information in the general section of the queues . conf
* file and potentially more , depending on the value of mask .
*
* \ param reload 0 if we are calling this the first time , 1 every other time
* \ param mask Gives flags telling us what information to actually reload
* \ param queuename If set to a non - zero string , then only reload information from
* that particular queue . Otherwise inspect all queues
* \ retval - 1 Failure occurred
* \ retval 0 All clear !
*/
static int reload_queues ( int reload , struct ast_flags * mask , const char * queuename )
{
struct ast_config * cfg ;
char * cat ;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 } ;
const int queue_reload = ast_test_flag ( mask , QUEUE_RELOAD_PARAMETERS ) ;
2007-08-16 21:09:46 +00:00
if ( ! ( cfg = ast_config_load ( " queues.conf " , config_flags ) ) ) {
2005-01-03 01:42:37 +00:00
ast_log ( LOG_NOTICE , " No call queueing config file (queues.conf), so no call queues \n " ) ;
2009-02-13 20:57:37 +00:00
return - 1 ;
2008-09-12 23:30:03 +00:00
} else if ( cfg = = CONFIG_STATUS_FILEUNCHANGED ) {
return 0 ;
} else if ( cfg = = CONFIG_STATUS_FILEINVALID ) {
ast_log ( LOG_ERROR , " Config file queues.conf is in an invalid format. Aborting. \n " ) ;
2009-02-13 20:57:37 +00:00
return - 1 ;
2008-09-12 23:30:03 +00:00
}
2009-02-13 20:57:37 +00:00
2011-10-24 07:40:18 +00:00
/* We've made it here, so it looks like we're doing operations on all queues. */
ao2_lock ( queues ) ;
2009-02-13 20:57:37 +00:00
/* Mark all queues as dead for the moment if we're reloading queues.
* For clarity , we could just be reloading members , in which case we don ' t want to mess
* with the other queue parameters at all */
if ( queue_reload ) {
2011-09-15 15:59:24 +00:00
ao2_callback ( queues , OBJ_NODATA | OBJ_NOLOCK , mark_dead_and_unfound , ( char * ) queuename ) ;
2007-08-31 19:50:26 +00:00
}
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
/* Chug through config file */
2006-02-14 21:50:35 +00:00
cat = NULL ;
while ( ( cat = ast_category_browse ( cfg , cat ) ) ) {
2009-02-13 20:57:37 +00:00
if ( ! strcasecmp ( cat , " general " ) & & queue_reload ) {
queue_set_global_params ( cfg ) ;
continue ;
2002-09-02 23:15:40 +00:00
}
2009-02-13 20:57:37 +00:00
if ( ast_strlen_zero ( queuename ) | | ! strcasecmp ( cat , queuename ) )
reload_single_queue ( cfg , mask , cat ) ;
2002-09-02 23:15:40 +00:00
}
2009-02-13 20:57:37 +00:00
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg ) ;
2009-02-13 20:57:37 +00:00
/* Unref all the dead queues if we were reloading queues */
if ( queue_reload ) {
2011-09-15 15:59:24 +00:00
ao2_callback ( queues , OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NOLOCK , kill_dead_queues , ( char * ) queuename ) ;
2002-09-02 23:15:40 +00:00
}
2011-10-24 07:40:18 +00:00
ao2_unlock ( queues ) ;
2009-02-13 20:57:37 +00:00
return 0 ;
}
2011-09-15 15:59:24 +00:00
2009-02-13 20:57:37 +00:00
/*! \brief Facilitates resetting statistics for a queue
*
* This function actually does not reset any statistics , but
* rather finds a call_queue struct which corresponds to the
2011-09-15 15:59:24 +00:00
* passed - in queue name and passes that structure to the
2009-02-13 20:57:37 +00:00
* clear_queue function . If no queuename is passed in , then
* all queues will have their statistics reset .
*
* \ param queuename The name of the queue to reset the statistics
* for . If this is NULL or zero - length , then this means to reset
* the statistics for all queues
* \ retval void
*/
static int clear_stats ( const char * queuename )
{
struct call_queue * q ;
2012-01-27 18:47:16 +00:00
struct ao2_iterator queue_iter ;
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2009-02-13 20:57:37 +00:00
ao2_lock ( q ) ;
if ( ast_strlen_zero ( queuename ) | | ! strcasecmp ( q - > name , queuename ) )
clear_queue ( q ) ;
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2009-02-13 20:57:37 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2009-02-13 20:57:37 +00:00
return 0 ;
}
/*! \brief The command center for all reload operations
*
* Whenever any piece of queue information is to be reloaded , this function
* is called . It interprets the flags set in the mask parameter and acts
* based on how they are set .
*
* \ param reload True if we are reloading information , false if we are loading
* information for the first time .
* \ param mask A bitmask which tells the handler what actions to take
* \ param queuename The name of the queue on which we wish to take action
* \ retval 0 All reloads were successful
* \ retval non - zero There was a failure
*/
static int reload_handler ( int reload , struct ast_flags * mask , const char * queuename )
{
int res = 0 ;
if ( ast_test_flag ( mask , QUEUE_RELOAD_RULES ) ) {
res | = reload_queue_rules ( reload ) ;
}
if ( ast_test_flag ( mask , QUEUE_RESET_STATS ) ) {
res | = clear_stats ( queuename ) ;
}
if ( ast_test_flag ( mask , ( QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER ) ) ) {
res | = reload_queues ( reload , mask , queuename ) ;
}
return res ;
2002-09-02 23:15:40 +00:00
}
2006-12-17 22:47:30 +00:00
/*! \brief direct ouput to manager or cli with proper terminator */
static void do_print ( struct mansession * s , int fd , const char * str )
{
if ( s )
astman_append ( s , " %s \r \n " , str ) ;
else
ast_cli ( fd , " %s \n " , str ) ;
}
2008-03-10 15:45:13 +00:00
/*!
* \ brief Show queue ( s ) status and statistics
*
* List the queues strategy , calls processed , members logged in ,
* other queue statistics such as avg hold time .
*/
2009-05-21 21:13:09 +00:00
static char * __queues_show ( struct mansession * s , int fd , int argc , const char * const * argv )
2002-09-02 23:15:40 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2007-02-17 16:39:24 +00:00
struct ast_str * out = ast_str_alloca ( 240 ) ;
2006-12-17 22:47:30 +00:00
int found = 0 ;
time_t now = time ( NULL ) ;
2007-09-20 21:21:28 +00:00
struct ao2_iterator queue_iter ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2004-03-13 06:00:41 +00:00
2011-10-23 11:37:50 +00:00
if ( argc ! = 2 & & argc ! = 3 ) {
2007-09-19 23:16:51 +00:00
return CLI_SHOWUSAGE ;
2011-10-23 11:37:50 +00:00
}
2006-01-21 20:32:17 +00:00
2008-06-26 20:57:41 +00:00
if ( argc = = 3 ) { /* specific queue */
2011-12-08 20:55:19 +00:00
if ( ( q = find_load_queue_rt_friendly ( argv [ 2 ] ) ) ) {
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with temporary pointer " ) ;
2008-12-15 16:10:43 +00:00
}
} else if ( ast_check_realtime ( " queues " ) ) {
/* This block is to find any queues which are defined in realtime but
* which have not yet been added to the in - core container
*/
2008-06-26 20:57:41 +00:00
struct ast_config * cfg = ast_load_realtime_multientry ( " queues " , " name LIKE " , " % " , SENTINEL ) ;
char * queuename ;
if ( cfg ) {
for ( queuename = ast_category_browse ( cfg , NULL ) ; ! ast_strlen_zero ( queuename ) ; queuename = ast_category_browse ( cfg , queuename ) ) {
2011-12-08 20:55:19 +00:00
if ( ( q = find_load_queue_rt_friendly ( queuename ) ) ) {
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with temporary pointer " ) ;
2008-12-15 16:10:43 +00:00
}
2008-06-26 20:57:41 +00:00
}
ast_config_destroy ( cfg ) ;
}
}
2006-01-21 20:32:17 +00:00
2008-11-14 16:53:38 +00:00
ao2_lock ( queues ) ;
2012-01-27 18:47:16 +00:00
queue_iter = ao2_iterator_init ( queues , AO2_ITERATOR_DONTLOCK ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2006-12-17 22:47:30 +00:00
float sl ;
2008-12-15 16:16:47 +00:00
struct call_queue * realtime_queue = NULL ;
2006-12-17 22:47:30 +00:00
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2008-12-15 16:10:43 +00:00
/* This check is to make sure we don't print information for realtime
* queues which have been deleted from realtime but which have not yet
* been deleted from the in - core container
*/
2011-08-22 20:01:30 +00:00
if ( q - > realtime ) {
2011-12-08 20:55:19 +00:00
realtime_queue = find_load_queue_rt_friendly ( q - > name ) ;
2011-08-22 20:01:30 +00:00
if ( ! realtime_queue ) {
ao2_unlock ( q ) ;
queue_t_unref ( q , " Done with iterator " ) ;
continue ;
}
2009-11-24 20:31:28 +00:00
queue_t_unref ( realtime_queue , " Queue is already in memory " ) ;
2008-12-15 16:10:43 +00:00
}
2011-08-22 20:01:30 +00:00
2007-02-17 16:48:20 +00:00
if ( argc = = 3 & & strcasecmp ( q - > name , argv [ 2 ] ) ) {
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2006-12-17 22:47:30 +00:00
continue ;
2004-02-03 16:59:04 +00:00
}
2006-12-17 22:47:30 +00:00
found = 1 ;
2010-01-07 18:58:23 +00:00
ast_str_set ( & out , 0 , " %s has %d calls (max " , q - > name , q - > count ) ;
2002-09-02 23:15:40 +00:00
if ( q - > maxlen )
2006-12-17 22:47:30 +00:00
ast_str_append ( & out , 0 , " %d " , q - > maxlen ) ;
2002-09-02 23:15:40 +00:00
else
2006-12-17 22:47:30 +00:00
ast_str_append ( & out , 0 , " unlimited " ) ;
2004-03-13 06:00:41 +00:00
sl = 0 ;
2006-06-14 23:20:08 +00:00
if ( q - > callscompleted > 0 )
sl = 100 * ( ( float ) q - > callscompletedinsl / ( float ) q - > callscompleted ) ;
2009-01-08 19:48:42 +00:00
ast_str_append ( & out , 0 , " ) in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds " ,
int2strat ( q - > strategy ) , q - > holdtime , q - > talktime , q - > weight ,
2006-12-17 22:47:30 +00:00
q - > callscompleted , q - > callsabandoned , sl , q - > servicelevel ) ;
2008-12-13 08:36:35 +00:00
do_print ( s , fd , ast_str_buffer ( out ) ) ;
2011-10-23 11:37:50 +00:00
if ( ! ao2_container_count ( q - > members ) ) {
2006-12-17 22:47:30 +00:00
do_print ( s , fd , " No Members " ) ;
2011-10-23 11:37:50 +00:00
} else {
2006-12-17 22:47:30 +00:00
struct member * mem ;
do_print ( s , fd , " Members: " ) ;
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( mem = ao2_iterator_next ( & mem_iter ) ) ) {
2007-09-18 19:06:34 +00:00
ast_str_set ( & out , 0 , " %s " , mem - > membername ) ;
2008-06-26 23:01:02 +00:00
if ( strcasecmp ( mem - > membername , mem - > interface ) ) {
2011-08-08 15:00:26 +00:00
ast_str_append ( & out , 0 , " (%s " , mem - > interface ) ;
if ( mem - > state_interface ) {
ast_str_append ( & out , 0 , " from %s " , mem - > state_interface ) ;
}
ast_str_append ( & out , 0 , " ) " ) ;
2008-06-26 23:01:02 +00:00
}
2011-10-23 11:37:50 +00:00
if ( mem - > penalty ) {
2006-12-17 22:47:30 +00:00
ast_str_append ( & out , 0 , " with penalty %d " , mem - > penalty ) ;
2011-10-23 11:37:50 +00:00
}
2007-08-29 15:57:27 +00:00
ast_str_append ( & out , 0 , " %s%s%s (%s) " ,
2006-12-17 22:47:30 +00:00
mem - > dynamic ? " (dynamic) " : " " ,
2007-08-29 15:57:27 +00:00
mem - > realtime ? " (realtime) " : " " ,
2006-12-17 22:47:30 +00:00
mem - > paused ? " (paused) " : " " ,
2008-11-04 18:47:20 +00:00
ast_devstate2str ( mem - > status ) ) ;
2011-10-23 11:37:50 +00:00
if ( mem - > calls ) {
2006-12-17 22:47:30 +00:00
ast_str_append ( & out , 0 , " has taken %d calls (last was %ld secs ago) " ,
2006-07-14 05:42:06 +00:00
mem - > calls , ( long ) ( time ( NULL ) - mem - > lastcall ) ) ;
2011-10-23 11:37:50 +00:00
} else {
2006-12-17 22:47:30 +00:00
ast_str_append ( & out , 0 , " has taken no calls yet " ) ;
2011-10-23 11:37:50 +00:00
}
2008-12-13 08:36:35 +00:00
do_print ( s , fd , ast_str_buffer ( out ) ) ;
2007-09-10 18:32:59 +00:00
ao2_ref ( mem , - 1 ) ;
2003-07-30 16:10:51 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2006-12-17 22:47:30 +00:00
}
2011-10-23 11:37:50 +00:00
if ( ! q - > head ) {
2006-12-17 22:47:30 +00:00
do_print ( s , fd , " No Callers " ) ;
2011-10-23 11:37:50 +00:00
} else {
2006-12-17 22:47:30 +00:00
struct queue_ent * qe ;
int pos = 1 ;
do_print ( s , fd , " Callers: " ) ;
2006-03-25 23:50:09 +00:00
for ( qe = q - > head ; qe ; qe = qe - > next ) {
2006-12-17 22:47:30 +00:00
ast_str_set ( & out , 0 , " %d. %s (wait: %ld:%2.2ld, prio: %d) " ,
2012-01-09 22:15:50 +00:00
pos + + , ast_channel_name ( qe - > chan ) , ( long ) ( now - qe - > start ) / 60 ,
2006-12-17 22:47:30 +00:00
( long ) ( now - qe - > start ) % 60 , qe - > prio ) ;
2008-12-13 08:36:35 +00:00
do_print ( s , fd , ast_str_buffer ( out ) ) ;
2006-03-25 23:50:09 +00:00
}
2006-12-17 22:47:30 +00:00
}
do_print ( s , fd , " " ) ; /* blank line between entries */
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ; /* Unref the iterator's reference */
2002-09-02 23:15:40 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2008-11-14 16:53:38 +00:00
ao2_unlock ( queues ) ;
2006-12-17 22:47:30 +00:00
if ( ! found ) {
2011-10-23 11:37:50 +00:00
if ( argc = = 3 ) {
2006-12-17 22:47:30 +00:00
ast_str_set ( & out , 0 , " No such queue: %s. " , argv [ 2 ] ) ;
2011-10-23 11:37:50 +00:00
} else {
2006-12-17 22:47:30 +00:00
ast_str_set ( & out , 0 , " No queues. " ) ;
2011-10-23 11:37:50 +00:00
}
2008-12-13 08:36:35 +00:00
do_print ( s , fd , ast_str_buffer ( out ) ) ;
2006-12-17 22:47:30 +00:00
}
2007-09-19 23:16:51 +00:00
return CLI_SUCCESS ;
2004-02-03 16:59:04 +00:00
}
2006-01-18 22:17:31 +00:00
static char * complete_queue ( const char * line , const char * word , int pos , int state )
2004-02-03 16:59:04 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2005-12-29 08:15:48 +00:00
char * ret = NULL ;
int which = 0 ;
int wordlen = strlen ( word ) ;
2007-09-20 21:21:28 +00:00
struct ao2_iterator queue_iter ;
2007-09-19 23:16:51 +00:00
2007-09-20 21:21:28 +00:00
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2006-03-29 00:30:29 +00:00
if ( ! strncasecmp ( word , q - > name , wordlen ) & & + + which > state ) {
2007-09-19 23:16:51 +00:00
ret = ast_strdup ( q - > name ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2006-02-14 21:50:35 +00:00
break ;
2004-02-03 16:59:04 +00:00
}
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2004-02-03 16:59:04 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2005-12-29 08:15:48 +00:00
return ret ;
2004-02-03 16:59:04 +00:00
}
2007-07-05 16:05:22 +00:00
static char * complete_queue_show ( const char * line , const char * word , int pos , int state )
{
if ( pos = = 2 )
return complete_queue ( line , word , pos , state ) ;
return NULL ;
}
2007-09-19 23:16:51 +00:00
static char * queue_show ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
switch ( cmd ) {
case CLI_INIT :
e - > command = " queue show " ;
e - > usage =
" Usage: queue show \n "
" Provides summary information on a specified queue. \n " ;
return NULL ;
case CLI_GENERATE :
return complete_queue_show ( a - > line , a - > word , a - > pos , a - > n ) ;
}
return __queues_show ( NULL , a - > fd , a - > argc , a - > argv ) ;
}
2006-07-14 05:42:06 +00:00
/*!\brief callback to display queues status in manager
\ addtogroup Group_AMI
2005-11-06 15:09:47 +00:00
*/
2007-01-05 22:43:18 +00:00
static int manager_queues_show ( struct mansession * s , const struct message * m )
2002-12-11 00:15:13 +00:00
{
2009-05-21 21:13:09 +00:00
static const char * const a [ ] = { " queue " , " show " } ;
2006-06-14 23:20:08 +00:00
2006-12-17 22:47:30 +00:00
__queues_show ( s , - 1 , 2 , a ) ;
2006-03-25 23:50:09 +00:00
astman_append ( s , " \r \n \r \n " ) ; /* Properly terminate Manager output */
2002-12-11 00:15:13 +00:00
2005-06-09 19:34:12 +00:00
return RESULT_SUCCESS ;
2006-07-14 05:42:06 +00:00
}
2003-07-01 03:32:14 +00:00
2007-12-21 00:44:17 +00:00
static int manager_queue_rule_show ( struct mansession * s , const struct message * m )
{
const char * rule = astman_get_header ( m , " Rule " ) ;
2011-06-29 18:07:26 +00:00
const char * id = astman_get_header ( m , " ActionID " ) ;
2007-12-21 00:44:17 +00:00
struct rule_list * rl_iter ;
struct penalty_rule * pr_iter ;
2011-06-29 18:07:26 +00:00
astman_append ( s , " Response: Success \r \n " ) ;
if ( ! ast_strlen_zero ( id ) ) {
astman_append ( s , " ActionID: %s \r \n " , id ) ;
}
2007-12-21 00:44:17 +00:00
AST_LIST_LOCK ( & rule_lists ) ;
AST_LIST_TRAVERSE ( & rule_lists , rl_iter , list ) {
if ( ast_strlen_zero ( rule ) | | ! strcasecmp ( rule , rl_iter - > name ) ) {
astman_append ( s , " RuleList: %s \r \n " , rl_iter - > name ) ;
AST_LIST_TRAVERSE ( & rl_iter - > rules , pr_iter , list ) {
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 ) ;
}
if ( ! ast_strlen_zero ( rule ) )
break ;
}
}
AST_LIST_UNLOCK ( & rule_lists ) ;
2011-06-29 18:07:26 +00:00
/*
* Two blank lines instead of one because the Response and
* ActionID headers used to not be present .
*/
2007-12-21 00:44:17 +00:00
astman_append ( s , " \r \n \r \n " ) ;
return RESULT_SUCCESS ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Summary of queue info via the AMI */
2007-01-05 22:43:18 +00:00
static int manager_queues_summary ( struct mansession * s , const struct message * m )
2006-09-26 21:30:41 +00:00
{
time_t now ;
int qmemcount = 0 ;
int qmemavail = 0 ;
int qchancount = 0 ;
2007-10-04 22:58:26 +00:00
int qlongestholdtime = 0 ;
2007-01-05 22:43:18 +00:00
const char * id = astman_get_header ( m , " ActionID " ) ;
const char * queuefilter = astman_get_header ( m , " Queue " ) ;
2006-09-26 21:30:41 +00:00
char idText [ 256 ] = " " ;
struct call_queue * q ;
struct queue_ent * qe ;
struct member * mem ;
2007-09-20 21:21:28 +00:00
struct ao2_iterator queue_iter ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2006-09-26 21:30:41 +00:00
astman_send_ack ( s , m , " Queue summary will follow " ) ;
time ( & now ) ;
if ( ! ast_strlen_zero ( id ) )
snprintf ( idText , 256 , " ActionID: %s \r \n " , id ) ;
2007-09-20 21:21:28 +00:00
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2006-09-26 21:30:41 +00:00
/* List queue properties */
if ( ast_strlen_zero ( queuefilter ) | | ! strcmp ( q - > name , queuefilter ) ) {
2007-10-04 22:58:26 +00:00
/* Reset the necessary local variables if no queuefilter is set*/
qmemcount = 0 ;
qmemavail = 0 ;
qchancount = 0 ;
qlongestholdtime = 0 ;
2006-09-26 21:30:41 +00:00
/* List Queue Members */
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( mem = ao2_iterator_next ( & mem_iter ) ) ) {
2006-09-26 21:30:41 +00:00
if ( ( mem - > status ! = AST_DEVICE_UNAVAILABLE ) & & ( mem - > status ! = AST_DEVICE_INVALID ) ) {
+ + qmemcount ;
if ( ( ( mem - > status = = AST_DEVICE_NOT_INUSE ) | | ( mem - > status = = AST_DEVICE_UNKNOWN ) ) & & ! ( mem - > paused ) ) {
+ + qmemavail ;
}
}
2007-09-10 18:32:59 +00:00
ao2_ref ( mem , - 1 ) ;
2006-09-26 21:30:41 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2006-09-26 21:30:41 +00:00
for ( qe = q - > head ; qe ; qe = qe - > next ) {
2007-10-04 22:58:26 +00:00
if ( ( now - qe - > start ) > qlongestholdtime ) {
qlongestholdtime = now - qe - > start ;
}
2006-09-26 21:30:41 +00:00
+ + qchancount ;
}
astman_append ( s , " Event: QueueSummary \r \n "
" Queue: %s \r \n "
" LoggedIn: %d \r \n "
" Available: %d \r \n "
" Callers: %d \r \n "
" HoldTime: %d \r \n "
2009-01-08 19:48:42 +00:00
" TalkTime: %d \r \n "
2007-10-04 22:58:26 +00:00
" LongestHoldTime: %d \r \n "
2006-09-26 21:30:41 +00:00
" %s "
" \r \n " ,
2009-01-08 19:48:42 +00:00
q - > name , qmemcount , qmemavail , qchancount , q - > holdtime , q - > talktime , qlongestholdtime , idText ) ;
2006-09-26 21:30:41 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2006-09-26 21:30:41 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2006-09-26 21:30:41 +00:00
astman_append ( s ,
" Event: QueueSummaryComplete \r \n "
" %s "
" \r \n " , idText ) ;
return RESULT_SUCCESS ;
}
2008-03-10 15:45:13 +00:00
/*! \brief Queue status info via AMI */
2007-01-05 22:43:18 +00:00
static int manager_queues_status ( struct mansession * s , const struct message * m )
2003-07-01 03:32:14 +00:00
{
time_t now ;
int pos ;
2007-01-05 22:43:18 +00:00
const char * id = astman_get_header ( m , " ActionID " ) ;
const char * queuefilter = astman_get_header ( m , " Queue " ) ;
const char * memberfilter = astman_get_header ( m , " Member " ) ;
2003-09-08 16:44:36 +00:00
char idText [ 256 ] = " " ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2003-07-01 03:32:14 +00:00
struct queue_ent * qe ;
2004-03-13 06:00:41 +00:00
float sl = 0 ;
struct member * mem ;
2007-09-20 21:21:28 +00:00
struct ao2_iterator queue_iter ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2005-01-03 01:42:37 +00:00
2003-09-08 16:44:36 +00:00
astman_send_ack ( s , m , " Queue status will follow " ) ;
2003-07-01 03:32:14 +00:00
time ( & now ) ;
2006-06-14 23:20:08 +00:00
if ( ! ast_strlen_zero ( id ) )
snprintf ( idText , sizeof ( idText ) , " ActionID: %s \r \n " , id ) ;
2007-09-20 21:21:28 +00:00
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
2004-03-13 06:00:41 +00:00
/* List queue properties */
2005-04-09 18:58:44 +00:00
if ( ast_strlen_zero ( queuefilter ) | | ! strcmp ( q - > name , queuefilter ) ) {
2007-07-05 13:23:49 +00:00
sl = ( ( q - > callscompleted > 0 ) ? 100 * ( ( float ) q - > callscompletedinsl / ( float ) q - > callscompleted ) : 0 ) ;
2006-03-25 23:50:09 +00:00
astman_append ( s , " Event: QueueParams \r \n "
2006-07-14 05:42:06 +00:00
" Queue: %s \r \n "
" Max: %d \r \n "
2006-10-03 20:17:40 +00:00
" Strategy: %s \r \n "
2006-07-14 05:42:06 +00:00
" Calls: %d \r \n "
" Holdtime: %d \r \n "
2009-01-08 19:48:42 +00:00
" TalkTime: %d \r \n "
2006-07-14 05:42:06 +00:00
" Completed: %d \r \n "
" Abandoned: %d \r \n "
" ServiceLevel: %d \r \n "
" ServicelevelPerf: %2.1f \r \n "
" Weight: %d \r \n "
" %s "
" \r \n " ,
2009-01-08 19:48:42 +00:00
q - > name , q - > maxlen , int2strat ( q - > strategy ) , q - > count , q - > holdtime , q - > talktime , q - > callscompleted ,
2006-07-14 05:42:06 +00:00
q - > callsabandoned , q - > servicelevel , sl , q - > weight , idText ) ;
2005-04-09 18:58:44 +00:00
/* List Queue Members */
2007-09-11 16:12:09 +00:00
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( mem = ao2_iterator_next ( & mem_iter ) ) ) {
2008-07-30 21:05:37 +00:00
if ( ast_strlen_zero ( memberfilter ) | | ! strcmp ( mem - > interface , memberfilter ) | | ! strcmp ( mem - > membername , memberfilter ) ) {
2006-03-25 23:50:09 +00:00
astman_append ( s , " Event: QueueMember \r \n "
2006-07-14 05:42:06 +00:00
" Queue: %s \r \n "
2007-09-14 18:34:53 +00:00
" Name: %s \r \n "
2006-07-14 05:42:06 +00:00
" Location: %s \r \n "
2011-08-08 15:00:26 +00:00
" StateInterface: %s \r \n "
2006-07-14 05:42:06 +00:00
" Membership: %s \r \n "
" Penalty: %d \r \n "
" CallsTaken: %d \r \n "
" LastCall: %d \r \n "
" Status: %d \r \n "
" Paused: %d \r \n "
" %s "
" \r \n " ,
2011-08-08 15:00:26 +00:00
q - > name , mem - > membername , mem - > interface , mem - > state_interface , mem - > dynamic ? " dynamic " : " static " ,
2006-07-14 05:42:06 +00:00
mem - > penalty , mem - > calls , ( int ) mem - > lastcall , mem - > status , mem - > paused , idText ) ;
2005-04-09 18:58:44 +00:00
}
2007-09-10 18:32:59 +00:00
ao2_ref ( mem , - 1 ) ;
2005-04-09 18:58:44 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2005-04-09 18:58:44 +00:00
/* List Queue Entries */
pos = 1 ;
for ( qe = q - > head ; qe ; qe = qe - > next ) {
2006-03-25 23:50:09 +00:00
astman_append ( s , " Event: QueueEntry \r \n "
2006-07-14 05:42:06 +00:00
" Queue: %s \r \n "
" Position: %d \r \n "
" Channel: %s \r \n "
2008-10-18 00:25:18 +00:00
" Uniqueid: %s \r \n "
2006-10-02 20:35:16 +00:00
" CallerIDNum: %s \r \n "
2006-07-14 05:42:06 +00:00
" CallerIDName: %s \r \n "
2011-05-25 17:14:11 +00:00
" ConnectedLineNum: %s \r \n "
" ConnectedLineName: %s \r \n "
2006-07-14 05:42:06 +00:00
" Wait: %ld \r \n "
" %s "
" \r \n " ,
2012-01-24 20:12:09 +00:00
q - > name , pos + + , ast_channel_name ( qe - > chan ) , ast_channel_uniqueid ( qe - > chan ) ,
2012-02-29 16:52:47 +00:00
S_COR ( ast_channel_caller ( qe - > chan ) - > id . number . valid , ast_channel_caller ( qe - > chan ) - > id . number . str , " unknown " ) ,
S_COR ( ast_channel_caller ( qe - > chan ) - > id . name . valid , ast_channel_caller ( qe - > chan ) - > id . name . str , " unknown " ) ,
S_COR ( ast_channel_connected ( qe - > chan ) - > id . number . valid , ast_channel_connected ( qe - > chan ) - > id . number . str , " unknown " ) ,
S_COR ( ast_channel_connected ( qe - > chan ) - > id . name . valid , ast_channel_connected ( qe - > chan ) - > id . name . str , " unknown " ) ,
2006-07-14 05:42:06 +00:00
( long ) ( now - qe - > start ) , idText ) ;
2005-04-09 18:58:44 +00:00
}
}
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2003-07-01 03:32:14 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2005-07-13 16:44:55 +00:00
2006-03-25 23:50:09 +00:00
astman_append ( s ,
2006-07-14 05:42:06 +00:00
" Event: QueueStatusComplete \r \n "
" %s "
" \r \n " , idText ) ;
2005-07-13 16:44:55 +00:00
2003-07-01 03:32:14 +00:00
return RESULT_SUCCESS ;
}
2007-01-05 22:43:18 +00:00
static int manager_add_queue_member ( struct mansession * s , const struct message * m )
2004-07-28 02:55:22 +00:00
{
2008-01-08 21:18:32 +00:00
const char * queuename , * interface , * penalty_s , * paused_s , * membername , * state_interface ;
2005-01-21 04:14:26 +00:00
int paused , penalty = 0 ;
2004-07-28 02:55:22 +00:00
queuename = astman_get_header ( m , " Queue " ) ;
interface = astman_get_header ( m , " Interface " ) ;
penalty_s = astman_get_header ( m , " Penalty " ) ;
2005-01-21 04:14:26 +00:00
paused_s = astman_get_header ( m , " Paused " ) ;
2006-09-20 05:59:53 +00:00
membername = astman_get_header ( m , " MemberName " ) ;
2008-01-08 21:18:32 +00:00
state_interface = astman_get_header ( m , " StateInterface " ) ;
2004-07-28 02:55:22 +00:00
if ( ast_strlen_zero ( queuename ) ) {
astman_send_error ( s , m , " 'Queue' not specified. " ) ;
return 0 ;
}
if ( ast_strlen_zero ( interface ) ) {
astman_send_error ( s , m , " 'Interface' not specified. " ) ;
return 0 ;
}
if ( ast_strlen_zero ( penalty_s ) )
penalty = 0 ;
2009-08-10 19:20:57 +00:00
else if ( sscanf ( penalty_s , " %30d " , & penalty ) ! = 1 | | penalty < 0 )
2004-07-28 02:55:22 +00:00
penalty = 0 ;
2005-01-21 04:14:26 +00:00
if ( ast_strlen_zero ( paused_s ) )
paused = 0 ;
else
paused = abs ( ast_true ( paused_s ) ) ;
2008-01-08 21:18:32 +00:00
switch ( add_to_queue ( queuename , interface , membername , penalty , paused , queue_persistent_members , state_interface ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2011-08-08 20:28:20 +00:00
if ( ast_strlen_zero ( membername ) | | ! log_membername_as_agent ) {
2011-11-14 22:27:42 +00:00
ast_queue_log ( queuename , " MANAGER " , interface , " ADDMEMBER " , " %s " , paused ? " PAUSED " : " " ) ;
2011-08-08 20:28:20 +00:00
} else {
2011-11-14 22:27:42 +00:00
ast_queue_log ( queuename , " MANAGER " , membername , " ADDMEMBER " , " %s " , paused ? " PAUSED " : " " ) ;
2011-08-08 20:28:20 +00:00
}
2004-07-28 02:55:22 +00:00
astman_send_ack ( s , m , " Added interface to queue " ) ;
break ;
case RES_EXISTS :
astman_send_error ( s , m , " Unable to add interface: Already there " ) ;
break ;
case RES_NOSUCHQUEUE :
astman_send_error ( s , m , " Unable to add interface to queue: No such queue " ) ;
break ;
case RES_OUTOFMEMORY :
astman_send_error ( s , m , " Out of memory " ) ;
break ;
}
2006-06-14 23:20:08 +00:00
2004-07-28 02:55:22 +00:00
return 0 ;
}
2007-01-05 22:43:18 +00:00
static int manager_remove_queue_member ( struct mansession * s , const struct message * m )
2004-07-28 02:55:22 +00:00
{
2007-01-05 22:43:18 +00:00
const char * queuename , * interface ;
2011-08-08 20:28:20 +00:00
struct member * mem = NULL ;
2004-07-28 02:55:22 +00:00
queuename = astman_get_header ( m , " Queue " ) ;
interface = astman_get_header ( m , " Interface " ) ;
if ( ast_strlen_zero ( queuename ) | | ast_strlen_zero ( interface ) ) {
astman_send_error ( s , m , " Need 'Queue' and 'Interface' parameters. " ) ;
return 0 ;
}
2011-08-08 20:28:20 +00:00
if ( log_membername_as_agent ) {
mem = find_member_by_queuename_and_interface ( queuename , interface ) ;
}
2004-07-28 02:55:22 +00:00
switch ( remove_from_queue ( queuename , interface ) ) {
case RES_OKAY :
2011-10-23 14:35:26 +00:00
if ( ! mem | | ast_strlen_zero ( mem - > membername ) ) {
2011-08-08 20:28:20 +00:00
ast_queue_log ( queuename , " MANAGER " , interface , " REMOVEMEMBER " , " %s " , " " ) ;
} else {
ast_queue_log ( queuename , " MANAGER " , mem - > membername , " REMOVEMEMBER " , " %s " , " " ) ;
}
2004-07-28 02:55:22 +00:00
astman_send_ack ( s , m , " Removed interface from queue " ) ;
break ;
case RES_EXISTS :
astman_send_error ( s , m , " Unable to remove interface: Not there " ) ;
break ;
case RES_NOSUCHQUEUE :
astman_send_error ( s , m , " Unable to remove interface from queue: No such queue " ) ;
break ;
case RES_OUTOFMEMORY :
astman_send_error ( s , m , " Out of memory " ) ;
break ;
2007-10-16 21:16:00 +00:00
case RES_NOT_DYNAMIC :
astman_send_error ( s , m , " Member not dynamic " ) ;
break ;
2004-07-28 02:55:22 +00:00
}
2006-06-14 23:20:08 +00:00
2011-10-23 14:35:26 +00:00
if ( mem ) {
ao2_ref ( mem , - 1 ) ;
}
2004-07-28 02:55:22 +00:00
return 0 ;
}
2007-01-05 22:43:18 +00:00
static int manager_pause_queue_member ( struct mansession * s , const struct message * m )
2005-01-21 04:14:26 +00:00
{
2007-09-07 16:13:57 +00:00
const char * queuename , * interface , * paused_s , * reason ;
2005-01-21 04:14:26 +00:00
int paused ;
interface = astman_get_header ( m , " Interface " ) ;
paused_s = astman_get_header ( m , " Paused " ) ;
2007-09-07 16:13:57 +00:00
queuename = astman_get_header ( m , " Queue " ) ; /* Optional - if not supplied, pause the given Interface in all queues */
reason = astman_get_header ( m , " Reason " ) ; /* Optional - Only used for logging purposes */
2005-01-21 04:14:26 +00:00
if ( ast_strlen_zero ( interface ) | | ast_strlen_zero ( paused_s ) ) {
astman_send_error ( s , m , " Need 'Interface' and 'Paused' parameters. " ) ;
return 0 ;
}
paused = abs ( ast_true ( paused_s ) ) ;
2007-09-07 16:13:57 +00:00
if ( set_member_paused ( queuename , interface , reason , paused ) )
2005-01-21 04:14:26 +00:00
astman_send_error ( s , m , " Interface not found " ) ;
else
2006-02-14 21:50:35 +00:00
astman_send_ack ( s , m , paused ? " Interface paused successfully " : " Interface unpaused successfully " ) ;
2005-01-21 04:14:26 +00:00
return 0 ;
}
2007-01-05 22:43:18 +00:00
static int manager_queue_log_custom ( struct mansession * s , const struct message * m )
2006-11-17 20:46:36 +00:00
{
2007-01-05 22:43:18 +00:00
const char * queuename , * event , * message , * interface , * uniqueid ;
2006-11-17 20:46:36 +00:00
queuename = astman_get_header ( m , " Queue " ) ;
uniqueid = astman_get_header ( m , " UniqueId " ) ;
interface = astman_get_header ( m , " Interface " ) ;
event = astman_get_header ( m , " Event " ) ;
message = astman_get_header ( m , " Message " ) ;
if ( ast_strlen_zero ( queuename ) | | ast_strlen_zero ( event ) ) {
astman_send_error ( s , m , " Need 'Queue' and 'Event' parameters. " ) ;
return 0 ;
}
ast_queue_log ( queuename , S_OR ( uniqueid , " NONE " ) , interface , event , " %s " , message ) ;
astman_send_ack ( s , m , " Event added successfully " ) ;
return 0 ;
}
2009-02-13 20:57:37 +00:00
static int manager_queue_reload ( struct mansession * s , const struct message * m )
{
struct ast_flags mask = { 0 , } ;
const char * queuename = NULL ;
int header_found = 0 ;
queuename = astman_get_header ( m , " Queue " ) ;
if ( ! strcasecmp ( S_OR ( astman_get_header ( m , " Members " ) , " " ) , " yes " ) ) {
ast_set_flag ( & mask , QUEUE_RELOAD_MEMBER ) ;
header_found = 1 ;
}
if ( ! strcasecmp ( S_OR ( astman_get_header ( m , " Rules " ) , " " ) , " yes " ) ) {
ast_set_flag ( & mask , QUEUE_RELOAD_RULES ) ;
header_found = 1 ;
}
if ( ! strcasecmp ( S_OR ( astman_get_header ( m , " Parameters " ) , " " ) , " yes " ) ) {
ast_set_flag ( & mask , QUEUE_RELOAD_PARAMETERS ) ;
header_found = 1 ;
}
if ( ! header_found ) {
ast_set_flag ( & mask , AST_FLAGS_ALL ) ;
}
if ( ! reload_handler ( 1 , & mask , queuename ) ) {
astman_send_ack ( s , m , " Queue reloaded successfully " ) ;
} else {
astman_send_error ( s , m , " Error encountered while reloading queue " ) ;
}
return 0 ;
}
static int manager_queue_reset ( struct mansession * s , const struct message * m )
{
const char * queuename = NULL ;
struct ast_flags mask = { QUEUE_RESET_STATS , } ;
queuename = astman_get_header ( m , " Queue " ) ;
if ( ! reload_handler ( 1 , & mask , queuename ) ) {
astman_send_ack ( s , m , " Queue stats reset successfully " ) ;
} else {
astman_send_error ( s , m , " Error encountered while resetting queue stats " ) ;
}
return 0 ;
}
2006-09-18 19:54:18 +00:00
static char * complete_queue_add_member ( const char * line , const char * word , int pos , int state )
2004-07-28 02:55:22 +00:00
{
2006-09-20 05:59:53 +00:00
/* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
2004-07-28 02:55:22 +00:00
switch ( pos ) {
2007-09-19 23:16:51 +00:00
case 3 : /* Don't attempt to complete name of interface (infinite possibilities) */
2004-07-28 02:55:22 +00:00
return NULL ;
2007-09-19 23:16:51 +00:00
case 4 : /* only one possible match, "to" */
2006-03-29 00:30:29 +00:00
return state = = 0 ? ast_strdup ( " to " ) : NULL ;
2007-09-19 23:16:51 +00:00
case 5 : /* <queue> */
2004-07-28 02:55:22 +00:00
return complete_queue ( line , word , pos , state ) ;
2006-02-14 21:50:35 +00:00
case 6 : /* only one possible match, "penalty" */
2006-03-29 00:30:29 +00:00
return state = = 0 ? ast_strdup ( " penalty " ) : NULL ;
2004-07-28 02:55:22 +00:00
case 7 :
2007-09-19 23:16:51 +00:00
if ( state < 100 ) { /* 0-99 */
2006-01-13 03:25:23 +00:00
char * num ;
if ( ( num = ast_malloc ( 3 ) ) ) {
2004-07-28 02:55:22 +00:00
sprintf ( num , " %d " , state ) ;
}
return num ;
} else {
return NULL ;
}
2006-09-20 05:59:53 +00:00
case 8 : /* only one possible match, "as" */
return state = = 0 ? ast_strdup ( " as " ) : NULL ;
2007-09-19 23:16:51 +00:00
case 9 : /* Don't attempt to complete name of member (infinite possibilities) */
2006-09-20 05:59:53 +00:00
return NULL ;
2004-07-28 02:55:22 +00:00
default :
return NULL ;
}
}
2007-11-06 22:05:56 +00:00
static int manager_queue_member_penalty ( struct mansession * s , const struct message * m )
{
const char * queuename , * interface , * penalty_s ;
int penalty ;
interface = astman_get_header ( m , " Interface " ) ;
penalty_s = astman_get_header ( m , " Penalty " ) ;
/* Optional - if not supplied, set the penalty value for the given Interface in all queues */
queuename = astman_get_header ( m , " Queue " ) ;
if ( ast_strlen_zero ( interface ) | | ast_strlen_zero ( penalty_s ) ) {
astman_send_error ( s , m , " Need 'Interface' and 'Penalty' parameters. " ) ;
return 0 ;
}
penalty = atoi ( penalty_s ) ;
if ( set_member_penalty ( ( char * ) queuename , ( char * ) interface , penalty ) )
2007-12-19 23:17:42 +00:00
astman_send_error ( s , m , " Invalid interface, queuename or penalty " ) ;
2007-11-06 22:05:56 +00:00
else
astman_send_ack ( s , m , " Interface penalty set successfully " ) ;
return 0 ;
}
2007-09-19 23:16:51 +00:00
static char * handle_queue_add_member ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2004-07-28 02:55:22 +00:00
{
2009-05-21 21:13:09 +00:00
const char * queuename , * interface , * membername = NULL , * state_interface = NULL ;
2007-09-19 23:16:51 +00:00
int penalty ;
2004-07-28 02:55:22 +00:00
2007-09-19 23:16:51 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " queue add member " ;
e - > usage =
2008-09-27 00:49:24 +00:00
" Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>] \n "
" Add a channel to a queue with optionally: a penalty, membername and a state_interface \n " ;
2007-09-19 23:16:51 +00:00
return NULL ;
case CLI_GENERATE :
return complete_queue_add_member ( a - > line , a - > word , a - > pos , a - > n ) ;
2004-07-28 02:55:22 +00:00
}
2008-01-08 21:18:32 +00:00
if ( ( a - > argc ! = 6 ) & & ( a - > argc ! = 8 ) & & ( a - > argc ! = 10 ) & & ( a - > argc ! = 12 ) ) {
2007-09-19 23:16:51 +00:00
return CLI_SHOWUSAGE ;
} else if ( strcmp ( a - > argv [ 4 ] , " to " ) ) {
return CLI_SHOWUSAGE ;
2008-01-08 21:18:32 +00:00
} else if ( ( a - > argc > = 8 ) & & strcmp ( a - > argv [ 6 ] , " penalty " ) ) {
return CLI_SHOWUSAGE ;
} else if ( ( a - > argc > = 10 ) & & strcmp ( a - > argv [ 8 ] , " as " ) ) {
2007-09-19 23:16:51 +00:00
return CLI_SHOWUSAGE ;
2008-01-08 21:18:32 +00:00
} else if ( ( a - > argc = = 12 ) & & strcmp ( a - > argv [ 10 ] , " state_interface " ) ) {
2007-09-19 23:16:51 +00:00
return CLI_SHOWUSAGE ;
}
2004-07-28 02:55:22 +00:00
2007-09-19 23:16:51 +00:00
queuename = a - > argv [ 5 ] ;
interface = a - > argv [ 3 ] ;
if ( a - > argc > = 8 ) {
2009-08-10 19:20:57 +00:00
if ( sscanf ( a - > argv [ 7 ] , " %30d " , & penalty ) = = 1 ) {
2007-09-19 23:16:51 +00:00
if ( penalty < 0 ) {
ast_cli ( a - > fd , " Penalty must be >= 0 \n " ) ;
penalty = 0 ;
}
} else {
ast_cli ( a - > fd , " Penalty must be an integer >= 0 \n " ) ;
penalty = 0 ;
}
} else {
penalty = 0 ;
}
if ( a - > argc > = 10 ) {
membername = a - > argv [ 9 ] ;
}
2008-01-08 21:18:32 +00:00
if ( a - > argc > = 12 ) {
state_interface = a - > argv [ 11 ] ;
}
switch ( add_to_queue ( queuename , interface , membername , penalty , 0 , queue_persistent_members , state_interface ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2011-08-08 20:28:20 +00:00
if ( ast_strlen_zero ( membername ) | | ! log_membername_as_agent ) {
ast_queue_log ( queuename , " CLI " , interface , " ADDMEMBER " , " %s " , " " ) ;
} else {
ast_queue_log ( queuename , " CLI " , membername , " ADDMEMBER " , " %s " , " " ) ;
}
2007-09-19 23:16:51 +00:00
ast_cli ( a - > fd , " Added interface '%s' to queue '%s' \n " , interface , queuename ) ;
return CLI_SUCCESS ;
2004-07-28 02:55:22 +00:00
case RES_EXISTS :
2007-09-19 23:16:51 +00:00
ast_cli ( a - > fd , " Unable to add interface '%s' to queue '%s': Already there \n " , interface , queuename ) ;
return CLI_FAILURE ;
2004-07-28 02:55:22 +00:00
case RES_NOSUCHQUEUE :
2007-09-19 23:16:51 +00:00
ast_cli ( a - > fd , " Unable to add interface to queue '%s': No such queue \n " , queuename ) ;
return CLI_FAILURE ;
2004-07-28 02:55:22 +00:00
case RES_OUTOFMEMORY :
2007-09-19 23:16:51 +00:00
ast_cli ( a - > fd , " Out of memory \n " ) ;
return CLI_FAILURE ;
2007-10-16 21:16:00 +00:00
case RES_NOT_DYNAMIC :
2007-10-16 21:51:26 +00:00
ast_cli ( a - > fd , " Member not dynamic \n " ) ;
return CLI_FAILURE ;
2004-07-28 02:55:22 +00:00
default :
2007-09-19 23:16:51 +00:00
return CLI_FAILURE ;
2004-07-28 02:55:22 +00:00
}
}
2006-09-18 19:54:18 +00:00
static char * complete_queue_remove_member ( const char * line , const char * word , int pos , int state )
2004-07-28 02:55:22 +00:00
{
int which = 0 ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2004-07-28 02:55:22 +00:00
struct member * m ;
2007-09-20 21:21:28 +00:00
struct ao2_iterator queue_iter ;
2007-09-11 16:12:09 +00:00
struct ao2_iterator mem_iter ;
2007-09-19 23:16:51 +00:00
int wordlen = strlen ( word ) ;
2004-07-28 02:55:22 +00:00
2006-09-18 19:54:18 +00:00
/* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
2006-03-29 00:30:29 +00:00
if ( pos > 5 | | pos < 3 )
return NULL ;
2007-09-19 23:16:51 +00:00
if ( pos = = 4 ) /* only one possible match, 'from' */
return ( state = = 0 ? ast_strdup ( " from " ) : NULL ) ;
2006-03-29 00:30:29 +00:00
2007-09-19 23:16:51 +00:00
if ( pos = = 5 ) /* No need to duplicate code */
2006-03-29 00:30:29 +00:00
return complete_queue ( line , word , pos , state ) ;
/* here is the case for 3, <member> */
2007-09-20 21:21:28 +00:00
queue_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & queue_iter , " Iterate through queues " ) ) ) {
2007-09-20 21:21:28 +00:00
ao2_lock ( q ) ;
mem_iter = ao2_iterator_init ( q - > members , 0 ) ;
while ( ( m = ao2_iterator_next ( & mem_iter ) ) ) {
if ( ! strncasecmp ( word , m - > membername , wordlen ) & & + + which > state ) {
char * tmp ;
2008-06-04 22:07:37 +00:00
tmp = ast_strdup ( m - > interface ) ;
2007-09-11 14:35:06 +00:00
ao2_ref ( m , - 1 ) ;
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2012-01-27 18:47:16 +00:00
ao2_unlock ( q ) ;
queue_t_unref ( q , " Done with iterator, returning interface name " ) ;
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2008-06-04 22:07:37 +00:00
return tmp ;
2004-07-28 02:55:22 +00:00
}
2007-09-20 21:21:28 +00:00
ao2_ref ( m , - 1 ) ;
2004-07-28 02:55:22 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & mem_iter ) ;
2007-09-20 21:21:28 +00:00
ao2_unlock ( q ) ;
2009-11-24 20:31:28 +00:00
queue_t_unref ( q , " Done with iterator " ) ;
2004-07-28 02:55:22 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & queue_iter ) ;
2006-06-14 23:20:08 +00:00
2004-07-28 02:55:22 +00:00
return NULL ;
}
2007-09-19 23:16:51 +00:00
static char * handle_queue_remove_member ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
2009-05-21 21:13:09 +00:00
const char * queuename , * interface ;
2011-08-08 20:28:20 +00:00
struct member * mem = NULL ;
2007-09-19 23:16:51 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " queue remove member " ;
2011-08-08 20:28:20 +00:00
e - > usage =
2008-09-27 00:49:24 +00:00
" Usage: queue remove member <channel> from <queue> \n "
" Remove a specific channel from a queue. \n " ;
2007-09-19 23:16:51 +00:00
return NULL ;
case CLI_GENERATE :
return complete_queue_remove_member ( a - > line , a - > word , a - > pos , a - > n ) ;
}
if ( a - > argc ! = 6 ) {
return CLI_SHOWUSAGE ;
} else if ( strcmp ( a - > argv [ 4 ] , " from " ) ) {
return CLI_SHOWUSAGE ;
}
queuename = a - > argv [ 5 ] ;
interface = a - > argv [ 3 ] ;
switch ( remove_from_queue ( queuename , interface ) ) {
case RES_OKAY :
2011-10-23 14:35:26 +00:00
if ( log_membername_as_agent ) {
mem = find_member_by_queuename_and_interface ( queuename , interface ) ;
}
if ( ! mem | | ast_strlen_zero ( mem - > membername ) ) {
2011-08-08 20:28:20 +00:00
ast_queue_log ( queuename , " CLI " , interface , " REMOVEMEMBER " , " %s " , " " ) ;
} else {
ast_queue_log ( queuename , " CLI " , mem - > membername , " REMOVEMEMBER " , " %s " , " " ) ;
}
2011-10-23 14:35:26 +00:00
if ( mem ) {
ao2_ref ( mem , - 1 ) ;
}
2011-08-08 20:28:20 +00:00
ast_cli ( a - > fd , " Removed interface %s from queue '%s' \n " , interface , queuename ) ;
2007-09-19 23:16:51 +00:00
return CLI_SUCCESS ;
case RES_EXISTS :
ast_cli ( a - > fd , " Unable to remove interface '%s' from queue '%s': Not there \n " , interface , queuename ) ;
return CLI_FAILURE ;
case RES_NOSUCHQUEUE :
ast_cli ( a - > fd , " Unable to remove interface from queue '%s': No such queue \n " , queuename ) ;
return CLI_FAILURE ;
case RES_OUTOFMEMORY :
ast_cli ( a - > fd , " Out of memory \n " ) ;
return CLI_FAILURE ;
2008-11-19 23:29:14 +00:00
case RES_NOT_DYNAMIC :
2008-11-19 23:30:42 +00:00
ast_cli ( a - > fd , " Unable to remove interface '%s' from queue '%s': Member is not dynamic \n " , interface , queuename ) ;
2008-11-19 23:29:14 +00:00
return CLI_FAILURE ;
2007-09-19 23:16:51 +00:00
default :
return CLI_FAILURE ;
}
}
2007-09-13 21:23:32 +00:00
static char * complete_queue_pause_member ( const char * line , const char * word , int pos , int state )
{
2007-09-13 21:53:01 +00:00
/* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
2007-09-13 21:23:32 +00:00
switch ( pos ) {
case 3 : /* Don't attempt to complete name of interface (infinite possibilities) */
return NULL ;
2007-09-13 21:53:01 +00:00
case 4 : /* only one possible match, "queue" */
return state = = 0 ? ast_strdup ( " queue " ) : NULL ;
2007-09-13 21:23:32 +00:00
case 5 : /* <queue> */
return complete_queue ( line , word , pos , state ) ;
case 6 : /* "reason" */
return state = = 0 ? ast_strdup ( " reason " ) : NULL ;
case 7 : /* Can't autocomplete a reason, since it's 100% customizeable */
return NULL ;
default :
return NULL ;
}
}
static char * handle_queue_pause_member ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
2009-05-21 21:13:09 +00:00
const char * queuename , * interface , * reason ;
2007-09-13 21:23:32 +00:00
int paused ;
switch ( cmd ) {
case CLI_INIT :
e - > command = " queue {pause|unpause} member " ;
e - > usage =
2007-09-13 21:53:01 +00:00
" Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]] \n "
2008-12-18 21:21:44 +00:00
" Pause or unpause a queue member. Not specifying a particular queue \n "
" will pause or unpause a member across all queues to which the member \n "
" belongs. \n " ;
2007-09-13 21:23:32 +00:00
return NULL ;
case CLI_GENERATE :
return complete_queue_pause_member ( a - > line , a - > word , a - > pos , a - > n ) ;
}
if ( a - > argc < 4 | | a - > argc = = 5 | | a - > argc = = 7 | | a - > argc > 8 ) {
return CLI_SHOWUSAGE ;
2007-09-13 21:53:01 +00:00
} else if ( a - > argc > = 5 & & strcmp ( a - > argv [ 4 ] , " queue " ) ) {
return CLI_SHOWUSAGE ;
} else if ( a - > argc = = 8 & & strcmp ( a - > argv [ 6 ] , " reason " ) ) {
2007-09-13 21:23:32 +00:00
return CLI_SHOWUSAGE ;
}
interface = a - > argv [ 3 ] ;
queuename = a - > argc > = 6 ? a - > argv [ 5 ] : NULL ;
reason = a - > argc = = 8 ? a - > argv [ 7 ] : NULL ;
paused = ! strcasecmp ( a - > argv [ 1 ] , " pause " ) ;
2007-12-21 00:44:17 +00:00
if ( set_member_paused ( queuename , interface , reason , paused ) = = RESULT_SUCCESS ) {
2007-09-13 21:23:32 +00:00
ast_cli ( a - > fd , " %spaused interface '%s' " , paused ? " " : " un " , interface ) ;
2007-12-21 00:44:17 +00:00
if ( ! ast_strlen_zero ( queuename ) )
2007-09-13 21:23:32 +00:00
ast_cli ( a - > fd , " in queue '%s' " , queuename ) ;
2007-12-21 00:44:17 +00:00
if ( ! ast_strlen_zero ( reason ) )
2007-09-13 21:23:32 +00:00
ast_cli ( a - > fd , " for reason '%s' " , reason ) ;
ast_cli ( a - > fd , " \n " ) ;
return CLI_SUCCESS ;
} else {
ast_cli ( a - > fd , " Unable to %spause interface '%s' " , paused ? " " : " un " , interface ) ;
2007-12-21 00:44:17 +00:00
if ( ! ast_strlen_zero ( queuename ) )
2007-09-13 21:23:32 +00:00
ast_cli ( a - > fd , " in queue '%s' " , queuename ) ;
2007-12-21 00:44:17 +00:00
if ( ! ast_strlen_zero ( reason ) )
2007-09-13 21:23:32 +00:00
ast_cli ( a - > fd , " for reason '%s' " , reason ) ;
ast_cli ( a - > fd , " \n " ) ;
return CLI_FAILURE ;
}
}
2007-11-06 22:05:56 +00:00
static char * complete_queue_set_member_penalty ( const char * line , const char * word , int pos , int state )
{
/* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
switch ( pos ) {
case 4 :
if ( state = = 0 ) {
return ast_strdup ( " on " ) ;
} else {
return NULL ;
}
case 6 :
if ( state = = 0 ) {
return ast_strdup ( " in " ) ;
} else {
return NULL ;
}
case 7 :
return complete_queue ( line , word , pos , state ) ;
default :
return NULL ;
}
}
static char * handle_queue_set_member_penalty ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
2009-05-21 21:13:09 +00:00
const char * queuename = NULL , * interface ;
2007-11-06 22:05:56 +00:00
int penalty = 0 ;
switch ( cmd ) {
case CLI_INIT :
e - > command = " queue set penalty " ;
e - > usage =
" Usage: queue set penalty <penalty> on <interface> [in <queue>] \n "
2008-12-18 21:21:44 +00:00
" Set a member's penalty in the queue specified. If no queue is specified \n "
" then that interface's penalty is set in all queues to which that interface is a member \n " ;
2007-11-06 22:05:56 +00:00
return NULL ;
case CLI_GENERATE :
return complete_queue_set_member_penalty ( a - > line , a - > word , a - > pos , a - > n ) ;
}
if ( a - > argc ! = 6 & & a - > argc ! = 8 ) {
return CLI_SHOWUSAGE ;
2007-12-19 23:17:42 +00:00
} else if ( strcmp ( a - > argv [ 4 ] , " on " ) | | ( a - > argc > 6 & & strcmp ( a - > argv [ 6 ] , " in " ) ) ) {
2007-11-06 22:05:56 +00:00
return CLI_SHOWUSAGE ;
}
2007-12-21 00:44:17 +00:00
if ( a - > argc = = 8 )
2007-11-06 22:05:56 +00:00
queuename = a - > argv [ 7 ] ;
interface = a - > argv [ 5 ] ;
penalty = atoi ( a - > argv [ 3 ] ) ;
switch ( set_member_penalty ( queuename , interface , penalty ) ) {
case RESULT_SUCCESS :
ast_cli ( a - > fd , " Set penalty on interface '%s' from queue '%s' \n " , interface , queuename ) ;
return CLI_SUCCESS ;
case RESULT_FAILURE :
ast_cli ( a - > fd , " Failed to set penalty on interface '%s' from queue '%s' \n " , interface , queuename ) ;
return CLI_FAILURE ;
default :
return CLI_FAILURE ;
}
}
2007-12-21 00:44:17 +00:00
static char * complete_queue_rule_show ( const char * line , const char * word , int pos , int state )
{
int which = 0 ;
struct rule_list * rl_iter ;
int wordlen = strlen ( word ) ;
char * ret = NULL ;
if ( pos ! = 3 ) /* Wha? */ {
return NULL ;
}
AST_LIST_LOCK ( & rule_lists ) ;
AST_LIST_TRAVERSE ( & rule_lists , rl_iter , list ) {
if ( ! strncasecmp ( word , rl_iter - > name , wordlen ) & & + + which > state ) {
ret = ast_strdup ( rl_iter - > name ) ;
break ;
}
}
AST_LIST_UNLOCK ( & rule_lists ) ;
return ret ;
}
static char * handle_queue_rule_show ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
2009-05-21 21:13:09 +00:00
const char * rule ;
2007-12-21 00:44:17 +00:00
struct rule_list * rl_iter ;
struct penalty_rule * pr_iter ;
switch ( cmd ) {
case CLI_INIT :
2008-09-28 23:32:14 +00:00
e - > command = " queue show rules " ;
2007-12-21 00:44:17 +00:00
e - > usage =
2008-09-28 23:32:14 +00:00
" Usage: queue show rules [rulename] \n "
2008-12-18 21:21:44 +00:00
" Show the list of rules associated with rulename. If no \n "
" rulename is specified, list all rules defined in queuerules.conf \n " ;
2007-12-21 00:44:17 +00:00
return NULL ;
case CLI_GENERATE :
return complete_queue_rule_show ( a - > line , a - > word , a - > pos , a - > n ) ;
}
if ( a - > argc ! = 3 & & a - > argc ! = 4 )
return CLI_SHOWUSAGE ;
rule = a - > argc = = 4 ? a - > argv [ 3 ] : " " ;
AST_LIST_LOCK ( & rule_lists ) ;
AST_LIST_TRAVERSE ( & rule_lists , rl_iter , list ) {
if ( ast_strlen_zero ( rule ) | | ! strcasecmp ( rl_iter - > name , rule ) ) {
ast_cli ( a - > fd , " Rule: %s \n " , rl_iter - > name ) ;
AST_LIST_TRAVERSE ( & rl_iter - > rules , pr_iter , list ) {
ast_cli ( a - > fd , " \t After %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_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 ) ;
}
}
}
AST_LIST_UNLOCK ( & rule_lists ) ;
return CLI_SUCCESS ;
}
2009-02-13 20:57:37 +00:00
static char * handle_queue_reset ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2007-12-21 00:44:17 +00:00
{
2009-02-13 20:57:37 +00:00
struct ast_flags mask = { QUEUE_RESET_STATS , } ;
int i ;
2007-12-21 00:44:17 +00:00
switch ( cmd ) {
case CLI_INIT :
2009-02-13 20:57:37 +00:00
e - > command = " queue reset stats " ;
e - > usage =
" Usage: queue reset stats [<queuenames>] \n "
" \n "
" Issuing this command will reset statistics for \n "
" <queuenames>, or for all queues if no queue is \n "
" specified. \n " ;
2007-12-21 00:44:17 +00:00
return NULL ;
case CLI_GENERATE :
2009-02-13 20:57:37 +00:00
if ( a - > pos > = 3 ) {
return complete_queue ( a - > line , a - > word , a - > pos , a - > n ) ;
} else {
return NULL ;
}
}
if ( a - > argc < 3 ) {
return CLI_SHOWUSAGE ;
}
if ( a - > argc = = 3 ) {
reload_handler ( 1 , & mask , NULL ) ;
return CLI_SUCCESS ;
}
for ( i = 3 ; i < a - > argc ; + + i ) {
reload_handler ( 1 , & mask , a - > argv [ i ] ) ;
}
return CLI_SUCCESS ;
}
static char * handle_queue_reload ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
struct ast_flags mask = { 0 , } ;
int i ;
switch ( cmd ) {
case CLI_INIT :
e - > command = " queue reload {parameters|members|rules|all} " ;
e - > usage =
" Usage: queue reload {parameters|members|rules|all} [<queuenames>] \n "
" Reload queues. If <queuenames> are specified, only reload information pertaining \n "
" to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be \n "
" specified in order to know what information to reload. Below is an explanation \n "
" of each of these qualifiers. \n "
" \n "
" \t 'members' - reload queue members from queues.conf \n "
" \t 'parameters' - reload all queue options except for queue members \n "
" \t 'rules' - reload the queuerules.conf file \n "
" \t 'all' - reload queue rules, parameters, and members \n "
" \n "
" Note: the 'rules' qualifier here cannot actually be applied to a specific queue. \n "
" Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only \n "
" one queue is specified when using this command, reloading queue rules may cause \n "
" other queues to be affected \n " ;
2007-12-21 00:44:17 +00:00
return NULL ;
2009-02-13 20:57:37 +00:00
case CLI_GENERATE :
if ( a - > pos > = 3 ) {
return complete_queue ( a - > line , a - > word , a - > pos , a - > n ) ;
} else {
return NULL ;
}
2007-12-21 00:44:17 +00:00
}
2009-02-13 20:57:37 +00:00
if ( a - > argc < 3 )
return CLI_SHOWUSAGE ;
if ( ! strcasecmp ( a - > argv [ 2 ] , " rules " ) ) {
ast_set_flag ( & mask , QUEUE_RELOAD_RULES ) ;
} else if ( ! strcasecmp ( a - > argv [ 2 ] , " members " ) ) {
ast_set_flag ( & mask , QUEUE_RELOAD_MEMBER ) ;
} else if ( ! strcasecmp ( a - > argv [ 2 ] , " parameters " ) ) {
ast_set_flag ( & mask , QUEUE_RELOAD_PARAMETERS ) ;
} else if ( ! strcasecmp ( a - > argv [ 2 ] , " all " ) ) {
ast_set_flag ( & mask , AST_FLAGS_ALL ) ;
}
if ( a - > argc = = 3 ) {
reload_handler ( 1 , & mask , NULL ) ;
return CLI_SUCCESS ;
}
for ( i = 3 ; i < a - > argc ; + + i ) {
reload_handler ( 1 , & mask , a - > argv [ i ] ) ;
}
2007-12-21 00:44:17 +00:00
return CLI_SUCCESS ;
}
2007-09-13 21:23:32 +00:00
static const char qpm_cmd_usage [ ] =
" Usage: queue pause member <channel> in <queue> reason <reason> \n " ;
static const char qum_cmd_usage [ ] =
" Usage: queue unpause member <channel> in <queue> reason <reason> \n " ;
2007-11-06 22:05:56 +00:00
static const char qsmp_cmd_usage [ ] =
" Usage: queue set member penalty <channel> from <queue> <penalty> \n " ;
2006-09-18 19:54:18 +00:00
static struct ast_cli_entry cli_queue [ ] = {
2007-10-22 20:05:18 +00:00
AST_CLI_DEFINE ( queue_show , " Show status of a specified queue " ) ,
AST_CLI_DEFINE ( handle_queue_add_member , " Add a channel to a specified queue " ) ,
AST_CLI_DEFINE ( handle_queue_remove_member , " Removes a channel from a specified queue " ) ,
AST_CLI_DEFINE ( handle_queue_pause_member , " Pause or unpause a queue member " ) ,
2007-11-06 22:05:56 +00:00
AST_CLI_DEFINE ( handle_queue_set_member_penalty , " Set penalty for a channel of a specified queue " ) ,
2007-12-21 00:44:17 +00:00
AST_CLI_DEFINE ( handle_queue_rule_show , " Show the rules defined in queuerules.conf " ) ,
2009-02-13 20:57:37 +00:00
AST_CLI_DEFINE ( handle_queue_reload , " Reload queues, members, queue rules, or parameters " ) ,
AST_CLI_DEFINE ( handle_queue_reset , " Reset statistics for a queue " ) ,
2006-09-18 19:54:18 +00:00
} ;
2004-07-28 02:55:22 +00:00
2010-04-22 18:07:02 +00:00
/* struct call_queue astdata mapping. */
# define DATA_EXPORT_CALL_QUEUE(MEMBER) \
MEMBER ( call_queue , name , AST_DATA_STRING ) \
MEMBER ( call_queue , moh , AST_DATA_STRING ) \
MEMBER ( call_queue , announce , AST_DATA_STRING ) \
MEMBER ( call_queue , context , AST_DATA_STRING ) \
MEMBER ( call_queue , membermacro , AST_DATA_STRING ) \
MEMBER ( call_queue , membergosub , AST_DATA_STRING ) \
MEMBER ( call_queue , defaultrule , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_next , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_thereare , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_calls , AST_DATA_STRING ) \
MEMBER ( call_queue , queue_quantity1 , AST_DATA_STRING ) \
MEMBER ( call_queue , queue_quantity2 , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_holdtime , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_minutes , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_minute , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_seconds , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_thanks , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_callerannounce , AST_DATA_STRING ) \
MEMBER ( call_queue , sound_reporthold , AST_DATA_STRING ) \
MEMBER ( call_queue , dead , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , eventwhencalled , AST_DATA_BOOLEAN ) \
2010-07-08 14:48:42 +00:00
MEMBER ( call_queue , ringinuse , AST_DATA_BOOLEAN ) \
2010-04-22 18:07:02 +00:00
MEMBER ( call_queue , setinterfacevar , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , setqueuevar , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , setqueueentryvar , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , reportholdtime , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , wrapped , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , timeoutrestart , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , announceholdtime , AST_DATA_INTEGER ) \
MEMBER ( call_queue , maskmemberstatus , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , realtime , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , found , AST_DATA_BOOLEAN ) \
MEMBER ( call_queue , announcepositionlimit , AST_DATA_INTEGER ) \
2010-07-08 14:48:42 +00:00
MEMBER ( call_queue , announcefrequency , AST_DATA_SECONDS ) \
MEMBER ( call_queue , minannouncefrequency , AST_DATA_SECONDS ) \
MEMBER ( call_queue , periodicannouncefrequency , AST_DATA_SECONDS ) \
2010-04-22 18:07:02 +00:00
MEMBER ( call_queue , numperiodicannounce , AST_DATA_INTEGER ) \
MEMBER ( call_queue , randomperiodicannounce , AST_DATA_INTEGER ) \
2010-07-08 14:48:42 +00:00
MEMBER ( call_queue , roundingseconds , AST_DATA_SECONDS ) \
MEMBER ( call_queue , holdtime , AST_DATA_SECONDS ) \
MEMBER ( call_queue , talktime , AST_DATA_SECONDS ) \
2010-04-22 18:07:02 +00:00
MEMBER ( call_queue , callscompleted , AST_DATA_INTEGER ) \
MEMBER ( call_queue , callsabandoned , AST_DATA_INTEGER ) \
MEMBER ( call_queue , servicelevel , AST_DATA_INTEGER ) \
MEMBER ( call_queue , callscompletedinsl , AST_DATA_INTEGER ) \
MEMBER ( call_queue , monfmt , AST_DATA_STRING ) \
MEMBER ( call_queue , montype , AST_DATA_INTEGER ) \
MEMBER ( call_queue , count , AST_DATA_INTEGER ) \
MEMBER ( call_queue , maxlen , AST_DATA_INTEGER ) \
2010-07-08 14:48:42 +00:00
MEMBER ( call_queue , wrapuptime , AST_DATA_SECONDS ) \
MEMBER ( call_queue , retry , AST_DATA_SECONDS ) \
MEMBER ( call_queue , timeout , AST_DATA_SECONDS ) \
2010-04-22 18:07:02 +00:00
MEMBER ( call_queue , weight , AST_DATA_INTEGER ) \
MEMBER ( call_queue , autopause , AST_DATA_INTEGER ) \
MEMBER ( call_queue , timeoutpriority , AST_DATA_INTEGER ) \
MEMBER ( call_queue , rrpos , AST_DATA_INTEGER ) \
MEMBER ( call_queue , memberdelay , AST_DATA_INTEGER ) \
MEMBER ( call_queue , autofill , AST_DATA_INTEGER ) \
Simplify queue membercount code
Despite an ominous sounding comment stating that membercount was for "logged
in" members only and thus we couldn't use ao2_container_count(), I could not
find a single place in the code where that seemed to be accurate. The only time
we decremented membercount was when we were marking something dead or actually
removing it. The only places we incremented it were either after ao2_link(), or
trying to correct for having set it to 0 during a reload. In every case where
we were correcting the value, it seemed that we were trying to make the count
actually match what ao2_container_count() would return. The only place I could
find where we made a determination about something being "logged in" or not, we
didn't trust the membercount, but instead looked at devicestate, paused, etc.
This patch removes membercount, replaces its use with ao2_container_count, and
manually adds the results of ao2_container_count to a "membercount" field for
ast_data queue query results. This patch also would fix AST-676, but as it is
slightly riskier than the previously committed fix, the two commits have been
made separately.
Reivew: https://reviewboard.asterisk.org/r/1541/
........
Merged revisions 342383 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 342384 from http://svn.asterisk.org/svn/asterisk/branches/10
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@342385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2011-10-25 20:07:59 +00:00
MEMBER ( call_queue , members , AST_DATA_CONTAINER )
2010-04-22 18:07:02 +00:00
AST_DATA_STRUCTURE ( call_queue , DATA_EXPORT_CALL_QUEUE ) ;
/* struct member astdata mapping. */
# define DATA_EXPORT_MEMBER(MEMBER) \
MEMBER ( member , interface , AST_DATA_STRING ) \
MEMBER ( member , state_interface , AST_DATA_STRING ) \
MEMBER ( member , membername , AST_DATA_STRING ) \
MEMBER ( member , penalty , AST_DATA_INTEGER ) \
MEMBER ( member , calls , AST_DATA_INTEGER ) \
MEMBER ( member , dynamic , AST_DATA_INTEGER ) \
MEMBER ( member , realtime , AST_DATA_INTEGER ) \
MEMBER ( member , status , AST_DATA_INTEGER ) \
MEMBER ( member , paused , AST_DATA_BOOLEAN ) \
MEMBER ( member , rt_uniqueid , AST_DATA_STRING )
AST_DATA_STRUCTURE ( member , DATA_EXPORT_MEMBER ) ;
# define DATA_EXPORT_QUEUE_ENT(MEMBER) \
MEMBER ( queue_ent , moh , AST_DATA_STRING ) \
MEMBER ( queue_ent , announce , AST_DATA_STRING ) \
MEMBER ( queue_ent , context , AST_DATA_STRING ) \
MEMBER ( queue_ent , digits , AST_DATA_STRING ) \
MEMBER ( queue_ent , valid_digits , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , pos , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , prio , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , last_pos_said , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , last_periodic_announce_time , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , last_periodic_announce_sound , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , last_pos , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , opos , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , handled , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , pending , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , max_penalty , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , min_penalty , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , linpos , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , linwrapped , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , start , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , expire , AST_DATA_INTEGER ) \
MEMBER ( queue_ent , cancel_answered_elsewhere , AST_DATA_INTEGER )
AST_DATA_STRUCTURE ( queue_ent , DATA_EXPORT_QUEUE_ENT ) ;
/*!
* \ internal
* \ brief Add a queue to the data_root node .
* \ param [ in ] search The search tree .
* \ param [ in ] data_root The main result node .
* \ param [ in ] queue The queue to add .
*/
static void queues_data_provider_get_helper ( const struct ast_data_search * search ,
struct ast_data * data_root , struct call_queue * queue )
{
struct ao2_iterator im ;
struct member * member ;
struct queue_ent * qe ;
2010-07-08 14:48:42 +00:00
struct ast_data * data_queue , * data_members = NULL , * enum_node ;
2010-04-22 18:07:02 +00:00
struct ast_data * data_member , * data_callers = NULL , * data_caller , * data_caller_channel ;
data_queue = ast_data_add_node ( data_root , " queue " ) ;
if ( ! data_queue ) {
return ;
}
ast_data_add_structure ( call_queue , data_queue , queue ) ;
2010-07-08 14:48:42 +00:00
ast_data_add_str ( data_queue , " strategy " , int2strat ( queue - > strategy ) ) ;
Simplify queue membercount code
Despite an ominous sounding comment stating that membercount was for "logged
in" members only and thus we couldn't use ao2_container_count(), I could not
find a single place in the code where that seemed to be accurate. The only time
we decremented membercount was when we were marking something dead or actually
removing it. The only places we incremented it were either after ao2_link(), or
trying to correct for having set it to 0 during a reload. In every case where
we were correcting the value, it seemed that we were trying to make the count
actually match what ao2_container_count() would return. The only place I could
find where we made a determination about something being "logged in" or not, we
didn't trust the membercount, but instead looked at devicestate, paused, etc.
This patch removes membercount, replaces its use with ao2_container_count, and
manually adds the results of ao2_container_count to a "membercount" field for
ast_data queue query results. This patch also would fix AST-676, but as it is
slightly riskier than the previously committed fix, the two commits have been
made separately.
Reivew: https://reviewboard.asterisk.org/r/1541/
........
Merged revisions 342383 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 342384 from http://svn.asterisk.org/svn/asterisk/branches/10
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@342385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2011-10-25 20:07:59 +00:00
ast_data_add_int ( data_queue , " membercount " , ao2_container_count ( queue - > members ) ) ;
2010-07-08 14:48:42 +00:00
/* announce position */
enum_node = ast_data_add_node ( data_queue , " announceposition " ) ;
if ( ! enum_node ) {
return ;
}
switch ( queue - > announceposition ) {
case ANNOUNCEPOSITION_LIMIT :
ast_data_add_str ( enum_node , " text " , " limit " ) ;
break ;
case ANNOUNCEPOSITION_MORE_THAN :
ast_data_add_str ( enum_node , " text " , " more " ) ;
break ;
case ANNOUNCEPOSITION_YES :
ast_data_add_str ( enum_node , " text " , " yes " ) ;
break ;
case ANNOUNCEPOSITION_NO :
ast_data_add_str ( enum_node , " text " , " no " ) ;
break ;
default :
ast_data_add_str ( enum_node , " text " , " unknown " ) ;
break ;
}
ast_data_add_int ( enum_node , " value " , queue - > announceposition ) ;
2010-04-22 18:07:02 +00:00
/* add queue members */
im = ao2_iterator_init ( queue - > members , 0 ) ;
while ( ( member = ao2_iterator_next ( & im ) ) ) {
if ( ! data_members ) {
data_members = ast_data_add_node ( data_queue , " members " ) ;
if ( ! data_members ) {
ao2_ref ( member , - 1 ) ;
continue ;
}
}
data_member = ast_data_add_node ( data_members , " member " ) ;
if ( ! data_member ) {
ao2_ref ( member , - 1 ) ;
continue ;
}
ast_data_add_structure ( member , data_member , member ) ;
ao2_ref ( member , - 1 ) ;
}
2012-01-27 18:47:16 +00:00
ao2_iterator_destroy ( & im ) ;
2010-04-22 18:07:02 +00:00
/* include the callers inside the result. */
if ( queue - > head ) {
for ( qe = queue - > head ; qe ; qe = qe - > next ) {
if ( ! data_callers ) {
data_callers = ast_data_add_node ( data_queue , " callers " ) ;
if ( ! data_callers ) {
continue ;
}
}
data_caller = ast_data_add_node ( data_callers , " caller " ) ;
if ( ! data_caller ) {
continue ;
}
ast_data_add_structure ( queue_ent , data_caller , qe ) ;
/* add the caller channel. */
data_caller_channel = ast_data_add_node ( data_caller , " channel " ) ;
if ( ! data_caller_channel ) {
continue ;
}
2010-07-08 14:48:42 +00:00
ast_channel_data_add_structure ( data_caller_channel , qe - > chan , 1 ) ;
2010-04-22 18:07:02 +00:00
}
}
/* if this queue doesn't match remove the added queue. */
2010-07-08 14:48:42 +00:00
if ( ! ast_data_search_match ( search , data_queue ) ) {
2010-04-22 18:07:02 +00:00
ast_data_remove_node ( data_root , data_queue ) ;
}
}
/*!
* \ internal
* \ brief Callback used to generate the queues tree .
* \ param [ in ] search The search pattern tree .
* \ retval NULL on error .
* \ retval non - NULL The generated tree .
*/
static int queues_data_provider_get ( const struct ast_data_search * search ,
struct ast_data * data_root )
{
struct ao2_iterator i ;
struct call_queue * queue , * queue_realtime = NULL ;
struct ast_config * cfg ;
char * queuename ;
/* load realtime queues. */
cfg = ast_load_realtime_multientry ( " queues " , " name LIKE " , " % " , SENTINEL ) ;
if ( cfg ) {
for ( queuename = ast_category_browse ( cfg , NULL ) ;
! ast_strlen_zero ( queuename ) ;
queuename = ast_category_browse ( cfg , queuename ) ) {
2011-12-08 20:55:19 +00:00
if ( ( queue = find_load_queue_rt_friendly ( queuename ) ) ) {
2010-04-22 18:07:02 +00:00
queue_unref ( queue ) ;
}
}
ast_config_destroy ( cfg ) ;
}
/* static queues. */
i = ao2_iterator_init ( queues , 0 ) ;
while ( ( queue = ao2_iterator_next ( & i ) ) ) {
ao2_lock ( queue ) ;
2011-08-22 20:01:30 +00:00
if ( queue - > realtime ) {
2011-12-08 20:55:19 +00:00
queue_realtime = find_load_queue_rt_friendly ( queue - > name ) ;
2011-08-22 20:01:30 +00:00
if ( ! queue_realtime ) {
ao2_unlock ( queue ) ;
queue_unref ( queue ) ;
continue ;
}
2010-04-22 18:07:02 +00:00
queue_unref ( queue_realtime ) ;
}
queues_data_provider_get_helper ( search , data_root , queue ) ;
ao2_unlock ( queue ) ;
queue_unref ( queue ) ;
}
2011-08-22 20:01:30 +00:00
ao2_iterator_destroy ( & i ) ;
2010-04-22 18:07:02 +00:00
return 0 ;
}
static const struct ast_data_handler queues_data_provider = {
. version = AST_DATA_HANDLER_VERSION ,
. get = queues_data_provider_get
} ;
static const struct ast_data_entry queue_data_providers [ ] = {
2010-07-08 14:48:42 +00:00
AST_DATA_ENTRY ( " asterisk/application/queue/list " , & queues_data_provider ) ,
2010-04-22 18:07:02 +00:00
} ;
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2002-09-02 23:15:40 +00:00
{
2005-10-18 22:52:21 +00:00
int res ;
2007-06-20 21:38:49 +00:00
struct ast_context * con ;
2008-03-28 00:12:52 +00:00
struct ao2_iterator q_iter ;
struct call_queue * q = NULL ;
2005-10-18 22:52:21 +00:00
2008-12-05 10:31:25 +00:00
ast_cli_unregister_multiple ( cli_queue , ARRAY_LEN ( cli_queue ) ) ;
2006-09-18 19:54:18 +00:00
res = ast_manager_unregister ( " QueueStatus " ) ;
2005-10-18 22:52:21 +00:00
res | = ast_manager_unregister ( " Queues " ) ;
2008-03-28 00:55:47 +00:00
res | = ast_manager_unregister ( " QueueRule " ) ;
2006-09-26 21:30:41 +00:00
res | = ast_manager_unregister ( " QueueSummary " ) ;
2005-10-18 22:52:21 +00:00
res | = ast_manager_unregister ( " QueueAdd " ) ;
res | = ast_manager_unregister ( " QueueRemove " ) ;
res | = ast_manager_unregister ( " QueuePause " ) ;
2006-11-17 20:46:36 +00:00
res | = ast_manager_unregister ( " QueueLog " ) ;
2007-11-06 22:05:56 +00:00
res | = ast_manager_unregister ( " QueuePenalty " ) ;
2005-10-18 22:52:21 +00:00
res | = ast_unregister_application ( app_aqm ) ;
res | = ast_unregister_application ( app_rqm ) ;
res | = ast_unregister_application ( app_pqm ) ;
res | = ast_unregister_application ( app_upqm ) ;
2006-07-16 19:36:29 +00:00
res | = ast_unregister_application ( app_ql ) ;
2006-09-18 19:54:18 +00:00
res | = ast_unregister_application ( app ) ;
2010-07-16 09:25:48 +00:00
res | = ast_custom_function_unregister ( & queueexists_function ) ;
2006-10-27 18:59:16 +00:00
res | = ast_custom_function_unregister ( & queuevar_function ) ;
2006-01-13 22:59:19 +00:00
res | = ast_custom_function_unregister ( & queuemembercount_function ) ;
2007-10-29 20:13:23 +00:00
res | = ast_custom_function_unregister ( & queuemembercount_dep ) ;
2006-01-13 22:59:19 +00:00
res | = ast_custom_function_unregister ( & queuememberlist_function ) ;
2006-04-26 22:04:42 +00:00
res | = ast_custom_function_unregister ( & queuewaitingcount_function ) ;
2007-11-06 22:05:56 +00:00
res | = ast_custom_function_unregister ( & queuememberpenalty_function ) ;
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
2010-04-22 18:07:02 +00:00
res | = ast_data_unregister ( NULL ) ;
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
if ( device_state_sub )
ast_event_unsubscribe ( device_state_sub ) ;
2005-10-18 22:52:21 +00:00
2009-11-03 21:16:14 +00:00
ast_extension_state_del ( 0 , extension_state_cb ) ;
2007-06-20 21:38:49 +00:00
if ( ( con = ast_context_find ( " app_queue_gosub_virtual_context " ) ) ) {
2008-06-16 20:43:46 +00:00
ast_context_remove_extension2 ( con , " s " , 1 , NULL , 0 ) ;
closes issue #11285, where an unload of a module that creates a dialplan context, causes a crash when you do a 'dialplan show' of that context. This is because the registrar string is defined in the module, and the stale pointer is traversed. The reporter offered a patch that would always strdup the registrar string, which is practical, but I preferred to destroy the created contexts in each module where one is created. That seemed more symmetric. There were only 6 place in asterisk where this is done: chan_sip, chan_iax2, chan_skinny, res_features, app_dial, and app_queue. The two apps destroyed the context, but left the contexts. All is fixed now and unloads should be dialplan friendly.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@89513 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-11-21 23:54:12 +00:00
ast_context_destroy ( con , " app_queue " ) ; /* leave no trace */
2007-06-20 21:38:49 +00:00
}
2008-03-28 00:12:52 +00:00
q_iter = ao2_iterator_init ( queues , 0 ) ;
2009-11-24 20:31:28 +00:00
while ( ( q = ao2_t_iterator_next ( & q_iter , " Iterate through queues " ) ) ) {
queues_t_unlink ( queues , q , " Remove queue from container due to unload " ) ;
queue_t_unref ( q , " Done with iterator " ) ;
2008-03-28 00:12:52 +00:00
}
2009-10-06 01:24:24 +00:00
ao2_iterator_destroy ( & q_iter ) ;
2007-09-20 21:21:28 +00:00
ao2_ref ( queues , - 1 ) ;
2008-05-03 03:57:42 +00:00
devicestate_tps = ast_taskprocessor_unreference ( devicestate_tps ) ;
2008-06-05 19:07:27 +00:00
ast_unload_realtime ( " queue_members " ) ;
2005-10-18 22:52:21 +00:00
return res ;
2002-09-02 23:15:40 +00:00
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2002-09-02 23:15:40 +00:00
{
int res ;
2007-06-20 21:38:49 +00:00
struct ast_context * con ;
2009-02-13 20:57:37 +00:00
struct ast_flags mask = { AST_FLAGS_ALL , } ;
2007-05-21 06:56:21 +00:00
2007-09-20 21:21:28 +00:00
queues = ao2_container_alloc ( MAX_QUEUE_BUCKETS , queue_hash_cb , queue_cmp_cb ) ;
2009-02-13 20:57:37 +00:00
use_weight = 0 ;
if ( reload_handler ( 0 , & mask , NULL ) )
2006-08-31 21:00:20 +00:00
return AST_MODULE_LOAD_DECLINE ;
2007-05-21 06:56:21 +00:00
(closes issue #6002)
Reported by: rizzo
Tested by: murf
Proposal of the changes to be made, and then an announcement of how they were accomplished:
http://lists.digium.com/pipermail/asterisk-dev/2008-February/032065.html
and:
http://lists.digium.com/pipermail/asterisk-dev/2008-March/032124.html
Here is a recap, file by file, of what I have done:
pbx/pbx_config.c
pbx/pbx_ael.c
All funcs that were passed a ptr to the context list, now will ALSO be passed a hashtab ptr to the same set.
Why? because (for the time being), the dialplan is stored in both, to facilitate a quick, low-cost move to
hash-tables to speed up dialplan processing. If it was deemed necessary to pass the context LIST, well, it
is just as necessary to have the TABLE available. This is because the list/table in question might not be
the global one, but temporary ones we would use to stage the dialplan on, and then swap into the global
position when things are ready.
We now have one external function for apps to use, "ast_context_find_or_create()" instead of the pre-existing
"find" and "create", as all existing usages used both in tandem anyway.
pbx_config, and pbx_ael, will stage the reloaded dialplan into local lists and tables, and
then call merge_contexts_and_delete, which will merge (now) existing contexts and
priorities from other registrars into this local set by copying them. Then, merge_contexts_and_delete will
lock down the contexts, swap the lists and tables, and unlock (real quick), and then
destroy the old dialplan.
chan_sip.c
chan_iax.c
chan_skinny.c
All the channel drivers that would add regcontexts now use the ast_context_find_or_create now.
chan_sip also includes a small fix to get rid of warnings about removing priorities that never got entered.
apps/app_meetme.c
apps/app_dial.c
apps/app_queue.c
All the apps that added a context/exten/priority were also modified to use ast_context_find_or_create instead.
include/asterisk/pbx.h
ast_context_create() is removed. Find_or_create_ is the new method.
ast_context_find_or_create() interface gets the hashtab added.
ast_merge_contexts_and_delete() gets the local hashtab arg added.
ast_wrlock_contexts_version() is added so you can detect if someone else got a writelock between your readlocking and writelocking.
ast_hashtab_compare_contexts was made public for use in pbx_config/pbx_ael
ast_hashtab_hash_contexts was in like fashion make public.
include/asterisk/pval.h
ast_compile_ael2() interface changed to include the local hashtab table ptr.
main/features.c
For the sake of the parking context, we use ast_context_find_or_create().
main/pbx.c
I changed all the "tree" names to "table" instead. That's because the original
implementation was based on binary trees. (had a free library). Then I moved
to hashtabs. Now, the names move forward too.
refcount field added to contexts, so you can keep track of how many modules
wanted this context to exist.
Some log messages that are warnings were inflated from LOG_NOTICE to LOG_WARNING.
Added some calls to ast_verb(3,...) for debug messages
Lots of little mods to ast_context_remove_extension2, which is now excersized in ways
it was not previously; one definite bug fixed.
find_or_create was upgraded to handle both local lists/tables as well as the globals.
context_merge() was added to do the per-context merging of the old/present contexts/extens/prios into the new/proposed local list/tables
ast_merge_contexts_and_delete() was heavily modified.
ast_add_extension2() was also upgraded to handle changes.
the context_destroy() code was re-engineered to handle the new way of doing things,
by exten/prio instead of by context.
res/ael/pval.c
res/ael/ael.tab.c
res/ael/ael.tab.h
res/ael/ael.y
res/ael/ael_lex.c
res/ael/ael.flex
utils/ael_main.c
utils/extconf.c
utils/conf2ael.c
utils/Makefile
Had to change the interface to ast_compile_ael2(), to include the hashtab ptr.
This ended up involving several external apps. The main gotcha was I had to
include lock.h and hashtab.h in several places.
As a side note, I tested this stuff pretty thoroughly, I replicated the problems
originally reported by Luigi, and made triply sure that reloads worked, and everything
worked thru "stop gracefully". I found a and fixed a few bugs as I was merging into
trunk, that did not appear in my tests of bug6002.
How's this for verbose commit messages?
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@106757 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-03-07 18:57:57 +00:00
con = ast_context_find_or_create ( NULL , NULL , " app_queue_gosub_virtual_context " , " app_queue " ) ;
2007-06-20 21:38:49 +00:00
if ( ! con )
ast_log ( LOG_ERROR , " Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create \n " ) ;
else
2008-12-29 18:04:52 +00:00
ast_add_extension2 ( con , 1 , " s " , 1 , NULL , NULL , " NoOp " , ast_strdup ( " " ) , ast_free_ptr , " app_queue " ) ;
2007-06-20 21:38:49 +00:00
2006-08-31 21:00:20 +00:00
if ( queue_persistent_members )
reload_queue_members ( ) ;
2007-05-21 06:56:21 +00:00
2010-04-22 18:07:02 +00:00
ast_data_register_multiple ( queue_data_providers , ARRAY_LEN ( queue_data_providers ) ) ;
2008-12-05 10:31:25 +00:00
ast_cli_register_multiple ( cli_queue , ARRAY_LEN ( cli_queue ) ) ;
2008-11-01 21:10:07 +00:00
res = ast_register_application_xml ( app , queue_exec ) ;
res | = ast_register_application_xml ( app_aqm , aqm_exec ) ;
res | = ast_register_application_xml ( app_rqm , rqm_exec ) ;
res | = ast_register_application_xml ( app_pqm , pqm_exec ) ;
res | = ast_register_application_xml ( app_upqm , upqm_exec ) ;
res | = ast_register_application_xml ( app_ql , ql_exec ) ;
2009-05-22 17:52:35 +00:00
res | = ast_manager_register_xml ( " Queues " , 0 , manager_queues_show ) ;
res | = ast_manager_register_xml ( " QueueStatus " , 0 , manager_queues_status ) ;
res | = ast_manager_register_xml ( " QueueSummary " , 0 , manager_queues_summary ) ;
res | = ast_manager_register_xml ( " QueueAdd " , EVENT_FLAG_AGENT , manager_add_queue_member ) ;
res | = ast_manager_register_xml ( " QueueRemove " , EVENT_FLAG_AGENT , manager_remove_queue_member ) ;
res | = ast_manager_register_xml ( " QueuePause " , EVENT_FLAG_AGENT , manager_pause_queue_member ) ;
res | = ast_manager_register_xml ( " QueueLog " , EVENT_FLAG_AGENT , manager_queue_log_custom ) ;
res | = ast_manager_register_xml ( " QueuePenalty " , EVENT_FLAG_AGENT , manager_queue_member_penalty ) ;
res | = ast_manager_register_xml ( " QueueRule " , 0 , manager_queue_rule_show ) ;
res | = ast_manager_register_xml ( " QueueReload " , 0 , manager_queue_reload ) ;
res | = ast_manager_register_xml ( " QueueReset " , 0 , manager_queue_reset ) ;
2006-10-27 18:59:16 +00:00
res | = ast_custom_function_register ( & queuevar_function ) ;
2010-07-16 09:25:48 +00:00
res | = ast_custom_function_register ( & queueexists_function ) ;
2006-01-13 22:59:19 +00:00
res | = ast_custom_function_register ( & queuemembercount_function ) ;
2007-10-29 20:13:23 +00:00
res | = ast_custom_function_register ( & queuemembercount_dep ) ;
2006-01-13 22:59:19 +00:00
res | = ast_custom_function_register ( & queuememberlist_function ) ;
2006-04-26 22:04:42 +00:00
res | = ast_custom_function_register ( & queuewaitingcount_function ) ;
2007-11-06 22:05:56 +00:00
res | = ast_custom_function_register ( & queuememberpenalty_function ) ;
2008-05-03 03:57:42 +00:00
if ( ! ( devicestate_tps = ast_taskprocessor_get ( " app_queue " , 0 ) ) ) {
ast_log ( LOG_WARNING , " devicestate taskprocessor reference failed - devicestate notifications will not occur \n " ) ;
}
2009-06-26 15:28:53 +00:00
/* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
if ( ! ( device_state_sub = ast_event_subscribe ( AST_EVENT_DEVICE_STATE , device_state_cb , " AppQueue Device state " , NULL , AST_EVENT_IE_END ) ) ) {
Merge major changes to the way device state is passed around Asterisk. The two
places that cared about device states were app_queue and the hint code in pbx.c.
The changes include converting it to use the Asterisk event system, as well as
other efficiency improvements.
* app_queue: This module used to register a callback into devicestate.c to
monitor device state changes. Now, it is just a subscriber to Asterisk
events with the type, device state.
* pbx.c hints: Previously, the device state processing thread in devicestate.c
would call ast_hint_state_changed() each time the state of a device changed.
Then, that code would go looking for all the hints that monitor that device,
and call their callbacks. All of this blocked the device state processing
thread. Now, the hint code is a subscriber of Asterisk events with the
type, device state. Furthermore, when this code receives a device state
change event, it queues it up to be processed by another thread so that it
doesn't block one of the event processing threads.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@66958 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2007-06-01 23:34:43 +00:00
res = - 1 ;
2008-06-10 15:12:17 +00:00
}
2009-11-03 21:16:14 +00:00
ast_extension_state_add ( NULL , NULL , extension_state_cb , NULL ) ;
2008-06-19 20:48:33 +00:00
ast_realtime_require_field ( " queue_members " , " paused " , RQ_INTEGER1 , 1 , " uniqueid " , RQ_UINTEGER2 , 5 , SENTINEL ) ;
2005-10-18 22:52:21 +00:00
2007-06-20 23:31:08 +00:00
return res ? AST_MODULE_LOAD_DECLINE : 0 ;
2002-09-02 23:15:40 +00:00
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2002-09-02 23:15:40 +00:00
{
2010-09-02 05:31:47 +00:00
struct ast_flags mask = { AST_FLAGS_ALL & ~ QUEUE_RESET_STATS , } ;
2008-06-05 19:07:27 +00:00
ast_unload_realtime ( " queue_members " ) ;
2009-02-13 20:57:37 +00:00
reload_handler ( 1 , & mask , NULL ) ;
2002-09-02 23:15:40 +00:00
return 0 ;
}
2011-08-08 20:28:20 +00:00
/* \brief Find a member by looking up queuename and interface.
* \ return Returns a member or NULL if member not found .
*/
static struct member * find_member_by_queuename_and_interface ( const char * queuename , const char * interface )
{
struct member * mem = NULL ;
struct call_queue * q ;
2011-12-08 20:55:19 +00:00
if ( ( q = find_load_queue_rt_friendly ( queuename ) ) ) {
2011-08-08 20:28:20 +00:00
ao2_lock ( q ) ;
mem = ao2_find ( q - > members , interface , OBJ_KEY ) ;
ao2_unlock ( q ) ;
queue_t_unref ( q , " Expiring temporary reference. " ) ;
}
return mem ;
}
2010-07-20 19:35:02 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_LOAD_ORDER , " True Call Queueing " ,
2006-08-21 02:11:39 +00:00
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
2010-07-20 19:35:02 +00:00
. load_pri = AST_MODPRI_DEVSTATE_CONSUMER ,
2010-09-02 05:27:53 +00:00
. nonoptreq = " res_monitor " ,
2006-08-21 02:11:39 +00:00
) ;
2006-04-14 14:08:19 +00:00