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
*/
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 <stdlib.h>
# include <errno.h>
# include <unistd.h>
# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include <sys/time.h>
# include <sys/signal.h>
# include <netinet/in.h>
2005-04-21 06:02:45 +00:00
# include "asterisk/lock.h"
# include "asterisk/file.h"
# include "asterisk/logger.h"
# include "asterisk/channel.h"
# include "asterisk/pbx.h"
# include "asterisk/options.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"
2004-02-12 22:11:02 +00:00
2006-05-09 07:27:03 +00:00
enum {
QUEUE_STRATEGY_RINGALL = 0 ,
QUEUE_STRATEGY_LEASTRECENT ,
QUEUE_STRATEGY_FEWESTCALLS ,
QUEUE_STRATEGY_RANDOM ,
QUEUE_STRATEGY_RRMEMORY
} ;
2003-07-27 01:55:22 +00:00
2003-07-30 16:10:51 +00:00
static struct strategy {
int strategy ;
char * name ;
} 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 " } ,
2003-07-30 16:10:51 +00:00
} ;
2002-09-02 23:15:40 +00:00
# define DEFAULT_RETRY 5
# define DEFAULT_TIMEOUT 15
2005-01-03 01:42:37 +00:00
# define RECHECK 1 /* Recheck every second to see we we're at the top yet */
2006-01-17 20:58:56 +00:00
# define MAX_PERIODIC_ANNOUNCEMENTS 10 /* The maximum periodic announcements we can have */
2002-09-02 23:15:40 +00:00
2005-01-03 01:42:37 +00:00
# define RES_OKAY 0 /* Action completed */
2004-07-28 02:55:22 +00:00
# define RES_EXISTS (-1) /* Entry already exists */
2005-01-03 01:42:37 +00:00
# define RES_OUTOFMEMORY (-2) /* Out of memory */
# define RES_NOSUCHQUEUE (-3) /* No such queue */
2004-07-28 02:55:22 +00:00
2002-09-02 23:15:40 +00:00
static char * app = " Queue " ;
static char * synopsis = " Queue a call for a call queue " ;
static char * descrip =
2006-10-27 18:59:16 +00:00
" Queue(queuename[|options[|URL][|announceoverride][|timeout][|AGI][|macro]): \n "
2002-09-02 23:15:40 +00:00
" Queues an incoming call in a particular call queue as defined in queues.conf. \n "
2005-11-07 22:01:22 +00:00
" This application will return to the dialplan if the queue does not exist, or \n "
" any of the join options cause the caller to not enter the queue. \n "
2002-09-02 23:15:40 +00:00
" The option string may contain zero or more of the following characters: \n "
" 'd' -- data-quality (modem) call (minimum delay). \n "
2004-07-28 19:25:14 +00:00
" 'h' -- allow callee to hang up by hitting *. \n "
2002-09-02 23:15:40 +00:00
" 'H' -- allow caller to hang up by hitting *. \n "
2005-01-03 01:42:37 +00:00
" 'n' -- no retries on the timeout; will exit this application and \n "
2006-07-14 05:42:06 +00:00
" go to the next step. \n "
2006-09-03 19:32:51 +00:00
" 'i' -- ignore call forward requests from queue members and do nothing \n "
" when they are requested. \n "
2004-05-09 19:13:43 +00:00
" 'r' -- ring instead of playing MOH \n "
2005-11-06 05:34:13 +00:00
" 't' -- allow the called user transfer the calling user \n "
" 'T' -- to allow the calling user to transfer the call. \n "
" 'w' -- allow the called user to write the conversation to disk via Monitor \n "
" 'W' -- allow the calling user to write the conversation to disk via Monitor \n "
2002-09-02 23:15:40 +00:00
" In addition to transferring the call, a call may be parked and then picked \n "
2002-12-11 00:15:13 +00:00
" up by another user. \n "
2004-02-27 04:38:30 +00:00
" The optional URL will be sent to the called party if the channel supports \n "
2004-03-13 06:00:41 +00:00
" it. \n "
2006-05-05 21:22:45 +00:00
" The optional AGI parameter will setup an AGI script to be executed on the \n "
" calling party's channel once they are connected to a queue member. \n "
2006-10-27 18:59:16 +00:00
" The optional macro parameter will run a macro on the \n "
" calling party's channel once they are connected to a queue member. \n "
2004-03-13 06:00:41 +00:00
" The timeout will cause the queue to fail out after a specified number of \n "
2005-03-28 20:48:24 +00:00
" seconds, checked between each queues.conf 'timeout' and 'retry' cycle. \n "
" This application sets the following channel variable upon completion: \n "
" QUEUESTATUS The status of the call as a text string, one of \n "
" TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL \n " ;
2002-09-02 23:15:40 +00:00
2003-06-27 23:07:59 +00:00
static char * app_aqm = " AddQueueMember " ;
static char * app_aqm_synopsis = " Dynamically adds queue members " ;
static char * app_aqm_descrip =
2005-11-08 04:48:00 +00:00
" AddQueueMember(queuename[|interface[|penalty[|options]]]): \n "
2004-04-26 06:57:15 +00:00
" Dynamically adds interface to an existing queue. \n "
" If the interface is already in the queue and there exists an n+101 priority \n "
" then it will then jump to this priority. Otherwise it will return an error \n "
2005-11-08 04:48:00 +00:00
" The option string may contain zero or more of the following characters: \n "
" 'j' -- jump to +101 priority when appropriate. \n "
" This application sets the following channel variable upon completion: \n "
" AQMSTATUS The status of the attempt to add a queue member as a \n "
" text string, one of \n "
" ADDED | MEMBERALREADY | NOSUCHQUEUE \n "
2003-06-27 23:07:59 +00:00
" Example: AddQueueMember(techsupport|SIP/3000) \n "
" " ;
static char * app_rqm = " RemoveQueueMember " ;
static char * app_rqm_synopsis = " Dynamically removes queue members " ;
static char * app_rqm_descrip =
2005-11-08 04:48:00 +00:00
" RemoveQueueMember(queuename[|interface[|options]]): \n "
2003-06-27 23:07:59 +00:00
" Dynamically removes interface to an existing queue \n "
2004-04-26 06:57:15 +00:00
" If the interface is NOT in the queue and there exists an n+101 priority \n "
" then it will then jump to this priority. Otherwise it will return an error \n "
2005-11-08 04:48:00 +00:00
" The option string may contain zero or more of the following characters: \n "
" 'j' -- jump to +101 priority when appropriate. \n "
" This application sets the following channel variable upon completion: \n "
" RQMSTATUS The status of the attempt to remove a queue member as a \n "
" text string, one of \n "
" REMOVED | NOTINQUEUE | NOSUCHQUEUE \n "
2003-06-27 23:07:59 +00:00
" Example: RemoveQueueMember(techsupport|SIP/3000) \n "
" " ;
2005-01-21 04:14:26 +00:00
static char * app_pqm = " PauseQueueMember " ;
static char * app_pqm_synopsis = " Pauses a queue member " ;
static char * app_pqm_descrip =
2005-11-08 04:48:00 +00:00
" PauseQueueMember([queuename]|interface[|options]): \n "
2005-01-21 04:14:26 +00:00
" Pauses (blocks calls for) a queue member. \n "
" The given interface will be paused in the given queue. This prevents \n "
" any calls from being sent from the queue to the interface until it is \n "
" unpaused with UnpauseQueueMember or the manager interface. If no \n "
" queuename is given, the interface is paused in every queue it is a \n "
" member of. If the interface is not in the named queue, or if no queue \n "
" is given and the interface is not in any queue, it will jump to \n "
2005-11-08 04:48:00 +00:00
" priority n+101, if it exists and the appropriate options are set. \n "
" The application will fail if the interface is not found and no extension \n "
" to jump to exists. \n "
" The option string may contain zero or more of the following characters: \n "
" 'j' -- jump to +101 priority when appropriate. \n "
" This application sets the following channel variable upon completion: \n "
" PQMSTATUS The status of the attempt to pause a queue member as a \n "
" text string, one of \n "
" PAUSED | NOTFOUND \n "
2005-01-21 04:14:26 +00:00
" Example: PauseQueueMember(|SIP/3000) \n " ;
static char * app_upqm = " UnpauseQueueMember " ;
static char * app_upqm_synopsis = " Unpauses a queue member " ;
static char * app_upqm_descrip =
2005-11-08 04:48:00 +00:00
" UnpauseQueueMember([queuename]|interface[|options]): \n "
2005-01-21 04:14:26 +00:00
" Unpauses (resumes calls to) a queue member. \n "
" This is the counterpart to PauseQueueMember and operates exactly the \n "
" same way, except it unpauses instead of pausing the given interface. \n "
2005-11-08 04:48:00 +00:00
" The option string may contain zero or more of the following characters: \n "
" 'j' -- jump to +101 priority when appropriate. \n "
" This application sets the following channel variable upon completion: \n "
" UPQMSTATUS The status of the attempt to unpause a queue \n "
" member as a text string, one of \n "
" UNPAUSED | NOTFOUND \n "
2005-01-21 04:14:26 +00:00
" Example: UnpauseQueueMember(|SIP/3000) \n " ;
2006-07-16 19:36:29 +00:00
static char * app_ql = " QueueLog " ;
static char * app_ql_synopsis = " Writes to the queue_log " ;
static char * app_ql_descrip =
" QueueLog(queuename|uniqueid|agent|event[|additionalinfo]): \n "
" Allows you to write your own events into the queue log \n "
" Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600) \n " ;
2005-11-06 15:09:47 +00:00
/*! \brief Persistent Members astdb family */
2004-12-06 05:54:16 +00:00
static const char * 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
2006-10-02 15:40:38 +00:00
/*! \brief queues.conf [general] option */
static int queue_keep_stats = 0 ;
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 */
static int autofill_default = 0 ;
2006-05-05 22:02:38 +00:00
/*! \brief queues.conf [general] option */
static int montype_default = 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 ,
} ;
2004-12-06 05:54:16 +00:00
2006-07-14 05:42:06 +00:00
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 " } ,
} ;
2004-12-19 21:44:24 +00:00
2005-11-06 15:09:47 +00:00
/*! \brief We define a custom "local user" structure because we
2002-09-02 23:15:40 +00:00
use it not only for keeping track of what is in use but
also for keeping track of who we ' re dialing . */
2006-02-14 21:50:35 +00:00
struct callattempt {
struct callattempt * q_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-10-26 22:25:43 +00:00
int oldstatus ;
2004-06-26 16:26:39 +00:00
time_t lastcall ;
2003-08-02 21:10:06 +00:00
struct member * member ;
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 {
2006-07-14 05:42:06 +00:00
struct call_queue * parent ; /*!< What queue is our parent */
char moh [ 80 ] ; /*!< Name of musiconhold to be used */
char announce [ 80 ] ; /*!< Announcement to play for member when call is answered */
char context [ AST_MAX_CONTEXT ] ; /*!< Context when user exits queue */
char digits [ AST_MAX_EXTENSION ] ; /*!< Digits entered while in queue */
int pos ; /*!< Where we are in the queue */
int prio ; /*!< Our priority */
int last_pos_said ; /*!< Last position we told the user */
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 max_penalty ; /*!< Limit the members that can take this call to this penalty or lower */
time_t start ; /*!< When we started holding */
time_t expire ; /*!< When this entry should expire (time out of queue) */
struct ast_channel * chan ; /*!< Our channel */
struct queue_ent * next ; /*!< The next queue entry */
2002-09-02 23:15:40 +00:00
} ;
struct member {
2006-07-14 05:42:06 +00:00
char interface [ 80 ] ; /*!< Technology/Location */
2006-09-20 05:59:53 +00:00
char membername [ 80 ] ; /*!< Member name to use in queue logs */
2006-07-14 05:42:06 +00:00
int penalty ; /*!< Are we a last resort? */
int calls ; /*!< Number of calls serviced by this member */
int dynamic ; /*!< Are we dynamically added? */
int status ; /*!< Status of queue member */
int paused ; /*!< Are we paused (not accepting calls)? */
time_t lastcall ; /*!< When last successful call was hungup */
unsigned int dead : 1 ; /*!< Used to detect members deleted in realtime */
unsigned int delme : 1 ; /*!< Flag to delete entry on reload */
struct member * next ; /*!< Next member */
2002-09-02 23:15:40 +00:00
} ;
2006-06-15 13:35:04 +00:00
struct member_interface {
2006-05-25 21:47:03 +00:00
char interface [ 80 ] ;
2006-06-15 13:35:04 +00:00
AST_LIST_ENTRY ( member_interface ) list ; /*!< Next call queue */
2006-05-25 21:47:03 +00:00
} ;
2006-06-15 13:35:04 +00:00
static AST_LIST_HEAD_STATIC ( interfaces , member_interface ) ;
2006-05-25 21:47:03 +00:00
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 QUEUE_EMPTY_NORMAL 1
# define QUEUE_EMPTY_STRICT 2
2006-11-13 18:23:55 +00:00
# define QUEUE_EMPTY_LOOSE 3
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
2006-06-15 13:35:04 +00:00
struct call_queue {
2005-03-28 20:48:24 +00:00
ast_mutex_t lock ;
2006-07-14 05:42:06 +00:00
char name [ 80 ] ; /*!< Name */
char moh [ 80 ] ; /*!< Music On Hold class to be used */
char announce [ 80 ] ; /*!< Announcement to play when call is answered */
char context [ AST_MAX_CONTEXT ] ; /*!< Exit context */
2006-01-13 17:39:56 +00:00
unsigned int monjoin : 1 ;
unsigned int dead : 1 ;
unsigned int joinempty : 2 ;
2006-06-22 15:43:02 +00:00
unsigned int eventwhencalled : 2 ;
2006-01-13 17:39:56 +00:00
unsigned int leavewhenempty : 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 ;
unsigned int strategy : 3 ;
unsigned int maskmemberstatus : 1 ;
unsigned int realtime : 1 ;
2006-07-14 05:42:06 +00:00
int announcefrequency ; /*!< How often to announce their position */
int periodicannouncefrequency ; /*!< How often to play periodic announcement */
int roundingseconds ; /*!< How many seconds do we round to? */
int holdtime ; /*!< Current avg holdtime, based on recursive boxcar filter */
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 */
2006-10-27 18:59:16 +00:00
char membermacro [ 32 ] ; /*!< Macro to run upon member connection */
2006-07-14 05:42:06 +00:00
char sound_next [ 80 ] ; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */
char sound_thereare [ 80 ] ; /*!< Sound file: "There are currently" (def. queue-thereare) */
char sound_calls [ 80 ] ; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/
char sound_holdtime [ 80 ] ; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */
char sound_minutes [ 80 ] ; /*!< Sound file: "minutes." (def. queue-minutes) */
char sound_lessthan [ 80 ] ; /*!< Sound file: "less-than" (def. queue-lessthan) */
char sound_seconds [ 80 ] ; /*!< Sound file: "seconds." (def. queue-seconds) */
char sound_thanks [ 80 ] ; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */
char sound_reporthold [ 80 ] ; /*!< Sound file: "Hold time" (def. queue-reporthold) */
char sound_periodicannounce [ MAX_PERIODIC_ANNOUNCEMENTS ] [ 80 ] ; /*!< Sound files: Custom announce, no default */
int count ; /*!< How many entries */
int maxlen ; /*!< Max number of entries */
int wrapuptime ; /*!< Wrapup Time */
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 */
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
2006-07-14 05:42:06 +00:00
struct member * members ; /*!< Head of the list of members */
struct queue_ent * head ; /*!< Head of the list of callers */
AST_LIST_ENTRY ( call_queue ) list ; /*!< Next call queue */
2002-09-02 23:15:40 +00:00
} ;
2006-06-15 13:35:04 +00:00
static AST_LIST_HEAD_STATIC ( queues , call_queue ) ;
2002-09-02 23:15:40 +00:00
2006-01-13 17:39:56 +00:00
static int set_member_paused ( char * queuename , char * interface , int paused ) ;
2005-03-28 20:48:24 +00:00
static void set_queue_result ( struct ast_channel * chan , enum queue_result res )
{
int i ;
for ( i = 0 ; i < sizeof ( queue_results ) / sizeof ( queue_results [ 0 ] ) ; i + + ) {
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
2003-07-30 16:10:51 +00:00
static char * int2strat ( int strategy )
{
int x ;
2006-06-14 23:20:08 +00:00
for ( x = 0 ; x < sizeof ( strategies ) / sizeof ( strategies [ 0 ] ) ; 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
for ( x = 0 ; x < sizeof ( strategies ) / sizeof ( strategies [ 0 ] ) ; 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
2006-10-27 18:59:16 +00:00
static void set_queue_variables ( struct queue_ent * qe )
{
char interfacevar [ 256 ] = " " ;
float sl = 0 ;
if ( qe - > parent - > setqueuevar ) {
sl = 0 ;
if ( qe - > parent - > callscompleted > 0 )
sl = 100 * ( ( float ) qe - > parent - > callscompletedinsl / ( float ) qe - > parent - > callscompleted ) ;
snprintf ( interfacevar , sizeof ( interfacevar ) ,
" QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f " ,
qe - > parent - > maxlen , int2strat ( qe - > parent - > strategy ) , qe - > parent - > count , qe - > parent - > holdtime , qe - > parent - > callscompleted ,
qe - > parent - > callsabandoned , qe - > parent - > servicelevel , sl ) ;
pbx_builtin_setvar ( qe - > chan , interfacevar ) ;
}
}
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 ;
new - > parent = q ;
new - > pos = + + ( * pos ) ;
new - > opos = * pos ;
}
2005-03-28 20:48:24 +00:00
enum queue_member_status {
QUEUE_NO_MEMBERS ,
QUEUE_NO_REACHABLE_MEMBERS ,
2006-11-13 18:23:55 +00:00
QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS ,
2005-03-28 20:48:24 +00:00
QUEUE_NORMAL
} ;
2006-09-28 15:32:48 +00:00
static enum queue_member_status get_member_status ( struct call_queue * q , int max_penalty )
2004-10-26 22:25:43 +00:00
{
struct member * member ;
2005-03-28 20:48:24 +00:00
enum queue_member_status result = QUEUE_NO_MEMBERS ;
2006-09-28 15:32:48 +00:00
ast_mutex_lock ( & q - > lock ) ;
2005-03-28 20:48:24 +00:00
for ( member = q - > members ; member ; member = member - > next ) {
2006-01-06 03:40:12 +00:00
if ( max_penalty & & ( member - > penalty > max_penalty ) )
continue ;
2005-03-28 20:48:24 +00:00
switch ( member - > status ) {
2004-11-16 01:24:31 +00:00
case AST_DEVICE_INVALID :
2005-03-28 20:48:24 +00:00
/* nothing to do */
break ;
case AST_DEVICE_UNAVAILABLE :
2006-11-13 18:23:55 +00:00
if ( result ! = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS )
result = QUEUE_NO_REACHABLE_MEMBERS ;
2004-10-26 22:25:43 +00:00
break ;
default :
2006-11-13 18:23:55 +00:00
if ( member - > paused ) {
result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS ;
} else {
ast_mutex_unlock ( & q - > lock ) ;
return QUEUE_NORMAL ;
}
break ;
2004-10-26 22:25:43 +00:00
}
}
2005-03-28 20:48:24 +00:00
2006-09-28 15:32:48 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2005-03-28 20:48:24 +00:00
return result ;
2004-10-26 22:25:43 +00:00
}
2004-11-13 22:44:33 +00:00
struct statechange {
int state ;
char dev [ 0 ] ;
} ;
static void * changethread ( void * data )
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2004-11-13 22:44:33 +00:00
struct statechange * sc = data ;
struct member * cur ;
2006-06-15 13:35:04 +00:00
struct member_interface * curint ;
2004-11-13 22:44:33 +00:00
char * loc ;
2005-01-18 20:43:53 +00:00
char * technology ;
2005-01-03 01:42:37 +00:00
2005-01-18 20:43:53 +00:00
technology = ast_strdupa ( sc - > dev ) ;
loc = strchr ( technology , ' / ' ) ;
2004-11-13 22:44:33 +00:00
if ( loc ) {
2006-06-14 22:35:49 +00:00
* loc + + = ' \0 ' ;
2004-11-13 22:44:33 +00:00
} else {
free ( sc ) ;
return NULL ;
}
2006-05-25 21:47:03 +00:00
AST_LIST_LOCK ( & interfaces ) ;
AST_LIST_TRAVERSE ( & interfaces , curint , list ) {
2006-09-28 16:54:01 +00:00
char * interface ;
char * slash_pos ;
interface = ast_strdupa ( curint - > interface ) ;
if ( ( slash_pos = strchr ( interface , ' / ' ) ) )
if ( ( slash_pos = strchr ( slash_pos + 1 , ' / ' ) ) )
* slash_pos = ' \0 ' ;
if ( ! strcasecmp ( interface , sc - > dev ) )
2006-07-14 05:42:06 +00:00
break ;
2006-05-25 21:47:03 +00:00
}
AST_LIST_UNLOCK ( & interfaces ) ;
2006-06-14 22:35:49 +00:00
if ( ! curint ) {
2006-05-25 21:47:03 +00:00
if ( option_debug )
2006-06-14 22:35:49 +00:00
ast_log ( LOG_DEBUG , " Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue. \n " , technology , loc , sc - > state , devstate2str ( sc - > state ) ) ;
free ( sc ) ;
return NULL ;
2006-07-14 05:42:06 +00:00
}
2006-06-14 22:35:49 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Device '%s/%s' changed to state '%d' (%s) \n " , technology , loc , sc - > state , devstate2str ( sc - > state ) ) ;
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
ast_mutex_lock ( & q - > lock ) ;
for ( cur = q - > members ; cur ; cur = cur - > next ) {
2006-09-28 16:54:01 +00:00
char * interface ;
char * slash_pos ;
interface = ast_strdupa ( cur - > interface ) ;
if ( ( slash_pos = strchr ( interface , ' / ' ) ) )
if ( ( slash_pos = strchr ( slash_pos + 1 , ' / ' ) ) )
* slash_pos = ' \0 ' ;
if ( strcasecmp ( sc - > dev , interface ) )
2006-06-14 22:35:49 +00:00
continue ;
if ( cur - > status ! = sc - > state ) {
cur - > status = sc - > state ;
if ( q - > maskmemberstatus )
continue ;
manager_event ( EVENT_FLAG_AGENT , " QueueMemberStatus " ,
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 "
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 " ,
2006-09-20 05:59:53 +00:00
q - > name , cur - > interface , cur - > membername , cur - > dynamic ? " dynamic " : " static " ,
2006-07-14 05:42:06 +00:00
cur - > penalty , cur - > calls , ( int ) cur - > lastcall , cur - > status , cur - > paused ) ;
2004-11-13 22:44:33 +00:00
}
}
2006-06-14 22:35:49 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2004-11-13 22:44:33 +00:00
}
2006-06-14 22:35:49 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2004-11-13 22:44:33 +00:00
return NULL ;
}
static int statechange_queue ( const char * dev , int state , void * ign )
{
/* Avoid potential for deadlocks by spawning a new thread to handle
the event */
struct statechange * sc ;
pthread_t t ;
pthread_attr_t attr ;
2006-06-14 22:35:49 +00:00
if ( ! ( sc = ast_calloc ( 1 , sizeof ( * sc ) + strlen ( dev ) + 1 ) ) )
return 0 ;
sc - > state = state ;
strcpy ( sc - > dev , dev ) ;
pthread_attr_init ( & attr ) ;
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED ) ;
2006-10-04 19:51:38 +00:00
if ( ast_pthread_create_background ( & t , & attr , changethread , sc ) ) {
2006-06-14 22:35:49 +00:00
ast_log ( LOG_WARNING , " Failed to create update thread! \n " ) ;
free ( sc ) ;
2004-11-13 22:44:33 +00:00
}
2006-06-14 22:35:49 +00:00
2004-11-13 22:44:33 +00:00
return 0 ;
}
2006-09-20 20:40:39 +00:00
static struct member * create_queue_member ( char * interface , const char * membername , int penalty , int paused )
2005-06-02 21:42:49 +00:00
{
struct member * cur ;
2006-01-13 03:25:23 +00:00
if ( ( cur = ast_calloc ( 1 , sizeof ( * cur ) ) ) ) {
2005-06-02 21:42:49 +00:00
cur - > penalty = penalty ;
cur - > paused = paused ;
ast_copy_string ( cur - > interface , interface , sizeof ( cur - > interface ) ) ;
2006-09-20 05:59:53 +00:00
ast_copy_string ( cur - > membername , membername , sizeof ( cur - > membername ) ) ;
2005-06-02 21:42:49 +00:00
if ( ! strchr ( cur - > interface , ' / ' ) )
ast_log ( LOG_WARNING , " No location at interface '%s' \n " , interface ) ;
cur - > status = ast_device_state ( interface ) ;
}
return cur ;
}
2006-06-15 13:35:04 +00:00
static struct call_queue * alloc_queue ( const char * queuename )
2005-06-02 21:42:49 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2005-06-02 21:42:49 +00:00
2006-01-13 03:25:23 +00:00
if ( ( q = ast_calloc ( 1 , sizeof ( * q ) ) ) ) {
2005-06-02 21:42:49 +00:00
ast_mutex_init ( & q - > lock ) ;
ast_copy_string ( q - > name , queuename , sizeof ( q - > name ) ) ;
}
return q ;
}
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 ;
2006-06-14 23:20:08 +00:00
2005-06-02 21:42:49 +00:00
q - > dead = 0 ;
q - > retry = DEFAULT_RETRY ;
q - > timeout = - 1 ;
q - > maxlen = 0 ;
q - > announcefrequency = 0 ;
q - > announceholdtime = 0 ;
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 ;
2006-10-27 18:59:16 +00:00
q - > membermacro [ 0 ] = ' \0 ' ;
2005-06-02 21:42:49 +00:00
q - > moh [ 0 ] = ' \0 ' ;
q - > announce [ 0 ] = ' \0 ' ;
q - > context [ 0 ] = ' \0 ' ;
q - > monfmt [ 0 ] = ' \0 ' ;
2005-07-31 22:07:58 +00:00
q - > periodicannouncefrequency = 0 ;
2005-06-02 21:42:49 +00:00
ast_copy_string ( q - > sound_next , " queue-youarenext " , sizeof ( q - > sound_next ) ) ;
ast_copy_string ( q - > sound_thereare , " queue-thereare " , sizeof ( q - > sound_thereare ) ) ;
ast_copy_string ( q - > sound_calls , " queue-callswaiting " , sizeof ( q - > sound_calls ) ) ;
ast_copy_string ( q - > sound_holdtime , " queue-holdtime " , sizeof ( q - > sound_holdtime ) ) ;
ast_copy_string ( q - > sound_minutes , " queue-minutes " , sizeof ( q - > sound_minutes ) ) ;
ast_copy_string ( q - > sound_seconds , " queue-seconds " , sizeof ( q - > sound_seconds ) ) ;
ast_copy_string ( q - > sound_thanks , " queue-thankyou " , sizeof ( q - > sound_thanks ) ) ;
ast_copy_string ( q - > sound_lessthan , " queue-less-than " , sizeof ( q - > sound_lessthan ) ) ;
ast_copy_string ( q - > sound_reporthold , " queue-reporthold " , sizeof ( q - > sound_reporthold ) ) ;
2006-01-17 20:58:56 +00:00
ast_copy_string ( q - > sound_periodicannounce [ 0 ] , " queue-periodic-announce " , sizeof ( q - > sound_periodicannounce [ 0 ] ) ) ;
2006-06-14 23:20:08 +00:00
for ( i = 1 ; i < MAX_PERIODIC_ANNOUNCEMENTS ; i + + ) {
2006-01-17 21:10:38 +00:00
q - > sound_periodicannounce [ i ] [ 0 ] = ' \0 ' ;
2006-01-17 20:58:56 +00:00
}
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 ;
q - > wrapuptime = 0 ;
}
2006-07-14 05:42:06 +00:00
static int add_to_interfaces ( char * interface )
2006-05-25 21:47:03 +00:00
{
2006-06-15 13:35:04 +00:00
struct member_interface * curint ;
2006-05-25 21:47:03 +00:00
AST_LIST_LOCK ( & interfaces ) ;
AST_LIST_TRAVERSE ( & interfaces , curint , list ) {
if ( ! strcasecmp ( curint - > interface , interface ) )
2006-07-14 05:42:06 +00:00
break ;
2006-05-25 21:47:03 +00:00
}
2006-06-14 22:35:49 +00:00
if ( curint ) {
AST_LIST_UNLOCK ( & interfaces ) ;
return 0 ;
}
2006-05-25 21:47:03 +00:00
2006-06-14 22:35:49 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Adding %s to the list of interfaces that make up all of our queue members. \n " , interface ) ;
if ( ( curint = ast_calloc ( 1 , sizeof ( * curint ) ) ) ) {
ast_copy_string ( curint - > interface , interface , sizeof ( curint - > interface ) ) ;
AST_LIST_INSERT_HEAD ( & interfaces , curint , list ) ;
2006-05-25 21:47:03 +00:00
}
AST_LIST_UNLOCK ( & interfaces ) ;
2006-06-14 22:35:49 +00:00
return 0 ;
2006-05-25 21:47:03 +00:00
}
static int interface_exists_global ( char * interface )
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2006-05-25 21:47:03 +00:00
struct member * mem ;
int ret = 0 ;
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
ast_mutex_lock ( & q - > lock ) ;
2006-06-14 22:35:49 +00:00
for ( mem = q - > members ; mem & & ! ret ; mem = mem - > next ) {
if ( ! strcasecmp ( interface , mem - > interface ) )
2006-05-25 21:47:03 +00:00
ret = 1 ;
2006-06-14 22:35:49 +00:00
}
2006-05-25 21:47:03 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2006-06-14 22:35:49 +00:00
if ( ret )
break ;
2006-05-25 21:47:03 +00:00
}
AST_LIST_UNLOCK ( & queues ) ;
return ret ;
}
static int remove_from_interfaces ( char * interface )
{
2006-06-15 13:35:04 +00:00
struct member_interface * curint ;
2006-05-25 21:47:03 +00:00
AST_LIST_LOCK ( & interfaces ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & interfaces , curint , list ) {
2006-06-14 22:35:49 +00:00
if ( ! strcasecmp ( curint - > interface , interface ) ) {
if ( ! interface_exists_global ( interface ) ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " Removing %s from the list of interfaces that make up all of our queue members. \n " , interface ) ;
AST_LIST_REMOVE_CURRENT ( & interfaces , list ) ;
free ( curint ) ;
}
break ;
2006-05-25 21:47:03 +00:00
}
}
AST_LIST_TRAVERSE_SAFE_END ;
AST_LIST_UNLOCK ( & interfaces ) ;
2006-07-14 05:42:06 +00:00
return 0 ;
2006-05-25 21:47:03 +00:00
}
static void clear_and_free_interfaces ( void )
{
2006-06-15 13:35:04 +00:00
struct member_interface * curint ;
2006-05-25 21:47:03 +00:00
AST_LIST_LOCK ( & interfaces ) ;
2006-06-14 22:35:49 +00:00
while ( ( curint = AST_LIST_REMOVE_HEAD ( & interfaces , list ) ) )
2006-05-25 21:47:03 +00:00
free ( curint ) ;
AST_LIST_UNLOCK ( & interfaces ) ;
}
2005-11-06 15:09:47 +00:00
/*! \brief Configure a queue parameter.
\ par
2005-06-02 21:42:49 +00:00
For error reporting , line number is passed for . conf static configuration .
For Realtime queues , linenum is - 1.
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 . */
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 " ) ) {
2005-06-02 21:42:49 +00:00
ast_copy_string ( q - > moh , val , sizeof ( q - > moh ) ) ;
} else if ( ! strcasecmp ( param , " announce " ) ) {
ast_copy_string ( q - > announce , val , sizeof ( q - > announce ) ) ;
} else if ( ! strcasecmp ( param , " context " ) ) {
ast_copy_string ( q - > context , val , sizeof ( q - > context ) ) ;
} 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-join " ) ) {
q - > monjoin = ast_true ( val ) ;
} 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 " ) ) {
ast_copy_string ( q - > membermacro , val , sizeof ( q - > membermacro ) ) ;
2005-06-02 21:42:49 +00:00
} else if ( ! strcasecmp ( param , " queue-youarenext " ) ) {
ast_copy_string ( q - > sound_next , val , sizeof ( q - > sound_next ) ) ;
} else if ( ! strcasecmp ( param , " queue-thereare " ) ) {
ast_copy_string ( q - > sound_thereare , val , sizeof ( q - > sound_thereare ) ) ;
} else if ( ! strcasecmp ( param , " queue-callswaiting " ) ) {
ast_copy_string ( q - > sound_calls , val , sizeof ( q - > sound_calls ) ) ;
} else if ( ! strcasecmp ( param , " queue-holdtime " ) ) {
ast_copy_string ( q - > sound_holdtime , val , sizeof ( q - > sound_holdtime ) ) ;
} else if ( ! strcasecmp ( param , " queue-minutes " ) ) {
ast_copy_string ( q - > sound_minutes , val , sizeof ( q - > sound_minutes ) ) ;
} else if ( ! strcasecmp ( param , " queue-seconds " ) ) {
ast_copy_string ( q - > sound_seconds , val , sizeof ( q - > sound_seconds ) ) ;
} else if ( ! strcasecmp ( param , " queue-lessthan " ) ) {
ast_copy_string ( q - > sound_lessthan , val , sizeof ( q - > sound_lessthan ) ) ;
} else if ( ! strcasecmp ( param , " queue-thankyou " ) ) {
ast_copy_string ( q - > sound_thanks , val , sizeof ( q - > sound_thanks ) ) ;
} else if ( ! strcasecmp ( param , " queue-reporthold " ) ) {
ast_copy_string ( q - > sound_reporthold , val , sizeof ( q - > sound_reporthold ) ) ;
} else if ( ! strcasecmp ( param , " announce-frequency " ) ) {
q - > announcefrequency = atoi ( val ) ;
} else if ( ! strcasecmp ( param , " announce-round-seconds " ) ) {
q - > roundingseconds = atoi ( val ) ;
if ( q - > roundingseconds > 60 | | q - > roundingseconds < 0 ) {
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 ;
2006-01-17 20:58:56 +00:00
} else if ( ! strcasecmp ( param , " periodic-announce " ) ) {
2006-10-25 14:44:50 +00:00
if ( strchr ( val , ' | ' ) ) {
char * s , * buf = ast_strdupa ( val ) ;
unsigned int i = 0 ;
while ( ( s = strsep ( & buf , " | " ) ) ) {
ast_copy_string ( q - > sound_periodicannounce [ i ] , s , sizeof ( q - > sound_periodicannounce [ i ] ) ) ;
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
}
} else {
2006-10-25 14:44:50 +00:00
ast_copy_string ( q - > sound_periodicannounce [ 0 ] , val , sizeof ( q - > sound_periodicannounce [ 0 ] ) ) ;
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 ) ;
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 ) ;
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 " ) ) {
q - > autopause = 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 " ) ) {
q - > strategy = strat2int ( val ) ;
if ( q - > strategy < 0 ) {
ast_log ( LOG_WARNING , " '%s' isn't a valid strategy for queue '%s', using ringall instead \n " ,
val , q - > name ) ;
2006-05-09 07:27:03 +00:00
q - > strategy = QUEUE_STRATEGY_RINGALL ;
2005-06-02 21:42:49 +00:00
}
} else if ( ! strcasecmp ( param , " joinempty " ) ) {
2006-11-13 18:23:55 +00:00
if ( ! strcasecmp ( val , " loose " ) )
q - > joinempty = QUEUE_EMPTY_LOOSE ;
else if ( ! strcasecmp ( val , " strict " ) )
2005-06-02 21:42:49 +00:00
q - > joinempty = QUEUE_EMPTY_STRICT ;
else if ( ast_true ( val ) )
q - > joinempty = QUEUE_EMPTY_NORMAL ;
else
q - > joinempty = 0 ;
} else if ( ! strcasecmp ( param , " leavewhenempty " ) ) {
2006-11-13 18:23:55 +00:00
if ( ! strcasecmp ( val , " loose " ) )
q - > leavewhenempty = QUEUE_EMPTY_LOOSE ;
else if ( ! strcasecmp ( val , " strict " ) )
2005-06-02 21:42:49 +00:00
q - > leavewhenempty = QUEUE_EMPTY_STRICT ;
else if ( ast_true ( val ) )
q - > leavewhenempty = QUEUE_EMPTY_NORMAL ;
else
q - > leavewhenempty = 0 ;
} 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 {
q - > eventwhencalled = ast_true ( val ) ;
}
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 ) ;
if ( q - > weight )
use_weight + + ;
/* With Realtime queues, if the last queue using weights is deleted in realtime,
we will not see any effect on use_weight until next reload . */
} else if ( ! strcasecmp ( param , " timeoutrestart " ) ) {
q - > timeoutrestart = ast_true ( val ) ;
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 ) ;
}
}
}
2006-09-20 20:40:39 +00:00
static void rt_handle_member_record ( struct call_queue * q , char * interface , const char * membername , const char * penalty_str )
2005-06-02 21:42:49 +00:00
{
struct member * m , * prev_m ;
int penalty = 0 ;
2006-06-14 23:20:08 +00:00
if ( penalty_str ) {
2005-06-02 21:42:49 +00:00
penalty = atoi ( penalty_str ) ;
2006-06-14 23:20:08 +00:00
if ( penalty < 0 )
2005-06-02 21:42:49 +00:00
penalty = 0 ;
}
/* Find the member, or the place to put a new one. */
2006-06-14 23:20:08 +00:00
for ( m = q - > members , prev_m = NULL ;
2006-07-14 05:42:06 +00:00
m & & strcmp ( m - > interface , interface ) ;
prev_m = m , m = m - > next ) ;
2005-06-02 21:42:49 +00:00
/* Create a new one if not found, else update penalty */
if ( ! m ) {
2006-09-20 05:59:53 +00:00
if ( ( m = create_queue_member ( interface , membername , penalty , 0 ) ) ) {
2005-06-02 21:42:49 +00:00
m - > dead = 0 ;
2006-05-25 21:47:03 +00:00
add_to_interfaces ( interface ) ;
2005-06-02 21:42:49 +00:00
if ( prev_m ) {
prev_m - > next = m ;
} else {
q - > members = m ;
}
}
} else {
m - > dead = 0 ; /* Do not delete this one. */
m - > penalty = penalty ;
}
}
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 */
struct member * curm , * next , * prev = NULL ;
for ( curm = q - > members ; curm ; curm = next ) {
next = curm - > next ;
if ( all | | ! curm - > dynamic ) {
if ( prev )
prev - > next = next ;
else
q - > members = next ;
2006-05-25 21:47:03 +00:00
remove_from_interfaces ( curm - > interface ) ;
2006-01-22 19:09:50 +00:00
free ( curm ) ;
2006-07-14 05:42:06 +00:00
} else
2006-01-22 19:09:50 +00:00
prev = curm ;
}
}
2006-06-15 13:35:04 +00:00
static void destroy_queue ( struct call_queue * q )
2006-01-22 19:09:50 +00:00
{
free_members ( q , 1 ) ;
ast_mutex_destroy ( & q - > lock ) ;
free ( q ) ;
}
2005-06-02 21:42:49 +00:00
2005-11-06 15:09:47 +00:00
/*!\brief Reload a single queue via realtime.
\ return Return the queue , or NULL if it doesn ' t exist .
2006-01-21 20:32:17 +00:00
\ note Should be called with the global qlock locked . */
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 ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2005-06-02 21:42:49 +00:00
struct member * m , * prev_m , * next_m ;
2006-06-21 18:39:56 +00:00
char * interface = NULL ;
2005-06-02 21:42:49 +00:00
char * tmp , * tmp_name ;
char tmpbuf [ 64 ] ; /* Must be longer than the longest queue param name. */
/* Find the queue in the in-core list (we will create a new one if not found). */
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE ( & queues , q , list ) {
2006-06-14 23:20:08 +00:00
if ( ! strcasecmp ( q - > name , queuename ) )
2005-06-02 21:42:49 +00:00
break ;
}
/* Static queues override realtime. */
if ( q ) {
ast_mutex_lock ( & q - > lock ) ;
if ( ! q - > realtime ) {
if ( q - > dead ) {
ast_mutex_unlock ( & q - > lock ) ;
return NULL ;
} else {
2006-01-21 20:32:17 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2005-06-02 21:42:49 +00:00
return q ;
}
}
2005-09-30 14:01:56 +00:00
} else if ( ! member_config )
/* Not found in the list, and it's not realtime ... */
return NULL ;
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 . */
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " 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). */
if ( ! q - > count ) {
/* Delete. */
2006-01-22 17:28:42 +00:00
AST_LIST_REMOVE ( & queues , q , list ) ;
2005-06-02 21:42:49 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2006-01-22 19:09:50 +00:00
destroy_queue ( q ) ;
2005-06-02 21:42:49 +00:00
} else
ast_mutex_unlock ( & q - > lock ) ;
}
return NULL ;
}
/* Create a new queue if an in-core entry does not exist yet. */
if ( ! q ) {
2006-01-13 03:25:23 +00:00
if ( ! ( q = alloc_queue ( queuename ) ) )
2005-06-02 21:42:49 +00:00
return NULL ;
ast_mutex_lock ( & q - > lock ) ;
clear_queue ( q ) ;
q - > realtime = 1 ;
2006-01-22 17:28:42 +00:00
AST_LIST_INSERT_HEAD ( & queues , q , list ) ;
2005-06-02 21:42:49 +00:00
}
init_queue ( q ) ; /* Ensure defaults for all parameters not set explicitly. */
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 ;
tmp = tmp_name ;
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 ;
queue_set_param ( q , tmp_name , v - > value , - 1 , 0 ) ;
}
2005-12-05 00:11:00 +00:00
/* Temporarily set non-dynamic members dead so we can detect deleted ones. */
2006-06-14 23:20:08 +00:00
for ( m = q - > members ; m ; m = m - > next ) {
2005-12-05 00:11:00 +00:00
if ( ! m - > dynamic )
m - > dead = 1 ;
2005-06-02 21:42:49 +00:00
}
2006-09-20 05:59:53 +00:00
while ( ( interface = ast_category_browse ( member_config , interface ) ) ) {
rt_handle_member_record ( q , interface ,
2006-09-20 19:46:18 +00:00
S_OR ( ast_variable_retrieve ( member_config , interface , " membername " ) , interface ) ,
2006-09-20 05:59:53 +00:00
ast_variable_retrieve ( member_config , interface , " penalty " ) ) ;
}
2005-06-02 21:42:49 +00:00
/* Delete all realtime members that have been deleted in DB. */
m = q - > members ;
prev_m = NULL ;
while ( m ) {
next_m = m - > next ;
if ( m - > dead ) {
if ( prev_m ) {
prev_m - > next = next_m ;
} else {
q - > members = next_m ;
}
2006-05-25 21:47:03 +00:00
remove_from_interfaces ( m - > interface ) ;
2005-06-02 21:42:49 +00:00
free ( m ) ;
} else {
prev_m = m ;
}
m = next_m ;
}
2006-01-21 20:32:17 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2005-06-02 21:42:49 +00:00
return q ;
}
2006-06-15 13:35:04 +00:00
static struct call_queue * load_realtime_queue ( 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 ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2006-01-21 20:32:17 +00:00
/* Find the queue in the in-core list first. */
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
2006-01-21 20:32:17 +00:00
if ( ! strcasecmp ( q - > name , queuename ) ) {
break ;
}
}
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2006-01-21 20:32:17 +00:00
2006-03-14 18:30:52 +00:00
if ( ! q | | q - > realtime ) {
2006-01-21 20:32:17 +00:00
/*! \note Load from realtime before taking the global qlock, to avoid blocking all
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 . */
queue_vars = ast_load_realtime ( " queues " , " name " , queuename , NULL ) ;
if ( queue_vars ) {
member_config = ast_load_realtime_multientry ( " queue_members " , " interface LIKE " , " % " , " queue_name " , queuename , NULL ) ;
if ( ! member_config ) {
ast_log ( LOG_ERROR , " no queue_members defined in your config (extconfig.conf). \n " ) ;
return NULL ;
}
}
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
2006-01-21 20:32:17 +00:00
q = find_queue_by_name_rt ( queuename , queue_vars , member_config ) ;
if ( member_config )
ast_config_destroy ( member_config ) ;
if ( queue_vars )
ast_variables_destroy ( queue_vars ) ;
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2006-01-21 20:32:17 +00:00
}
return q ;
}
static int join_queue ( char * queuename , struct queue_ent * qe , enum queue_result * reason )
{
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
enum queue_member_status stat ;
2006-06-14 23:20:08 +00:00
if ( ! ( q = load_realtime_queue ( queuename ) ) )
2006-01-21 20:32:17 +00:00
return res ;
2004-06-23 20:05:18 +00:00
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
2006-01-21 20:32:17 +00:00
ast_mutex_lock ( & q - > lock ) ;
2005-06-02 21:42:49 +00:00
/* This is our one */
2006-01-06 03:40:12 +00:00
stat = get_member_status ( q , qe - > max_penalty ) ;
2005-06-02 21:42:49 +00:00
if ( ! q - > joinempty & & ( stat = = QUEUE_NO_MEMBERS ) )
* reason = QUEUE_JOINEMPTY ;
2006-11-13 18:23:55 +00:00
else if ( ( q - > joinempty = = QUEUE_EMPTY_STRICT ) & & ( stat = = QUEUE_NO_REACHABLE_MEMBERS | | stat = = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS ) )
* reason = QUEUE_JOINUNAVAIL ;
else if ( ( q - > joinempty = = QUEUE_EMPTY_LOOSE ) & & ( stat = = QUEUE_NO_REACHABLE_MEMBERS ) )
2005-06-02 21:42:49 +00:00
* reason = QUEUE_JOINUNAVAIL ;
else if ( q - > maxlen & & ( q - > count > = q - > maxlen ) )
* reason = QUEUE_FULL ;
else {
/* 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 ;
}
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 ;
2006-07-14 05:42:06 +00:00
manager_event ( EVENT_FLAG_CALL , " Join " ,
" Channel: %s \r \n CallerID: %s \r \n CallerIDName: %s \r \n Queue: %s \r \n Position: %d \r \n Count: %d \r \n Uniqueid: %s \r \n " ,
qe - > chan - > name ,
S_OR ( qe - > chan - > cid . cid_num , " unknown " ) , /* XXX somewhere else it is <unknown> */
S_OR ( qe - > chan - > cid . cid_name , " unknown " ) ,
q - > name , qe - > pos , q - > count , qe - > chan - > uniqueid ) ;
2006-05-25 21:47:03 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Queue '%s' Join, Channel '%s', Position '%d' \n " , q - > name , qe - > chan - > name , qe - > pos ) ;
2002-09-02 23:15:40 +00:00
}
2005-06-02 21:42:49 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
return res ;
}
2004-03-13 06:00:41 +00:00
static int play_file ( struct ast_channel * chan , char * filename )
{
int res ;
ast_stopstream ( chan ) ;
res = ast_streamfile ( chan , filename , chan - > language ) ;
if ( ! res )
2005-05-15 05:42:51 +00:00
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
2004-03-13 06:00:41 +00:00
ast_stopstream ( chan ) ;
return res ;
}
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 */
if ( ! ast_canmatch_extension ( qe - > chan , qe - > context , qe - > digits , 1 , qe - > chan - > cid . cid_num ) ) {
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 ) ) {
/* 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 ;
}
2004-03-13 06:00:41 +00:00
static int say_position ( struct queue_ent * qe )
{
2004-06-28 20:17:20 +00:00
int res = 0 , avgholdmins , avgholdsecs ;
2004-03-13 06:00:41 +00:00
time_t now ;
/* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
time ( & now ) ;
2006-06-14 23:20:08 +00:00
if ( ( now - qe - > last_pos ) < 15 )
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
ast_moh_stop ( qe - > chan ) ;
/* Say we're next, if we are */
if ( qe - > pos = = 1 ) {
2005-05-15 05:42:51 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_next ) ;
if ( res & & valid_exit ( qe , res ) )
goto playout ;
else
goto posout ;
2004-03-13 06:00:41 +00:00
} else {
2005-05-15 05:42:51 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_thereare ) ;
2005-06-02 18:26:18 +00:00
if ( res & & valid_exit ( qe , res ) )
2005-05-15 05:42:51 +00:00
goto playout ;
res = ast_say_number ( qe - > chan , qe - > pos , AST_DIGIT_ANY , qe - > chan - > language , ( char * ) NULL ) ; /* Needs gender */
if ( res & & valid_exit ( qe , res ) )
goto playout ;
res = play_file ( qe - > chan , qe - > parent - > sound_calls ) ;
if ( res & & valid_exit ( qe , res ) )
goto playout ;
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
}
2004-03-23 21:45:38 +00:00
if ( option_verbose > 2 )
2004-06-28 20:17:20 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Hold time for %s is %d minutes %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 */
2005-03-28 20:48:24 +00:00
if ( ( avgholdmins + avgholdsecs ) > 0 & & ( qe - > parent - > announceholdtime ) & &
2006-07-14 05:42:06 +00:00
( ! ( qe - > parent - > announceholdtime = = ANNOUNCEHOLDTIME_ONCE ) & & qe - > last_pos ) ) {
2005-05-15 05:42:51 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_holdtime ) ;
if ( res & & valid_exit ( qe , res ) )
goto playout ;
2006-06-14 23:20:08 +00:00
if ( avgholdmins > 0 ) {
2004-09-25 14:22:27 +00:00
if ( avgholdmins < 2 ) {
2005-05-15 05:42:51 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_lessthan ) ;
if ( res & & valid_exit ( qe , res ) )
goto playout ;
2006-06-14 23:20:08 +00:00
res = ast_say_number ( qe - > chan , 2 , AST_DIGIT_ANY , qe - > chan - > language , NULL ) ;
2005-05-15 05:42:51 +00:00
if ( res & & valid_exit ( qe , res ) )
goto playout ;
} else {
2006-06-14 23:20:08 +00:00
res = ast_say_number ( qe - > chan , avgholdmins , AST_DIGIT_ANY , qe - > chan - > language , NULL ) ;
2005-05-15 05:42:51 +00:00
if ( res & & valid_exit ( qe , res ) )
goto playout ;
}
res = play_file ( qe - > chan , qe - > parent - > sound_minutes ) ;
if ( res & & valid_exit ( qe , res ) )
goto playout ;
2004-06-28 20:17:20 +00:00
}
2005-05-15 05:42:51 +00:00
if ( avgholdsecs > 0 ) {
2006-06-14 23:20:08 +00:00
res = ast_say_number ( qe - > chan , avgholdsecs , AST_DIGIT_ANY , qe - > chan - > language , NULL ) ;
2005-05-15 05:42:51 +00:00
if ( res & & valid_exit ( qe , res ) )
goto playout ;
res = play_file ( qe - > chan , qe - > parent - > sound_seconds ) ;
if ( res & & valid_exit ( qe , res ) )
goto playout ;
2004-06-28 20:17:20 +00:00
}
2004-03-13 06:00:41 +00:00
}
2006-07-14 05:42:06 +00:00
posout :
2005-05-15 05:42:51 +00:00
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Told %s in %s their queue position (which was %d) \n " ,
2006-07-14 05:42:06 +00:00
qe - > chan - > name , qe - > parent - > name , qe - > pos ) ;
2005-05-15 05:42:51 +00:00
res = play_file ( qe - > chan , qe - > parent - > sound_thanks ) ;
2006-05-24 16:54:10 +00:00
if ( res & & ! valid_exit ( qe , res ) )
res = 0 ;
2005-05-15 05:42:51 +00:00
2006-07-14 05:42:06 +00:00
playout :
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 */
2006-05-24 16:54:10 +00:00
if ( ! res )
2006-07-19 20:44:39 +00:00
ast_moh_start ( qe - > chan , qe - > moh , NULL ) ;
2004-03-13 06:00:41 +00:00
2005-05-15 05:42:51 +00:00
return res ;
2004-03-13 06:00:41 +00:00
}
static void recalc_holdtime ( struct queue_ent * qe )
{
int oldvalue , newvalue ;
/* Calculate holdtime using a recursive boxcar filter */
/* Thanks to SRT for this contribution */
/* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
newvalue = time ( NULL ) - qe - > start ;
ast_mutex_lock ( & qe - > parent - > lock ) ;
if ( newvalue < = qe - > parent - > servicelevel )
2006-04-04 18:02:40 +00:00
qe - > parent - > callscompletedinsl + + ;
2004-03-13 06:00:41 +00:00
oldvalue = qe - > parent - > holdtime ;
qe - > parent - > holdtime = ( ( ( oldvalue < < 2 ) - oldvalue ) + newvalue ) > > 2 ;
ast_mutex_unlock ( & qe - > parent - > lock ) ;
}
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 ;
2002-09-02 23:15:40 +00:00
struct queue_ent * cur , * prev = NULL ;
int pos = 0 ;
2005-03-28 20:48:24 +00:00
2006-06-14 23:20:08 +00:00
if ( ! ( q = qe - > parent ) )
2002-09-02 23:15:40 +00:00
return ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & q - > lock ) ;
2003-06-23 23:40:56 +00:00
2002-09-02 23:15:40 +00:00
prev = NULL ;
2006-06-14 23:20:08 +00:00
for ( cur = q - > head ; cur ; cur = cur - > next ) {
2002-09-02 23:15:40 +00:00
if ( cur = = qe ) {
q - > count - - ;
2003-06-23 23:40:56 +00:00
/* Take us out of the queue */
manager_event ( EVENT_FLAG_CALL , " Leave " ,
2006-05-22 15:28:28 +00:00
" Channel: %s \r \n Queue: %s \r \n Count: %d \r \n Uniqueid: %s \r \n " ,
qe - > chan - > name , q - > name , q - > count , qe - > chan - > uniqueid ) ;
2006-05-05 22:02:38 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Queue '%s' Leave, Channel '%s' \n " , q - > name , qe - > chan - > name ) ;
2002-09-02 23:15:40 +00:00
/* Take us out of the queue */
if ( prev )
prev - > next = cur - > next ;
else
q - > head = cur - > next ;
} else {
2004-03-13 06:00:41 +00:00
/* Renumber the people after us in the queue based on a new count */
2002-09-02 23:15:40 +00:00
cur - > pos = + + pos ;
prev = cur ;
}
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2006-06-14 23:20:08 +00:00
2005-03-28 20:48:24 +00:00
if ( q - > dead & & ! q - > count ) {
2002-09-02 23:15:40 +00:00
/* It's dead and nobody is in it, so kill it */
2006-01-22 19:09:50 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_REMOVE ( & queues , q , list ) ;
AST_LIST_UNLOCK ( & queues ) ;
2002-09-02 23:15:40 +00:00
destroy_queue ( q ) ;
}
}
2005-03-28 20:48:24 +00:00
/* Hang up a list of outgoing calls */
2006-02-14 21:50:35 +00:00
static void hangupcalls ( struct callattempt * outgoing , struct ast_channel * exception )
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 ) {
2002-09-02 23:15:40 +00:00
/* Hangup any existing lines we have open */
2003-07-27 01:55:22 +00:00
if ( outgoing - > chan & & ( outgoing - > chan ! = exception ) )
2002-09-02 23:15:40 +00:00
ast_hangup ( outgoing - > chan ) ;
oo = outgoing ;
2006-06-14 23:20:08 +00:00
outgoing = outgoing - > q_next ;
2002-09-02 23:15:40 +00:00
free ( oo ) ;
}
}
2006-06-15 13:35:04 +00:00
static int update_status ( struct call_queue * q , struct member * member , int status )
2004-10-26 22:25:43 +00:00
{
struct member * cur ;
2005-03-28 20:48:24 +00:00
2004-10-26 22:25:43 +00:00
/* Since a reload could have taken place, we have to traverse the list to
be sure it ' s still valid */
ast_mutex_lock ( & q - > lock ) ;
2006-02-14 21:50:35 +00:00
for ( cur = q - > members ; cur ; cur = cur - > next ) {
2006-06-14 23:20:08 +00:00
if ( member ! = cur )
continue ;
cur - > status = status ;
if ( ! q - > maskmemberstatus ) {
manager_event ( EVENT_FLAG_AGENT , " QueueMemberStatus " ,
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 "
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 " ,
2006-09-20 05:59:53 +00:00
q - > name , cur - > interface , cur - > membername , cur - > dynamic ? " dynamic " : " static " ,
2006-07-14 05:42:06 +00:00
cur - > penalty , cur - > calls , ( int ) cur - > lastcall , cur - > status , cur - > paused ) ;
2004-10-26 22:25:43 +00:00
}
}
ast_mutex_unlock ( & q - > lock ) ;
return 0 ;
}
2006-06-15 13:35:04 +00:00
static int update_dial_status ( struct call_queue * q , struct member * member , int status )
2004-11-13 22:44:33 +00:00
{
if ( status = = AST_CAUSE_BUSY )
status = AST_DEVICE_BUSY ;
else if ( status = = AST_CAUSE_UNREGISTERED )
status = AST_DEVICE_UNAVAILABLE ;
else if ( status = = AST_CAUSE_NOSUCHDRIVER )
status = AST_DEVICE_INVALID ;
else
status = AST_DEVICE_UNKNOWN ;
return update_status ( q , member , status ) ;
}
2005-01-07 04:05:22 +00:00
/* 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 ;
2005-01-07 04:05:22 +00:00
2005-03-21 22:08:36 +00:00
/* &qlock and &rq->lock already set by try_calling()
* to solve deadlock */
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE ( & queues , q , list ) {
2005-03-21 22:08:36 +00:00
if ( q = = rq ) /* don't check myself, could deadlock */
2006-07-14 05:42:06 +00:00
continue ;
2005-03-19 17:35:59 +00:00
ast_mutex_lock ( & q - > lock ) ;
if ( q - > count & & q - > members ) {
for ( mem = q - > members ; mem ; mem = mem - > next ) {
2006-06-14 23:20:08 +00:00
if ( strcmp ( mem - > interface , member - > interface ) )
continue ;
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Found matching member %s in queue '%s' \n " , mem - > interface , q - > name ) ;
2006-06-14 23:20:08 +00:00
if ( q - > weight > rq - > weight ) {
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " 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 ;
break ;
2005-01-07 04:05:22 +00:00
}
}
}
ast_mutex_unlock ( & q - > lock ) ;
2006-07-14 05:42:06 +00:00
if ( found )
2005-01-07 04:05:22 +00:00
break ;
}
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 ;
}
2006-06-22 15:43:02 +00:00
static char * vars2manager ( struct ast_channel * chan , char * vars , size_t len )
{
char * tmp = alloca ( len ) ;
if ( pbx_builtin_serialize_variables ( chan , tmp , len ) ) {
int i , j ;
/* convert "\n" to "\nVariable: " */
strcpy ( vars , " Variable: " ) ;
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 ' ) {
vars [ j ] = ' \r ' ;
vars [ + + j ] = ' \n ' ;
ast_copy_string ( & ( vars [ j ] ) , " Variable: " , len - j ) ;
j + = 9 ;
}
}
if ( j > len - 1 )
j = len - 1 ;
vars [ j - 2 ] = ' \r ' ;
vars [ j - 1 ] = ' \n ' ;
vars [ j ] = ' \0 ' ;
} else {
/* there are no channel variables; leave it blank */
* vars = ' \0 ' ;
}
return vars ;
}
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 ;
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 */
2004-06-26 16:26:39 +00:00
if ( qe - > parent - > wrapuptime & & ( time ( NULL ) - tmp - > lastcall < qe - > parent - > wrapuptime ) ) {
2005-01-03 01:42:37 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Wrapuptime not yet expired for %s \n " , tmp - > interface ) ;
2004-06-26 16:26:39 +00:00
if ( qe - > chan - > cdr )
ast_cdr_busy ( qe - > chan - > cdr ) ;
tmp - > stillgoing = 0 ;
2005-01-18 16:40:56 +00:00
( * busies ) + + ;
2004-06-26 16:26:39 +00:00
return 0 ;
}
2006-02-15 02:52:19 +00:00
2006-09-02 18:36:53 +00:00
if ( ! qe - > parent - > ringinuse & & ( tmp - > member - > status ! = AST_DEVICE_NOT_INUSE ) & & ( tmp - > member - > status ! = AST_DEVICE_UNKNOWN ) ) {
2006-02-15 02:52:19 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " %s in use, can't receive call \n " , tmp - > interface ) ;
if ( qe - > chan - > cdr )
ast_cdr_busy ( qe - > chan - > cdr ) ;
tmp - > stillgoing = 0 ;
return 0 ;
}
2005-01-21 04:14:26 +00:00
if ( tmp - > member - > paused ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " %s paused, can't receive call \n " , tmp - > interface ) ;
if ( qe - > chan - > cdr )
ast_cdr_busy ( qe - > chan - > cdr ) ;
tmp - > stillgoing = 0 ;
return 0 ;
}
2005-03-19 17:35:59 +00:00
if ( use_weight & & compare_weight ( qe - > parent , tmp - > member ) ) {
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Priority queue delaying call to %s:%s \n " , qe - > parent - > name , tmp - > interface ) ;
2005-03-19 17:35:59 +00:00
if ( qe - > chan - > cdr )
ast_cdr_busy ( qe - > chan - > cdr ) ;
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 */
2004-12-19 21:30:55 +00:00
tmp - > chan = ast_request ( tech , qe - > chan - > nativeformats , location , & status ) ;
2003-07-30 16:10:51 +00:00
if ( ! tmp - > chan ) { /* If we can't, just go on to the next call */
if ( qe - > chan - > cdr )
ast_cdr_busy ( qe - > chan - > cdr ) ;
tmp - > stillgoing = 0 ;
2004-11-13 22:44:33 +00:00
update_dial_status ( qe - > parent , tmp - > member , status ) ;
2006-09-03 17:12:30 +00:00
ast_mutex_lock ( & qe - > parent - > lock ) ;
qe - > parent - > rrpos + + ;
ast_mutex_unlock ( & qe - > parent - > lock ) ;
2005-01-18 16:40:56 +00:00
( * busies ) + + ;
2003-07-30 16:10:51 +00:00
return 0 ;
2006-07-14 05:42:06 +00:00
} else if ( status ! = tmp - > oldstatus )
2004-11-13 22:44:33 +00:00
update_dial_status ( qe - > parent , tmp - > member , status ) ;
2004-10-26 22:25:43 +00:00
2003-07-30 16:10:51 +00:00
tmp - > chan - > appl = " AppQueue " ;
tmp - > chan - > data = " (Outgoing Line) " ;
tmp - > chan - > whentohangup = 0 ;
2004-10-02 00:58:31 +00:00
if ( tmp - > chan - > cid . cid_num )
free ( tmp - > chan - > cid . cid_num ) ;
2006-04-21 10:57:03 +00:00
tmp - > chan - > cid . cid_num = ast_strdup ( qe - > chan - > cid . cid_num ) ;
2004-10-02 00:58:31 +00:00
if ( tmp - > chan - > cid . cid_name )
free ( tmp - > chan - > cid . cid_name ) ;
2006-04-21 10:57:03 +00:00
tmp - > chan - > cid . cid_name = ast_strdup ( qe - > chan - > cid . cid_name ) ;
2004-10-02 00:58:31 +00:00
if ( tmp - > chan - > cid . cid_ani )
free ( tmp - > chan - > cid . cid_ani ) ;
2006-03-29 00:30:29 +00:00
tmp - > chan - > cid . cid_ani = ast_strdup ( qe - > chan - > cid . cid_ani ) ;
2005-01-08 17:23:29 +00:00
/* Inherit specially named variables from parent channel */
ast_channel_inherit_variables ( qe - > chan , tmp - > chan ) ;
2003-07-30 16:10:51 +00:00
/* Presense of ADSI CPE on outgoing channel follows ours */
tmp - > chan - > adsicpe = qe - > chan - > adsicpe ;
2005-01-08 17:23:29 +00:00
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 */
if ( option_debug )
ast_log ( LOG_DEBUG , " ast call on peer returned %d \n " , res ) ;
2006-09-19 16:23:45 +00:00
if ( option_verbose > 2 )
2004-12-19 21:30:55 +00:00
ast_verbose ( VERBOSE_PREFIX_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 ) + + ;
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 ] ;
manager_event ( EVENT_FLAG_AGENT , " AgentCalled " ,
" AgentCalled: %s \r \n "
" ChannelCalling: %s \r \n "
2006-10-02 20:35:16 +00:00
" CallerIDNum: %s \r \n "
2006-06-22 15:43:02 +00:00
" CallerIDName: %s \r \n "
" Context: %s \r \n "
" Extension: %s \r \n "
" Priority: %d \r \n "
" %s " ,
tmp - > interface , qe - > chan - > name ,
tmp - > chan - > cid . cid_num ? tmp - > chan - > cid . cid_num : " unknown " ,
tmp - > chan - > cid . cid_name ? tmp - > chan - > cid . cid_name : " unknown " ,
qe - > chan - > context , qe - > chan - > exten , qe - > chan - > priority ,
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
2003-07-30 16:10:51 +00:00
if ( option_verbose > 2 )
2004-12-19 21:30:55 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Called %s \n " , tmp - > interface ) ;
2004-07-27 04:13:04 +00:00
}
2006-06-14 23:20:08 +00:00
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 ;
}
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 ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " Nobody left to try ringing in queue \n " ) ;
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 ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " (Parallel) Trying '%s' with metric %d \n " , cur - > interface , cur - > metric ) ;
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 */
if ( option_debug )
ast_log ( LOG_DEBUG , " Trying '%s' with metric %d \n " , best - > interface , best - > metric ) ;
ring_entry ( qe , best , busies ) ;
2003-07-30 16:10:51 +00:00
}
2006-02-14 21:50:35 +00:00
if ( best - > chan ) /* break out with result = 1 */
ret = 1 ;
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
}
2006-02-14 21:50:35 +00:00
static int store_next ( 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 */
2005-01-03 01:42:37 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " 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 ;
}
2005-07-31 22:07:58 +00:00
static int background_file ( struct queue_ent * qe , struct ast_channel * chan , char * filename )
{
int res ;
ast_stopstream ( chan ) ;
res = ast_streamfile ( chan , filename , chan - > language ) ;
if ( ! res ) {
/* Wait for a keypress */
res = ast_waitstream ( chan , AST_DIGIT_ANY ) ;
2006-05-24 16:54:10 +00:00
if ( res < 0 | | ! valid_exit ( qe , res ) )
2005-07-31 22:07:58 +00:00
res = 0 ;
/* Stop playback */
ast_stopstream ( chan ) ;
}
return res ;
}
static int say_periodic_announcement ( struct queue_ent * qe )
{
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 */
ast_moh_stop ( qe - > chan ) ;
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Playing periodic announcement \n " ) ;
2006-01-17 20:58:56 +00:00
/* Check to make sure we have a sound file. If not, reset to the first sound file */
if ( qe - > last_periodic_announce_sound > = MAX_PERIODIC_ANNOUNCEMENTS | | ! strlen ( qe - > parent - > sound_periodicannounce [ qe - > last_periodic_announce_sound ] ) ) {
qe - > last_periodic_announce_sound = 0 ;
}
2005-07-31 22:07:58 +00:00
/* play the announcement */
2006-01-17 20:58:56 +00:00
res = background_file ( qe , qe - > chan , qe - > parent - > sound_periodicannounce [ qe - > last_periodic_announce_sound ] ) ;
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 */
if ( ! res )
2006-07-19 20:44:39 +00:00
ast_moh_start ( qe - > chan , qe - > moh , NULL ) ;
2005-07-31 22:07:58 +00:00
/* update last_periodic_announce_time */
qe - > last_periodic_announce_time = now ;
2006-01-17 20:58:56 +00:00
/* Update the current periodic announcement to the next announcement */
qe - > last_periodic_announce_sound + + ;
2005-09-26 00:41:28 +00:00
return res ;
2005-07-31 22:07:58 +00:00
}
static void record_abandoned ( struct queue_ent * qe )
{
ast_mutex_lock ( & qe - > parent - > lock ) ;
2006-10-27 19:28:34 +00:00
set_queue_variables ( qe ) ;
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 " ,
qe - > parent - > name , qe - > chan - > uniqueid , 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 + + ;
ast_mutex_unlock ( & qe - > parent - > lock ) ;
}
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. */
2006-09-20 05:59:53 +00:00
static void rna ( int rnatime , struct queue_ent * qe , char * interface , char * membername )
2006-05-25 00:11:30 +00:00
{
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Nobody picked up in %d ms \n " , rnatime ) ;
ast_queue_log ( qe - > parent - > name , qe - > chan - > uniqueid , membername , " RINGNOANSWER " , " %d " , rnatime ) ;
if ( qe - > parent - > autopause ) {
2006-09-20 05:59:53 +00:00
if ( ! set_member_paused ( qe - > parent - > name , interface , 1 ) ) {
2006-05-25 00:11:30 +00:00
if ( option_verbose > 2 )
2006-09-20 05:59:53 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Auto-Pausing Queue Member %s in queue %s since they failed to answer. \n " , interface , qe - > parent - > name ) ;
2006-05-25 00:11:30 +00:00
} else {
if ( option_verbose > 2 )
2006-09-20 05:59:53 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Failed to pause Queue Member %s in queue %s! \n " , interface , qe - > parent - > name ) ;
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
2003-08-01 23:42:49 +00:00
2006-09-03 19:32:51 +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 )
2003-08-01 23:42:49 +00:00
{
char * queue = qe - > parent - > name ;
2006-02-14 21:50:35 +00:00
struct callattempt * o ;
2004-10-26 22:25:43 +00:00
int status ;
2002-09-02 23:15:40 +00:00
int sentringing = 0 ;
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 ;
long endtime = 0 ;
2006-06-14 23:20:08 +00:00
starttime = ( long ) time ( NULL ) ;
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 ;
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 ;
if ( o - > chan )
watchers [ pos + + ] = o - > chan ;
}
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 ) ) {
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Everyone is busy at this time \n " ) ;
2002-09-02 23:15:40 +00:00
} else {
2005-01-18 16:40:56 +00:00
ast_log ( LOG_NOTICE , " No one is answering queue '%s' (%d/%d/%d) \n " , queue , numlines , numbusies , numnochan ) ;
2002-09-02 23:15:40 +00:00
}
* to = 0 ;
return NULL ;
}
winner = ast_waitfor_n ( watchers , pos , to ) ;
2006-02-14 21:50:35 +00:00
for ( o = outgoing ; o ; o = o - > q_next ) {
2003-07-30 16:10:51 +00:00
if ( o - > stillgoing & & ( o - > chan ) & & ( o - > chan - > _state = = AST_STATE_UP ) ) {
2002-09-02 23:15:40 +00:00
if ( ! peer ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s answered %s \n " , o - > chan - > name , in - > name ) ;
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 ) ) ;
2006-09-03 19:32:51 +00:00
if ( ! ast_strlen_zero ( o - > chan - > call_forward ) & & ! forwardsallowed ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Forwarding %s to '%s' prevented. \n " , in - > name , o - > chan - > call_forward ) ;
winner = o - > chan = NULL ;
} else if ( ! ast_strlen_zero ( o - > chan - > call_forward ) ) {
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
2005-05-15 05:42:51 +00:00
ast_copy_string ( tmpchan , o - > chan - > call_forward , 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 {
snprintf ( tmpchan , sizeof ( tmpchan ) , " %s@%s " , o - > chan - > call_forward , o - > chan - > context ) ;
stuff = tmpchan ;
tech = " Local " ;
}
/* Before processing channel, go ahead and check for forwarding */
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " Now forwarding %s to '%s/%s' (thanks to %s) \n " , in - > name , tech , stuff , o - > chan - > name ) ;
/* Setup parameters */
2004-10-26 22:25:43 +00:00
o - > chan = ast_request ( tech , in - > nativeformats , stuff , & status ) ;
2006-07-14 05:42:06 +00:00
if ( status ! = o - > oldstatus )
2004-11-13 22:44:33 +00:00
update_dial_status ( qe - > parent , o - > member , status ) ;
2004-10-05 06:46:11 +00:00
if ( ! o - > chan ) {
ast_log ( LOG_NOTICE , " Unable to create local channel for call forward to '%s/%s' \n " , tech , stuff ) ;
o - > stillgoing = 0 ;
numnochan + + ;
} else {
2006-09-03 17:40:08 +00:00
ast_channel_inherit_variables ( in , o - > chan ) ;
2004-10-05 06:46:11 +00:00
if ( o - > chan - > cid . cid_num )
free ( o - > chan - > cid . cid_num ) ;
2006-03-29 00:30:29 +00:00
o - > chan - > cid . cid_num = ast_strdup ( in - > cid . cid_num ) ;
2004-10-05 06:46:11 +00:00
if ( o - > chan - > cid . cid_name )
free ( o - > chan - > cid . cid_name ) ;
2006-03-29 00:30:29 +00:00
o - > chan - > cid . cid_name = ast_strdup ( in - > cid . cid_name ) ;
2004-10-05 06:46:11 +00:00
2006-02-01 23:05:28 +00:00
ast_string_field_set ( o - > chan , accountcode , in - > accountcode ) ;
2004-10-05 06:46:11 +00:00
o - > chan - > cdrflags = in - > cdrflags ;
if ( in - > cid . cid_ani ) {
if ( o - > chan - > cid . cid_ani )
free ( o - > chan - > cid . cid_ani ) ;
2006-01-13 18:38:55 +00:00
o - > chan - > cid . cid_ani = ast_strdup ( in - > cid . cid_ani ) ;
2004-10-05 06:46:11 +00:00
}
2006-07-14 05:42:06 +00:00
if ( o - > chan - > cid . cid_rdnis )
2004-10-05 06:46:11 +00:00
free ( o - > chan - > cid . cid_rdnis ) ;
2006-04-21 10:57:03 +00:00
o - > chan - > cid . cid_rdnis = ast_strdup ( S_OR ( in - > macroexten , in - > exten ) ) ;
2004-10-05 06:46:11 +00:00
if ( ast_call ( o - > chan , tmpchan , 0 ) ) {
ast_log ( LOG_NOTICE , " Failed to dial on local channel for call forward to '%s' \n " , tmpchan ) ;
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 ) {
2006-07-14 05:42:06 +00:00
switch ( f - > subclass ) {
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 ) {
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s answered %s \n " , o - > chan - > name , in - > name ) ;
2003-08-02 21:10:06 +00:00
peer = o ;
2002-09-02 23:15:40 +00:00
}
break ;
case AST_CONTROL_BUSY :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is busy \n " , o - > chan - > name ) ;
if ( in - > cdr )
ast_cdr_busy ( in - > cdr ) ;
2006-02-14 21:50:35 +00:00
do_hang ( o ) ;
2006-05-25 00:11:30 +00:00
endtime = ( long ) time ( NULL ) ;
endtime - = starttime ;
2006-09-20 05:59:53 +00:00
rna ( endtime * 1000 , qe , on , membername ) ;
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 ;
2005-01-18 16:40:56 +00:00
ring_one ( qe , outgoing , & numbusies ) ;
2005-03-04 00:59:58 +00:00
}
2002-09-02 23:15:40 +00:00
numbusies + + ;
break ;
case AST_CONTROL_CONGESTION :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is circuit-busy \n " , o - > chan - > name ) ;
if ( in - > cdr )
ast_cdr_busy ( in - > cdr ) ;
2006-05-25 00:11:30 +00:00
endtime = ( long ) time ( NULL ) ;
endtime - = starttime ;
2006-09-20 05:59:53 +00:00
rna ( endtime * 1000 , qe , on , membername ) ;
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 ;
2005-01-18 16:40:56 +00:00
ring_one ( qe , outgoing , & numbusies ) ;
2005-03-04 00:59:58 +00:00
}
2002-09-02 23:15:40 +00:00
numbusies + + ;
break ;
case AST_CONTROL_RINGING :
if ( option_verbose > 2 )
ast_verbose ( VERBOSE_PREFIX_3 " %s is ringing \n " , o - > chan - > name ) ;
if ( ! sentringing ) {
#if 0
ast_indicate ( in , AST_CONTROL_RINGING ) ;
# endif
sentringing + + ;
}
break ;
case AST_CONTROL_OFFHOOK :
/* Ignore going off hook */
break ;
default :
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Dunno what to do with control type %d \n " , f - > subclass ) ;
2002-09-02 23:15:40 +00:00
}
}
ast_frfree ( f ) ;
} else {
2006-06-14 23:20:08 +00:00
endtime = ( long ) time ( NULL ) - starttime ;
2006-09-20 05:59:53 +00:00
rna ( endtime * 1000 , qe , on , membername ) ;
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 ;
2005-01-18 16:40:56 +00:00
ring_one ( qe , outgoing , & numbusies ) ;
2005-03-04 00:59:58 +00:00
}
2002-09-02 23:15:40 +00:00
}
}
}
if ( winner = = in ) {
f = ast_read ( in ) ;
if ( ! f | | ( ( f - > frametype = = AST_FRAME_CONTROL ) & & ( f - > subclass = = AST_CONTROL_HANGUP ) ) ) {
/* Got hung up */
2006-06-14 23:20:08 +00:00
* to = - 1 ;
2005-05-30 13:34:23 +00:00
if ( f )
ast_frfree ( f ) ;
2002-09-02 23:15:40 +00:00
return NULL ;
}
2005-05-30 13:34:23 +00:00
if ( ( f - > frametype = = AST_FRAME_DTMF ) & & caller_disconnect & & ( f - > subclass = = ' * ' ) ) {
2005-01-03 01:42:37 +00:00
if ( option_verbose > 3 )
2004-03-23 21:45:38 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " User hit %c to disconnect call. \n " , f - > subclass ) ;
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 ;
}
2005-05-30 13:34:23 +00:00
if ( ( f - > frametype = = AST_FRAME_DTMF ) & & ( f - > subclass ! = ' * ' ) & & valid_exit ( qe , f - > subclass ) ) {
2003-08-01 23:42:49 +00:00
if ( option_verbose > 3 )
2005-04-13 03:41:20 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " User pressed digit: %c \n " , f - > subclass ) ;
2006-06-14 23:20:08 +00:00
* to = 0 ;
* digit = f - > subclass ;
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
}
2006-07-14 05:42:06 +00:00
if ( ! * to )
2006-09-20 05:59:53 +00:00
rna ( orig , qe , on , membername ) ;
2002-09-02 23:15:40 +00:00
}
return peer ;
}
2004-06-23 20:05:18 +00:00
static int is_our_turn ( struct queue_ent * qe )
{
struct queue_ent * ch ;
2006-05-03 20:01:30 +00:00
struct member * cur ;
int avl = 0 ;
int idx = 0 ;
2004-06-23 20:05:18 +00:00
int res ;
2006-05-03 20:01:30 +00:00
if ( ! qe - > parent - > autofill ) {
/* Atomically read the parent head -- does not need a lock */
ch = qe - > parent - > head ;
/* If we are now at the top of the head, break out */
2006-05-17 22:51:28 +00:00
if ( ch = = qe ) {
2006-05-03 20:01:30 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " It's our turn (%s). \n " , qe - > chan - > name ) ;
res = 1 ;
} else {
if ( option_debug )
ast_log ( LOG_DEBUG , " It's not our turn (%s). \n " , qe - > chan - > name ) ;
res = 0 ;
}
2004-06-23 20:05:18 +00:00
} else {
2006-05-03 20:01:30 +00:00
/* This needs a lock. How many members are available to be served? */
ast_mutex_lock ( & qe - > parent - > lock ) ;
ch = qe - > parent - > head ;
2006-05-09 07:27:03 +00:00
if ( qe - > parent - > strategy = = QUEUE_STRATEGY_RINGALL ) {
2006-05-03 20:01:30 +00:00
if ( option_debug )
2006-06-14 23:20:08 +00:00
ast_log ( LOG_DEBUG , " Even though there are %d available members, the strategy is ringall so only the head call is allowed in \n " , avl ) ;
2006-05-03 20:01:30 +00:00
avl = 1 ;
2006-06-14 23:20:08 +00:00
} else {
2006-06-14 23:24:26 +00:00
for ( cur = qe - > parent - > members ; cur ; cur = cur - > next ) {
switch ( cur - > status ) {
case AST_DEVICE_NOT_INUSE :
case AST_DEVICE_UNKNOWN :
2006-06-14 23:20:08 +00:00
avl + + ;
2006-06-14 23:24:26 +00:00
break ;
}
2006-06-14 23:20:08 +00:00
}
2006-05-03 20:01:30 +00:00
}
2006-06-14 23:20:08 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " There are %d available members. \n " , avl ) ;
2006-05-03 20:01:30 +00:00
while ( ( idx < avl ) & & ( ch ) & & ( ch ! = qe ) ) {
idx + + ;
ch = ch - > next ;
}
/* If the queue entry is within avl [the number of available members] calls from the top ... */
if ( ch & & idx < avl ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " It's our turn (%s). \n " , qe - > chan - > name ) ;
res = 1 ;
} else {
if ( option_debug )
ast_log ( LOG_DEBUG , " It's not our turn (%s). \n " , qe - > chan - > name ) ;
res = 0 ;
}
ast_mutex_unlock ( & qe - > parent - > lock ) ;
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 ;
}
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
enum queue_member_status stat ;
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 */
2005-03-28 20:48:24 +00:00
if ( qe - > expire & & ( time ( NULL ) > qe - > expire ) ) {
* reason = QUEUE_TIMEOUT ;
2002-09-02 23:15:40 +00:00
break ;
2005-03-28 20:48:24 +00:00
}
2006-01-06 03:40:12 +00:00
stat = get_member_status ( qe - > parent , qe - > max_penalty ) ;
2004-03-13 06:00:41 +00:00
2004-09-28 03:32:21 +00:00
/* leave the queue if no agents, if enabled */
2005-03-28 20:48:24 +00:00
if ( qe - > parent - > leavewhenempty & & ( stat = = QUEUE_NO_MEMBERS ) ) {
* reason = QUEUE_LEAVEEMPTY ;
2006-05-03 20:47:34 +00:00
ast_queue_log ( qe - > parent - > name , qe - > chan - > uniqueid , " NONE " , " EXITEMPTY " , " %d|%d|%ld " , qe - > pos , qe - > opos , ( long ) time ( NULL ) - qe - > start ) ;
2005-03-28 20:48:24 +00:00
leave_queue ( qe ) ;
break ;
}
/* leave the queue if no reachable agents, if enabled */
2006-11-13 18:23:55 +00:00
if ( ( qe - > parent - > leavewhenempty = = QUEUE_EMPTY_STRICT ) & & ( stat = = QUEUE_NO_REACHABLE_MEMBERS | | stat = = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS ) ) {
* reason = QUEUE_LEAVEUNAVAIL ;
ast_queue_log ( qe - > parent - > name , qe - > chan - > uniqueid , " NONE " , " EXITEMPTY " , " %d|%d|%ld " , qe - > pos , qe - > opos , ( long ) time ( NULL ) - qe - > start ) ;
leave_queue ( qe ) ;
break ;
}
if ( ( qe - > parent - > leavewhenempty = = QUEUE_EMPTY_LOOSE ) & & ( stat = = QUEUE_NO_REACHABLE_MEMBERS ) ) {
2005-03-28 20:48:24 +00:00
* reason = QUEUE_LEAVEUNAVAIL ;
2006-05-03 20:47:34 +00:00
ast_queue_log ( qe - > parent - > name , qe - > chan - > uniqueid , " NONE " , " EXITEMPTY " , " %d|%d|%ld " , qe - > pos , qe - > opos , ( long ) time ( NULL ) - qe - > start ) ;
2004-09-28 03:32:21 +00:00
leave_queue ( qe ) ;
break ;
}
2004-03-13 06:00:41 +00:00
/* Make a position announcement, if enabled */
2006-05-24 16:54:10 +00:00
if ( qe - > parent - > announcefrequency & & ! ringing & &
2006-07-14 05:42:06 +00:00
( res = say_position ( qe ) ) )
2005-05-15 05:42:51 +00:00
break ;
2004-03-13 06:00:41 +00:00
2005-07-31 22:07:58 +00:00
/* Make a periodic announcement, if enabled */
2006-05-24 16:54:10 +00:00
if ( qe - > parent - > periodicannouncefrequency & & ! ringing & &
2006-07-14 05:42:06 +00:00
( res = say_periodic_announcement ( qe ) ) )
2006-05-24 16:54:10 +00:00
break ;
2005-07-31 22:07:58 +00:00
2002-09-02 23:15:40 +00:00
/* Wait a second before checking again */
2006-05-24 16:54:10 +00:00
if ( ( res = ast_waitfordigit ( qe - > chan , RECHECK * 1000 ) ) )
2002-09-02 23:15:40 +00:00
break ;
}
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
return res ;
}
2006-06-15 13:35:04 +00:00
static int update_queue ( struct call_queue * q , struct member * member )
2003-08-02 21:10:06 +00:00
{
struct member * cur ;
2005-03-28 20:48:24 +00:00
2003-08-02 21:10:06 +00:00
/* Since a reload could have taken place, we have to traverse the list to
be sure it ' s still valid */
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & q - > lock ) ;
2003-08-02 21:10:06 +00:00
cur = q - > members ;
2006-07-14 05:42:06 +00:00
while ( cur ) {
2004-05-30 20:43:55 +00:00
if ( member = = cur ) {
2003-08-02 21:10:06 +00:00
time ( & cur - > lastcall ) ;
cur - > calls + + ;
break ;
}
cur = cur - > next ;
}
2004-03-13 06:00:41 +00:00
q - > callscompleted + + ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2003-08-02 21:10:06 +00:00
return 0 ;
}
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
{
2006-07-13 15:37:56 +00:00
if ( qe - > max_penalty & & ( mem - > penalty > qe - > max_penalty ) )
2006-01-06 03:40:12 +00:00
return - 1 ;
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 */
tmp - > metric = mem - > penalty * 1000000 ;
2003-07-27 01:55:22 +00:00
break ;
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 ;
}
2003-08-02 21:10:06 +00:00
tmp - > metric + = mem - > penalty * 1000000 ;
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 ;
2003-08-02 21:10:06 +00:00
tmp - > metric + = mem - > penalty * 1000000 ;
break ;
case QUEUE_STRATEGY_FEWESTCALLS :
tmp - > metric = mem - > calls ;
tmp - > metric + = mem - > penalty * 1000000 ;
break ;
case QUEUE_STRATEGY_LEASTRECENT :
if ( ! mem - > lastcall )
tmp - > metric = 0 ;
else
tmp - > metric = 1000000 - ( time ( NULL ) - mem - > lastcall ) ;
tmp - > metric + = mem - > penalty * 1000000 ;
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 ;
}
2006-10-27 18:59:16 +00:00
static int try_calling ( struct queue_ent * qe , const char * options , char * announceoverride , const char * url , int * go_on , const char * agi , const char * macro )
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 */
2002-09-02 23:15:40 +00:00
int to ;
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 ;
2006-05-05 21:22:45 +00:00
struct ast_app * app ;
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 ;
2006-05-05 21:22:45 +00:00
int ret = 0 ;
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 ;
2005-03-28 20:48:24 +00:00
memset ( & bridge_config , 0 , sizeof ( bridge_config ) ) ;
2005-08-14 03:08:08 +00:00
time ( & now ) ;
2005-03-28 20:48:24 +00:00
for ( ; options & & * options ; options + + )
switch ( * options ) {
case ' t ' :
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_REDIRECT ) ;
break ;
case ' T ' :
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_REDIRECT ) ;
break ;
2005-10-13 23:14:19 +00:00
case ' w ' :
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_AUTOMON ) ;
break ;
case ' W ' :
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_AUTOMON ) ;
break ;
2005-03-28 20:48:24 +00:00
case ' d ' :
nondataquality = 0 ;
break ;
case ' h ' :
ast_set_flag ( & ( bridge_config . features_callee ) , AST_FEATURE_DISCONNECT ) ;
break ;
case ' H ' :
ast_set_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_DISCONNECT ) ;
break ;
case ' n ' :
if ( ( now - qe - > start > = qe - > parent - > timeout ) )
* go_on = 1 ;
break ;
2006-09-03 19:32:51 +00:00
case ' i ' :
forwardsallowed = 0 ;
break ;
2005-03-28 20:48:24 +00:00
}
2002-09-02 23:15:40 +00:00
/* Hold the lock while we setup the outgoing calls */
2006-07-14 05:42:06 +00:00
if ( use_weight )
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & qe - > parent - > lock ) ;
2004-06-23 20:05:18 +00:00
if ( option_debug )
2006-07-14 05:42:06 +00:00
ast_log ( LOG_DEBUG , " %s is trying to call a queue member. \n " ,
2004-06-23 20:05:18 +00:00
qe - > chan - > name ) ;
2005-05-15 05:42:51 +00:00
ast_copy_string ( queuename , qe - > parent - > name , sizeof ( queuename ) ) ;
2002-09-02 23:15:40 +00:00
cur = qe - > parent - > members ;
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
2006-06-14 23:20:08 +00:00
for ( ; cur ; cur = cur - > next ) {
2006-03-29 00:30:29 +00:00
struct callattempt * tmp = ast_calloc ( 1 , sizeof ( * tmp ) ) ;
2006-06-14 23:20:08 +00:00
2006-02-14 21:50:35 +00:00
if ( ! tmp ) {
2003-11-08 04:35:57 +00:00
ast_mutex_unlock ( & qe - > parent - > lock ) ;
2006-07-14 05:42:06 +00:00
if ( use_weight )
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2002-09-02 23:15:40 +00:00
goto out ;
}
2003-07-27 01:55:22 +00:00
tmp - > stillgoing = - 1 ;
2003-08-02 21:10:06 +00:00
tmp - > member = cur ; /* Never directly dereference! Could change on reload */
2004-10-26 22:25:43 +00:00
tmp - > oldstatus = cur - > status ;
2004-06-26 16:26:39 +00:00
tmp - > lastcall = cur - > lastcall ;
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 */
if ( outgoing - > chan & & ( outgoing - > chan - > _state = = AST_STATE_UP ) )
break ;
} else {
free ( tmp ) ;
}
2002-09-02 23:15:40 +00:00
}
2006-05-20 02:51:53 +00:00
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 ;
2005-01-18 16:40:56 +00:00
ring_one ( qe , outgoing , & numbusies ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & qe - > parent - > lock ) ;
2006-07-14 05:42:06 +00:00
if ( use_weight )
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2006-09-03 19:32:51 +00:00
lpeer = wait_for_answer ( qe , outgoing , & to , & digit , numbusies , ast_test_flag ( & ( bridge_config . features_caller ) , AST_FEATURE_DISCONNECT ) , forwardsallowed ) ;
2004-05-18 05:41:53 +00:00
ast_mutex_lock ( & qe - > parent - > lock ) ;
if ( qe - > parent - > strategy = = QUEUE_STRATEGY_RRMEMORY ) {
store_next ( qe , outgoing ) ;
}
ast_mutex_unlock ( & qe - > parent - > lock ) ;
2006-02-14 21:50:35 +00:00
peer = lpeer ? lpeer - > chan : NULL ;
2002-09-02 23:15:40 +00:00
if ( ! peer ) {
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 {
2005-07-12 22:28:51 +00:00
res = digit ;
2003-08-01 23:42:49 +00:00
}
2004-06-23 20:05:18 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " %s: Nobody answered. \n " , qe - > chan - > name ) ;
2006-02-14 21:50:35 +00:00
} else { /* peer is valid */
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 . */
2004-02-12 22:11:02 +00:00
qe - > handled + + ;
2006-02-01 23:05:28 +00:00
if ( ! strcmp ( qe - > chan - > tech - > type , " Zap " ) )
2005-03-28 20:48:24 +00:00
ast_channel_setoption ( qe - > chan , AST_OPTION_TONE_VERIFY , & nondataquality , sizeof ( nondataquality ) , 0 ) ;
2006-02-01 23:05:28 +00:00
if ( ! strcmp ( peer - > tech - > type , " Zap " ) )
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 */
2004-03-13 06:00:41 +00:00
recalc_holdtime ( qe ) ;
2004-05-30 22:10:09 +00:00
member = lpeer - > member ;
2005-03-28 20:48:24 +00:00
hangupcalls ( outgoing , peer ) ;
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 ) {
2004-10-03 16:46:06 +00:00
if ( play_file ( peer , announce ) )
ast_log ( LOG_WARNING , " Announcement file '%s' is unavailable, continuing anyway... \n " , announce ) ;
}
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 ) ) {
int holdtime ;
time ( & now ) ;
holdtime = abs ( ( now - qe - > start ) / 60 ) ;
if ( holdtime < 2 ) {
play_file ( peer , qe - > parent - > sound_lessthan ) ;
ast_say_number ( peer , 2 , AST_DIGIT_ANY , peer - > language , NULL ) ;
2006-07-14 05:42:06 +00:00
} else
2004-10-03 16:46:06 +00:00
ast_say_number ( peer , holdtime , AST_DIGIT_ANY , peer - > language , NULL ) ;
play_file ( peer , qe - > parent - > sound_minutes ) ;
}
2004-04-10 21:10:22 +00:00
}
}
2003-07-30 16:10:51 +00:00
res2 | = ast_autoservice_stop ( qe - > chan ) ;
2005-02-13 16:53:29 +00:00
if ( peer - > _softhangup ) {
2002-09-02 23:15:40 +00:00
/* Agent must have hung up */
ast_log ( LOG_WARNING , " Agent on %s hungup on the customer. They're going to be pissed. \n " , peer - > name ) ;
2006-09-20 05:59:53 +00:00
ast_queue_log ( queuename , qe - > chan - > uniqueid , member - > membername , " AGENTDUMP " , " %s " , " " ) ;
2006-04-04 18:02:40 +00:00
record_abandoned ( qe ) ;
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 " ,
2006-09-20 05:59:53 +00:00
queuename , qe - > chan - > uniqueid , peer - > name , 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 ) ;
2005-02-13 16:53:29 +00:00
goto out ;
} else if ( res2 ) {
/* Caller must have hung up just before being connected*/
ast_log ( LOG_NOTICE , " Caller was about to talk to agent on %s but the caller hungup. \n " , peer - > name ) ;
2006-09-20 05:59:53 +00:00
ast_queue_log ( queuename , qe - > chan - > uniqueid , 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 ) ;
2002-09-02 23:15:40 +00:00
return - 1 ;
}
}
2004-04-08 05:03:47 +00:00
/* Stop music on hold */
ast_moh_stop ( qe - > chan ) ;
2002-09-02 23:15:40 +00:00
/* If appropriate, log that we have a destination channel */
if ( qe - > chan - > cdr )
ast_cdr_setdestchan ( qe - > chan - > cdr , peer - > name ) ;
/* Make sure channels are compatible */
res = ast_channel_make_compatible ( qe - > chan , peer ) ;
if ( res < 0 ) {
2006-09-20 05:59:53 +00:00
ast_queue_log ( queuename , qe - > chan - > uniqueid , member - > membername , " SYSCOMPAT " , " %s " , " " ) ;
2002-09-02 23:15:40 +00:00
ast_log ( LOG_WARNING , " Had to drop call because I couldn't make %s compatible with %s \n " , qe - > chan - > name , peer - > name ) ;
2006-05-24 16:54:10 +00:00
record_abandoned ( qe ) ;
2002-09-02 23:15:40 +00:00
ast_hangup ( peer ) ;
return - 1 ;
}
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 ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " Starting Monitor as requested. \n " ) ;
monitorfilename = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_FILENAME " ) ;
if ( pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_EXEC " ) | | pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_EXEC_ARGS " ) )
which = qe - > chan ;
else
which = peer ;
if ( monitorfilename )
ast_monitor_start ( which , qe - > parent - > monfmt , monitorfilename , 1 ) ;
2006-07-14 05:42:06 +00:00
else if ( qe - > chan - > cdr )
2006-05-05 22:02:38 +00:00
ast_monitor_start ( which , qe - > parent - > monfmt , qe - > chan - > cdr - > uniqueid , 1 ) ;
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 ) ;
}
if ( qe - > parent - > monjoin )
ast_monitor_setjoinfiles ( which , 1 ) ;
} else {
if ( option_debug )
ast_log ( LOG_DEBUG , " Starting MixMonitor as requested. \n " ) ;
monitorfilename = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_FILENAME " ) ;
if ( ! monitorfilename ) {
if ( qe - > chan - > cdr )
ast_copy_string ( tmpid , qe - > chan - > cdr - > uniqueid , sizeof ( tmpid ) - 1 ) ;
2006-07-14 05:42:06 +00:00
else
2006-05-05 22:02:38 +00:00
snprintf ( tmpid , sizeof ( tmpid ) , " chan-%lx " , ast_random ( ) ) ;
} else {
ast_copy_string ( tmpid2 , monitorfilename , sizeof ( tmpid2 ) - 1 ) ;
for ( p = tmpid2 ; * p ; p + + ) {
if ( * p = = ' ^ ' & & * ( p + 1 ) = = ' { ' ) {
* p = ' $ ' ;
}
}
2006-05-25 14:32:15 +00:00
memset ( tmpid , 0 , sizeof ( tmpid ) ) ;
2006-05-05 22:02:38 +00:00
pbx_substitute_variables_helper ( qe - > chan , tmpid2 , tmpid , sizeof ( tmpid ) - 1 ) ;
}
monitor_exec = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_EXEC " ) ;
monitor_options = pbx_builtin_getvar_helper ( qe - > chan , " MONITOR_OPTIONS " ) ;
if ( monitor_exec ) {
ast_copy_string ( meid2 , monitor_exec , sizeof ( meid2 ) - 1 ) ;
for ( p = meid2 ; * p ; p + + ) {
if ( * p = = ' ^ ' & & * ( p + 1 ) = = ' { ' ) {
* p = ' $ ' ;
}
}
2006-05-25 14:32:15 +00:00
memset ( meid , 0 , sizeof ( meid ) ) ;
2006-05-05 22:02:38 +00:00
pbx_substitute_variables_helper ( qe - > chan , meid2 , meid , sizeof ( meid ) - 1 ) ;
2006-07-14 05:42:06 +00:00
}
2006-05-05 22:02:38 +00:00
snprintf ( tmpid2 , sizeof ( tmpid2 ) - 1 , " %s.%s " , tmpid , qe - > parent - > monfmt ) ;
mixmonapp = pbx_findapp ( " MixMonitor " ) ;
if ( strchr ( tmpid2 , ' | ' ) ) {
ast_log ( LOG_WARNING , " monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording. \n " ) ;
mixmonapp = NULL ;
}
2006-05-25 13:51:44 +00:00
if ( ! monitor_options )
2006-06-14 23:20:08 +00:00
monitor_options = " " ;
2006-05-05 22:02:38 +00:00
if ( strchr ( monitor_options , ' | ' ) ) {
ast_log ( LOG_WARNING , " MONITOR_OPTIONS cannot contain a '|'! Not recording. \n " ) ;
mixmonapp = NULL ;
}
if ( mixmonapp ) {
2006-07-14 05:42:06 +00:00
if ( ! ast_strlen_zero ( monitor_exec ) & & ! ast_strlen_zero ( monitor_options ) )
2006-05-05 22:02:38 +00:00
snprintf ( mixmonargs , sizeof ( mixmonargs ) - 1 , " %s|b%s|%s " , tmpid2 , monitor_options , monitor_exec ) ;
2006-07-14 05:42:06 +00:00
else
2006-05-08 12:30:46 +00:00
snprintf ( mixmonargs , sizeof ( mixmonargs ) - 1 , " %s|b%s " , tmpid2 , monitor_options ) ;
2006-05-05 22:02:38 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Arguments being passed to MixMonitor: %s \n " , mixmonargs ) ;
ret = pbx_exec ( qe - > chan , mixmonapp , mixmonargs ) ;
} else
ast_log ( LOG_WARNING , " Asked to run MixMonitor on this call, but cannot find the MixMonitor app! \n " ) ;
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 ) ) {
2004-06-23 20:05:18 +00:00
if ( option_debug )
2006-07-14 05:42:06 +00:00
ast_log ( LOG_DEBUG , " app_queue: sendurl=%s. \n " , url ) ;
ast_channel_sendurl ( peer , url ) ;
}
2006-10-27 18:59:16 +00:00
2006-10-27 19:28:34 +00:00
ast_mutex_lock ( & qe - > parent - > lock ) ;
2006-10-27 18:59:16 +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 */
2006-10-02 20:58:48 +00:00
if ( qe - > parent - > setinterfacevar ) {
2006-10-27 18:59:16 +00:00
snprintf ( interfacevar , sizeof ( interfacevar ) , " MEMBERINTERFACE=%s|MEMBERNAME=%s|MEMBERCALLS=%d|MEMBERLASTCALL=%ld|MEMBERPENALTY=%d|MEMBERDYNAMIC=%d " ,
2006-11-07 22:26:48 +00:00
member - > interface , member - > membername , member - > calls , ( long ) member - > lastcall , member - > penalty , member - > dynamic ) ;
2006-10-27 18:59:16 +00:00
pbx_builtin_setvar ( qe - > chan , interfacevar ) ;
2006-10-02 20:58:48 +00:00
}
2006-10-27 18:59:16 +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 ) {
snprintf ( interfacevar , sizeof ( interfacevar ) , " QEHOLDTIME=%ld|QEORIGINALPOS=%d " ,
( long ) time ( NULL ) - qe - > start , qe - > opos ) ;
pbx_builtin_setvar ( qe - > chan , interfacevar ) ;
}
/* try to set queue variables if configured to do so*/
set_queue_variables ( qe ) ;
2006-10-27 19:28:34 +00:00
ast_mutex_unlock ( & qe - > parent - > lock ) ;
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 ) ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " app_queue: macro=%s. \n " , macroexec ) ;
res = ast_autoservice_start ( qe - > chan ) ;
if ( res ) {
ast_log ( LOG_ERROR , " Unable to start autoservice on calling channel \n " ) ;
res = - 1 ;
}
app = pbx_findapp ( " Macro " ) ;
if ( app ) {
res = pbx_exec ( qe - > chan , app , macroexec ) ;
if ( option_debug )
ast_log ( LOG_DEBUG , " Macro exited with status %d \n " , res ) ;
res = 0 ;
} else {
ast_log ( LOG_ERROR , " Could not find application Macro \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 ) ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " app_queue: agi=%s. \n " , agi ) ;
app = pbx_findapp ( " agi " ) ;
if ( app ) {
agiexec = ast_strdupa ( agi ) ;
ret = pbx_exec ( qe - > chan , app , 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 " ) ;
}
2006-09-20 05:59:53 +00:00
ast_queue_log ( queuename , qe - > chan - > uniqueid , member - > membername , " CONNECT " , " %ld|%s " , ( long ) time ( NULL ) - qe - > start , peer - > uniqueid ) ;
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 "
" %s " ,
2006-09-20 05:59:53 +00:00
queuename , qe - > chan - > uniqueid , peer - > name , member - > interface , member - > membername ,
2006-06-22 15:43:02 +00:00
( long ) time ( NULL ) - qe - > start , peer - > uniqueid ,
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
2005-05-15 05:42:51 +00:00
ast_copy_string ( oldcontext , qe - > chan - > context , sizeof ( oldcontext ) ) ;
ast_copy_string ( oldexten , qe - > chan - > exten , sizeof ( oldexten ) ) ;
2004-02-12 22:11:02 +00:00
time ( & callstart ) ;
2004-04-26 23:22:34 +00:00
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
2004-02-12 22:11:02 +00:00
if ( strcasecmp ( oldcontext , qe - > chan - > context ) | | strcasecmp ( oldexten , qe - > chan - > exten ) ) {
2006-09-20 05:59:53 +00:00
ast_queue_log ( queuename , qe - > chan - > uniqueid , member - > membername , " TRANSFER " , " %s|%s|%ld|%ld " ,
2006-07-14 05:42:06 +00:00
qe - > chan - > exten , qe - > chan - > context , ( long ) ( callstart - qe - > start ) ,
( long ) ( time ( NULL ) - callstart ) ) ;
2004-02-12 22:11:02 +00:00
} else if ( qe - > chan - > _softhangup ) {
2005-03-28 20:48:24 +00:00
ast_queue_log ( queuename , qe - > chan - > uniqueid , peer - > name , " COMPLETECALLER " , " %ld|%ld " ,
2006-07-14 05:42:06 +00:00
( long ) ( callstart - qe - > start ) , ( long ) ( time ( NULL ) - callstart ) ) ;
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 , " AgentComplete " ,
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 "
" TalkTime: %ld \r \n "
" Reason: caller \r \n "
" %s " ,
2006-09-20 05:59:53 +00:00
queuename , qe - > chan - > uniqueid , peer - > name , member - > interface , member - > membername ,
2006-06-22 15:43:02 +00:00
( long ) ( callstart - qe - > start ) , ( long ) ( time ( NULL ) - callstart ) ,
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
2004-02-12 22:11:02 +00:00
} else {
2006-09-20 05:59:53 +00:00
ast_queue_log ( queuename , qe - > chan - > uniqueid , member - > membername , " COMPLETEAGENT " , " %ld|%ld " ,
2006-07-14 05:42:06 +00:00
( long ) ( callstart - qe - > start ) , ( long ) ( time ( NULL ) - callstart ) ) ;
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 , " AgentComplete " ,
2006-06-22 15:43:02 +00:00
" Queue: %s \r \n "
" Uniqueid: %s \r \n "
" Channel: %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 "
" TalkTime: %ld \r \n "
" Reason: agent \r \n "
" %s " ,
2006-09-20 05:59:53 +00:00
queuename , qe - > chan - > uniqueid , peer - > name , member - > membername , ( long ) ( callstart - qe - > start ) ,
2006-06-22 15:43:02 +00:00
( long ) ( time ( NULL ) - callstart ) ,
qe - > parent - > eventwhencalled = = QUEUE_EVENT_VARIABLES ? vars2manager ( qe - > chan , vars , sizeof ( vars ) ) : " " ) ;
2004-02-12 22:11:02 +00:00
}
2003-07-02 14:06:12 +00:00
2006-05-24 16:54:10 +00:00
if ( bridge ! = AST_PBX_NO_HANGUP_PEER )
2003-07-02 14:06:12 +00:00
ast_hangup ( peer ) ;
2004-05-30 22:10:09 +00:00
update_queue ( qe - > parent , member ) ;
2006-06-04 03:45:54 +00:00
res = bridge ? bridge : 1 ;
2006-02-14 21:50:35 +00:00
}
2002-09-02 23:15:40 +00:00
out :
2005-03-28 20:48:24 +00:00
hangupcalls ( outgoing , NULL ) ;
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
2002-09-02 23:15:40 +00:00
return ast_waitfordigit ( qe - > chan , retrywait ) ;
}
2006-06-15 13:35:04 +00:00
static struct member * interface_exists ( struct call_queue * q , char * interface )
2003-06-27 23:07:59 +00:00
{
struct member * mem ;
2006-06-14 23:20:08 +00:00
if ( ! q )
return NULL ;
for ( mem = q - > members ; mem ; mem = mem - > next ) {
if ( ! strcasecmp ( interface , mem - > interface ) )
return mem ;
}
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
}
2006-01-16 17:37:44 +00:00
/* Dump all members in a specific queue to the database
2004-12-06 05:54:16 +00:00
*
2005-03-03 20:31:21 +00:00
* < pm_family > / < queuename > = < interface > ; < penalty > ; < paused > [ | . . . ]
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 ;
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
2005-03-03 20:31:21 +00:00
for ( cur_member = pm_queue - > members ; cur_member ; cur_member = cur_member - > next ) {
if ( ! cur_member - > dynamic )
continue ;
2004-12-06 05:54:16 +00:00
2006-09-20 06:14:05 +00:00
res = snprintf ( value + value_len , sizeof ( value ) - value_len , " %s;%d;%d;%s%s " ,
cur_member - > interface , cur_member - > penalty , cur_member - > paused , cur_member - > membername ,
2006-07-14 05:42:06 +00:00
cur_member - > next ? " | " : " " ) ;
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
}
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
}
2004-07-28 02:55:22 +00:00
static int remove_from_queue ( char * queuename , char * interface )
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2004-07-28 02:55:22 +00:00
struct member * last_member , * look ;
int res = RES_NOSUCHQUEUE ;
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
2004-07-28 02:55:22 +00:00
ast_mutex_lock ( & q - > lock ) ;
2006-06-14 23:20:08 +00:00
if ( strcmp ( q - > name , queuename ) ) {
ast_mutex_unlock ( & q - > lock ) ;
continue ;
}
2004-12-06 05:54:16 +00:00
2006-06-14 23:20:08 +00:00
if ( ( last_member = interface_exists ( q , interface ) ) ) {
if ( ( look = q - > members ) = = last_member ) {
q - > members = last_member - > next ;
2004-07-28 02:55:22 +00:00
} else {
2006-06-14 23:20:08 +00:00
while ( look ! = NULL ) {
if ( look - > next = = last_member ) {
look - > next = last_member - > next ;
break ;
} else {
look = look - > next ;
}
}
2004-07-28 02:55:22 +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 " ,
q - > name , last_member - > interface , last_member - > membername ) ;
2006-06-14 23:20:08 +00:00
free ( last_member ) ;
if ( queue_persistent_members )
dump_queue_members ( q ) ;
res = RES_OKAY ;
} else {
res = RES_EXISTS ;
2004-07-28 02:55:22 +00:00
}
ast_mutex_unlock ( & q - > lock ) ;
2006-06-14 23:20:08 +00:00
break ;
2004-07-28 02:55:22 +00:00
}
2006-06-14 23:20:08 +00:00
if ( res = = RES_OKAY )
2006-05-25 21:47:03 +00:00
remove_from_interfaces ( interface ) ;
2006-06-14 23:20:08 +00:00
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2006-06-14 23:20:08 +00:00
2004-07-28 02:55:22 +00:00
return res ;
}
2006-05-25 21:47:03 +00:00
2006-09-20 05:59:53 +00:00
static int add_to_queue ( char * queuename , char * interface , char * membername , int penalty , int paused , int dump )
2004-07-28 02:55:22 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2004-07-28 02:55:22 +00:00
struct member * new_member ;
int res = RES_NOSUCHQUEUE ;
2006-01-21 20:32:17 +00:00
/* \note Ensure the appropriate realtime queue is loaded. Note that this
* short - circuits if the queue is already in memory . */
2006-06-14 23:20:08 +00:00
if ( ! ( q = load_realtime_queue ( queuename ) ) )
return res ;
2006-01-21 20:32:17 +00:00
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
2006-01-21 20:32:17 +00:00
2006-06-14 23:20:08 +00:00
ast_mutex_lock ( & q - > lock ) ;
if ( interface_exists ( q , interface ) = = NULL ) {
add_to_interfaces ( interface ) ;
2006-09-20 05:59:53 +00:00
if ( ( new_member = create_queue_member ( interface , membername , penalty , paused ) ) ) {
2006-06-14 23:20:08 +00:00
new_member - > dynamic = 1 ;
new_member - > next = q - > members ;
q - > members = new_member ;
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 "
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 " ,
2006-09-20 05:59:53 +00:00
q - > name , new_member - > interface , new_member - > membername ,
new_member - > dynamic ? " dynamic " : " static " ,
2006-07-14 05:42:06 +00:00
new_member - > penalty , new_member - > calls , ( int ) new_member - > lastcall ,
new_member - > status , new_member - > paused ) ;
2006-06-14 23:20:08 +00:00
if ( dump )
dump_queue_members ( q ) ;
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 {
res = RES_EXISTS ;
2004-07-28 02:55:22 +00:00
}
2006-06-14 23:20:08 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
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
2005-01-21 04:14:26 +00:00
static int set_member_paused ( char * queuename , char * interface , int paused )
{
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 ;
/* 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 " , " " ) ;
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
2005-01-21 04:14:26 +00:00
ast_mutex_lock ( & q - > lock ) ;
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 ) ) ) {
found + + ;
2006-10-03 15:53:07 +00:00
if ( mem - > paused = = paused ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " %spausing already-%spaused queue member %s:%s \n " , ( paused ? " " : " un " ) , ( paused ? " " : " un " ) , q - > name , interface ) ;
}
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
2006-09-20 05:59:53 +00:00
ast_queue_log ( q - > name , " NONE " , mem - > membername , ( paused ? " PAUSE " : " UNPAUSE " ) , " %s " , " " ) ;
2005-01-21 04:14:26 +00:00
manager_event ( EVENT_FLAG_AGENT , " QueueMemberPaused " ,
" Queue: %s \r \n "
" Location: %s \r \n "
2006-09-20 05:59:53 +00:00
" MemberName: %s \r \n "
2005-01-21 04:14:26 +00:00
" Paused: %d \r \n " ,
2006-09-20 05:59:53 +00:00
q - > name , mem - > interface , mem - > membername , paused ) ;
2005-01-21 04:14:26 +00:00
}
}
ast_mutex_unlock ( & q - > lock ) ;
}
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
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
}
2005-03-03 20:31:21 +00:00
/* Reload dynamic queue members persisted into the astdb */
2004-12-06 05:54:16 +00:00
static void reload_queue_members ( void )
{
2005-03-03 20:31:21 +00:00
char * cur_ptr ;
char * queue_name ;
char * member ;
char * interface ;
2006-09-20 05:59:53 +00:00
char * membername ;
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 ;
2006-06-15 13:35:04 +00:00
struct call_queue * cur_queue ;
2004-12-06 05:54:16 +00:00
char queue_data [ PM_MAX_LEN ] ;
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
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
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE ( & queues , cur_queue , list ) {
2004-12-06 05:54:16 +00:00
ast_mutex_lock ( & cur_queue - > lock ) ;
2005-03-03 20:31:21 +00:00
if ( ! strcmp ( queue_name , cur_queue - > name ) )
2005-01-03 01:42:37 +00:00
break ;
2004-12-06 05:54:16 +00:00
ast_mutex_unlock ( & cur_queue - > lock ) ;
}
if ( ! cur_queue ) {
/* If the queue no longer exists, remove it from the
* database */
2005-03-03 20:31:21 +00:00
ast_db_del ( pm_family , queue_name ) ;
2004-12-06 05:54:16 +00:00
continue ;
} else
2005-01-03 01:42:37 +00:00
ast_mutex_unlock ( & cur_queue - > lock ) ;
2004-12-06 05:54:16 +00:00
2005-03-03 20:31:21 +00:00
if ( ast_db_get ( pm_family , queue_name , queue_data , PM_MAX_LEN ) )
continue ;
2005-01-21 04:14:26 +00:00
2005-03-03 20:31:21 +00:00
cur_ptr = queue_data ;
while ( ( member = strsep ( & cur_ptr , " | " ) ) ) {
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 , " ; " ) ;
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 ;
}
2006-09-20 05:59:53 +00:00
if ( ast_strlen_zero ( membername ) )
membername = interface ;
2005-03-03 20:31:21 +00:00
if ( option_debug )
2006-09-20 05:59:53 +00:00
ast_log ( LOG_DEBUG , " 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
2006-09-28 20:13:09 +00:00
if ( add_to_queue ( queue_name , interface , membername , penalty , paused , 0 ) = = 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
}
}
}
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
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
}
}
2005-01-21 04:14:26 +00:00
static int pqm_exec ( struct ast_channel * chan , void * data )
{
2006-08-21 02:11:39 +00:00
struct ast_module_user * lu ;
2005-11-08 04:48:00 +00:00
char * parse ;
int priority_jump = 0 ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
AST_APP_ARG ( options ) ;
) ;
2005-01-21 04:14:26 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " PauseQueueMember requires an argument ([queuename]|interface[|options]) \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 ) ;
2006-08-21 02:11:39 +00:00
lu = ast_module_user_add ( chan ) ;
2006-01-23 17:23:22 +00:00
2005-11-08 04:48:00 +00:00
if ( args . options ) {
if ( strchr ( args . options , ' j ' ) )
priority_jump = 1 ;
}
if ( ast_strlen_zero ( args . interface ) ) {
ast_log ( LOG_WARNING , " Missing interface argument to PauseQueueMember ([queuename]|interface[|options]) \n " ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2005-11-08 04:48:00 +00:00
if ( set_member_paused ( args . queuename , args . interface , 1 ) ) {
ast_log ( LOG_WARNING , " Attempt to pause interface %s, not found \n " , args . interface ) ;
2005-12-04 20:40:46 +00:00
if ( priority_jump | | ast_opt_priority_jumping ) {
2005-11-08 04:48:00 +00:00
if ( ast_goto_if_exists ( chan , chan - > context , chan - > exten , chan - > priority + 101 ) ) {
pbx_builtin_setvar_helper ( chan , " PQMSTATUS " , " NOTFOUND " ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2005-11-08 04:48:00 +00:00
return 0 ;
}
2005-01-21 04:14:26 +00:00
}
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2005-11-08 04:48:00 +00:00
pbx_builtin_setvar_helper ( chan , " PQMSTATUS " , " NOTFOUND " ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
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 ;
}
static int upqm_exec ( struct ast_channel * chan , void * data )
{
2006-08-21 02:11:39 +00:00
struct ast_module_user * lu ;
2005-11-08 04:48:00 +00:00
char * parse ;
int priority_jump = 0 ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( queuename ) ;
AST_APP_ARG ( interface ) ;
AST_APP_ARG ( options ) ;
) ;
2005-01-21 04:14:26 +00:00
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2005-11-08 04:48:00 +00:00
ast_log ( LOG_WARNING , " UnpauseQueueMember requires an argument ([queuename]|interface[|options]) \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 ) ;
2006-08-21 02:11:39 +00:00
lu = ast_module_user_add ( chan ) ;
2006-01-23 17:23:22 +00:00
2005-11-08 04:48:00 +00:00
if ( args . options ) {
if ( strchr ( args . options , ' j ' ) )
priority_jump = 1 ;
}
if ( ast_strlen_zero ( args . interface ) ) {
ast_log ( LOG_WARNING , " Missing interface argument to PauseQueueMember ([queuename]|interface[|options]) \n " ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2005-11-08 04:48:00 +00:00
if ( set_member_paused ( args . queuename , args . interface , 0 ) ) {
ast_log ( LOG_WARNING , " Attempt to unpause interface %s, not found \n " , args . interface ) ;
2005-12-04 20:40:46 +00:00
if ( priority_jump | | ast_opt_priority_jumping ) {
2005-11-08 04:48:00 +00:00
if ( ast_goto_if_exists ( chan , chan - > context , chan - > exten , chan - > priority + 101 ) ) {
pbx_builtin_setvar_helper ( chan , " UPQMSTATUS " , " NOTFOUND " ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2005-11-08 04:48:00 +00:00
return 0 ;
}
2005-01-21 04:14:26 +00:00
}
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2005-11-08 04:48:00 +00:00
pbx_builtin_setvar_helper ( chan , " UPQMSTATUS " , " NOTFOUND " ) ;
2005-01-21 04:14:26 +00:00
return - 1 ;
}
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
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 ;
}
2003-06-27 23:07:59 +00:00
static int rqm_exec ( struct ast_channel * chan , void * data )
{
int res = - 1 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * lu ;
2005-11-08 04:48:00 +00:00
char * parse , * temppos = NULL ;
int priority_jump = 0 ;
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 ) ) {
2005-11-08 04:48:00 +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 ) ;
2006-08-21 02:11:39 +00:00
lu = ast_module_user_add ( chan ) ;
2006-01-23 17:23:22 +00:00
2005-11-08 04:48:00 +00:00
if ( ast_strlen_zero ( args . interface ) ) {
2005-11-10 23:22:37 +00:00
args . interface = ast_strdupa ( chan - > name ) ;
2005-11-08 04:48:00 +00:00
temppos = strrchr ( args . interface , ' - ' ) ;
if ( temppos )
* temppos = ' \0 ' ;
2003-06-27 23:07:59 +00:00
}
2005-11-08 04:48:00 +00:00
if ( args . options ) {
if ( strchr ( args . options , ' j ' ) )
priority_jump = 1 ;
}
switch ( remove_from_queue ( args . queuename , args . interface ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2006-11-10 16:37:42 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , args . interface , " REMOVEMEMBER " , " %s " , " " ) ;
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 :
2006-10-03 15:53:07 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Unable to remove interface '%s' from queue '%s': Not there \n " , args . interface , args . queuename ) ;
2006-07-14 05:42:06 +00:00
if ( priority_jump | | ast_opt_priority_jumping )
2005-11-08 04:48:00 +00:00
ast_goto_if_exists ( chan , chan - > context , chan - > exten , chan - > priority + 101 ) ;
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 ;
}
2003-06-27 23:07:59 +00:00
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2006-06-14 23:20:08 +00:00
2003-06-27 23:07:59 +00:00
return res ;
}
static int aqm_exec ( struct ast_channel * chan , void * data )
{
int res = - 1 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * lu ;
2005-11-08 04:48:00 +00:00
char * parse , * temppos = NULL ;
int priority_jump = 0 ;
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 ) ;
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 ) ) {
2006-09-20 05:59:53 +00:00
ast_log ( LOG_WARNING , " AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]]) \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 ) ;
2006-08-21 02:11:39 +00:00
lu = ast_module_user_add ( chan ) ;
2006-01-23 17:23:22 +00:00
2005-11-08 04:48:00 +00:00
if ( ast_strlen_zero ( args . interface ) ) {
2005-11-10 23:22:37 +00:00
args . interface = ast_strdupa ( chan - > name ) ;
2005-11-08 04:48:00 +00:00
temppos = strrchr ( args . interface , ' - ' ) ;
if ( temppos )
* temppos = ' \0 ' ;
}
if ( ! ast_strlen_zero ( args . penalty ) ) {
if ( ( sscanf ( args . penalty , " %d " , & penalty ) ! = 1 ) | | penalty < 0 ) {
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
if ( args . options ) {
if ( strchr ( args . options , ' j ' ) )
priority_jump = 1 ;
}
2006-09-20 05:59:53 +00:00
if ( ast_strlen_zero ( args . membername ) )
args . membername = args . interface ;
2003-06-27 23:07:59 +00:00
2006-09-20 05:59:53 +00:00
switch ( add_to_queue ( args . queuename , args . interface , args . membername , penalty , 0 , queue_persistent_members ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2006-11-10 16:37:42 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , args . interface , " ADDMEMBER " , " %s " , " " ) ;
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 ) ;
2006-07-14 05:42:06 +00:00
if ( priority_jump | | ast_opt_priority_jumping )
2005-11-08 04:48:00 +00:00
ast_goto_if_exists ( chan , chan - > context , chan - > exten , chan - > priority + 101 ) ;
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 :
2005-11-08 04:48:00 +00:00
ast_log ( LOG_ERROR , " Out of memory adding member %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
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2006-06-14 23:20:08 +00:00
2003-06-27 23:07:59 +00:00
return res ;
}
2006-07-16 19:36:29 +00:00
static int ql_exec ( struct ast_channel * chan , void * data )
{
2006-08-21 02:11:39 +00:00
struct ast_module_user * u ;
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 ) ) {
2006-09-20 05:59:53 +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-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan ) ;
2006-07-16 19:36:29 +00:00
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 ) ) {
ast_log ( LOG_WARNING , " QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]) \n " ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u ) ;
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 : " " ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u ) ;
2006-07-16 19:36:29 +00:00
return 0 ;
}
2002-09-02 23:15:40 +00:00
static int queue_exec ( struct ast_channel * chan , void * data )
{
int res = - 1 ;
2004-05-09 19:13:43 +00:00
int ringing = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * lu ;
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 ;
2004-06-23 20:05:18 +00:00
int prio ;
2006-01-06 03:40:12 +00:00
int max_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 */
int go_on = 0 ;
2006-01-23 17:23:22 +00:00
char * parse ;
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 ) ;
2006-01-23 17:23:22 +00:00
) ;
2002-09-02 23:15:40 +00:00
/* Our queue entry */
struct queue_ent qe ;
2005-10-26 19:48:14 +00:00
if ( ast_strlen_zero ( data ) ) {
2006-10-25 19:10:43 +00:00
ast_log ( LOG_WARNING , " Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]] \n " ) ;
2002-09-02 23:15:40 +00:00
return - 1 ;
}
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
2006-08-21 02:11:39 +00:00
lu = ast_module_user_add ( chan ) ;
2004-03-13 06:00:41 +00:00
/* Setup our queue entry */
memset ( & qe , 0 , sizeof ( qe ) ) ;
2004-12-19 21:23:36 +00:00
qe . start = time ( NULL ) ;
/* set the expire time based on the supplied timeout; */
2006-01-23 17:23:22 +00:00
if ( args . queuetimeoutstr )
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} */
user_priority = pbx_builtin_getvar_helper ( chan , " QUEUE_PRIO " ) ;
if ( user_priority ) {
if ( sscanf ( user_priority , " %d " , & prio ) = = 1 ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " %s: Got priority %d from ${QUEUE_PRIO}. \n " ,
2005-03-28 20:48:24 +00:00
chan - > name , prio ) ;
2004-06-23 20:05:18 +00:00
} else {
ast_log ( LOG_WARNING , " ${QUEUE_PRIO}: Invalid value (%s), channel %s. \n " ,
2005-03-28 20:48:24 +00:00
user_priority , chan - > name ) ;
2004-06-23 20:05:18 +00:00
prio = 0 ;
}
} else {
2005-01-03 01:42:37 +00:00
if ( option_debug > 2 )
2004-06-23 20:05:18 +00:00
ast_log ( LOG_DEBUG , " NO QUEUE_PRIO variable found. Using default. \n " ) ;
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} */
if ( ( max_penalty_str = pbx_builtin_getvar_helper ( chan , " QUEUE_MAX_PENALTY " ) ) ) {
if ( sscanf ( max_penalty_str , " %d " , & max_penalty ) = = 1 ) {
if ( option_debug )
ast_log ( LOG_DEBUG , " %s: Got max penalty %d from ${QUEUE_MAX_PENALTY}. \n " ,
chan - > name , max_penalty ) ;
} else {
ast_log ( LOG_WARNING , " ${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s. \n " ,
max_penalty_str , chan - > name ) ;
max_penalty = 0 ;
}
} else {
max_penalty = 0 ;
}
2006-01-23 17:23:22 +00:00
if ( args . options & & ( strchr ( args . options , ' r ' ) ) )
2005-03-28 20:48:24 +00:00
ringing = 1 ;
2004-03-13 06:00:41 +00:00
2006-07-14 05:42:06 +00:00
if ( option_debug )
2004-12-19 21:23:36 +00:00
ast_log ( LOG_DEBUG , " queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d \n " ,
2006-01-23 17:23:22 +00:00
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 ;
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 ;
2006-01-23 17:23:22 +00:00
if ( ! join_queue ( args . queuename , & qe , & reason ) ) {
2006-04-21 10:57:03 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " ENTERQUEUE " , " %s|%s " , S_OR ( args . url , " " ) ,
2006-07-14 05:42:06 +00:00
S_OR ( chan - > cid . cid_num , " " ) ) ;
2004-06-23 20:05:18 +00:00
check_turns :
2004-05-09 19:13:43 +00:00
if ( ringing ) {
ast_indicate ( chan , AST_CONTROL_RINGING ) ;
2006-04-04 18:02:40 +00:00
} else {
2006-07-19 20:44:39 +00:00
ast_moh_start ( chan , qe . moh , NULL ) ;
2004-05-09 19:13:43 +00:00
}
2002-09-02 23:15:40 +00:00
for ( ; ; ) {
2004-03-13 06:00:41 +00:00
/* This is the wait loop for callers 2 through maxlen */
2005-03-28 20:48:24 +00:00
res = wait_our_turn ( & qe , ringing , & reason ) ;
2002-09-02 23:15:40 +00:00
/* If they hungup, return immediately */
if ( res < 0 ) {
2004-03-13 06:00:41 +00:00
/* Record this abandoned call */
record_abandoned ( & qe ) ;
2006-06-14 23:20:08 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " ABANDON " , " %d|%d|%ld " ,
2006-07-14 05:42:06 +00:00
qe . pos , qe . opos , ( long ) time ( NULL ) - qe . start ) ;
2002-09-02 23:15:40 +00:00
if ( option_verbose > 2 ) {
2006-01-23 17:23:22 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " User disconnected from queue %s while waiting their turn \n " , args . queuename ) ;
2002-09-02 23:15:40 +00:00
}
2006-04-04 18:02:40 +00:00
res = - 1 ;
2002-09-02 23:15:40 +00:00
break ;
}
2006-07-14 05:42:06 +00:00
if ( ! res )
2002-09-02 23:15:40 +00:00
break ;
2004-02-12 22:11:02 +00:00
if ( valid_exit ( & qe , res ) ) {
2006-01-23 17:23:22 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHKEY " , " %s|%d " , qe . digits , qe . pos ) ;
2002-09-02 23:15:40 +00:00
break ;
2004-02-12 22:11:02 +00:00
}
2002-09-02 23:15:40 +00:00
}
if ( ! res ) {
2004-10-26 22:25:43 +00:00
int makeannouncement = 0 ;
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
for ( ; ; ) {
2004-03-13 06:00:41 +00:00
/* 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; */
2004-09-28 03:32:21 +00:00
/* or, they may timeout. */
2004-03-13 06:00:41 +00:00
2005-03-28 20:48:24 +00:00
enum queue_member_status stat ;
2004-03-13 06:00:41 +00:00
/* Leave if we have exceeded our queuetimeout */
2004-12-19 21:23:36 +00:00
if ( qe . expire & & ( time ( NULL ) > qe . expire ) ) {
2006-04-04 18:02:40 +00:00
record_abandoned ( & qe ) ;
2005-03-28 20:48:24 +00:00
reason = QUEUE_TIMEOUT ;
2004-03-13 06:00:41 +00:00
res = 0 ;
2006-09-20 05:59:53 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHTIMEOUT " , " %d " , qe . pos ) ;
2004-03-13 06:00:41 +00:00
break ;
}
2004-10-26 22:25:43 +00:00
if ( makeannouncement ) {
/* Make a position announcement, if enabled */
2006-05-24 16:54:10 +00:00
if ( qe . parent - > announcefrequency & & ! ringing & &
2006-07-14 05:42:06 +00:00
( res = say_position ( & qe ) ) ) {
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHKEY " , " %s|%d " , qe . digits , qe . pos ) ;
2005-05-15 05:42:51 +00:00
break ;
}
2004-09-28 03:32:21 +00:00
}
2004-10-26 22:25:43 +00:00
makeannouncement = 1 ;
2004-03-13 06:00:41 +00:00
2005-07-31 22:07:58 +00:00
/* Make a periodic announcement, if enabled */
2006-05-24 16:54:10 +00:00
if ( qe . parent - > periodicannouncefrequency & & ! ringing & &
2006-07-14 05:42:06 +00:00
( res = say_periodic_announcement ( & qe ) ) ) {
2006-01-23 17:23:22 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHKEY " , " %c|%d " , res , qe . pos ) ;
2005-07-31 22:07:58 +00:00
break ;
}
2004-03-13 06:00:41 +00:00
/* Try calling all queue members for 'timeout' seconds */
2006-10-27 18:59:16 +00:00
res = try_calling ( & qe , args . options , args . announceoverride , args . url , & go_on , args . agi , args . macro ) ;
2004-02-12 22:11:02 +00:00
if ( res ) {
if ( res < 0 ) {
2005-08-07 14:11:48 +00:00
if ( ! qe . handled ) {
record_abandoned ( & qe ) ;
2006-06-14 23:20:08 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " ABANDON " ,
2006-07-14 05:42:06 +00:00
" %d|%d|%ld " , qe . pos , qe . opos ,
( long ) time ( NULL ) - qe . start ) ;
2005-08-07 14:11:48 +00:00
}
2006-05-24 16:54:10 +00:00
} else if ( valid_exit ( & qe , res ) ) {
2006-07-14 05:42:06 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHKEY " ,
" %s|%d " , qe . digits , qe . pos ) ;
2006-05-24 16:54:10 +00:00
}
2002-09-02 23:15:40 +00:00
break ;
2004-02-12 22:11:02 +00:00
}
2004-03-13 06:00:41 +00:00
2006-01-06 03:40:12 +00:00
stat = get_member_status ( qe . parent , qe . max_penalty ) ;
2005-03-28 20:48:24 +00:00
2004-10-26 22:25:43 +00:00
/* leave the queue if no agents, if enabled */
2005-03-28 20:48:24 +00:00
if ( qe . parent - > leavewhenempty & & ( stat = = QUEUE_NO_MEMBERS ) ) {
2005-08-07 14:11:48 +00:00
record_abandoned ( & qe ) ;
2005-03-28 20:48:24 +00:00
reason = QUEUE_LEAVEEMPTY ;
res = 0 ;
break ;
}
/* leave the queue if no reachable agents, if enabled */
2006-11-13 18:23:55 +00:00
if ( ( qe . parent - > leavewhenempty = = QUEUE_EMPTY_STRICT ) & & ( stat = = QUEUE_NO_REACHABLE_MEMBERS | | stat = = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS ) ) {
record_abandoned ( & qe ) ;
reason = QUEUE_LEAVEUNAVAIL ;
res = 0 ;
break ;
}
if ( ( qe . parent - > leavewhenempty = = QUEUE_EMPTY_LOOSE ) & & ( stat = = QUEUE_NO_REACHABLE_MEMBERS ) ) {
2005-08-07 14:11:48 +00:00
record_abandoned ( & qe ) ;
2005-03-28 20:48:24 +00:00
reason = QUEUE_LEAVEUNAVAIL ;
2004-10-26 22:25:43 +00:00
res = 0 ;
break ;
}
2004-03-13 06:00:41 +00:00
/* Leave if we have exceeded our queuetimeout */
2004-12-19 21:23:36 +00:00
if ( qe . expire & & ( time ( NULL ) > qe . expire ) ) {
2005-08-07 14:11:48 +00:00
record_abandoned ( & qe ) ;
2005-03-28 20:48:24 +00:00
reason = QUEUE_TIMEOUT ;
2004-03-13 06:00:41 +00:00
res = 0 ;
2006-09-20 05:59:53 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHTIMEOUT " , " %d " , qe . pos ) ;
2004-03-13 06:00:41 +00:00
break ;
}
/* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
2002-09-02 23:15:40 +00:00
res = wait_a_bit ( & qe ) ;
if ( res < 0 ) {
2005-08-07 14:11:48 +00:00
record_abandoned ( & qe ) ;
2006-01-23 17:23:22 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " ABANDON " , " %d|%d|%ld " , qe . pos , qe . opos , ( long ) time ( NULL ) - qe . start ) ;
2002-09-02 23:15:40 +00:00
if ( option_verbose > 2 ) {
2006-01-23 17:23:22 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " User disconnected from queue %s when they almost made it \n " , args . queuename ) ;
2002-09-02 23:15:40 +00:00
}
2006-04-04 18:02:40 +00:00
res = - 1 ;
2002-09-02 23:15:40 +00:00
break ;
}
2004-02-12 22:11:02 +00:00
if ( res & & valid_exit ( & qe , res ) ) {
2006-01-23 17:23:22 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHKEY " , " %s|%d " , qe . digits , qe . pos ) ;
2002-09-02 23:15:40 +00:00
break ;
2004-02-12 22:11:02 +00:00
}
2004-03-13 06:00:41 +00:00
/* exit after 'timeout' cycle if 'n' option enabled */
2004-02-03 00:53:21 +00:00
if ( go_on ) {
2006-04-04 18:02:40 +00:00
if ( option_verbose > 2 )
2004-03-23 21:45:38 +00:00
ast_verbose ( VERBOSE_PREFIX_3 " Exiting on time-out cycle \n " ) ;
2006-01-23 17:23:22 +00:00
ast_queue_log ( args . queuename , chan - > uniqueid , " NONE " , " EXITWITHTIMEOUT " , " %d " , qe . pos ) ;
2006-04-04 18:02:40 +00:00
record_abandoned ( & qe ) ;
2005-03-28 20:48:24 +00:00
reason = QUEUE_TIMEOUT ;
2004-02-03 00:53:21 +00:00
res = 0 ;
break ;
}
2006-07-14 05:42:06 +00:00
/* Since this is a priority queue and
2004-06-23 20:05:18 +00:00
* 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 ) ) {
2005-01-03 01:42:37 +00:00
if ( option_debug )
ast_log ( LOG_DEBUG , " Darn priorities, going back in queue (%s)! \n " ,
2006-05-24 16:54:10 +00:00
qe . chan - > name ) ;
2004-06-23 20:05:18 +00:00
goto check_turns ;
}
2002-09-02 23:15:40 +00:00
}
}
/* Don't allow return code > 0 */
2004-11-20 15:15:51 +00:00
if ( res > = 0 & & res ! = AST_PBX_KEEPALIVE ) {
2002-09-02 23:15:40 +00:00
res = 0 ;
2004-05-09 19:13:43 +00:00
if ( ringing ) {
ast_indicate ( chan , - 1 ) ;
} else {
ast_moh_stop ( chan ) ;
}
2004-03-13 06:00:41 +00:00
ast_stopstream ( chan ) ;
2003-06-14 16:01:32 +00:00
}
2006-10-27 18:59:16 +00:00
set_queue_variables ( & qe ) ;
2002-09-02 23:15:40 +00:00
leave_queue ( & qe ) ;
2005-03-28 20:48:24 +00:00
if ( reason ! = QUEUE_UNKNOWN )
set_queue_result ( chan , reason ) ;
2002-09-02 23:15:40 +00:00
} else {
2006-01-23 17:23:22 +00:00
ast_log ( LOG_WARNING , " Unable to join queue '%s' \n " , args . queuename ) ;
2005-03-28 20:48:24 +00:00
set_queue_result ( chan , reason ) ;
res = 0 ;
2002-09-02 23:15:40 +00:00
}
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2006-06-14 23:20:08 +00:00
2002-09-02 23:15:40 +00:00
return res ;
}
2006-10-27 18:59:16 +00:00
static int queue_function_var ( struct ast_channel * chan , char * cmd , char * data , char * buf , size_t len )
{
int res = - 1 ;
struct call_queue * q ;
struct ast_module_user * lu ;
char interfacevar [ 256 ] = " " ;
float sl = 0 ;
buf [ 0 ] = ' \0 ' ;
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_ERROR , " %s requires an argument: queuename \n " , cmd ) ;
return - 1 ;
}
lu = ast_module_user_add ( chan ) ;
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
if ( ! strcasecmp ( q - > name , data ) ) {
ast_mutex_lock ( & q - > lock ) ;
break ;
}
}
AST_LIST_UNLOCK ( & queues ) ;
if ( q ) {
if ( q - > setqueuevar ) {
sl = 0 ;
res = 0 ;
if ( q - > callscompleted > 0 )
sl = 100 * ( ( float ) q - > callscompletedinsl / ( float ) q - > callscompleted ) ;
snprintf ( interfacevar , sizeof ( interfacevar ) ,
" QUEUEMAX=%d|QUEUESTRATEGY=%s|QUEUECALLS=%d|QUEUEHOLDTIME=%d|QUEUECOMPLETED=%d|QUEUEABANDONED=%d|QUEUESRVLEVEL=%d|QUEUESRVLEVELPERF=%2.1f " ,
q - > maxlen , int2strat ( q - > strategy ) , q - > count , q - > holdtime , q - > callscompleted , q - > callsabandoned , q - > servicelevel , sl ) ;
pbx_builtin_setvar ( chan , interfacevar ) ;
}
ast_mutex_unlock ( & q - > lock ) ;
} else
ast_log ( LOG_WARNING , " queue %s was not found \n " , data ) ;
snprintf ( buf , len , " %d " , res ) ;
ast_module_user_remove ( lu ) ;
return 0 ;
}
2006-02-12 04:28:58 +00:00
static int queue_function_qac ( struct ast_channel * chan , char * cmd , char * data , char * buf , size_t len )
2005-07-12 22:28:51 +00:00
{
int count = 0 ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * lu ;
2005-07-12 22:28:51 +00:00
struct member * m ;
2006-02-12 04:28:58 +00:00
buf [ 0 ] = ' \0 ' ;
2005-10-19 18:19:02 +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
}
2006-08-21 02:11:39 +00:00
lu = ast_module_user_add ( chan ) ;
2006-01-23 17:23:22 +00:00
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
2005-07-12 22:28:51 +00:00
if ( ! strcasecmp ( q - > name , data ) ) {
ast_mutex_lock ( & q - > lock ) ;
break ;
}
}
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2005-07-12 22:28:51 +00:00
if ( q ) {
for ( m = q - > members ; m ; m = m - > next ) {
/* Count the agents who are logged in and presently answering calls */
if ( ( m - > status ! = AST_DEVICE_UNAVAILABLE ) & & ( m - > status ! = AST_DEVICE_INVALID ) ) {
count + + ;
}
}
ast_mutex_unlock ( & q - > lock ) ;
2006-04-26 22:04:42 +00:00
} else
ast_log ( LOG_WARNING , " queue %s was not found \n " , data ) ;
snprintf ( buf , len , " %d " , count ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2006-06-14 23:20:08 +00:00
2006-04-26 22:04:42 +00:00
return 0 ;
}
static int queue_function_queuewaitingcount ( struct ast_channel * chan , char * cmd , char * data , char * buf , size_t len )
{
int count = 0 ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * lu ;
2006-04-26 22:04:42 +00:00
buf [ 0 ] = ' \0 ' ;
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_ERROR , " %s requires an argument: queuename \n " , cmd ) ;
return - 1 ;
}
2006-08-21 02:11:39 +00:00
lu = ast_module_user_add ( chan ) ;
2006-04-26 22:04:42 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
if ( ! strcasecmp ( q - > name , data ) ) {
ast_mutex_lock ( & q - > lock ) ;
break ;
}
2005-07-12 22:28:51 +00:00
}
2006-04-26 22:04:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
if ( q ) {
count = q - > count ;
ast_mutex_unlock ( & q - > lock ) ;
} 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 ) ;
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( lu ) ;
2006-02-12 04:28:58 +00:00
return 0 ;
2005-07-12 22:28:51 +00:00
}
2006-02-12 04:28:58 +00:00
static int queue_function_queuememberlist ( struct ast_channel * chan , char * cmd , char * data , char * buf , size_t len )
2006-01-13 22:59:19 +00:00
{
2006-08-21 02:11:39 +00:00
struct ast_module_user * u ;
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
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
}
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan ) ;
2006-01-13 22:59:19 +00:00
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
2006-01-13 22:59:19 +00:00
if ( ! strcasecmp ( q - > name , data ) ) {
ast_mutex_lock ( & q - > lock ) ;
break ;
}
}
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2006-01-13 22:59:19 +00:00
if ( q ) {
int buflen = 0 , count = 0 ;
2006-06-14 23:20:08 +00:00
2006-01-13 22:59:19 +00:00
for ( m = q - > members ; m ; m = m - > next ) {
/* strcat() is always faster than printf() */
if ( count + + ) {
strncat ( buf + buflen , " , " , len - buflen - 1 ) ;
buflen + + ;
}
strncat ( buf + buflen , m - > interface , len - buflen - 1 ) ;
buflen + = strlen ( m - > interface ) ;
/* Safeguard against overflow (negative length) */
if ( buflen > = len - 2 ) {
ast_log ( LOG_WARNING , " Truncating list \n " ) ;
break ;
}
}
ast_mutex_unlock ( & q - > lock ) ;
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-08-21 02:11:39 +00:00
ast_module_user_remove ( u ) ;
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
}
2006-10-27 18:59:16 +00:00
static struct ast_custom_function queuevar_function = {
. name = " QUEUE_VARIABLES " ,
. synopsis = " Return Queue information in variables " ,
. syntax = " QUEUE_VARIABLES(<queuename>) " ,
. desc =
" Makes the following queue variables available. \n "
" QUEUEMAX maxmimum number of calls allowed \n "
" QUEUESTRATEGY the strategy of the queue \n "
" QUEUECALLS number of calls currently in the queue \n "
" QUEUEHOLDTIME current average hold time \n "
" QUEUECOMPLETED number of completed calls for the queue \n "
" QUEUEABANDONED number of abandoned calls \n "
" QUEUESRVLEVEL queue service level \n "
" QUEUESRVLEVELPERF current service level performance \n "
" Returns 0 if queue is found and setqueuevar is defined, -1 otherwise " ,
. read = queue_function_var ,
} ;
2006-01-13 22:59:19 +00:00
static struct ast_custom_function queuemembercount_function = {
. name = " QUEUE_MEMBER_COUNT " ,
. synopsis = " Count number of members answering a queue " ,
. syntax = " QUEUE_MEMBER_COUNT(<queuename>) " ,
. desc =
" Returns the number of members currently associated with the specified queue. \n " ,
. read = queue_function_qac ,
} ;
2006-04-26 22:04:42 +00:00
static struct ast_custom_function queuewaitingcount_function = {
. name = " QUEUE_WAITING_COUNT " ,
. synopsis = " Count number of calls currently waiting in a queue " ,
. syntax = " QUEUE_WAITING_COUNT(<queuename>) " ,
2006-07-14 05:42:06 +00:00
. desc =
2006-04-26 22:04:42 +00:00
" Returns the number of callers currently waiting in the specified queue. \n " ,
. read = queue_function_queuewaitingcount ,
} ;
2006-01-13 22:59:19 +00:00
static struct ast_custom_function queuememberlist_function = {
. name = " QUEUE_MEMBER_LIST " ,
. synopsis = " Returns a list of interfaces on a queue " ,
. syntax = " QUEUE_MEMBER_LIST(<queuename>) " ,
. desc =
" Returns a comma-separated list of members associated with the specified queue. \n " ,
. read = queue_function_queuememberlist ,
} ;
2006-08-31 21:00:20 +00:00
static int reload_queues ( void )
2002-09-02 23:15:40 +00:00
{
2006-06-15 13:35:04 +00:00
struct call_queue * q ;
2002-09-02 23:15:40 +00:00
struct ast_config * cfg ;
char * cat , * tmp ;
struct ast_variable * var ;
2006-09-28 17:38:07 +00:00
struct member * prev , * cur , * newm , * next ;
2002-09-02 23:15:40 +00:00
int new ;
2006-09-20 20:40:39 +00:00
const char * general_val = NULL ;
2006-09-20 05:59:53 +00:00
char parse [ 80 ] ;
char * interface ;
char * membername ;
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 ) ;
) ;
2004-12-06 05:54:16 +00:00
2006-06-14 23:20:08 +00:00
if ( ! ( cfg = ast_config_load ( " queues.conf " ) ) ) {
2005-01-03 01:42:37 +00:00
ast_log ( LOG_NOTICE , " No call queueing config file (queues.conf), so no call queues \n " ) ;
2006-08-31 21:00:20 +00:00
return 0 ;
2002-09-02 23:15:40 +00:00
}
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
2005-01-07 04:05:22 +00:00
use_weight = 0 ;
2002-09-02 23:15:40 +00:00
/* Mark all queues as dead for the moment */
2006-06-14 23:20:08 +00:00
AST_LIST_TRAVERSE ( & queues , q , list )
2005-03-28 20:48:24 +00:00
q - > dead = 1 ;
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 ) ) ) {
2005-06-02 21:42:49 +00:00
if ( ! strcasecmp ( cat , " general " ) ) {
/* Initialize global settings */
2006-10-02 15:40:38 +00:00
queue_keep_stats = 0 ;
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " keepstats " ) ) )
queue_keep_stats = ast_true ( general_val ) ;
2005-06-02 21:42:49 +00:00
queue_persistent_members = 0 ;
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " persistentmembers " ) ) )
queue_persistent_members = ast_true ( general_val ) ;
2006-05-03 20:01:30 +00:00
autofill_default = 0 ;
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " autofill " ) ) )
autofill_default = ast_true ( general_val ) ;
2006-05-05 22:02:38 +00:00
montype_default = 0 ;
if ( ( general_val = ast_variable_retrieve ( cfg , " general " , " monitor-type " ) ) )
if ( ! strcasecmp ( general_val , " mixmonitor " ) )
montype_default = 1 ;
2005-06-02 21:42:49 +00:00
} else { /* Define queue */
2002-09-02 23:15:40 +00:00
/* Look for an existing one */
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE ( & queues , q , list ) {
2002-09-02 23:15:40 +00:00
if ( ! strcmp ( q - > name , cat ) )
break ;
}
if ( ! q ) {
/* Make one then */
2006-01-13 03:25:23 +00:00
if ( ! ( q = alloc_queue ( cat ) ) ) {
/* TODO: Handle memory allocation failure */
}
2005-06-02 21:42:49 +00:00
new = 1 ;
2002-09-02 23:15:40 +00:00
} else
2005-06-02 21:42:49 +00:00
new = 0 ;
2002-09-02 23:15:40 +00:00
if ( q ) {
2005-06-02 21:42:49 +00:00
if ( ! new )
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & q - > lock ) ;
2005-06-02 21:42:49 +00:00
/* Re-initialize the queue, and clear statistics */
init_queue ( q ) ;
2006-10-02 15:40:38 +00:00
if ( ! queue_keep_stats )
clear_queue ( q ) ;
2006-05-04 16:38:32 +00:00
for ( cur = q - > members ; cur ; cur = cur - > next ) {
if ( ! cur - > dynamic ) {
cur - > delme = 1 ;
}
2003-08-18 20:51:54 +00:00
}
2006-02-14 21:50:35 +00:00
for ( var = ast_variable_browse ( cfg , cat ) ; var ; var = var - > next ) {
2002-09-02 23:15:40 +00:00
if ( ! strcasecmp ( var - > name , " member " ) ) {
/* Add a new member */
2006-09-20 05:59:53 +00:00
ast_copy_string ( parse , var - > value , sizeof ( parse ) ) ;
AST_NONSTANDARD_APP_ARGS ( args , parse , ' , ' ) ;
interface = args . interface ;
if ( ! ast_strlen_zero ( args . penalty ) ) {
tmp = args . penalty ;
while ( * tmp & & * tmp < 33 ) tmp + + ;
2005-06-02 21:42:49 +00:00
penalty = atoi ( tmp ) ;
if ( penalty < 0 ) {
penalty = 0 ;
2003-07-30 16:10:51 +00:00
}
2005-06-02 21:42:49 +00:00
} else
penalty = 0 ;
2006-05-04 16:38:32 +00:00
2006-09-20 05:59:53 +00:00
if ( ! ast_strlen_zero ( args . membername ) ) {
membername = args . membername ;
while ( * membername & & * membername < 33 ) membername + + ;
} else
membername = interface ;
2006-05-04 16:38:32 +00:00
/* Find the old position in the list */
for ( prev = NULL , cur = q - > members ; cur ; prev = cur , cur = cur - > next ) {
if ( ! strcmp ( cur - > interface , interface ) ) {
break ;
}
}
2006-09-20 05:59:53 +00:00
newm = create_queue_member ( interface , membername , penalty , cur ? cur - > paused : 0 ) ;
2006-05-04 16:38:32 +00:00
2005-06-02 21:42:49 +00:00
if ( cur ) {
2006-05-04 16:38:32 +00:00
/* Delete it now */
newm - > next = cur - > next ;
if ( prev ) {
prev - > next = newm ;
} else {
q - > members = newm ;
}
free ( cur ) ;
} else {
2006-05-25 21:47:03 +00:00
/* Add them to the master int list if necessary */
add_to_interfaces ( interface ) ;
2006-05-04 16:38:32 +00:00
newm - > next = q - > members ;
q - > members = newm ;
2002-09-02 23:15:40 +00:00
}
} else {
2005-06-02 21:42:49 +00:00
queue_set_param ( q , var - > name , var - > value , var - > lineno , 1 ) ;
2002-09-02 23:15:40 +00:00
}
}
2006-05-04 16:38:32 +00:00
/* Free remaining members marked as delme */
2006-10-03 20:19:32 +00:00
for ( prev = NULL , cur = q - > members ;
2006-09-28 17:38:07 +00:00
cur ;
2006-10-03 20:19:32 +00:00
cur = next ) {
next = cur - > next ;
if ( ! cur - > delme ) {
prev = cur ;
2006-09-28 17:38:07 +00:00
continue ;
2006-10-03 20:19:32 +00:00
}
2006-09-28 17:38:07 +00:00
if ( prev )
prev - > next = next ;
else
q - > members = next ;
2006-05-04 16:38:32 +00:00
2006-09-28 17:38:07 +00:00
remove_from_interfaces ( cur - > interface ) ;
free ( cur ) ;
2006-05-04 16:38:32 +00:00
}
2002-09-02 23:15:40 +00:00
if ( new ) {
2006-01-22 17:28:42 +00:00
AST_LIST_INSERT_HEAD ( & queues , q , list ) ;
} else
ast_mutex_unlock ( & q - > lock ) ;
2002-09-02 23:15:40 +00:00
}
}
}
2005-01-25 06:10:20 +00:00
ast_config_destroy ( cfg ) ;
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE_SAFE_BEGIN ( & queues , q , list ) {
2005-03-28 20:48:24 +00:00
if ( q - > dead ) {
2006-01-22 17:28:42 +00:00
AST_LIST_REMOVE_CURRENT ( & queues , list ) ;
2006-01-22 19:09:50 +00:00
if ( ! q - > count )
destroy_queue ( q ) ;
else
ast_log ( LOG_DEBUG , " XXX Leaking a little memory :( XXX \n " ) ;
2004-11-13 22:44:33 +00:00
} else {
2006-06-23 11:33:02 +00:00
ast_mutex_lock ( & q - > lock ) ;
2004-12-19 21:30:55 +00:00
for ( cur = q - > members ; cur ; cur = cur - > next )
cur - > status = ast_device_state ( cur - > interface ) ;
2006-06-23 11:33:02 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2004-11-13 22:44:33 +00:00
}
2002-09-02 23:15:40 +00:00
}
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE_SAFE_END ;
AST_LIST_UNLOCK ( & queues ) ;
2006-08-31 21:00:20 +00:00
return 1 ;
2002-09-02 23:15:40 +00:00
}
2006-11-02 23:16:09 +00:00
static int __queues_show ( struct mansession * s , int manager , int fd , int argc , char * * argv )
2002-09-02 23:15:40 +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 * qe ;
struct member * mem ;
2006-11-02 23:16:09 +00:00
int pos , queue_show ;
2002-09-02 23:15:40 +00:00
time_t now ;
2005-08-31 20:16:47 +00:00
char max_buf [ 80 ] ;
char * max ;
size_t max_left ;
2004-03-13 06:00:41 +00:00
float sl = 0 ;
2005-10-17 22:26:33 +00:00
char * term = manager ? " \r \n " : " \n " ;
2004-03-13 06:00:41 +00:00
2002-09-02 23:15:40 +00:00
time ( & now ) ;
2006-11-02 23:16:09 +00:00
if ( argc = = 2 )
queue_show = 0 ;
else if ( argc = = 3 )
queue_show = 1 ;
else
2002-09-02 23:15:40 +00:00
return RESULT_SHOWUSAGE ;
2006-01-21 20:32:17 +00:00
/* We only want to load realtime queues when a specific queue is asked for. */
if ( queue_show )
load_realtime_queue ( argv [ 2 ] ) ;
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
if ( AST_LIST_EMPTY ( & queues ) ) {
AST_LIST_UNLOCK ( & queues ) ;
2006-03-25 23:50:09 +00:00
if ( queue_show ) {
if ( s )
astman_append ( s , " No such queue: %s.%s " , argv [ 2 ] , term ) ;
else
ast_cli ( fd , " No such queue: %s.%s " , argv [ 2 ] , term ) ;
} else {
if ( s )
astman_append ( s , " No queues.%s " , term ) ;
else
ast_cli ( fd , " No queues.%s " , term ) ;
}
2002-09-02 23:15:40 +00:00
return RESULT_SUCCESS ;
}
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE ( & queues , q , list ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & q - > lock ) ;
2004-02-03 16:59:04 +00:00
if ( queue_show ) {
if ( strcasecmp ( q - > name , argv [ 2 ] ) ! = 0 ) {
ast_mutex_unlock ( & q - > lock ) ;
2006-01-22 17:28:42 +00:00
if ( ! AST_LIST_NEXT ( q , list ) ) {
2005-10-17 22:26:33 +00:00
ast_cli ( fd , " No such queue: %s.%s " , argv [ 2 ] , term ) ;
2004-02-03 16:59:04 +00:00
break ;
}
continue ;
}
}
2005-08-31 20:16:47 +00:00
max_buf [ 0 ] = ' \0 ' ;
max = max_buf ;
max_left = sizeof ( max_buf ) ;
2002-09-02 23:15:40 +00:00
if ( q - > maxlen )
2005-08-31 20:16:47 +00:00
ast_build_string ( & max , & max_left , " %d " , q - > maxlen ) ;
2002-09-02 23:15:40 +00:00
else
2005-08-31 20:16:47 +00:00
ast_build_string ( & max , & max_left , " 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 ) ;
2006-03-25 23:50:09 +00:00
if ( s )
astman_append ( s , " %-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s " ,
2006-07-14 05:42:06 +00:00
q - > name , q - > count , max_buf , int2strat ( q - > strategy ) , q - > holdtime , q - > weight ,
q - > callscompleted , q - > callsabandoned , sl , q - > servicelevel , term ) ;
2006-03-25 23:50:09 +00:00
else
ast_cli ( fd , " %-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s " ,
q - > name , q - > count , max_buf , int2strat ( q - > strategy ) , q - > holdtime , q - > weight , q - > callscompleted , q - > callsabandoned , sl , q - > servicelevel , term ) ;
2002-09-02 23:15:40 +00:00
if ( q - > members ) {
2006-03-25 23:50:09 +00:00
if ( s )
astman_append ( s , " Members: %s " , term ) ;
else
ast_cli ( fd , " Members: %s " , term ) ;
2003-07-30 16:10:51 +00:00
for ( mem = q - > members ; mem ; mem = mem - > next ) {
2005-08-31 20:16:47 +00:00
max_buf [ 0 ] = ' \0 ' ;
max = max_buf ;
max_left = sizeof ( max_buf ) ;
2003-07-30 16:10:51 +00:00
if ( mem - > penalty )
2005-08-31 20:16:47 +00:00
ast_build_string ( & max , & max_left , " with penalty %d " , mem - > penalty ) ;
2003-08-15 04:38:39 +00:00
if ( mem - > dynamic )
2005-08-31 20:16:47 +00:00
ast_build_string ( & max , & max_left , " (dynamic) " ) ;
2005-01-21 04:14:26 +00:00
if ( mem - > paused )
2005-08-31 20:16:47 +00:00
ast_build_string ( & max , & max_left , " (paused) " ) ;
ast_build_string ( & max , & max_left , " (%s) " , devstate2str ( mem - > status ) ) ;
2003-08-02 21:10:06 +00:00
if ( mem - > calls ) {
2005-08-31 20:16:47 +00:00
ast_build_string ( & max , & max_left , " has taken %d calls (last was %ld secs ago) " ,
2006-07-14 05:42:06 +00:00
mem - > calls , ( long ) ( time ( NULL ) - mem - > lastcall ) ) ;
2003-08-02 21:10:06 +00:00
} else
2005-08-31 20:16:47 +00:00
ast_build_string ( & max , & max_left , " has taken no calls yet " ) ;
2006-03-25 23:50:09 +00:00
if ( s )
astman_append ( s , " %s%s%s " , mem - > interface , max_buf , term ) ;
else
ast_cli ( fd , " %s%s%s " , mem - > interface , max_buf , term ) ;
2003-07-30 16:10:51 +00:00
}
2006-03-25 23:50:09 +00:00
} else if ( s )
astman_append ( s , " No Members%s " , term ) ;
else
2005-10-17 22:26:33 +00:00
ast_cli ( fd , " No Members%s " , term ) ;
2002-09-02 23:15:40 +00:00
if ( q - > head ) {
pos = 1 ;
2006-03-25 23:50:09 +00:00
if ( s )
astman_append ( s , " Callers: %s " , term ) ;
else
ast_cli ( fd , " Callers: %s " , term ) ;
for ( qe = q - > head ; qe ; qe = qe - > next ) {
if ( s )
2006-06-14 23:20:08 +00:00
astman_append ( s , " %d. %s (wait: %ld:%2.2ld, prio: %d)%s " ,
2006-07-14 05:42:06 +00:00
pos + + , qe - > chan - > name , ( long ) ( now - qe - > start ) / 60 ,
( long ) ( now - qe - > start ) % 60 , qe - > prio , term ) ;
2006-03-25 23:50:09 +00:00
else
2006-06-14 23:20:08 +00:00
ast_cli ( fd , " %d. %s (wait: %ld:%2.2ld, prio: %d)%s " , pos + + ,
qe - > chan - > name , ( long ) ( now - qe - > start ) / 60 ,
( long ) ( now - qe - > start ) % 60 , qe - > prio , term ) ;
2006-03-25 23:50:09 +00:00
}
} else if ( s )
astman_append ( s , " No Callers%s " , term ) ;
else
2005-10-17 22:26:33 +00:00
ast_cli ( fd , " No Callers%s " , term ) ;
2006-03-25 23:50:09 +00:00
if ( s )
astman_append ( s , " %s " , term ) ;
else
ast_cli ( fd , " %s " , term ) ;
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2004-02-03 16:59:04 +00:00
if ( queue_show )
break ;
2002-09-02 23:15:40 +00:00
}
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2002-09-02 23:15:40 +00:00
return RESULT_SUCCESS ;
}
2004-02-03 16:59:04 +00:00
static int queue_show ( int fd , int argc , char * * argv )
{
2006-11-02 23:16:09 +00:00
return __queues_show ( NULL , 0 , fd , argc , argv ) ;
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 ) ;
2004-02-03 16:59:04 +00:00
2006-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
2006-03-29 00:30:29 +00:00
if ( ! strncasecmp ( word , q - > name , wordlen ) & & + + which > state ) {
ret = ast_strdup ( q - > name ) ;
2006-02-14 21:50:35 +00:00
break ;
2004-02-03 16:59:04 +00:00
}
}
2006-01-22 17:28:42 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2005-12-29 08:15:48 +00:00
return ret ;
2004-02-03 16:59:04 +00:00
}
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
*/
2002-12-11 00:15:13 +00:00
static int manager_queues_show ( struct mansession * s , struct message * m )
{
2006-11-02 23:16:09 +00:00
char * a [ ] = { " queue " , " show " } ;
2006-06-14 23:20:08 +00:00
2006-11-02 23:16:09 +00:00
__queues_show ( s , 1 , - 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
2006-09-26 21:30:41 +00:00
/* Dump summary of queue info */
static int manager_queues_summary ( struct mansession * s , struct message * m )
{
time_t now ;
int qmemcount = 0 ;
int qmemavail = 0 ;
int qchancount = 0 ;
char * id = astman_get_header ( m , " ActionID " ) ;
char * queuefilter = astman_get_header ( m , " Queue " ) ;
char idText [ 256 ] = " " ;
struct call_queue * q ;
struct queue_ent * qe ;
struct member * mem ;
astman_send_ack ( s , m , " Queue summary will follow " ) ;
time ( & now ) ;
AST_LIST_LOCK ( & queues ) ;
if ( ! ast_strlen_zero ( id ) )
snprintf ( idText , 256 , " ActionID: %s \r \n " , id ) ;
AST_LIST_TRAVERSE ( & queues , q , list ) {
ast_mutex_lock ( & q - > lock ) ;
/* List queue properties */
if ( ast_strlen_zero ( queuefilter ) | | ! strcmp ( q - > name , queuefilter ) ) {
/* List Queue Members */
for ( mem = q - > members ; mem ; mem = mem - > next ) {
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 ;
}
}
}
for ( qe = q - > head ; qe ; qe = qe - > next ) {
+ + 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 "
" %s "
" \r \n " ,
q - > name , qmemcount , qmemavail , qchancount , q - > holdtime , idText ) ;
}
ast_mutex_unlock ( & q - > lock ) ;
}
astman_append ( s ,
" Event: QueueSummaryComplete \r \n "
" %s "
" \r \n " , idText ) ;
AST_LIST_UNLOCK ( & queues ) ;
return RESULT_SUCCESS ;
}
2003-07-01 03:32:14 +00:00
/* Dump queue status */
2006-09-26 21:30:41 +00:00
static int manager_queues_status ( struct mansession * s , struct message * m )
2003-07-01 03:32:14 +00:00
{
time_t now ;
int pos ;
2003-09-08 16:44:36 +00:00
char * id = astman_get_header ( m , " ActionID " ) ;
2005-04-09 18:58:44 +00:00
char * queuefilter = astman_get_header ( m , " Queue " ) ;
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 ;
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-01-22 17:28:42 +00:00
AST_LIST_LOCK ( & queues ) ;
2006-06-14 23:20:08 +00:00
if ( ! ast_strlen_zero ( id ) )
snprintf ( idText , sizeof ( idText ) , " ActionID: %s \r \n " , id ) ;
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE ( & queues , q , list ) {
2003-08-13 15:25:16 +00:00
ast_mutex_lock ( & q - > lock ) ;
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 ) ) {
2006-06-14 23:20:08 +00:00
if ( q - > callscompleted > 0 )
sl = 100 * ( ( float ) q - > callscompletedinsl / ( float ) q - > callscompleted ) ;
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 "
" Completed: %d \r \n "
" Abandoned: %d \r \n "
" ServiceLevel: %d \r \n "
" ServicelevelPerf: %2.1f \r \n "
" Weight: %d \r \n "
" %s "
" \r \n " ,
2006-10-03 17:41:41 +00:00
q - > name , q - > maxlen , int2strat ( q - > strategy ) , q - > count , q - > holdtime , 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 */
for ( mem = q - > members ; mem ; mem = mem - > next ) {
if ( ast_strlen_zero ( memberfilter ) | | ! strcmp ( mem - > interface , 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 "
" Location: %s \r \n "
" 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 " ,
q - > name , mem - > interface , mem - > dynamic ? " dynamic " : " static " ,
mem - > penalty , mem - > calls , ( int ) mem - > lastcall , mem - > status , mem - > paused , idText ) ;
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 "
2006-10-02 20:35:16 +00:00
" CallerIDNum: %s \r \n "
2006-07-14 05:42:06 +00:00
" CallerIDName: %s \r \n "
" Wait: %ld \r \n "
" %s "
" \r \n " ,
q - > name , pos + + , qe - > chan - > name ,
S_OR ( qe - > chan - > cid . cid_num , " unknown " ) ,
S_OR ( qe - > chan - > cid . cid_name , " unknown " ) ,
( long ) ( now - qe - > start ) , idText ) ;
2005-04-09 18:58:44 +00:00
}
}
2003-08-13 15:25:16 +00:00
ast_mutex_unlock ( & q - > lock ) ;
2003-07-01 03:32:14 +00:00
}
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
2006-05-08 13:38:14 +00:00
AST_LIST_UNLOCK ( & queues ) ;
2005-07-13 16:44:55 +00:00
2003-07-01 03:32:14 +00:00
return RESULT_SUCCESS ;
}
2004-07-28 02:55:22 +00:00
static int manager_add_queue_member ( struct mansession * s , struct message * m )
{
2006-09-20 05:59:53 +00:00
char * queuename , * interface , * penalty_s , * paused_s , * membername ;
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 " ) ;
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 ;
2006-02-14 21:50:35 +00:00
else if ( sscanf ( penalty_s , " %d " , & penalty ) ! = 1 )
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 ) ) ;
2006-09-20 05:59:53 +00:00
if ( ast_strlen_zero ( membername ) )
membername = interface ;
switch ( add_to_queue ( queuename , interface , membername , penalty , paused , queue_persistent_members ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2006-11-10 16:37:42 +00:00
ast_queue_log ( queuename , " MANAGER " , interface , " ADDMEMBER " , " %s " , " " ) ;
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 ;
}
static int manager_remove_queue_member ( struct mansession * s , struct message * m )
{
char * queuename , * interface ;
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 ;
}
switch ( remove_from_queue ( queuename , interface ) ) {
case RES_OKAY :
2006-11-10 16:37:42 +00:00
ast_queue_log ( queuename , " MANAGER " , interface , " 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 ;
}
2006-06-14 23:20:08 +00:00
2004-07-28 02:55:22 +00:00
return 0 ;
}
2005-01-21 04:14:26 +00:00
static int manager_pause_queue_member ( struct mansession * s , struct message * m )
{
char * queuename , * interface , * paused_s ;
int paused ;
interface = astman_get_header ( m , " Interface " ) ;
paused_s = astman_get_header ( m , " Paused " ) ;
queuename = astman_get_header ( m , " Queue " ) ; /* Optional - if not supplied, pause the given Interface in all queues */
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 ) ) ;
if ( set_member_paused ( queuename , interface , paused ) )
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 ;
}
2006-11-17 20:46:36 +00:00
static int manager_queue_log_custom ( struct mansession * s , struct message * m )
{
char * queuename , * event , * message , * interface , * uniqueid ;
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 ;
}
2006-09-18 19:54:18 +00:00
static int handle_queue_add_member ( int fd , int argc , char * argv [ ] )
2004-07-28 02:55:22 +00:00
{
2006-09-20 05:59:53 +00:00
char * queuename , * interface , * membername ;
2004-07-28 02:55:22 +00:00
int penalty ;
2006-09-20 05:59:53 +00:00
if ( ( argc ! = 6 ) & & ( argc ! = 8 ) & & ( argc ! = 10 ) ) {
2004-07-28 02:55:22 +00:00
return RESULT_SHOWUSAGE ;
} else if ( strcmp ( argv [ 4 ] , " to " ) ) {
return RESULT_SHOWUSAGE ;
2004-09-02 04:07:28 +00:00
} else if ( ( argc = = 8 ) & & strcmp ( argv [ 6 ] , " penalty " ) ) {
2004-07-28 02:55:22 +00:00
return RESULT_SHOWUSAGE ;
2006-09-20 05:59:53 +00:00
} else if ( ( argc = = 10 ) & & strcmp ( argv [ 8 ] , " as " ) ) {
return RESULT_SHOWUSAGE ;
2004-07-28 02:55:22 +00:00
}
queuename = argv [ 5 ] ;
interface = argv [ 3 ] ;
2006-09-20 05:59:53 +00:00
if ( argc > = 8 ) {
2004-07-28 02:55:22 +00:00
if ( sscanf ( argv [ 7 ] , " %d " , & penalty ) = = 1 ) {
if ( penalty < 0 ) {
ast_cli ( fd , " Penalty must be >= 0 \n " ) ;
penalty = 0 ;
}
} else {
ast_cli ( fd , " Penalty must be an integer >= 0 \n " ) ;
penalty = 0 ;
}
} else {
penalty = 0 ;
}
2006-09-20 05:59:53 +00:00
if ( argc > = 10 ) {
membername = argv [ 9 ] ;
} else {
membername = interface ;
}
switch ( add_to_queue ( queuename , interface , membername , penalty , 0 , queue_persistent_members ) ) {
2004-07-28 02:55:22 +00:00
case RES_OKAY :
2006-11-10 16:37:42 +00:00
ast_queue_log ( queuename , " CLI " , interface , " ADDMEMBER " , " %s " , " " ) ;
2004-07-28 02:55:22 +00:00
ast_cli ( fd , " Added interface '%s' to queue '%s' \n " , interface , queuename ) ;
return RESULT_SUCCESS ;
case RES_EXISTS :
ast_cli ( fd , " Unable to add interface '%s' to queue '%s': Already there \n " , interface , queuename ) ;
return RESULT_FAILURE ;
case RES_NOSUCHQUEUE :
ast_cli ( fd , " Unable to add interface to queue '%s': No such queue \n " , queuename ) ;
return RESULT_FAILURE ;
case RES_OUTOFMEMORY :
ast_cli ( fd , " Out of memory \n " ) ;
return RESULT_FAILURE ;
default :
return RESULT_FAILURE ;
}
}
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 ) {
2006-09-20 05:59:53 +00:00
case 3 : /* Don't attempt to complete name of interface (infinite possibilities) */
2004-07-28 02:55:22 +00:00
return NULL ;
2006-02-14 21:50:35 +00:00
case 4 : /* only one possible match, "to" */
2006-03-29 00:30:29 +00:00
return state = = 0 ? ast_strdup ( " to " ) : NULL ;
2006-02-14 21:50:35 +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 :
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 ;
case 9 : /* Don't attempt to complete name of member (infinite possibilities) */
return NULL ;
2004-07-28 02:55:22 +00:00
default :
return NULL ;
}
}
2006-09-18 19:54:18 +00:00
static int handle_queue_remove_member ( int fd , int argc , char * argv [ ] )
2004-07-28 02:55:22 +00:00
{
char * queuename , * interface ;
if ( argc ! = 6 ) {
return RESULT_SHOWUSAGE ;
} else if ( strcmp ( argv [ 4 ] , " from " ) ) {
return RESULT_SHOWUSAGE ;
}
queuename = argv [ 5 ] ;
interface = argv [ 3 ] ;
switch ( remove_from_queue ( queuename , interface ) ) {
case RES_OKAY :
2006-11-10 16:37:42 +00:00
ast_queue_log ( queuename , " CLI " , interface , " REMOVEMEMBER " , " %s " , " " ) ;
2004-07-28 02:55:22 +00:00
ast_cli ( fd , " Removed interface '%s' from queue '%s' \n " , interface , queuename ) ;
return RESULT_SUCCESS ;
case RES_EXISTS :
ast_cli ( fd , " Unable to remove interface '%s' from queue '%s': Not there \n " , interface , queuename ) ;
return RESULT_FAILURE ;
case RES_NOSUCHQUEUE :
ast_cli ( fd , " Unable to remove interface from queue '%s': No such queue \n " , queuename ) ;
return RESULT_FAILURE ;
case RES_OUTOFMEMORY :
ast_cli ( fd , " Out of memory \n " ) ;
return RESULT_FAILURE ;
default :
return RESULT_FAILURE ;
}
}
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 ;
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 ;
if ( pos = = 4 ) /* only one possible match, 'from' */
return state = = 0 ? ast_strdup ( " from " ) : NULL ;
if ( pos = = 5 ) /* No need to duplicate code */
return complete_queue ( line , word , pos , state ) ;
/* here is the case for 3, <member> */
if ( ! AST_LIST_EMPTY ( & queues ) ) { /* XXX unnecessary ? the traverse does that for us */
2006-01-22 17:28:42 +00:00
AST_LIST_TRAVERSE ( & queues , q , list ) {
2004-07-28 02:55:22 +00:00
ast_mutex_lock ( & q - > lock ) ;
for ( m = q - > members ; m ; m = m - > next ) {
if ( + + which > state ) {
ast_mutex_unlock ( & q - > lock ) ;
2006-03-29 00:30:29 +00:00
return ast_strdup ( m - > interface ) ;
2004-07-28 02:55:22 +00:00
}
}
ast_mutex_unlock ( & q - > lock ) ;
}
}
2006-06-14 23:20:08 +00:00
2004-07-28 02:55:22 +00:00
return NULL ;
}
2006-09-18 19:54:18 +00:00
static char queue_show_usage [ ] =
" Usage: queue show \n "
2004-02-03 16:59:04 +00:00
" Provides summary information on a specified queue. \n " ;
2006-09-18 19:54:18 +00:00
static char qam_cmd_usage [ ] =
" Usage: queue add member <channel> to <queue> [penalty <penalty>] \n " ;
static char qrm_cmd_usage [ ] =
" Usage: queue remove member <channel> from <queue> \n " ;
static struct ast_cli_entry cli_queue [ ] = {
{ { " queue " , " show " , NULL } ,
queue_show , " Show status of a specified queue " ,
2006-09-21 21:17:39 +00:00
queue_show_usage , complete_queue , NULL } ,
2004-07-28 02:55:22 +00:00
2006-09-18 19:54:18 +00:00
{ { " queue " , " add " , " member " , NULL } ,
handle_queue_add_member , " Add a channel to a specified queue " ,
2006-09-21 21:17:39 +00:00
qam_cmd_usage , complete_queue_add_member , NULL } ,
2006-09-18 19:54:18 +00:00
{ { " queue " , " remove " , " member " , NULL } ,
handle_queue_remove_member , " Removes a channel from a specified queue " ,
2006-09-21 21:17:39 +00:00
qrm_cmd_usage , complete_queue_remove_member , NULL } ,
2006-09-18 19:54:18 +00:00
} ;
2004-07-28 02:55:22 +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 ;
2006-09-18 19:54:18 +00:00
ast_cli_unregister_multiple ( cli_queue , sizeof ( cli_queue ) / sizeof ( struct ast_cli_entry ) ) ;
res = ast_manager_unregister ( " QueueStatus " ) ;
2005-10-18 22:52:21 +00:00
res | = ast_manager_unregister ( " Queues " ) ;
res | = ast_manager_unregister ( " QueueStatus " ) ;
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 " ) ;
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 ) ;
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 ) ;
res | = ast_custom_function_unregister ( & queuememberlist_function ) ;
2006-04-26 22:04:42 +00:00
res | = ast_custom_function_unregister ( & queuewaitingcount_function ) ;
2005-10-18 22:52:21 +00:00
2006-08-21 02:11:39 +00:00
ast_module_user_hangup_all ( ) ;
2006-06-14 23:20:08 +00:00
2006-08-21 02:11:39 +00:00
clear_and_free_interfaces ( ) ;
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 ;
2006-08-31 21:00:20 +00:00
if ( ! reload_queues ( ) )
return AST_MODULE_LOAD_DECLINE ;
if ( queue_persistent_members )
reload_queue_members ( ) ;
2006-09-18 19:54:18 +00:00
ast_cli_register_multiple ( cli_queue , sizeof ( cli_queue ) / sizeof ( struct ast_cli_entry ) ) ;
2002-09-02 23:15:40 +00:00
res = ast_register_application ( app , queue_exec , synopsis , descrip ) ;
2006-07-16 19:36:29 +00:00
res | = ast_register_application ( app_aqm , aqm_exec , app_aqm_synopsis , app_aqm_descrip ) ;
res | = ast_register_application ( app_rqm , rqm_exec , app_rqm_synopsis , app_rqm_descrip ) ;
res | = ast_register_application ( app_pqm , pqm_exec , app_pqm_synopsis , app_pqm_descrip ) ;
res | = ast_register_application ( app_upqm , upqm_exec , app_upqm_synopsis , app_upqm_descrip ) ;
res | = ast_register_application ( app_ql , ql_exec , app_ql_synopsis , app_ql_descrip ) ;
2006-09-18 19:54:18 +00:00
res | = ast_manager_register ( " Queues " , 0 , manager_queues_show , " Queues " ) ;
res | = ast_manager_register ( " QueueStatus " , 0 , manager_queues_status , " Queue Status " ) ;
2006-09-26 21:30:41 +00:00
res | = ast_manager_register ( " QueueSummary " , 0 , manager_queues_summary , " Queue Summary " ) ;
2006-09-18 19:54:18 +00:00
res | = ast_manager_register ( " QueueAdd " , EVENT_FLAG_AGENT , manager_add_queue_member , " Add interface to queue. " ) ;
res | = ast_manager_register ( " QueueRemove " , EVENT_FLAG_AGENT , manager_remove_queue_member , " Remove interface from queue. " ) ;
res | = ast_manager_register ( " QueuePause " , EVENT_FLAG_AGENT , manager_pause_queue_member , " Makes a queue member temporarily unavailable " ) ;
2006-11-17 20:46:36 +00:00
res | = ast_manager_register ( " QueueLog " , EVENT_FLAG_AGENT , manager_queue_log_custom , " Adds custom entry in queue_log " ) ;
2006-10-27 18:59:16 +00:00
res | = ast_custom_function_register ( & queuevar_function ) ;
2006-01-13 22:59:19 +00:00
res | = ast_custom_function_register ( & queuemembercount_function ) ;
res | = ast_custom_function_register ( & queuememberlist_function ) ;
2006-04-26 22:04:42 +00:00
res | = ast_custom_function_register ( & queuewaitingcount_function ) ;
2006-05-25 21:47:03 +00:00
res | = ast_devstate_add ( statechange_queue , NULL ) ;
2005-10-18 22:52:21 +00:00
2002-09-02 23:15:40 +00:00
return res ;
}
2006-08-21 02:11:39 +00:00
static int reload ( void )
2002-09-02 23:15:40 +00:00
{
reload_queues ( ) ;
return 0 ;
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_DEFAULT , " True Call Queueing " ,
. load = load_module ,
. unload = unload_module ,
. reload = reload ,
) ;
2006-04-14 14:08:19 +00:00