Fri Jun 19 12:09:59 2009

Asterisk developer's documentation


app_queue.c File Reference

True call queues with optional send URL on answer. More...

#include "asterisk.h"
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <ctype.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#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"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/event.h"
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
#include "asterisk/global_datastores.h"
#include "asterisk/taskprocessor.h"

Go to the source code of this file.

Data Structures

struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  interfaces
struct  member
struct  member_interface
struct  penalty_rule
struct  queue_end_bridge
struct  queue_ent
struct  queue_transfer_ds
struct  rule_list
struct  rule_lists
struct  statechange
struct  strategy

Defines

#define ANNOUNCEHOLDTIME_ALWAYS   1
#define ANNOUNCEHOLDTIME_ONCE   2
#define ANNOUNCEPOSITION_LIMIT   4
#define ANNOUNCEPOSITION_MORE_THAN   3
#define ANNOUNCEPOSITION_NO   2
#define ANNOUNCEPOSITION_YES   1
#define AST_MAX_WATCHERS   256
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15
#define DEFAULT_RETRY   5
#define DEFAULT_TIMEOUT   15
#define MAX_PERIODIC_ANNOUNCEMENTS   10
#define MAX_QUEUE_BUCKETS   53
#define PM_MAX_LEN   8192
#define QUEUE_EMPTY_LOOSE   3
#define QUEUE_EMPTY_NORMAL   1
#define QUEUE_EMPTY_STRICT   2
#define QUEUE_EVENT_VARIABLES   3
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

enum  {
  QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM,
  QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM
}
enum  agent_complete_reason { CALLER, AGENT, TRANSFER }
enum  queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_NORMAL }
enum  queue_result {
  QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3,
  QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7
}
enum  queue_timeout_priority { TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF }

Functions

static char * __queues_show (struct mansession *s, int fd, int argc, char **argv)
 Show queue(s) status and statistics.
static void __reg_module (void)
static void __unreg_module (void)
static int add_to_interfaces (const char *interface)
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
 Add member to queue.
static struct call_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, void *data)
 AddQueueMember application.
static int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
 Calculate the metric of each member in the outgoing callattempts.
static void clear_and_free_interfaces (void)
static void clear_queue (struct call_queue *q)
static int compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state)
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_pause_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_rule_show (const char *line, const char *word, int pos, int state)
static char * complete_queue_set_member_penalty (const char *line, const char *word, int pos, int state)
static char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static void copy_rules (struct queue_ent *qe, const char *rulename)
 Copy rule from global list into specified queue.
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (void *obj)
 Free queue's member list then its string fields.
static void device_state_cb (const struct ast_event *event, void *unused)
static void do_hang (struct callattempt *o)
 common hangup actions
static void do_print (struct mansession *s, int fd, const char *str)
 direct ouput to manager or cli with proper terminator
static void dump_queue_members (struct call_queue *pm_queue)
 Dump all members in a specific queue to the database.
static void end_bridge_callback (void *data)
static void end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
static struct callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
 Iterate through queue's member list and delete them.
static int get_member_penalty (char *queuename, char *interface)
static enum queue_member_status get_member_status (struct call_queue *q, int max_penalty, int min_penalty)
 Check if members are available.
static char * handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int handle_statechange (void *datap)
 set a member's status based on device state of that member's interface
static void hangupcalls (struct callattempt *outgoing, struct ast_channel *exception)
 Hang up a list of outgoing calls.
static void init_queue (struct call_queue *q)
 Initialize Queue default values.
static void insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
 Insert the 'new' entry after the 'prev' entry of queue 'q'.
static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
 Change queue penalty by adding rule.
static const char * int2strat (int strategy)
static struct memberinterface_exists (struct call_queue *q, const char *interface)
static int interface_exists_global (const char *interface, int lock_queue_container)
static int is_our_turn (struct queue_ent *qe)
 Check if we should start attempting to call queue members.
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason, const char *overriding_rule)
static void leave_queue (struct queue_ent *qe)
 Caller leaving queue.
static int load_module (void)
static struct call_queueload_realtime_queue (const char *queuename)
static int manager_add_queue_member (struct mansession *s, const struct message *m)
static int manager_pause_queue_member (struct mansession *s, const struct message *m)
static int manager_queue_log_custom (struct mansession *s, const struct message *m)
static int manager_queue_member_penalty (struct mansession *s, const struct message *m)
static int manager_queue_rule_show (struct mansession *s, const struct message *m)
static int manager_queues_show (struct mansession *s, const struct message *m)
static int manager_queues_status (struct mansession *s, const struct message *m)
 Queue status info via AMI.
static int manager_queues_summary (struct mansession *s, const struct message *m)
 Summary of queue info via the AMI.
static int manager_remove_queue_member (struct mansession *s, const struct message *m)
static int member_cmp_fn (void *obj1, void *obj2, int flags)
static int member_hash_fn (const void *obj, const int flags)
static int num_available_members (struct call_queue *q)
 Get the number of members available to accept a call.
static int play_file (struct ast_channel *chan, const char *filename)
static int pqm_exec (struct ast_channel *chan, void *data)
 PauseQueueMember application.
static int ql_exec (struct ast_channel *chan, void *data)
 QueueLog application.
static int queue_cmp_cb (void *obj, void *arg, int flags)
static int queue_exec (struct ast_channel *chan, void *data)
 The starting point for all queue calls.
static int queue_function_memberpenalty_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.
static int queue_function_memberpenalty_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
 Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.
static int queue_function_qac (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get number either busy / free or total members of a specific queue.
static int queue_function_qac_dep (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get the total number of members in a specific queue (Deprecated).
static int queue_function_queuememberlist (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue.
static int queue_function_queuewaitingcount (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue.
static int queue_function_var (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 create interface var with all queue details.
static int queue_hash_cb (const void *obj, const int flags)
static struct call_queuequeue_ref (struct call_queue *q)
static void queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
 Configure a queue parameter.
static char * queue_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void queue_transfer_destroy (void *data)
static void queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 Log an attended transfer when a queue caller channel is masqueraded.
static struct call_queuequeue_unref (struct call_queue *q)
static void recalc_holdtime (struct queue_ent *qe, int newholdtime)
static void record_abandoned (struct queue_ent *qe)
 Record that a caller gave up on waiting in queue.
static int reload (void)
static void reload_queue_members (void)
 Reload dynamic queue members persisted into the astdb.
static int reload_queue_rules (int reload)
static int reload_queues (int reload)
static int remove_from_interfaces (const char *interface, int lock_queue_container)
static int remove_from_queue (const char *queuename, const char *interface)
 Remove member from queue.
static int ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies)
 Part 2 of ring_one.
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 Place a call to a queue member.
static void rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
 RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
static int rqm_exec (struct ast_channel *chan, void *data)
 RemoveQueueMember application.
static void rt_handle_member_record (struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface)
 Find rt member record to update otherwise create one.
static int say_periodic_announcement (struct queue_ent *qe, int ringing)
 Playback announcement to queued members if peroid has elapsed.
static int say_position (struct queue_ent *qe, int ringing)
static void send_agent_complete (const struct queue_ent *qe, const char *queuename, const struct ast_channel *peer, const struct member *member, time_t callstart, char *vars, size_t vars_len, enum agent_complete_reason rsn)
 Send out AMI message with member call completion status information.
static int set_member_paused (const char *queuename, const char *interface, const char *reason, int paused)
static int set_member_penalty (char *queuename, char *interface, int penalty)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
 sets the QUEUESTATUS channel variable
static void set_queue_variables (struct call_queue *q, struct ast_channel *chan)
 Set variables of queue.
static struct ast_datastoresetup_transfer_datastore (struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
 create a datastore for storing relevant info to log attended transfers in the queue_log
static int store_next_lin (struct queue_ent *qe, struct callattempt *outgoing)
 Search for best metric and add to Linear queue.
static int store_next_rr (struct queue_ent *qe, struct callattempt *outgoing)
 Search for best metric and add to Round Robbin queue.
static int strat2int (const char *strategy)
static int try_calling (struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
 A large function which calls members, updates statistics, and bridges the caller and a member.
static int unload_module (void)
static void update_qe_rule (struct queue_ent *qe)
 update rules for queues
static int update_queue (struct call_queue *q, struct member *member, int callcompletedinsl)
 update the queue status
static int update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value)
static void update_realtime_members (struct call_queue *q)
static int update_status (const char *interface, const int status)
 set a member's status based on device state of that member's state_interface.
static int upqm_exec (struct ast_channel *chan, void *data)
 UnPauseQueueMember application.
static int valid_exit (struct queue_ent *qe, char digit)
 Check for valid exit from queue via goto.
static char * vars2manager (struct ast_channel *chan, char *vars, size_t len)
 convert "\n" to "\nVariable: " ready for manager to use
static int wait_a_bit (struct queue_ent *qe)
static struct callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
 Wait for a member to answer the call.
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)
 The waiting areas for callers who are not actively calling members.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_aqm_descrip
static char * app_aqm_synopsis = "Dynamically adds queue members"
static char * app_pqm = "PauseQueueMember"
static char * app_pqm_descrip
static char * app_pqm_synopsis = "Pauses a queue member"
static char * app_ql = "QueueLog"
static char * app_ql_descrip
static char * app_ql_synopsis = "Writes to the queue_log"
static char * app_rqm = "RemoveQueueMember"
static char * app_rqm_descrip
static char * app_rqm_synopsis = "Dynamically removes queue members"
static char * app_upqm = "UnpauseQueueMember"
static char * app_upqm_descrip
static char * app_upqm_synopsis = "Unpauses a queue member"
static struct ast_module_infoast_module_info = &__mod_info
static int autofill_default = 0
 queues.conf [general] option
static struct ast_cli_entry cli_queue []
static char * descrip
static struct ast_event_subdevice_state_sub
 Subscription to device state change events.
static struct ast_taskprocessordevicestate_tps
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage []
static const char qsmp_cmd_usage []
static int queue_keep_stats = 0
 queues.conf [general] option
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queuemembercount_dep
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpenalty_function
static struct ao2_containerqueues
static struct ast_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 0
 queues.conf [general] option
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int update_cdr = 0
 queues.conf [general] option
static int use_weight = 0
 queues.conf per-queue weight option


Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>
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 configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).

These features added by David C. Troy <dave@toad.net>:

Patch Version 1.07 2003-12-24 01

Added servicelevel statistic by Michiel Betel <michiel@betel.nl> Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>

Fixed to work with CVS as of 2004-02-25 and released as 1.07a by Matthew Enger <m.enger@xi.com.au>

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 418 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 419 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_LIMIT   4

We not announce position more than <limit>

Definition at line 434 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_MORE_THAN   3

We say "Currently there are more than <limit>"

Definition at line 433 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_NO   2

We don't announce position

Definition at line 432 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 431 of file app_queue.c.

Referenced by init_queue(), queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2563 of file app_queue.c.

#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15

The minimum number of seconds between position announcements \ The default value of 15 provides backwards compatibility

Definition at line 142 of file app_queue.c.

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 138 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 139 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

The maximum periodic announcements we can have

Definition at line 141 of file app_queue.c.

Referenced by destroy_queue(), init_queue(), and queue_set_param().

#define MAX_QUEUE_BUCKETS   53

Definition at line 144 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 280 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_LOOSE   3

Definition at line 417 of file app_queue.c.

Referenced by join_queue(), queue_exec(), queue_set_param(), and wait_our_turn().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 415 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 416 of file app_queue.c.

Referenced by join_queue(), queue_exec(), queue_set_param(), and wait_our_turn().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 420 of file app_queue.c.

Referenced by queue_set_param(), ring_entry(), send_agent_complete(), and try_calling().

#define RECHECK   1

Recheck every second to see we we're at the top yet

Definition at line 140 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Entry already exists

Definition at line 147 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_NOSUCHQUEUE   (-3)

No such queue

Definition at line 149 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_NOT_DYNAMIC   (-4)

Member is not dynamic

Definition at line 150 of file app_queue.c.

Referenced by handle_queue_add_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OKAY   0

Action completed

Definition at line 146 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OUTOFMEMORY   (-2)

Out of memory

Definition at line 148 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), and reload_queue_members().


Enumeration Type Documentation

anonymous enum

Please read before modifying this file.
There are three locks which are regularly used throughout this file, the queue list lock, the lock for each individual queue, and the interface list lock. Please be extra careful to always lock in the following order 1) queue list lock 2) individual queue lock 3) interface list lock This order has sort of "evolved" over the lifetime of this application, but it is now in place this way, so please adhere to this order!
Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 
QUEUE_STRATEGY_LINEAR 
QUEUE_STRATEGY_WRANDOM 

Definition at line 112 of file app_queue.c.

enum agent_complete_reason

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 3114 of file app_queue.c.

03114                            {
03115    CALLER,
03116    AGENT,
03117    TRANSFER
03118 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 654 of file app_queue.c.

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 306 of file app_queue.c.

00306                   {
00307    QUEUE_UNKNOWN = 0,
00308    QUEUE_TIMEOUT = 1,
00309    QUEUE_JOINEMPTY = 2,
00310    QUEUE_LEAVEEMPTY = 3,
00311    QUEUE_JOINUNAVAIL = 4,
00312    QUEUE_LEAVEUNAVAIL = 5,
00313    QUEUE_FULL = 6,
00314    QUEUE_CONTINUE = 7,
00315 };

enum queue_timeout_priority

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 331 of file app_queue.c.

00331                             {
00332    TIMEOUT_PRIORITY_APP,
00333    TIMEOUT_PRIORITY_CONF,
00334 };


Function Documentation

static char* __queues_show ( struct mansession s,
int  fd,
int  argc,
char **  argv 
) [static]

Show queue(s) status and statistics.

List the queues strategy, calls processed, members logged in, other queue statistics such as avg hold time.

Definition at line 5679 of file app_queue.c.

References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_load_realtime_multientry(), ast_str_alloca, ast_str_append(), ast_str_set(), ast_strlen_zero(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, CLI_SHOWUSAGE, CLI_SUCCESS, call_queue::count, devstate2str(), do_print(), member::dynamic, F_AO2I_DONTLOCK, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::pos, queue_ent::prio, queue_unref(), queues, member::realtime, call_queue::realtime, s, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, ast_str::str, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

05680 {
05681    struct call_queue *q;
05682    struct ast_str *out = ast_str_alloca(240);
05683    int found = 0;
05684    time_t now = time(NULL);
05685    struct ao2_iterator queue_iter;
05686    struct ao2_iterator mem_iter;
05687 
05688    if (argc != 2 && argc != 3)
05689       return CLI_SHOWUSAGE;
05690 
05691    if (argc == 3) { /* specific queue */
05692       if ((q = load_realtime_queue(argv[2]))) {
05693          queue_unref(q);
05694       }
05695    } else if (ast_check_realtime("queues")) {
05696       /* This block is to find any queues which are defined in realtime but
05697        * which have not yet been added to the in-core container
05698        */
05699       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05700       char *queuename;
05701       if (cfg) {
05702          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05703             if ((q = load_realtime_queue(queuename))) {
05704                queue_unref(q);
05705             }
05706          }
05707          ast_config_destroy(cfg);
05708       }
05709    }
05710 
05711    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05712    ao2_lock(queues);
05713    while ((q = ao2_iterator_next(&queue_iter))) {
05714       float sl;
05715       struct call_queue *realtime_queue = NULL;
05716 
05717       ao2_lock(q);
05718       /* This check is to make sure we don't print information for realtime
05719        * queues which have been deleted from realtime but which have not yet
05720        * been deleted from the in-core container
05721        */
05722       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05723          ao2_unlock(q);
05724          queue_unref(q);
05725          continue;
05726       } else if (q->realtime) {
05727          queue_unref(realtime_queue);
05728       }
05729       if (argc == 3 && strcasecmp(q->name, argv[2])) {
05730          ao2_unlock(q);
05731          queue_unref(q);
05732          continue;
05733       }
05734       found = 1;
05735 
05736       ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
05737       if (q->maxlen)
05738          ast_str_append(&out, 0, "%d", q->maxlen);
05739       else
05740          ast_str_append(&out, 0, "unlimited");
05741       sl = 0;
05742       if (q->callscompleted > 0)
05743          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05744       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05745          int2strat(q->strategy), q->holdtime, q->weight,
05746          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05747       do_print(s, fd, out->str);
05748       if (!ao2_container_count(q->members))
05749          do_print(s, fd, "   No Members");
05750       else {
05751          struct member *mem;
05752 
05753          do_print(s, fd, "   Members: ");
05754          mem_iter = ao2_iterator_init(q->members, 0);
05755          while ((mem = ao2_iterator_next(&mem_iter))) {
05756             ast_str_set(&out, 0, "      %s", mem->membername);
05757             if (strcasecmp(mem->membername, mem->interface)) {
05758                ast_str_append(&out, 0, " (%s)", mem->interface);
05759             }
05760             if (mem->penalty)
05761                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05762             ast_str_append(&out, 0, "%s%s%s (%s)",
05763                mem->dynamic ? " (dynamic)" : "",
05764                mem->realtime ? " (realtime)" : "",
05765                mem->paused ? " (paused)" : "",
05766                devstate2str(mem->status));
05767             if (mem->calls)
05768                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05769                   mem->calls, (long) (time(NULL) - mem->lastcall));
05770             else
05771                ast_str_append(&out, 0, " has taken no calls yet");
05772             do_print(s, fd, out->str);
05773             ao2_ref(mem, -1);
05774          }
05775       }
05776       if (!q->head)
05777          do_print(s, fd, "   No Callers");
05778       else {
05779          struct queue_ent *qe;
05780          int pos = 1;
05781 
05782          do_print(s, fd, "   Callers: ");
05783          for (qe = q->head; qe; qe = qe->next) {
05784             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
05785                pos++, qe->chan->name, (long) (now - qe->start) / 60,
05786                (long) (now - qe->start) % 60, qe->prio);
05787             do_print(s, fd, out->str);
05788          }
05789       }
05790       do_print(s, fd, ""); /* blank line between entries */
05791       ao2_unlock(q);
05792       queue_unref(q); /* Unref the iterator's reference */
05793    }
05794    ao2_unlock(queues);
05795    if (!found) {
05796       if (argc == 3)
05797          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05798       else
05799          ast_str_set(&out, 0, "No queues.");
05800       do_print(s, fd, out->str);
05801    }
05802    return CLI_SUCCESS;
05803 }

static void __reg_module ( void   )  [static]

Definition at line 6737 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 6737 of file app_queue.c.

static int add_to_interfaces ( const char *  interface  )  [static]

Definition at line 962 of file app_queue.c.

References ast_calloc, ast_copy_string(), ast_debug, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member_interface::interface, and member_interface::list.

Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().

00963 {
00964    struct member_interface *curint;
00965 
00966    AST_LIST_LOCK(&interfaces);
00967    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00968       if (!strcasecmp(curint->interface, interface))
00969          break;
00970    }
00971 
00972    if (curint) {
00973       AST_LIST_UNLOCK(&interfaces);
00974       return 0;
00975    }
00976 
00977    ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00978    
00979    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00980       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00981       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00982    }
00983    AST_LIST_UNLOCK(&interfaces);
00984 
00985    return 0;
00986 }

static int add_to_queue ( const char *  queuename,
const char *  interface,
const char *  membername,
int  penalty,
int  paused,
int  dump,
const char *  state_interface 
) [static]

Add member to queue.

Return values:
RES_NOT_DYNAMIC when they aren't a RT member
RES_NOSUCHQUEUE queue does not exist
RES_OKAY added member from queue
RES_EXISTS queue exists but no members
RES_OUT_OF_MEMORY queue exists but not enough memory to create member

Note:
Ensure the appropriate realtime queue is loaded. Note that this short-circuits if the queue is already in memory.

Definition at line 4157 of file app_queue.c.

References add_to_interfaces(), ao2_link, ao2_lock(), ao2_ref, ao2_unlock(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), manager_event, call_queue::membercount, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, queues, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, member::state_interface, and member::status.

Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().

04158 {
04159    struct call_queue *q;
04160    struct member *new_member, *old_member;
04161    int res = RES_NOSUCHQUEUE;
04162 
04163    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
04164     * short-circuits if the queue is already in memory. */
04165    if (!(q = load_realtime_queue(queuename)))
04166       return res;
04167 
04168    ao2_lock(queues);
04169 
04170    ao2_lock(q);
04171    if ((old_member = interface_exists(q, interface)) == NULL) {
04172       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04173          add_to_interfaces(new_member->state_interface);
04174          new_member->dynamic = 1;
04175          ao2_link(q->members, new_member);
04176          q->membercount++;
04177          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04178             "Queue: %s\r\n"
04179             "Location: %s\r\n"
04180             "MemberName: %s\r\n"
04181             "Membership: %s\r\n"
04182             "Penalty: %d\r\n"
04183             "CallsTaken: %d\r\n"
04184             "LastCall: %d\r\n"
04185             "Status: %d\r\n"
04186             "Paused: %d\r\n",
04187             q->name, new_member->interface, new_member->membername,
04188             "dynamic",
04189             new_member->penalty, new_member->calls, (int) new_member->lastcall,
04190             new_member->status, new_member->paused);
04191          
04192          ao2_ref(new_member, -1);
04193          new_member = NULL;
04194 
04195          if (dump)
04196             dump_queue_members(q);
04197          
04198          res = RES_OKAY;
04199       } else {
04200          res = RES_OUTOFMEMORY;
04201       }
04202    } else {
04203       ao2_ref(old_member, -1);
04204       res = RES_EXISTS;
04205    }
04206    ao2_unlock(q);
04207    ao2_unlock(queues);
04208 
04209    return res;
04210 }

static struct call_queue* alloc_queue ( const char *  queuename  )  [static]

Definition at line 1436 of file app_queue.c.

References ao2_alloc, ao2_ref, ast_string_field_init, ast_string_field_set, and destroy_queue().

Referenced by find_queue_by_name_rt(), and reload_queues().

01437 {
01438    struct call_queue *q;
01439 
01440    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
01441       if (ast_string_field_init(q, 64)) {
01442          ao2_ref(q, -1);
01443          return NULL;
01444       }
01445       ast_string_field_set(q, name, queuename);
01446    }
01447    return q;
01448 }

static int aqm_exec ( struct ast_channel chan,
void *  data 
) [static]

AddQueueMember application.

Definition at line 4591 of file app_queue.c.

References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and ast_channel::uniqueid.

Referenced by load_module().

04592 {
04593    int res=-1;
04594    char *parse, *temppos = NULL;
04595    AST_DECLARE_APP_ARGS(args,
04596       AST_APP_ARG(queuename);
04597       AST_APP_ARG(interface);
04598       AST_APP_ARG(penalty);
04599       AST_APP_ARG(options);
04600       AST_APP_ARG(membername);
04601       AST_APP_ARG(state_interface);
04602    );
04603    int penalty = 0;
04604 
04605    if (ast_strlen_zero(data)) {
04606       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04607       return -1;
04608    }
04609 
04610    parse = ast_strdupa(data);
04611 
04612    AST_STANDARD_APP_ARGS(args, parse);
04613 
04614    if (ast_strlen_zero(args.interface)) {
04615       args.interface = ast_strdupa(chan->name);
04616       temppos = strrchr(args.interface, '-');
04617       if (temppos)
04618          *temppos = '\0';
04619    }
04620 
04621    if (!ast_strlen_zero(args.penalty)) {
04622       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
04623          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04624          penalty = 0;
04625       }
04626    }
04627 
04628    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04629    case RES_OKAY:
04630       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04631       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04632       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04633       res = 0;
04634       break;
04635    case RES_EXISTS:
04636       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04637       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04638       res = 0;
04639       break;
04640    case RES_NOSUCHQUEUE:
04641       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04642       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04643       res = 0;
04644       break;
04645    case RES_OUTOFMEMORY:
04646       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04647       break;
04648    }
04649 
04650    return res;
04651 }

static int attended_transfer_occurred ( struct ast_channel chan  )  [static]

mechanism to tell if a queue caller was atxferred by a queue member.

When a caller is atxferred, then the queue_transfer_info datastore is removed from the channel. If it's still there after the bridge is broken, then the caller was not atxferred.

Note:
Only call this with chan locked

Definition at line 3218 of file app_queue.c.

References ast_channel_datastore_find(), chan, and queue_transfer_info.

Referenced by try_calling().

03219 {
03220    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03221 }

static int calc_metric ( struct call_queue q,
struct member mem,
int  pos,
struct queue_ent qe,
struct callattempt tmp 
) [static]

Calculate the metric of each member in the outgoing callattempts.

A numeric metric is given to each member depending on the ring strategy used by the queue. Members with lower metrics will be called before members with higher metrics

Return values:
-1 if penalties are exceeded
0 otherwise

Definition at line 3057 of file app_queue.c.

References ast_log(), ast_random(), member::calls, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_WARNING, queue_ent::max_penalty, callattempt::metric, queue_ent::min_penalty, member::penalty, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_RRMEMORY, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

03058 {
03059    if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03060       return -1;
03061 
03062    switch (q->strategy) {
03063    case QUEUE_STRATEGY_RINGALL:
03064       /* Everyone equal, except for penalty */
03065       tmp->metric = mem->penalty * 1000000;
03066       break;
03067    case QUEUE_STRATEGY_LINEAR:
03068       if (pos < qe->linpos) {
03069          tmp->metric = 1000 + pos;
03070       } else {
03071          if (pos > qe->linpos)
03072             /* Indicate there is another priority */
03073             qe->linwrapped = 1;
03074          tmp->metric = pos;
03075       }
03076       tmp->metric += mem->penalty * 1000000;
03077       break;
03078    case QUEUE_STRATEGY_RRMEMORY:
03079       if (pos < q->rrpos) {
03080          tmp->metric = 1000 + pos;
03081       } else {
03082          if (pos > q->rrpos)
03083             /* Indicate there is another priority */
03084             q->wrapped = 1;
03085          tmp->metric = pos;
03086       }
03087       tmp->metric += mem->penalty * 1000000;
03088       break;
03089    case QUEUE_STRATEGY_RANDOM:
03090       tmp->metric = ast_random() % 1000;
03091       tmp->metric += mem->penalty * 1000000;
03092       break;
03093    case QUEUE_STRATEGY_WRANDOM:
03094       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03095       break;
03096    case QUEUE_STRATEGY_FEWESTCALLS:
03097       tmp->metric = mem->calls;
03098       tmp->metric += mem->penalty * 1000000;
03099       break;
03100    case QUEUE_STRATEGY_LEASTRECENT:
03101       if (!mem->lastcall)
03102          tmp->metric = 0;
03103       else
03104          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03105       tmp->metric += mem->penalty * 1000000;
03106       break;
03107    default:
03108       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03109       break;
03110    }
03111    return 0;
03112 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1036 of file app_queue.c.

References ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and member_interface::list.

Referenced by unload_module().

01037 {
01038    struct member_interface *curint;
01039 
01040    AST_LIST_LOCK(&interfaces);
01041    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01042       ast_free(curint);
01043    AST_LIST_UNLOCK(&interfaces);
01044 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 953 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00954 {
00955    q->holdtime = 0;
00956    q->callscompleted = 0;
00957    q->callsabandoned = 0;
00958    q->callscompletedinsl = 0;
00959    q->wrapuptime = 0;
00960 }

static int compare_weight ( struct call_queue rq,
struct member member 
) [static]

Definition at line 2123 of file app_queue.c.

References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_debug, call_queue::count, member::interface, call_queue::members, call_queue::name, num_available_members(), OBJ_POINTER, queue_unref(), queues, and call_queue::weight.

Referenced by ring_entry().

02124 {
02125    struct call_queue *q;
02126    struct member *mem;
02127    int found = 0;
02128    struct ao2_iterator queue_iter;
02129    
02130    /* q's lock and rq's lock already set by try_calling()
02131     * to solve deadlock */
02132    queue_iter = ao2_iterator_init(queues, 0);
02133    while ((q = ao2_iterator_next(&queue_iter))) {
02134       if (q == rq) { /* don't check myself, could deadlock */
02135          queue_unref(q);
02136          continue;
02137       }
02138       ao2_lock(q);
02139       if (q->count && q->members) {
02140          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02141             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02142             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02143                ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
02144                found = 1;
02145             }
02146             ao2_ref(mem, -1);
02147          }
02148       }
02149       ao2_unlock(q);
02150       queue_unref(q);
02151       if (found) {
02152          break;
02153       }
02154    }
02155    return found;
02156 }

static char* complete_queue ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 5805 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ast_strdup, call_queue::name, queue_unref(), and queues.

Referenced by complete_queue_add_member(), complete_queue_pause_member(), complete_queue_remove_member(), complete_queue_set_member_penalty(), and complete_queue_show().

05806 {
05807    struct call_queue *q;
05808    char *ret = NULL;
05809    int which = 0;
05810    int wordlen = strlen(word);
05811    struct ao2_iterator queue_iter;
05812 
05813    queue_iter = ao2_iterator_init(queues, 0);
05814    while ((q = ao2_iterator_next(&queue_iter))) {
05815       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05816          ret = ast_strdup(q->name);
05817          queue_unref(q);
05818          break;
05819       }
05820       queue_unref(q);
05821    }
05822 
05823    return ret;
05824 }

static char* complete_queue_add_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 6182 of file app_queue.c.

References ast_malloc, ast_strdup, complete_queue(), and num.

Referenced by handle_queue_add_member().

06183 {
06184    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
06185    switch (pos) {
06186    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
06187       return NULL;
06188    case 4: /* only one possible match, "to" */
06189       return state == 0 ? ast_strdup("to") : NULL;
06190    case 5: /* <queue> */
06191       return complete_queue(line, word, pos, state);
06192    case 6: /* only one possible match, "penalty" */
06193       return state == 0 ? ast_strdup("penalty") : NULL;
06194    case 7:
06195       if (state < 100) {      /* 0-99 */
06196          char *num;
06197          if ((num = ast_malloc(3))) {
06198             sprintf(num, "%d", state);
06199          }
06200          return num;
06201       } else {
06202          return NULL;
06203       }
06204    case 8: /* only one possible match, "as" */
06205       return state == 0 ? ast_strdup("as") : NULL;
06206    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
06207       return NULL;
06208    default:
06209       return NULL;
06210    }
06211 }

static char* complete_queue_pause_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 6396 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

06397 {
06398    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
06399    switch (pos) {
06400    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
06401       return NULL;
06402    case 4:  /* only one possible match, "queue" */
06403       return state == 0 ? ast_strdup("queue") : NULL;
06404    case 5:  /* <queue> */
06405       return complete_queue(line, word, pos, state);
06406    case 6: /* "reason" */
06407       return state == 0 ? ast_strdup("reason") : NULL;
06408    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
06409       return NULL;
06410    default:
06411       return NULL;
06412    }
06413 }

static char* complete_queue_remove_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 6312 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_strdup, complete_queue(), member::interface, member::membername, queue_unref(), and queues.

Referenced by handle_queue_remove_member().

06313 {
06314    int which = 0;
06315    struct call_queue *q;
06316    struct member *m;
06317    struct ao2_iterator queue_iter;
06318    struct ao2_iterator mem_iter;
06319    int wordlen = strlen(word);
06320 
06321    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
06322    if (pos > 5 || pos < 3)
06323       return NULL;
06324    if (pos == 4)   /* only one possible match, 'from' */
06325       return (state == 0 ? ast_strdup("from") : NULL);
06326 
06327    if (pos == 5)   /* No need to duplicate code */
06328       return complete_queue(line, word, pos, state);
06329 
06330    /* here is the case for 3, <member> */
06331    queue_iter = ao2_iterator_init(queues, 0);
06332    while ((q = ao2_iterator_next(&queue_iter))) {
06333       ao2_lock(q);
06334       mem_iter = ao2_iterator_init(q->members, 0);
06335       while ((m = ao2_iterator_next(&mem_iter))) {
06336          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06337             char *tmp;
06338             ao2_unlock(q);
06339             tmp = ast_strdup(m->interface);
06340             ao2_ref(m, -1);
06341             queue_unref(q);
06342             return tmp;
06343          }
06344          ao2_ref(m, -1);
06345       }
06346       ao2_unlock(q);
06347       queue_unref(q);
06348    }
06349 
06350    return NULL;
06351 }

static char* complete_queue_rule_show ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 6529 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, rule_list::list, and rule_list::name.

Referenced by handle_queue_rule_show().

06530 {
06531    int which = 0;
06532    struct rule_list *rl_iter;
06533    int wordlen = strlen(word);
06534    char *ret = NULL;
06535    if (pos != 3) /* Wha? */ {
06536       return NULL;
06537    }
06538 
06539    AST_LIST_LOCK(&rule_lists);
06540    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06541       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06542          ret = ast_strdup(rl_iter->name);
06543          break;
06544       }
06545    }
06546    AST_LIST_UNLOCK(&rule_lists);
06547 
06548    return ret;
06549 }

static char* complete_queue_set_member_penalty ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 6466 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

06467 {
06468    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
06469    switch (pos) {
06470    case 4:
06471       if (state == 0) {
06472          return ast_strdup("on");
06473       } else {
06474          return NULL;
06475       }
06476    case 6:
06477       if (state == 0) {
06478          return ast_strdup("in");
06479       } else {
06480          return NULL;
06481       }
06482    case 7:
06483       return complete_queue(line, word, pos, state);
06484    default:
06485       return NULL;
06486    }
06487 }

static char* complete_queue_show ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 5826 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

05827 {
05828    if (pos == 2)
05829       return complete_queue(line, word, pos, state);
05830    return NULL;
05831 }

static int compress_char ( const char  c  )  [static]

Definition at line 851 of file app_queue.c.

00852 {
00853    if (c < 32)
00854       return 0;
00855    else if (c > 96)
00856       return c - 64;
00857    else
00858       return c - 32;
00859 }

static void copy_rules ( struct queue_ent qe,
const char *  rulename 
) [static]

Copy rule from global list into specified queue.

Definition at line 4688 of file app_queue.c.

References ast_calloc, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strlen_zero(), penalty_rule::list, rule_list::list, LOG_ERROR, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, queue_ent::qe_rules, rule_list::rules, and penalty_rule::time.

Referenced by join_queue().

04689 {
04690    struct penalty_rule *pr_iter;
04691    struct rule_list *rl_iter;
04692    const char *tmp = rulename;
04693    if (ast_strlen_zero(tmp)) {
04694       return;
04695    }
04696    AST_LIST_LOCK(&rule_lists);
04697    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04698       if (!strcasecmp(rl_iter->name, tmp))
04699          break;
04700    }
04701    if (rl_iter) {
04702       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04703          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04704          if (!new_pr) {
04705             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04706             AST_LIST_UNLOCK(&rule_lists);
04707             break;
04708          }
04709          new_pr->time = pr_iter->time;
04710          new_pr->max_value = pr_iter->max_value;
04711          new_pr->min_value = pr_iter->min_value;
04712          new_pr->max_relative = pr_iter->max_relative;
04713          new_pr->min_relative = pr_iter->min_relative;
04714          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04715       }
04716    }
04717    AST_LIST_UNLOCK(&rule_lists);
04718 }

static struct member* create_queue_member ( const char *  interface,
const char *  membername,
int  penalty,
int  paused,
const char *  state_interface 
) [static]

allocate space for new queue member and set fields based on parameters passed

Definition at line 826 of file app_queue.c.

References ao2_alloc, ast_copy_string(), ast_device_state(), ast_log(), ast_strlen_zero(), LOG_WARNING, and member::penalty.

Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().

00827 {
00828    struct member *cur;
00829    
00830    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00831       cur->penalty = penalty;
00832       cur->paused = paused;
00833       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00834       if (!ast_strlen_zero(state_interface))
00835          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00836       else
00837          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00838       if (!ast_strlen_zero(membername))
00839          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00840       else
00841          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00842       if (!strchr(cur->interface, '/'))
00843          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00844       cur->status = ast_device_state(cur->state_interface);
00845    }
00846 
00847    return cur;
00848 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

Definition at line 1422 of file app_queue.c.

References ao2_ref, ast_string_field_free_memory, free, free_members(), MAX_PERIODIC_ANNOUNCEMENTS, call_queue::members, and call_queue::sound_periodicannounce.

Referenced by alloc_queue().

01423 {
01424    struct call_queue *q = obj;
01425    int i;
01426 
01427    free_members(q, 1);
01428    ast_string_field_free_memory(q);
01429    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01430       if (q->sound_periodicannounce[i])
01431          free(q->sound_periodicannounce[i]);
01432    }
01433    ao2_ref(q->members, -1);
01434 }

static void device_state_cb ( const struct ast_event event,
void *  unused 
) [static]

Definition at line 799 of file app_queue.c.

References ast_calloc, ast_device_state(), ast_event_get_ie_str(), ast_event_get_ie_uint(), AST_EVENT_IE_DEVICE, AST_EVENT_IE_STATE, ast_free, ast_log(), ast_strlen_zero(), ast_taskprocessor_push(), statechange::dev, devicestate_tps, handle_statechange(), LOG_ERROR, and statechange::state.

00800 {
00801    enum ast_device_state state;
00802    const char *device;
00803    struct statechange *sc;
00804    size_t datapsize;
00805 
00806    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00807    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00808 
00809    if (ast_strlen_zero(device)) {
00810       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
00811       return;
00812    }
00813    datapsize = sizeof(*sc) + strlen(device) + 1;
00814    if (!(sc = ast_calloc(1, datapsize))) {
00815       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
00816       return;
00817    }
00818    sc->state = state;
00819    strcpy(sc->dev, device);
00820    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
00821       ast_free(sc);
00822    }
00823 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2159 of file app_queue.c.

References ast_hangup(), callattempt::chan, and callattempt::stillgoing.

Referenced by ring_entry().

02160 {
02161    o->stillgoing = 0;
02162    ast_hangup(o->chan);
02163    o->chan = NULL;
02164 }

static void do_print ( struct mansession s,
int  fd,
const char *  str 
) [static]

direct ouput to manager or cli with proper terminator

Definition at line 5665 of file app_queue.c.

References ast_cli(), astman_append(), and s.

Referenced by __queues_show().

05666 {
05667    if (s)
05668       astman_append(s, "%s\r\n", str);
05669    else
05670       ast_cli(fd, "%s\n", str);
05671 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Dump all members in a specific queue to the database.

<pm_family>/<queuename> = <interface>;<penalty>;<paused>;<state_interface>[|...]

Definition at line 4058 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, PM_MAX_LEN, and member::state_interface.

Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().

04059 {
04060    struct member *cur_member;
04061    char value[PM_MAX_LEN];
04062    int value_len = 0;
04063    int res;
04064    struct ao2_iterator mem_iter;
04065 
04066    memset(value, 0, sizeof(value));
04067 
04068    if (!pm_queue)
04069       return;
04070 
04071    mem_iter = ao2_iterator_init(pm_queue->members, 0);
04072    while ((cur_member = ao2_iterator_next(&mem_iter))) {
04073       if (!cur_member->dynamic) {
04074          ao2_ref(cur_member, -1);
04075          continue;
04076       }
04077 
04078       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04079          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04080 
04081       ao2_ref(cur_member, -1);
04082 
04083       if (res != strlen(value + value_len)) {
04084          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04085          break;
04086       }
04087       value_len += res;
04088    }
04089    
04090    if (value_len && !cur_member) {
04091       if (ast_db_put(pm_family, pm_queue->name, value))
04092          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04093    } else
04094       /* Delete the entry if the queue is empty or there is an error */
04095       ast_db_del(pm_family, pm_queue->name);
04096 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 3265 of file app_queue.c.

References ao2_lock(), ao2_ref, ao2_unlock(), queue_end_bridge::chan, chan, queue_end_bridge::q, queue_unref(), and set_queue_variables().

03266 {
03267    struct queue_end_bridge *qeb = data;
03268    struct call_queue *q = qeb->q;
03269    struct ast_channel *chan = qeb->chan;
03270 
03271    if (ao2_ref(qeb, -1) == 1) {
03272       ao2_lock(q);
03273       set_queue_variables(q, chan);
03274       ao2_unlock(q);
03275       /* This unrefs the reference we made in try_calling when we allocated qeb */
03276       queue_unref(q);
03277    }
03278 }

static void end_bridge_callback_data_fixup ( struct ast_bridge_config bconfig,
struct ast_channel originator,
struct ast_channel terminator 
) [static]

Definition at line 3258 of file app_queue.c.

References ao2_ref, queue_end_bridge::chan, and ast_bridge_config::end_bridge_callback_data.

03259 {
03260    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03261    ao2_ref(qeb, +1);
03262    qeb->chan = originator;
03263 }

static struct callattempt* find_best ( struct callattempt outgoing  )  [static]

find the entry with the best metric, or NULL

Definition at line 2355 of file app_queue.c.

References callattempt::metric, and callattempt::q_next.

02356 {
02357    struct callattempt *best = NULL, *cur;
02358 
02359    for (cur = outgoing; cur; cur = cur->q_next) {
02360       if (cur->stillgoing &&              /* Not already done */
02361          !cur->chan &&              /* Isn't already going */
02362          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02363          best = cur;
02364       }
02365    }
02366 
02367    return best;
02368 }

static struct call_queue* find_queue_by_name_rt ( const char *  queuename,
struct ast_variable queue_vars,
struct ast_config member_config 
) [static]

Reload a single queue via realtime.

Check for statically defined queue first, check if deleted RT queue, check for new RT queue, if queue vars are not defined init them with defaults. reload RT queue vars, set RT queue members dead and reload them, return finished queue.

Return values:
the queue,
NULL if it doesn't exist.
Note:
Should be called with the "queues" container locked.

Note:
Hmm, can't seem to distinguish a DB failure from a not found condition... So we might delete an in-core queue in case of DB failure.

Definition at line 1460 of file app_queue.c.

References alloc_queue(), ao2_find, ao2_lock(), ao2_unlink, ao2_unlock(), ast_debug, ast_log(), clear_queue(), LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, QUEUE_STRATEGY_RINGALL, queue_unref(), queues, call_queue::realtime, strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

01461 {
01462    struct ast_variable *v;
01463    struct call_queue *q, tmpq = {
01464       .name = queuename,   
01465    };
01466    struct member *m;
01467    struct ao2_iterator mem_iter;
01468    char *interface = NULL;
01469    const char *tmp_name;
01470    char *tmp;
01471    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01472 
01473    /* Static queues override realtime. */
01474    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
01475       ao2_lock(q);
01476       if (!q->realtime) {
01477          if (q->dead) {
01478             ao2_unlock(q);
01479             queue_unref(q);
01480             return NULL;
01481          } else {
01482             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01483             ao2_unlock(q);
01484             return q;
01485          }
01486       }
01487       queue_unref(q);
01488    } else if (!member_config)
01489       /* Not found in the list, and it's not realtime ... */
01490       return NULL;
01491 
01492    /* Check if queue is defined in realtime. */
01493    if (!queue_vars) {
01494       /* Delete queue from in-core list if it has been deleted in realtime. */
01495       if (q) {
01496          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01497             found condition... So we might delete an in-core queue
01498             in case of DB failure. */
01499          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
01500 
01501          q->dead = 1;
01502          /* Delete if unused (else will be deleted when last caller leaves). */
01503          ao2_unlink(queues, q);
01504          ao2_unlock(q);
01505          queue_unref(q);
01506       }
01507       return NULL;
01508    }
01509 
01510    /* Create a new queue if an in-core entry does not exist yet. */
01511    if (!q) {
01512       struct ast_variable *tmpvar = NULL;
01513       if (!(q = alloc_queue(queuename)))
01514          return NULL;
01515       ao2_lock(q);
01516       clear_queue(q);
01517       q->realtime = 1;
01518       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
01519        * will allocate the members properly
01520        */
01521       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
01522          if (!strcasecmp(tmpvar->name, "strategy")) {
01523             q->strategy = strat2int(tmpvar->value);
01524             if (q->strategy < 0) {
01525                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01526                tmpvar->value, q->name);
01527                q->strategy = QUEUE_STRATEGY_RINGALL;
01528             }
01529             break;
01530          }
01531       }
01532       /* We traversed all variables and didn't find a strategy */
01533       if (!tmpvar)
01534          q->strategy = QUEUE_STRATEGY_RINGALL;
01535       ao2_link(queues, q);
01536    }
01537    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01538 
01539    memset(tmpbuf, 0, sizeof(tmpbuf));
01540    for (v = queue_vars; v; v = v->next) {
01541       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01542       if ((tmp = strchr(v->name, '_'))) {
01543          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01544          tmp_name = tmpbuf;
01545          tmp = tmpbuf;
01546          while ((tmp = strchr(tmp, '_')))
01547             *tmp++ = '-';
01548       } else
01549          tmp_name = v->name;
01550 
01551       if (!ast_strlen_zero(v->value)) {
01552          /* Don't want to try to set the option if the value is empty */
01553          queue_set_param(q, tmp_name, v->value, -1, 0);
01554       }
01555    }
01556 
01557    /* Temporarily set realtime members dead so we can detect deleted ones. 
01558     * Also set the membercount correctly for realtime*/
01559    mem_iter = ao2_iterator_init(q->members, 0);
01560    while ((m = ao2_iterator_next(&mem_iter))) {
01561       q->membercount++;
01562       if (m->realtime)
01563          m->dead = 1;
01564       ao2_ref(m, -1);
01565    }
01566 
01567    while ((interface = ast_category_browse(member_config, interface))) {
01568       rt_handle_member_record(q, interface,
01569          ast_variable_retrieve(member_config, interface, "uniqueid"),
01570          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
01571          ast_variable_retrieve(member_config, interface, "penalty"),
01572          ast_variable_retrieve(member_config, interface, "paused"),
01573          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01574    }
01575 
01576    /* Delete all realtime members that have been deleted in DB. */
01577    mem_iter = ao2_iterator_init(q->members, 0);
01578    while ((m = ao2_iterator_next(&mem_iter))) {
01579       if (m->dead) {
01580          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01581          ao2_unlink(q->members, m);
01582          remove_from_interfaces(m->state_interface, 0);
01583          q->membercount--;
01584       }
01585       ao2_ref(m, -1);
01586    }
01587 
01588    ao2_unlock(q);
01589 
01590    return q;
01591 }

static void free_members ( struct call_queue q,
int  all 
) [static]

Iterate through queue's member list and delete them.

Definition at line 1405 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, member::dynamic, call_queue::membercount, call_queue::members, remove_from_interfaces(), and member::state_interface.

Referenced by destroy_queue().

01406 {
01407    /* Free non-dynamic members */
01408    struct member *cur;
01409    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01410 
01411    while ((cur = ao2_iterator_next(&mem_iter))) {
01412       if (all || !cur->dynamic) {
01413          ao2_unlink(q->members, cur);
01414          remove_from_interfaces(cur->state_interface, 1);
01415          q->membercount--;
01416       }
01417       ao2_ref(cur, -1);
01418    }
01419 }

static int get_member_penalty ( char *  queuename,
char *  interface 
) [static]

Definition at line 4335 of file app_queue.c.

References ao2_find, ao2_lock(), ao2_ref, ao2_unlock(), ast_log(), interface_exists(), LOG_ERROR, OBJ_POINTER, member::penalty, queue_unref(), queues, and RESULT_FAILURE.

Referenced by queue_function_memberpenalty_read().

04336 {
04337    int foundqueue = 0, penalty;
04338    struct call_queue *q, tmpq = {
04339       .name = queuename,   
04340    };
04341    struct member *mem;
04342    
04343    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04344       foundqueue = 1;
04345       ao2_lock(q);
04346       if ((mem = interface_exists(q, interface))) {
04347          penalty = mem->penalty;
04348          ao2_ref(mem, -1);
04349          ao2_unlock(q);
04350          queue_unref(q);
04351          return penalty;
04352       }
04353       ao2_unlock(q);
04354       queue_unref(q);
04355    }
04356 
04357    /* some useful debuging */
04358    if (foundqueue) 
04359       ast_log (LOG_ERROR, "Invalid queuename\n");
04360    else 
04361       ast_log (LOG_ERROR, "Invalid interface\n");
04362 
04363    return RESULT_FAILURE;
04364 }

static enum queue_member_status get_member_status ( struct call_queue q,
int  max_penalty,
int  min_penalty 
) [static]

Check if members are available.

This function checks to see if members are available to be called. If any member is available, the function immediately returns QUEUE_NORMAL. If no members are available, the appropriate reason why is returned

Definition at line 667 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, call_queue::members, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, and member::status.

Referenced by join_queue(), queue_exec(), and wait_our_turn().

00668 {
00669    struct member *member;
00670    struct ao2_iterator mem_iter;
00671    enum queue_member_status result = QUEUE_NO_MEMBERS;
00672 
00673    ao2_lock(q);
00674    mem_iter = ao2_iterator_init(q->members, 0);
00675    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
00676       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
00677          continue;
00678 
00679       switch (member->status) {
00680       case AST_DEVICE_INVALID:
00681          /* nothing to do */
00682          break;
00683       case AST_DEVICE_UNAVAILABLE:
00684          if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS) 
00685             result = QUEUE_NO_REACHABLE_MEMBERS;
00686          break;
00687       default:
00688          if (member->paused) {
00689             result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
00690          } else {
00691             ao2_unlock(q);
00692             ao2_ref(member, -1);
00693             return QUEUE_NORMAL;
00694          }
00695          break;
00696       }
00697    }
00698 
00699    ao2_unlock(q);
00700    return result;
00701 }

static char* handle_queue_add_member ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 6238 of file app_queue.c.

References add_to_queue(), ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_add_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, ast_cli_entry::usage, and ast_cli_args::word.

06239 {
06240    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06241    int penalty;
06242 
06243    switch ( cmd ) {
06244    case CLI_INIT:
06245       e->command = "queue add member";
06246       e->usage =
06247          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06248          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
06249       return NULL;
06250    case CLI_GENERATE:
06251       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06252    }
06253 
06254    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06255       return CLI_SHOWUSAGE;
06256    } else if (strcmp(a->argv[4], "to")) {
06257       return CLI_SHOWUSAGE;
06258    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06259       return CLI_SHOWUSAGE;
06260    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06261       return CLI_SHOWUSAGE;
06262    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06263       return CLI_SHOWUSAGE;
06264    }
06265 
06266    queuename = a->argv[5];
06267    interface = a->argv[3];
06268    if (a->argc >= 8) {
06269       if (sscanf(a->argv[7], "%d", &penalty) == 1) {
06270          if (penalty < 0) {
06271             ast_cli(a->fd, "Penalty must be >= 0\n");
06272             penalty = 0;
06273          }
06274       } else {
06275          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06276          penalty = 0;
06277       }
06278    } else {
06279       penalty = 0;
06280    }
06281 
06282    if (a->argc >= 10) {
06283       membername = a->argv[9];
06284    }
06285 
06286    if (a->argc >= 12) {
06287       state_interface = a->argv[11];
06288    }
06289 
06290    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06291    case RES_OKAY:
06292       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06293       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06294       return CLI_SUCCESS;
06295    case RES_EXISTS:
06296       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06297       return CLI_FAILURE;
06298    case RES_NOSUCHQUEUE:
06299       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06300       return CLI_FAILURE;
06301    case RES_OUTOFMEMORY:
06302       ast_cli(a->fd, "Out of memory\n");
06303       return CLI_FAILURE;
06304    case RES_NOT_DYNAMIC:
06305       ast_cli(a->fd, "Member not dynamic\n");
06306       return CLI_FAILURE;
06307    default:
06308       return CLI_FAILURE;
06309    }
06310 }

static char* handle_queue_pause_member ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 6415 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_pause_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, RESULT_SUCCESS, set_member_paused(), and ast_cli_entry::usage.

06416 {
06417    char *queuename, *interface, *reason;
06418    int paused;
06419 
06420    switch (cmd) {
06421    case CLI_INIT:
06422       e->command = "queue {pause|unpause} member";
06423       e->usage = 
06424          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06425          "  Pause or unpause a queue member. Not specifying a particular queue\n"
06426          "  will pause or unpause a member across all queues to which the member\n"
06427          "  belongs.\n";
06428       return NULL;
06429    case CLI_GENERATE:
06430       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06431    }
06432 
06433    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06434       return CLI_SHOWUSAGE;
06435    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06436       return CLI_SHOWUSAGE;
06437    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06438       return CLI_SHOWUSAGE;
06439    }
06440 
06441 
06442    interface = a->argv[3];
06443    queuename = a->argc >= 6 ? a->argv[5] : NULL;
06444    reason = a->argc == 8 ? a->argv[7] : NULL;
06445    paused = !strcasecmp(a->argv[1], "pause");
06446 
06447    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06448       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06449       if (!ast_strlen_zero(queuename))
06450          ast_cli(a->fd, " in queue '%s'", queuename);
06451       if (!ast_strlen_zero(reason))
06452          ast_cli(a->fd, " for reason '%s'", reason);
06453       ast_cli(a->fd, "\n");
06454       return CLI_SUCCESS;
06455    } else {
06456       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06457       if (!ast_strlen_zero(queuename))
06458          ast_cli(a->fd, " in queue '%s'", queuename);
06459       if (!ast_strlen_zero(reason))
06460          ast_cli(a->fd, " for reason '%s'", reason);
06461       ast_cli(a->fd, "\n");
06462       return CLI_FAILURE;
06463    }
06464 }

static char* handle_queue_remove_member ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 6353 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_remove_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, ast_cli_entry::usage, and ast_cli_args::word.

06354 {
06355    char *queuename, *interface;
06356 
06357    switch (cmd) {
06358    case CLI_INIT:
06359       e->command = "queue remove member";
06360       e->usage = 
06361          "Usage: queue remove member <channel> from <queue>\n"
06362          "       Remove a specific channel from a queue.\n";
06363       return NULL;
06364    case CLI_GENERATE:
06365       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06366    }
06367 
06368    if (a->argc != 6) {
06369       return CLI_SHOWUSAGE;
06370    } else if (strcmp(a->argv[4], "from")) {
06371       return CLI_SHOWUSAGE;
06372    }
06373 
06374    queuename = a->argv[5];
06375    interface = a->argv[3];
06376 
06377    switch (remove_from_queue(queuename, interface)) {
06378    case RES_OKAY:
06379       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06380       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06381       return CLI_SUCCESS;
06382    case RES_EXISTS:
06383       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06384       return CLI_FAILURE;
06385    case RES_NOSUCHQUEUE:
06386       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06387       return CLI_FAILURE;
06388    case RES_OUTOFMEMORY:
06389       ast_cli(a->fd, "Out of memory\n");
06390       return CLI_FAILURE;
06391    default:
06392       return CLI_FAILURE;
06393    }
06394 }

static char* handle_queue_rule_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 6585 of file app_queue.c.

References CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, reload_queue_rules(), and ast_cli_entry::usage.

06586 {
06587    switch (cmd) {
06588       case CLI_INIT:
06589          e->command = "queue rules reload";
06590          e->usage = 
06591             "Usage: queue rules reload\n"
06592             "  Reloads rules defined in queuerules.conf\n";
06593          return NULL;
06594       case CLI_GENERATE:
06595          return NULL;
06596    }
06597    reload_queue_rules(1);
06598    return CLI_SUCCESS;
06599 }

static char* handle_queue_rule_show ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 6551 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_rule_show(), ast_cli_args::fd, ast_cli_args::line, penalty_rule::list, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, ast_cli_args::n, rule_list::name, ast_cli_args::pos, rule_list::rules, penalty_rule::time, ast_cli_entry::usage, and ast_cli_args::word.

06552 {
06553    char *rule;
06554    struct rule_list *rl_iter;
06555    struct penalty_rule *pr_iter;
06556    switch (cmd) {
06557    case CLI_INIT:
06558       e->command = "queue rules show";
06559       e->usage =
06560       "Usage: queue rules show [rulename]\n"
06561       "  Show the list of rules associated with rulename. If no\n"
06562       "  rulename is specified, list all rules defined in queuerules.conf\n";
06563       return NULL;
06564    case CLI_GENERATE:
06565       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06566    }
06567 
06568    if (a->argc != 3 && a->argc != 4)
06569       return CLI_SHOWUSAGE;
06570 
06571    rule = a->argc == 4 ? a->argv[3] : "";
06572    AST_LIST_LOCK(&rule_lists);
06573    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06574       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06575          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06576          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06577             ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
06578          }
06579       }
06580    }
06581    AST_LIST_UNLOCK(&rule_lists);
06582    return CLI_SUCCESS; 
06583 }

static char* handle_queue_set_member_penalty ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 6489 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_set_member_penalty(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, RESULT_FAILURE, RESULT_SUCCESS, set_member_penalty(), ast_cli_entry::usage, and ast_cli_args::word.

06490 {
06491    char *queuename = NULL, *interface;
06492    int penalty = 0;
06493 
06494    switch (cmd) {
06495    case CLI_INIT:
06496       e->command = "queue set penalty";
06497       e->usage = 
06498       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06499       "  Set a member's penalty in the queue specified. If no queue is specified\n"
06500       "  then that interface's penalty is set in all queues to which that interface is a member\n";
06501       return NULL;
06502    case CLI_GENERATE:
06503       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06504    }
06505 
06506    if (a->argc != 6 && a->argc != 8) {
06507       return CLI_SHOWUSAGE;
06508    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06509       return CLI_SHOWUSAGE;
06510    }
06511 
06512    if (a->argc == 8)
06513       queuename = a->argv[7];
06514    interface = a->argv[5];
06515    penalty = atoi(a->argv[3]);
06516 
06517    switch (set_member_penalty(queuename, interface, penalty)) {
06518    case RESULT_SUCCESS:
06519       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06520       return CLI_SUCCESS;
06521    case RESULT_FAILURE:
06522       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06523       return CLI_FAILURE;
06524    default:
06525       return CLI_FAILURE;
06526    }
06527 }

static int handle_statechange ( void *  datap  )  [static]

set a member's status based on device state of that member's interface

Definition at line 767 of file app_queue.c.

References ast_debug, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdupa, statechange::dev, devstate2str(), member_interface::interface, statechange::state, and update_status().

00768 {
00769    struct member_interface *curint;
00770    struct statechange *sc = datap;
00771 
00772    AST_LIST_LOCK(&interfaces);
00773    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00774       char *interface;
00775       char *slash_pos;
00776       interface = ast_strdupa(curint->interface);
00777       if ((slash_pos = strchr(interface, '/')))
00778          if ((slash_pos = strchr(slash_pos + 1, '/')))
00779             *slash_pos = '\0';
00780 
00781       if (!strcasecmp(interface, sc->dev))
00782          break;
00783    }
00784    AST_LIST_UNLOCK(&interfaces);
00785 
00786    if (!curint) {
00787       ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, devstate2str(sc->state));
00788       ast_free(sc);
00789       return 0;
00790    }
00791 
00792    ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state));
00793 
00794    update_status(sc->dev, sc->state);
00795    ast_free(sc);
00796    return 0;
00797 }

static void hangupcalls ( struct callattempt outgoing,
struct ast_channel exception 
) [static]

Hang up a list of outgoing calls.

Definition at line 2057 of file app_queue.c.

References ao2_ref, ast_free, ast_hangup(), callattempt::chan, callattempt::member, and callattempt::q_next.

Referenced by try_calling().

02058 {
02059    struct callattempt *oo;
02060 
02061    while (outgoing) {
02062       /* Hangup any existing lines we have open */
02063       if (outgoing->chan && (outgoing->chan != exception))
02064          ast_hangup(outgoing->chan);
02065       oo = outgoing;
02066       outgoing = outgoing->q_next;
02067       if (oo->member)
02068          ao2_ref(oo->member, -1);
02069       ast_free(oo);
02070    }
02071 }

static void init_queue ( struct call_queue q  )  [static]

Initialize Queue default values.

Note:
the queue's lock must be held before executing this function

Definition at line 883 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, penalty_rule::list, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::numperiodicannounce, call_queue::periodicannouncefrequency, QUEUE_STRATEGY_LINEAR, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_queues().

00884 {
00885    int i;
00886    struct penalty_rule *pr_iter;
00887 
00888    q->dead = 0;
00889    q->retry = DEFAULT_RETRY;
00890    q->timeout = DEFAULT_TIMEOUT;
00891    q->maxlen = 0;
00892    q->announcefrequency = 0;
00893    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
00894    q->announceholdtime = 1;
00895    q->announcepositionlimit = 10; /* Default 10 positions */
00896    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
00897    q->roundingseconds = 0; /* Default - don't announce seconds */
00898    q->servicelevel = 0;
00899    q->ringinuse = 1;
00900    q->setinterfacevar = 0;
00901    q->setqueuevar = 0;
00902    q->setqueueentryvar = 0;
00903    q->autofill = autofill_default;
00904    q->montype = montype_default;
00905    q->monfmt[0] = '\0';
00906    q->reportholdtime = 0;
00907    q->wrapuptime = 0;
00908    q->joinempty = 0;
00909    q->leavewhenempty = 0;
00910    q->memberdelay = 0;
00911    q->maskmemberstatus = 0;
00912    q->eventwhencalled = 0;
00913    q->weight = 0;
00914    q->timeoutrestart = 0;
00915    q->periodicannouncefrequency = 0;
00916    q->randomperiodicannounce = 0;
00917    q->numperiodicannounce = 0;
00918    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
00919    if (!q->members) {
00920       if (q->strategy == QUEUE_STRATEGY_LINEAR)
00921          /* linear strategy depends on order, so we have to place all members in a single bucket */
00922          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
00923       else
00924          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00925    }
00926    q->membercount = 0;
00927    q->found = 1;
00928 
00929    ast_string_field_set(q, sound_next, "queue-youarenext");
00930    ast_string_field_set(q, sound_thereare, "queue-thereare");
00931    ast_string_field_set(q, sound_calls, "queue-callswaiting");
00932    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
00933    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
00934    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
00935    ast_string_field_set(q, sound_minutes, "queue-minutes");
00936    ast_string_field_set(q, sound_minute, "queue-minute");
00937    ast_string_field_set(q, sound_seconds, "queue-seconds");
00938    ast_string_field_set(q, sound_thanks, "queue-thankyou");
00939    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
00940 
00941    if ((q->sound_periodicannounce[0] = ast_str_create(32)))
00942       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
00943 
00944    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00945       if (q->sound_periodicannounce[i])
00946          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
00947    }
00948 
00949    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
00950       ast_free(pr_iter);
00951 }

static void insert_entry ( struct call_queue q,
struct queue_ent prev,
struct queue_ent new,
int *  pos 
) [inline, static]

Insert the 'new' entry after the 'prev' entry of queue 'q'.

Definition at line 635 of file app_queue.c.

References call_queue::head, queue_ent::next, and queue_ent::parent.

Referenced by join_queue().

00636 {
00637    struct queue_ent *cur;
00638 
00639    if (!q || !new)
00640       return;
00641    if (prev) {
00642       cur = prev->next;
00643       prev->next = new;
00644    } else {
00645       cur = q->head;
00646       q->head = new;
00647    }
00648    new->next = cur;
00649    new->parent = q;
00650    new->pos = ++(*pos);
00651    new->opos = *pos;
00652 }

static int insert_penaltychange ( const char *  list_name,
const char *  content,
const int  linenum 
) [static]

Change queue penalty by adding rule.

Check rule for errors with time or fomatting, see if rule is relative to rest of queue, iterate list of rules to find correct insertion point, insert and return.

Return values:
-1 on failure
0 on success
Note:
Call this with the rule_lists locked

Definition at line 1055 of file app_queue.c.

References ast_calloc, ast_free, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ast_strdupa, ast_strlen_zero(), rule_list::list, LOG_ERROR, LOG_WARNING, rule_list::name, and rule_list::rules.

Referenced by reload_queue_rules().

01056 {
01057    char *timestr, *maxstr, *minstr, *contentdup;
01058    struct penalty_rule *rule = NULL, *rule_iter;
01059    struct rule_list *rl_iter;
01060    int penaltychangetime, inserted = 0;
01061 
01062    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01063       ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
01064       return -1;
01065    }
01066 
01067    contentdup = ast_strdupa(content);
01068    
01069    if (!(maxstr = strchr(contentdup, ','))) {
01070       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01071       ast_free(rule);
01072       return -1;
01073    }
01074 
01075    *maxstr++ = '\0';
01076    timestr = contentdup;
01077 
01078    if ((penaltychangetime = atoi(timestr)) < 0) {
01079       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01080       ast_free(rule);
01081       return -1;
01082    }
01083 
01084    rule->time = penaltychangetime;
01085 
01086    if ((minstr = strchr(maxstr,',')))
01087       *minstr++ = '\0';
01088    
01089    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01090     * OR if a min penalty change is indicated but no max penalty change is */
01091    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01092       rule->max_relative = 1;
01093    }
01094 
01095    rule->max_value = atoi(maxstr);
01096 
01097    if (!ast_strlen_zero(minstr)) {
01098       if (*minstr == '+' || *minstr == '-')
01099          rule->min_relative = 1;
01100       rule->min_value = atoi(minstr);
01101    } else /*there was no minimum specified, so assume this means no change*/
01102       rule->min_relative = 1;
01103 
01104    /*We have the rule made, now we need to insert it where it belongs*/
01105    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01106       if (strcasecmp(rl_iter->name, list_name))
01107          continue;
01108 
01109       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01110          if (rule->time < rule_iter->time) {
01111             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01112             inserted = 1;
01113             break;
01114          }
01115       }
01116       AST_LIST_TRAVERSE_SAFE_END;
01117    
01118       if (!inserted) {
01119          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01120       }
01121    }
01122 
01123    return 0;
01124 }

static const char* int2strat ( int  strategy  )  [static]

Definition at line 566 of file app_queue.c.

References ARRAY_LEN, strategy::name, and strategies.

Referenced by __queues_show(), manager_queues_status(), queue_function_var(), and set_queue_variables().

00567 {
00568    int x;
00569 
00570    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00571       if (strategy == strategies[x].strategy)
00572          return strategies[x].name;
00573    }
00574 
00575    return "<unknown>";
00576 }

static struct member* interface_exists ( struct call_queue q,
const char *  interface 
) [static]

Definition at line 4035 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_ref, member::interface, and call_queue::members.

Referenced by add_to_queue(), get_member_penalty(), set_member_paused(), and set_member_penalty().

04036 {
04037    struct member *mem;
04038    struct ao2_iterator mem_iter;
04039 
04040    if (!q)
04041       return NULL;
04042 
04043    mem_iter = ao2_iterator_init(q->members, 0);
04044    while ((mem = ao2_iterator_next(&mem_iter))) {
04045       if (!strcasecmp(interface, mem->interface))
04046          return mem;
04047       ao2_ref(mem, -1);
04048    }
04049 
04050    return NULL;
04051 }

static int interface_exists_global ( const char *  interface,
int  lock_queue_container 
) [static]

Definition at line 988 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_copy_string(), F_AO2I_DONTLOCK, member::interface, queue_unref(), queues, and member::state_interface.

Referenced by remove_from_interfaces().

00989 {
00990    struct call_queue *q;
00991    struct member *mem, tmpmem;
00992    struct ao2_iterator queue_iter, mem_iter;
00993    int ret = 0;
00994 
00995    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00996    queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : F_AO2I_DONTLOCK);
00997    while ((q = ao2_iterator_next(&queue_iter))) {
00998       ao2_lock(q);
00999       mem_iter = ao2_iterator_init(q->members, 0);
01000       while ((mem = ao2_iterator_next(&mem_iter))) { 
01001          if (!strcasecmp(mem->state_interface, interface)) {
01002             ao2_ref(mem, -1);
01003             ret = 1;
01004             break;
01005          }
01006       }
01007       ao2_unlock(q);
01008       queue_unref(q);
01009    }
01010 
01011    return ret;
01012 }

static int is_our_turn ( struct queue_ent qe  )  [static]

Check if we should start attempting to call queue members.

A simple process, really. Count the number of members who are available to take our call and then see if we are in a position in the queue at which a member could accept our call.

Parameters:
[in] qe The caller who wants to know if it is his turn
Return values:
0 It is not our turn
1 It is our turn

Definition at line 2833 of file app_queue.c.

References ao2_lock(), ao2_unlock(), ast_debug, queue_ent::chan, call_queue::head, ast_channel::name, queue_ent::next, num_available_members(), queue_ent::parent, and queue_ent::pending.

Referenced by queue_exec(), and wait_our_turn().

02834 {
02835    struct queue_ent *ch;
02836    int res;
02837    int avl;
02838    int idx = 0;
02839    /* This needs a lock. How many members are available to be served? */
02840    ao2_lock(qe->parent);
02841 
02842    avl = num_available_members(qe->parent);
02843 
02844    ch = qe->parent->head;
02845 
02846    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02847 
02848    while ((idx < avl) && (ch) && (ch != qe)) {
02849       if (!ch->pending)
02850          idx++;
02851       ch = ch->next;       
02852    }
02853 
02854    ao2_unlock(qe->parent);
02855 
02856    /* If the queue entry is within avl [the number of available members] calls from the top ... */
02857    if (ch && idx < avl) {
02858       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02859       res = 1;
02860    } else {
02861       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02862       res = 0;
02863    }
02864 
02865    return res;
02866 }

static int join_queue ( char *  queuename,
struct queue_ent qe,
enum queue_result reason,
const char *  overriding_rule 
) [static]

Definition at line 1701 of file app_queue.c.

References queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), ast_debug, AST_LIST_FIRST, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, queue_ent::context, copy_rules(), call_queue::count, call_queue::defaultrule, EVENT_FLAG_CALL, get_member_status(), call_queue::head, insert_entry(), call_queue::joinempty, load_realtime_queue(), manager_event, queue_ent::max_penalty, call_queue::maxlen, queue_ent::min_penalty, queue_ent::moh, ast_channel::name, queue_ent::next, queue_ent::pos, queue_ent::pr, queue_ent::prio, queue_ent::qe_rules, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, queues, S_OR, status, ast_channel::uniqueid, and update_qe_rule().

Referenced by queue_exec().

01702 {
01703    struct call_queue *q;
01704    struct queue_ent *cur, *prev = NULL;
01705    int res = -1;
01706    int pos = 0;
01707    int inserted = 0;
01708    enum queue_member_status status;
01709    int exit = 0;
01710 
01711    if (!(q = load_realtime_queue(queuename)))
01712       return res;
01713 
01714    ao2_lock(queues);
01715    ao2_lock(q);
01716 
01717    copy_rules(qe, S_OR(overriding_rule, q->defaultrule));
01718    qe->pr = AST_LIST_FIRST(&qe->qe_rules);
01719 
01720    /* This is our one */
01721    while (!exit) {
01722       status = get_member_status(q, qe->max_penalty, qe->min_penalty);
01723       if (!q->joinempty && (status == QUEUE_NO_MEMBERS))
01724          *reason = QUEUE_JOINEMPTY;
01725       else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01726          *reason = QUEUE_JOINUNAVAIL;
01727       else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01728          *reason = QUEUE_JOINUNAVAIL;
01729       else if (q->maxlen && (q->count >= q->maxlen))
01730          *reason = QUEUE_FULL;
01731       else {
01732          /* There's space for us, put us at the right position inside
01733           * the queue.
01734           * Take into account the priority of the calling user */
01735          inserted = 0;
01736          prev = NULL;
01737          cur = q->head;
01738          while (cur) {
01739             /* We have higher priority than the current user, enter
01740              * before him, after all the other users with priority
01741              * higher or equal to our priority. */
01742             if ((!inserted) && (qe->prio > cur->prio)) {
01743                insert_entry(q, prev, qe, &pos);
01744                inserted = 1;
01745             }
01746             cur->pos = ++pos;
01747             prev = cur;
01748             cur = cur->next;
01749          }
01750          /* No luck, join at the end of the queue */
01751          if (!inserted)
01752             insert_entry(q, prev, qe, &pos);
01753          ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01754          ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01755          ast_copy_string(qe->context, q->context, sizeof(qe->context));
01756          q->count++;
01757          res = 0;
01758          manager_event(EVENT_FLAG_CALL, "Join",
01759             "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01760             qe->chan->name,
01761             S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01762             S_OR(qe->chan->cid.cid_name, "unknown"),
01763             q->name, qe->pos, q->count, qe->chan->uniqueid );
01764          ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01765       }
01766       if (!exit && qe->pr && res) {
01767          /* We failed to join the queue, but perhaps we can join if we move
01768           * to the next defined penalty rule
01769           */
01770          update_qe_rule(qe);
01771       } else {
01772          exit = 1;
01773       }
01774    }
01775    ao2_unlock(q);
01776    ao2_unlock(queues);
01777 
01778    return res;
01779 }

static void leave_queue ( struct queue_ent qe  )  [static]

Caller leaving queue.

Search the queue to find the leaving client, if found remove from queue create manager event, move others up the queue.

Definition at line 2000 of file app_queue.c.

References ao2_lock(), ao2_unlink, ao2_unlock(), ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_load_realtime(), ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, penalty_rule::list, manager_event, call_queue::name, ast_channel::name, queue_ent::next, queue_ent::parent, queue_ent::pos, queue_ent::qe_rules, queue_ref(), queue_unref(), queues, call_queue::realtime, SENTINEL, ast_channel::uniqueid, and var.

Referenced by try_calling(), and wait_our_turn().

02001 {
02002    struct call_queue *q;
02003    struct queue_ent *current, *prev = NULL;
02004    struct penalty_rule *pr_iter;
02005    int pos = 0;
02006 
02007    if (!(q = qe->parent))
02008       return;
02009    queue_ref(q);
02010    ao2_lock(q);
02011 
02012    prev = NULL;
02013    for (current = q->head; current; current = current->next) {
02014       if (current == qe) {
02015          q->count--;
02016 
02017          /* Take us out of the queue */
02018          manager_event(EVENT_FLAG_CALL, "Leave",
02019             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02020             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
02021          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02022          /* Take us out of the queue */
02023          if (prev)
02024             prev->next = current->next;
02025          else
02026             q->head = current->next;
02027          /* Free penalty rules */
02028          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02029             ast_free(pr_iter);
02030       } else {
02031          /* Renumber the people after us in the queue based on a new count */
02032          current->pos = ++pos;
02033          prev = current;
02034       }
02035    }
02036    ao2_unlock(q);
02037 
02038    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02039    if (q->realtime) {
02040       struct ast_variable *var;
02041       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02042          q->dead = 1;
02043       } else {
02044          ast_variables_destroy(var);
02045       }
02046    }
02047 
02048    if (q->dead) { 
02049       /* It's dead and nobody is in it, so kill it */
02050       ao2_unlink(queues, q);
02051    }
02052    /* unref the explicit ref earlier in the function */
02053    queue_unref(q);
02054 }

static int load_module ( void   )  [static]

Definition at line 6671 of file app_queue.c.

References ao2_container_alloc, aqm_exec(), ast_add_extension2(), ast_cli_register_multiple(), ast_context_find_or_create(), ast_custom_function_register, AST_EVENT_DEVICE_STATE, AST_EVENT_IE_END, ast_event_subscribe(), ast_free_ptr, ast_log(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application, ast_strdup, ast_taskprocessor_get(), cli_queue, device_state_cb(), device_state_sub, devicestate_tps, EVENT_FLAG_AGENT, LOG_ERROR, LOG_WARNING, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_log_custom(), manager_queue_member_penalty(), manager_queue_rule_show(), manager_queues_show(), manager_queues_status(), manager_queues_summary(), manager_remove_queue_member(), MAX_QUEUE_BUCKETS, pqm_exec(), ql_exec(), queue_cmp_cb(), queue_exec(), queue_hash_cb(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().

06672 {
06673    int res;
06674    struct ast_context *con;
06675 
06676    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06677 
06678    if (!reload_queues(0))
06679       return AST_MODULE_LOAD_DECLINE;
06680 
06681    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06682    if (!con)
06683       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06684    else
06685       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06686 
06687    if (queue_persistent_members)
06688       reload_queue_members();
06689 
06690    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06691    res = ast_register_application(app, queue_exec, synopsis, descrip);
06692    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06693    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06694    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06695    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06696    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06697    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06698    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06699    res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06700    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06701    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06702    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06703    res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06704    res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 
06705    res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06706    res |= ast_custom_function_register(&queuevar_function);
06707    res |= ast_custom_function_register(&queuemembercount_function);
06708    res |= ast_custom_function_register(&queuemembercount_dep);
06709    res |= ast_custom_function_register(&queuememberlist_function);
06710    res |= ast_custom_function_register(&queuewaitingcount_function);
06711    res |= ast_custom_function_register(&queuememberpenalty_function);
06712 
06713    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
06714       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
06715    }
06716 
06717    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
06718       res = -1;
06719    }
06720 
06721    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
06722 
06723    return res ? AST_MODULE_LOAD_DECLINE : 0;
06724 }

static struct call_queue* load_realtime_queue ( const char *  queuename  )  [static]

Definition at line 1593 of file app_queue.c.

References ao2_find, ao2_lock(), ao2_unlock(), ast_config_destroy(), ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, OBJ_POINTER, queues, call_queue::realtime, SENTINEL, and update_realtime_members().

Referenced by __queues_show(), add_to_queue(), join_queue(), queue_function_qac(), queue_function_qac_dep(), and reload_queue_members().

01594 {
01595    struct ast_variable *queue_vars;
01596    struct ast_config *member_config = NULL;
01597    struct call_queue *q = NULL, tmpq = {
01598       .name = queuename,   
01599    };
01600 
01601    /* Find the queue in the in-core list first. */
01602    q = ao2_find(queues, &tmpq, OBJ_POINTER);
01603 
01604    if (!q || q->realtime) {
01605       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
01606          queue operations while waiting for the DB.
01607 
01608          This will be two separate database transactions, so we might
01609          see queue parameters as they were before another process
01610          changed the queue and member list as it was after the change.
01611          Thus we might see an empty member list when a queue is
01612          deleted. In practise, this is unlikely to cause a problem. */
01613 
01614       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
01615       if (queue_vars) {
01616          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
01617          if (!member_config) {
01618             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01619             ast_variables_destroy(queue_vars);
01620             return NULL;
01621          }
01622       }
01623 
01624       ao2_lock(queues);
01625       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01626       if (member_config)
01627          ast_config_destroy(member_config);
01628       if (queue_vars)
01629          ast_variables_destroy(queue_vars);
01630       ao2_unlock(queues);
01631 
01632    } else {
01633       update_realtime_members(q);
01634    }
01635    return q;
01636 }

static int manager_add_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 6052 of file app_queue.c.

References add_to_queue(), ast_queue_log(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

06053 {
06054    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06055    int paused, penalty = 0;
06056 
06057    queuename = astman_get_header(m, "Queue");
06058    interface = astman_get_header(m, "Interface");
06059    penalty_s = astman_get_header(m, "Penalty");
06060    paused_s = astman_get_header(m, "Paused");
06061    membername = astman_get_header(m, "MemberName");
06062    state_interface = astman_get_header(m, "StateInterface");
06063 
06064    if (ast_strlen_zero(queuename)) {
06065       astman_send_error(s, m, "'Queue' not specified.");
06066       return 0;
06067    }
06068 
06069    if (ast_strlen_zero(interface)) {
06070       astman_send_error(s, m, "'Interface' not specified.");
06071       return 0;
06072    }
06073 
06074    if (ast_strlen_zero(penalty_s))
06075       penalty = 0;
06076    else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
06077       penalty = 0;
06078 
06079    if (ast_strlen_zero(paused_s))
06080       paused = 0;
06081    else
06082       paused = abs(ast_true(paused_s));
06083 
06084    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06085    case RES_OKAY:
06086       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06087       astman_send_ack(s, m, "Added interface to queue");
06088       break;
06089    case RES_EXISTS:
06090       astman_send_error(s, m, "Unable to add interface: Already there");
06091       break;
06092    case RES_NOSUCHQUEUE:
06093       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06094       break;
06095    case RES_OUTOFMEMORY:
06096       astman_send_error(s, m, "Out of memory");
06097       break;
06098    }
06099 
06100    return 0;
06101 }

static int manager_pause_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 6137 of file app_queue.c.

References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_paused().

Referenced by load_module().

06138 {
06139    const char *queuename, *interface, *paused_s, *reason;
06140    int paused;
06141 
06142    interface = astman_get_header(m, "Interface");
06143    paused_s = astman_get_header(m, "Paused");
06144    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
06145    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
06146 
06147    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06148       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06149       return 0;
06150    }
06151 
06152    paused = abs(ast_true(paused_s));
06153 
06154    if (set_member_paused(queuename, interface, reason, paused))
06155       astman_send_error(s, m, "Interface not found");
06156    else
06157       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06158    return 0;
06159 }

static int manager_queue_log_custom ( struct mansession s,
const struct message m 
) [static]

Definition at line 6161 of file app_queue.c.

References ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and S_OR.

Referenced by load_module().

06162 {
06163    const char *queuename, *event, *message, *interface, *uniqueid;
06164 
06165    queuename = astman_get_header(m, "Queue");
06166    uniqueid = astman_get_header(m, "UniqueId");
06167    interface = astman_get_header(m, "Interface");
06168    event = astman_get_header(m, "Event");
06169    message = astman_get_header(m, "Message");
06170 
06171    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06172       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06173       return 0;
06174    }
06175 
06176    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06177    astman_send_ack(s, m, "Event added successfully");
06178 
06179    return 0;
06180 }

static int manager_queue_member_penalty ( struct mansession s,
const struct message m 
) [static]

Definition at line 6213 of file app_queue.c.

References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_penalty().

Referenced by load_module().

06214 {
06215    const char *queuename, *interface, *penalty_s;
06216    int penalty;
06217 
06218    interface = astman_get_header(m, "Interface");
06219    penalty_s = astman_get_header(m, "Penalty");
06220    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
06221    queuename = astman_get_header(m, "Queue");
06222 
06223    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06224       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06225       return 0;
06226    }
06227  
06228    penalty = atoi(penalty_s);
06229 
06230    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06231       astman_send_error(s, m, "Invalid interface, queuename or penalty");
06232    else
06233       astman_send_ack(s, m, "Interface penalty set successfully");
06234 
06235    return 0;
06236 }

static int manager_queue_rule_show ( struct mansession s,
const struct message m 
) [static]

Definition at line 5862 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), astman_append(), astman_get_header(), penalty_rule::list, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, RESULT_SUCCESS, rule_list::rules, s, and penalty_rule::time.

Referenced by load_module().

05863 {
05864    const char *rule = astman_get_header(m, "Rule");
05865    struct rule_list *rl_iter;
05866    struct penalty_rule *pr_iter;
05867 
05868    AST_LIST_LOCK(&rule_lists);
05869    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05870       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05871          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05872          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05873             astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
05874          }
05875          if (!ast_strlen_zero(rule))
05876             break;
05877       }
05878    }
05879    AST_LIST_UNLOCK(&rule_lists);
05880 
05881    astman_append(s, "\r\n\r\n");
05882 
05883    return RESULT_SUCCESS;
05884 }

static int manager_queues_show ( struct mansession s,
const struct message m 
) [static]

Definition at line 5852 of file app_queue.c.

References __queues_show(), astman_append(), RESULT_SUCCESS, and s.

Referenced by load_module().

05853 {
05854    char *a[] = { "queue", "show" };
05855 
05856    __queues_show(s, -1, 2, a);
05857    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05858 
05859    return RESULT_SUCCESS;
05860 }

static int manager_queues_status ( struct mansession s,
const struct message m 
) [static]

Queue status info via AMI.

Definition at line 5959 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, member::dynamic, int2strat(), member::interface, member::lastcall, member::membername, ast_channel::name, queue_ent::next, member::paused, member::penalty, queue_unref(), queues, RESULT_SUCCESS, s, S_OR, queue_ent::start, and member::status.

Referenced by load_module().

05960 {
05961    time_t now;
05962    int pos;
05963    const char *id = astman_get_header(m,"ActionID");
05964    const char *queuefilter = astman_get_header(m,"Queue");
05965    const char *memberfilter = astman_get_header(m,"Member");
05966    char idText[256] = "";
05967    struct call_queue *q;
05968    struct queue_ent *qe;
05969    float sl = 0;
05970    struct member *mem;
05971    struct ao2_iterator queue_iter;
05972    struct ao2_iterator mem_iter;
05973 
05974    astman_send_ack(s, m, "Queue status will follow");
05975    time(&now);
05976    if (!ast_strlen_zero(id))
05977       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05978 
05979    queue_iter = ao2_iterator_init(queues, 0);
05980    while ((q = ao2_iterator_next(&queue_iter))) {
05981       ao2_lock(q);
05982 
05983       /* List queue properties */
05984       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05985          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05986          astman_append(s, "Event: QueueParams\r\n"
05987             "Queue: %s\r\n"
05988             "Max: %d\r\n"
05989             "Strategy: %s\r\n"
05990             "Calls: %d\r\n"
05991             "Holdtime: %d\r\n"
05992             "Completed: %d\r\n"
05993             "Abandoned: %d\r\n"
05994             "ServiceLevel: %d\r\n"
05995             "ServicelevelPerf: %2.1f\r\n"
05996             "Weight: %d\r\n"
05997             "%s"
05998             "\r\n",
05999             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
06000             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06001          /* List Queue Members */
06002          mem_iter = ao2_iterator_init(q->members, 0);
06003          while ((mem = ao2_iterator_next(&mem_iter))) {
06004             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06005                astman_append(s, "Event: QueueMember\r\n"
06006                   "Queue: %s\r\n"
06007                   "Name: %s\r\n"
06008                   "Location: %s\r\n"
06009                   "Membership: %s\r\n"
06010                   "Penalty: %d\r\n"
06011                   "CallsTaken: %d\r\n"
06012                   "LastCall: %d\r\n"
06013                   "Status: %d\r\n"
06014                   "Paused: %d\r\n"
06015                   "%s"
06016                   "\r\n",
06017                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06018                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06019             }
06020             ao2_ref(mem, -1);
06021          }
06022          /* List Queue Entries */
06023          pos = 1;
06024          for (qe = q->head; qe; qe = qe->next) {
06025             astman_append(s, "Event: QueueEntry\r\n"
06026                "Queue: %s\r\n"
06027                "Position: %d\r\n"
06028                "Channel: %s\r\n"
06029                "CallerIDNum: %s\r\n"
06030                "CallerIDName: %s\r\n"
06031                "Wait: %ld\r\n"
06032                "%s"
06033                "\r\n",
06034                q->name, pos++, qe->chan->name,
06035                S_OR(qe->chan->cid.cid_num, "unknown"),
06036                S_OR(qe->chan->cid.cid_name, "unknown"),
06037                (long) (now - qe->start), idText);
06038          }
06039       }
06040       ao2_unlock(q);
06041       queue_unref(q);
06042    }
06043 
06044    astman_append(s,
06045       "Event: QueueStatusComplete\r\n"
06046       "%s"
06047       "\r\n",idText);
06048 
06049    return RESULT_SUCCESS;
06050 }

static int manager_queues_summary ( struct mansession s,
const struct message m 
) [static]

Summary of queue info via the AMI.

Definition at line 5887 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), queue_ent::next, member::paused, queue_unref(), queues, RESULT_SUCCESS, s, queue_ent::start, and member::status.

Referenced by load_module().

05888 {
05889    time_t now;
05890    int qmemcount = 0;
05891    int qmemavail = 0;
05892    int qchancount = 0;
05893    int qlongestholdtime = 0;
05894    const char *id = astman_get_header(m, "ActionID");
05895    const char *queuefilter = astman_get_header(m, "Queue");
05896    char idText[256] = "";
05897    struct call_queue *q;
05898    struct queue_ent *qe;
05899    struct member *mem;
05900    struct ao2_iterator queue_iter;
05901    struct ao2_iterator mem_iter;
05902 
05903    astman_send_ack(s, m, "Queue summary will follow");
05904    time(&now);
05905    if (!ast_strlen_zero(id))
05906       snprintf(idText, 256, "ActionID: %s\r\n", id);
05907    queue_iter = ao2_iterator_init(queues, 0);
05908    while ((q = ao2_iterator_next(&queue_iter))) {
05909       ao2_lock(q);
05910 
05911       /* List queue properties */
05912       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05913          /* Reset the necessary local variables if no queuefilter is set*/
05914          qmemcount = 0;
05915          qmemavail = 0;
05916          qchancount = 0;
05917          qlongestholdtime = 0;
05918 
05919          /* List Queue Members */
05920          mem_iter = ao2_iterator_init(q->members, 0);
05921          while ((mem = ao2_iterator_next(&mem_iter))) {
05922             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
05923                ++qmemcount;
05924                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
05925                   ++qmemavail;
05926                }
05927             }
05928             ao2_ref(mem, -1);
05929          }
05930          for (qe = q->head; qe; qe = qe->next) {
05931             if ((now - qe->start) > qlongestholdtime) {
05932                qlongestholdtime = now - qe->start;
05933             }
05934             ++qchancount;
05935          }
05936          astman_append(s, "Event: QueueSummary\r\n"
05937             "Queue: %s\r\n"
05938             "LoggedIn: %d\r\n"
05939             "Available: %d\r\n"
05940             "Callers: %d\r\n" 
05941             "HoldTime: %d\r\n"
05942             "LongestHoldTime: %d\r\n"
05943             "%s"
05944             "\r\n",
05945             q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
05946       }
05947       ao2_unlock(q);
05948       queue_unref(q);
05949    }
05950    astman_append(s,
05951       "Event: QueueSummaryComplete\r\n"
05952       "%s"
05953       "\r\n", idText);
05954 
05955    return RESULT_SUCCESS;
05956 }

static int manager_remove_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 6103 of file app_queue.c.

References ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

06104 {
06105    const char *queuename, *interface;
06106 
06107    queuename = astman_get_header(m, "Queue");
06108    interface = astman_get_header(m, "Interface");
06109 
06110    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06111       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06112       return 0;
06113    }
06114 
06115    switch (remove_from_queue(queuename, interface)) {
06116    case RES_OKAY:
06117       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06118       astman_send_ack(s, m, "Removed interface from queue");
06119       break;
06120    case RES_EXISTS:
06121       astman_send_error(s, m, "Unable to remove interface: Not there");
06122       break;
06123    case RES_NOSUCHQUEUE:
06124       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06125       break;
06126    case RES_OUTOFMEMORY:
06127       astman_send_error(s, m, "Out of memory");
06128       break;
06129    case RES_NOT_DYNAMIC:
06130       astman_send_error(s, m, "Member not dynamic");
06131       break;
06132    }
06133 
06134    return 0;
06135 }

static int member_cmp_fn ( void *  obj1,
void *  obj2,
int  flags 
) [static]

Definition at line 873 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

00874 {
00875    struct member *mem1 = obj1, *mem2 = obj2;
00876    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00877 }

static int member_hash_fn ( const void *  obj,
const int  flags 
) [static]

Definition at line 861 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00862 {
00863    const struct member *mem = obj;
00864    const char *chname = strchr(mem->interface, '/');
00865    int ret = 0, i;
00866    if (!chname)
00867       chname = mem->interface;
00868    for (i = 0; i < 5 && chname[i]; i++)
00869       ret += compress_char(chname[i]) << (i * 6);
00870    return ret;
00871 }

static int num_available_members ( struct call_queue q  )  [static]

Get the number of members available to accept a call.

Note:
The queue passed in should be locked prior to this function call
Parameters:
[in] q The queue for which we are couting the number of available members
Returns:
Return the number of available members in queue q

Definition at line 2081 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_ref, AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, call_queue::autofill, call_queue::members, member::paused, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.

Referenced by compare_weight(), and is_our_turn().

02082 {
02083    struct member *mem;
02084    int avl = 0;
02085    struct ao2_iterator mem_iter;
02086 
02087    mem_iter = ao2_iterator_init(q->members, 0);
02088    while ((mem = ao2_iterator_next(&mem_iter))) {
02089       switch (mem->status) {
02090       case AST_DEVICE_INUSE:
02091          if (!q->ringinuse)
02092             break;
02093          /* else fall through */
02094       case AST_DEVICE_NOT_INUSE:
02095       case AST_DEVICE_UNKNOWN:
02096          if (!mem->paused) {
02097             avl++;
02098          }
02099          break;
02100       }
02101       ao2_ref(mem, -1);
02102 
02103       /* If autofill is not enabled or if the queue's strategy is ringall, then
02104        * we really don't care about the number of available members so much as we
02105        * do that there is at least one available.
02106        *
02107        * In fact, we purposely will return from this function stating that only
02108        * one member is available if either of those conditions hold. That way,
02109        * functions which determine what action to take based on the number of available
02110        * members will operate properly. The reasoning is that even if multiple
02111        * members are available, only the head caller can actually be serviced.
02112        */
02113       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02114          break;
02115       }
02116    }
02117 
02118    return avl;
02119 }

static int play_file ( struct ast_channel chan,
const char *  filename 
) [static]

Definition at line 1781 of file app_queue.c.

References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_waitstream(), queue_ent::chan, and ast_channel::language.

Referenced by say_periodic_announcement(), say_position(), and try_calling().

01782 {
01783    int res;
01784 
01785    if (ast_strlen_zero(filename)) {
01786       return 0;
01787    }
01788 
01789    ast_stopstream(chan);
01790 
01791    res = ast_streamfile(chan, filename, chan->language);
01792    if (!res)
01793       res = ast_waitstream(chan, AST_DIGIT_ANY);
01794 
01795    ast_stopstream(chan);
01796 
01797    return res;
01798 }

static int pqm_exec ( struct ast_channel chan,
void *  data 
) [static]

PauseQueueMember application.

Definition at line 4464 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

04465 {
04466    char *parse;
04467    AST_DECLARE_APP_ARGS(args,
04468       AST_APP_ARG(queuename);
04469       AST_APP_ARG(interface);
04470       AST_APP_ARG(options);
04471       AST_APP_ARG(reason);
04472    );
04473 
04474    if (ast_strlen_zero(data)) {
04475       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04476       return -1;
04477    }
04478 
04479    parse = ast_strdupa(data);
04480 
04481    AST_STANDARD_APP_ARGS(args, parse);
04482 
04483    if (ast_strlen_zero(args.interface)) {
04484       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04485       return -1;
04486    }
04487 
04488    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04489       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04490       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04491       return 0;
04492    }
04493 
04494    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04495 
04496    return 0;
04497 }

static int ql_exec ( struct ast_channel chan,
void *  data 
) [static]

QueueLog application.

Definition at line 4654 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, and parse().

Referenced by load_module().

04655 {
04656    char *parse;
04657 
04658    AST_DECLARE_APP_ARGS(args,
04659       AST_APP_ARG(queuename);
04660       AST_APP_ARG(uniqueid);
04661       AST_APP_ARG(membername);
04662       AST_APP_ARG(event);
04663       AST_APP_ARG(params);
04664    );
04665 
04666    if (ast_strlen_zero(data)) {
04667       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04668       return -1;
04669    }
04670 
04671    parse = ast_strdupa(data);
04672 
04673    AST_STANDARD_APP_ARGS(args, parse);
04674 
04675    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04676        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04677       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04678       return -1;
04679    }
04680 
04681    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04682       "%s", args.params ? args.params : "");
04683 
04684    return 0;
04685 }

static int queue_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 597 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

00598 {
00599    struct call_queue *q = obj, *q2 = arg;
00600    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
00601 }

static int queue_exec ( struct ast_channel chan,
void *  data 
) [static]

The starting point for all queue calls.

The process involved here is to 1. Parse the options specified in the call to Queue() 2. Join the queue 3. Wait in a loop until it is our turn to try calling a queue member 4. Attempt to call a queue member 5. If 4. did not result in a bridged call, then check for between call options such as periodic announcements etc. 6. Try 4 again unless some condition (such as an expiration time) causes us to exit the queue.

Definition at line 4732 of file app_queue.c.

References AST_APP_ARG, ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_moh_start(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, chan, ast_channel::cid, ast_callerid::cid_num, get_member_status(), is_our_turn(), join_queue(), LOG_WARNING, ast_channel::name, parse(), pbx_builtin_getvar_helper(), QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), status, stop, try_calling(), ast_channel::uniqueid, update_qe_rule(), update_realtime_members(), url, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

04733 {
04734    int res=-1;
04735    int ringing=0;
04736    const char *user_priority;
04737    const char *max_penalty_str;
04738    const char *min_penalty_str;
04739    int prio;
04740    int qcontinue = 0;
04741    int max_penalty, min_penalty;
04742    enum queue_result reason = QUEUE_UNKNOWN;
04743    /* whether to exit Queue application after the timeout hits */
04744    int tries = 0;
04745    int noption = 0;
04746    char *parse;
04747    int makeannouncement = 0;
04748    AST_DECLARE_APP_ARGS(args,
04749       AST_APP_ARG(queuename);
04750       AST_APP_ARG(options);
04751       AST_APP_ARG(url);
04752       AST_APP_ARG(announceoverride);
04753       AST_APP_ARG(queuetimeoutstr);
04754       AST_APP_ARG(agi);
04755       AST_APP_ARG(macro);
04756       AST_APP_ARG(gosub);
04757       AST_APP_ARG(rule);
04758    );
04759    /* Our queue entry */
04760    struct queue_ent qe;
04761    
04762    if (ast_strlen_zero(data)) {
04763       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04764       return -1;
04765    }
04766    
04767    parse = ast_strdupa(data);
04768    AST_STANDARD_APP_ARGS(args, parse);
04769 
04770    /* Setup our queue entry */
04771    memset(&qe, 0, sizeof(qe));
04772    qe.start = time(NULL);
04773 
04774    /* set the expire time based on the supplied timeout; */
04775    if (!ast_strlen_zero(args.queuetimeoutstr))
04776       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04777    else
04778       qe.expire = 0;
04779 
04780    /* Get the priority from the variable ${QUEUE_PRIO} */
04781    ast_channel_lock(chan);
04782    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04783    if (user_priority) {
04784       if (sscanf(user_priority, "%d", &prio) == 1) {
04785          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04786       } else {
04787          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04788             user_priority, chan->name);
04789          prio = 0;
04790       }
04791    } else {
04792       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04793       prio = 0;
04794    }
04795 
04796    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04797 
04798    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04799       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
04800          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04801       } else {
04802          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04803             max_penalty_str, chan->name);
04804          max_penalty = 0;
04805       }
04806    } else {
04807       max_penalty = 0;
04808    }
04809 
04810    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04811       if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
04812          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04813       } else {
04814          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04815             min_penalty_str, chan->name);
04816          min_penalty = 0;
04817       }
04818    } else {
04819       min_penalty = 0;
04820    }
04821    ast_channel_unlock(chan);
04822 
04823    if (args.options && (strchr(args.options, 'r')))
04824       ringing = 1;
04825 
04826    if (args.options && (strchr(args.options, 'c')))
04827       qcontinue = 1;
04828 
04829    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04830       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04831 
04832    qe.chan = chan;
04833    qe.prio = prio;
04834    qe.max_penalty = max_penalty;
04835    qe.min_penalty = min_penalty;
04836    qe.last_pos_said = 0;
04837    qe.last_pos = 0;
04838    qe.last_periodic_announce_time = time(NULL);
04839    qe.last_periodic_announce_sound = 0;
04840    qe.valid_digits = 0;
04841    if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04842       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04843       set_queue_result(chan, reason);
04844       return 0;
04845    }
04846    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04847       S_OR(chan->cid.cid_num, ""));
04848 check_turns:
04849    if (ringing) {
04850       ast_indicate(chan, AST_CONTROL_RINGING);
04851    } else {
04852       ast_moh_start(chan, qe.moh, NULL);
04853    }
04854 
04855    /* This is the wait loop for callers 2 through maxlen */
04856    res = wait_our_turn(&qe, ringing, &reason);
04857    if (res) {
04858       goto stop;
04859    }
04860 
04861    makeannouncement = 0;
04862 
04863    for (;;) {
04864       /* This is the wait loop for the head caller*/
04865       /* To exit, they may get their call answered; */
04866       /* they may dial a digit from the queue context; */
04867       /* or, they may timeout. */
04868 
04869       enum queue_member_status status = QUEUE_NORMAL;
04870       int exit = 0;
04871 
04872       /* Leave if we have exceeded our queuetimeout */
04873       if (qe.expire && (time(NULL) >= qe.expire)) {
04874          record_abandoned(&qe);
04875          reason = QUEUE_TIMEOUT;
04876          res = 0;
04877          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
04878             qe.pos, qe.opos, (long) time(NULL) - qe.start);
04879          break;
04880       }
04881 
04882       if (makeannouncement) {
04883          /* Make a position announcement, if enabled */
04884          if (qe.parent->announcefrequency)
04885             if ((res = say_position(&qe,ringing)))
04886                goto stop;
04887       }
04888       makeannouncement = 1;
04889 
04890       /* Make a periodic announcement, if enabled */
04891       if (qe.parent->periodicannouncefrequency)
04892          if ((res = say_periodic_announcement(&qe,ringing)))
04893             goto stop;
04894    
04895       /* Leave if we have exceeded our queuetimeout */
04896       if (qe.expire && (time(NULL) >= qe.expire)) {
04897          record_abandoned(&qe);
04898          reason = QUEUE_TIMEOUT;
04899          res = 0;
04900          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04901          break;
04902       }
04903 
04904       /* see if we need to move to the next penalty level for this queue */
04905       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
04906          update_qe_rule(&qe);
04907       }
04908 
04909       /* Try calling all queue members for 'timeout' seconds */
04910       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
04911       if (res) {
04912          goto stop;
04913       }
04914 
04915       /* exit after 'timeout' cycle if 'n' option enabled */
04916       if (noption && tries >= qe.parent->membercount) {
04917          ast_verb(3, "Exiting on time-out cycle\n");
04918          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04919          record_abandoned(&qe);
04920          reason = QUEUE_TIMEOUT;
04921          res = 0;
04922          break;
04923       }
04924 
04925       for (; !exit || qe.pr; update_qe_rule(&qe)) {
04926          status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
04927 
04928          if (!qe.pr || status == QUEUE_NORMAL) {
04929             break;
04930          }
04931 
04932          if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
04933                ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
04934                ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
04935             continue;
04936          } else {
04937             exit = 1;
04938          }
04939       }
04940 
04941       /* leave the queue if no agents, if enabled */
04942       if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
04943          record_abandoned(&qe);
04944          reason = QUEUE_LEAVEEMPTY;
04945          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04946          res = 0;
04947          break;
04948       }
04949 
04950       /* leave the queue if no reachable agents, if enabled */
04951       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
04952          record_abandoned(&qe);
04953          reason = QUEUE_LEAVEUNAVAIL;
04954          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04955          res = 0;
04956          break;
04957       }
04958       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
04959          record_abandoned(&qe);
04960          reason = QUEUE_LEAVEUNAVAIL;
04961          res = 0;
04962          break;
04963       }
04964 
04965       /* Leave if we have exceeded our queuetimeout */
04966       if (qe.expire && (time(NULL) >= qe.expire)) {
04967          record_abandoned(&qe);
04968          reason = QUEUE_TIMEOUT;
04969          res = 0;
04970          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
04971          break;
04972       }
04973 
04974       /* If using dynamic realtime members, we should regenerate the member list for this queue */
04975       update_realtime_members(qe.parent);
04976       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04977       res = wait_a_bit(&qe);
04978       if (res)
04979          goto stop;
04980 
04981       /* Since this is a priority queue and
04982        * it is not sure that we are still at the head
04983        * of the queue, go and check for our turn again.
04984        */
04985       if (!is_our_turn(&qe)) {
04986          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
04987          goto check_turns;
04988       }
04989    }
04990 
04991 stop:
04992    if (res) {
04993       if (res < 0) {
04994          if (!qe.handled) {
04995             record_abandoned(&qe);
04996             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04997                "%d|%d|%ld", qe.pos, qe.opos,
04998                (long) time(NULL) - qe.start);
04999             res = -1;
05000          } else if (qcontinue) {
05001             reason = QUEUE_CONTINUE;
05002             res = 0;
05003          }
05004       } else if (qe.valid_digits) {
05005          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05006             "%s|%d", qe.digits, qe.pos);
05007       }
05008    }
05009 
05010    /* Don't allow return code > 0 */
05011    if (res >= 0) {
05012       res = 0; 
05013       if (ringing) {
05014          ast_indicate(chan, -1);
05015       } else {
05016          ast_moh_stop(chan);
05017       }        
05018       ast_stopstream(chan);
05019    }
05020 
05021    set_queue_variables(qe.parent, qe.chan);
05022 
05023    leave_queue(&qe);
05024    if (reason != QUEUE_UNKNOWN)
05025       set_queue_result(chan, reason);
05026 
05027    return res;
05028 }

static int queue_function_memberpenalty_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.

Definition at line 5260 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), get_member_penalty(), and LOG_ERROR.

05261 {
05262    int penalty;
05263    AST_DECLARE_APP_ARGS(args,
05264       AST_APP_ARG(queuename);
05265       AST_APP_ARG(interface);
05266    );
05267    /* Make sure the returned value on error is NULL. */
05268    buf[0] = '\0';
05269 
05270    if (ast_strlen_zero(data)) {
05271       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05272       return -1;
05273    }
05274 
05275    AST_STANDARD_APP_ARGS(args, data);
05276 
05277    if (args.argc < 2) {
05278       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05279       return -1;
05280    }
05281 
05282    penalty = get_member_penalty (args.queuename, args.interface);
05283    
05284    if (penalty >= 0) /* remember that buf is already '\0' */
05285       snprintf (buf, len, "%d", penalty);
05286 
05287    return 0;
05288 }

static int queue_function_memberpenalty_write ( struct ast_channel chan,
const char *  cmd,
char *  data,
const char *  value 
) [static]

Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.

Definition at line 5291 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_ERROR, and set_member_penalty().

05292 {
05293    int penalty;
05294    AST_DECLARE_APP_ARGS(args,
05295       AST_APP_ARG(queuename);
05296       AST_APP_ARG(interface);
05297    );
05298 
05299    if (ast_strlen_zero(data)) {
05300       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05301       return -1;
05302    }
05303 
05304    AST_STANDARD_APP_ARGS(args, data);
05305 
05306    if (args.argc < 2) {
05307       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05308       return -1;
05309    }
05310 
05311    penalty = atoi(value);
05312 
05313    if (ast_strlen_zero(args.interface)) {
05314       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05315       return -1;
05316    }
05317 
05318    /* if queuename = NULL then penalty will be set for interface in all the queues. */
05319    if (set_member_penalty(args.queuename, args.interface, penalty)) {
05320       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05321       return -1;
05322    }
05323 
05324    return 0;
05325 }

static int queue_function_qac ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Get number either busy / free or total members of a specific queue.

Return values:
number of members (busy / free / total)
-1 on error

Definition at line 5083 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), ast_strlen_zero(), call_queue::count, load_realtime_queue(), LOG_ERROR, LOG_WARNING, member::paused, queue_unref(), and member::status.

05084 {
05085    int count = 0;
05086    struct member *m;
05087    struct ao2_iterator mem_iter;
05088    struct call_queue *q;
05089    char *option;
05090 
05091    if (ast_strlen_zero(data)) {
05092       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05093       return -1;
05094    }
05095 
05096    if ((option = strchr(data, ',')))
05097       *option++ = '\0';
05098    else
05099       option = "logged";
05100    if ((q = load_realtime_queue(data))) {
05101       ao2_lock(q);
05102       if (!strcasecmp(option, "logged")) {
05103          mem_iter = ao2_iterator_init(q->members, 0);
05104          while ((m = ao2_iterator_next(&mem_iter))) {
05105             /* Count the agents who are logged in and presently answering calls */
05106             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05107                count++;
05108             }
05109             ao2_ref(m, -1);
05110          }
05111       } else if (!strcasecmp(option, "free")) {
05112          mem_iter = ao2_iterator_init(q->members, 0);
05113          while ((m = ao2_iterator_next(&mem_iter))) {
05114             /* Count the agents who are logged in and presently answering calls */
05115             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05116                count++;
05117             }
05118             ao2_ref(m, -1);
05119          }
05120       } else /* must be "count" */
05121          count = q->membercount;
05122       ao2_unlock(q);
05123       queue_unref(q);
05124    } else
05125       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05126 
05127    snprintf(buf, len, "%d", count);
05128 
05129    return 0;
05130 }

static int queue_function_qac_dep ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Get the total number of members in a specific queue (Deprecated).

Return values:
number of members
-1 on error

Definition at line 5137 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_log(), ast_strlen_zero(), call_queue::count, load_realtime_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, queue_unref(), and member::status.

05138 {
05139    int count = 0;
05140    struct member *m;
05141    struct call_queue *q;
05142    struct ao2_iterator mem_iter;
05143    static int depflag = 1;
05144 
05145    if (depflag) {
05146       depflag = 0;
05147       ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
05148    }
05149 
05150    if (ast_strlen_zero(data)) {
05151       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05152       return -1;
05153    }
05154    
05155    if ((q = load_realtime_queue(data))) {
05156       ao2_lock(q);
05157       mem_iter = ao2_iterator_init(q->members, 0);
05158       while ((m = ao2_iterator_next(&mem_iter))) {
05159          /* Count the agents who are logged in and presently answering calls */
05160          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05161             count++;
05162          }
05163          ao2_ref(m, -1);
05164       }
05165       ao2_unlock(q);
05166       queue_unref(q);
05167    } else
05168       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05169 
05170    snprintf(buf, len, "%d", count);
05171 
05172    return 0;
05173 }

static int queue_function_queuememberlist ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue.

Definition at line 5212 of file app_queue.c.

References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_log(), ast_strlen_zero(), LOG_ERROR, LOG_WARNING, member::membername, call_queue::members, OBJ_POINTER, queue_unref(), and queues.

05213 {
05214    struct call_queue *q, tmpq = {
05215       .name = data,  
05216    };
05217    struct member *m;
05218 
05219    /* Ensure an otherwise empty list doesn't return garbage */
05220    buf[0] = '\0';
05221 
05222    if (ast_strlen_zero(data)) {
05223       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05224       return -1;
05225    }
05226 
05227    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05228       int buflen = 0, count = 0;
05229       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05230 
05231       ao2_lock(q);
05232       while ((m = ao2_iterator_next(&mem_iter))) {
05233          /* strcat() is always faster than printf() */
05234          if (count++) {
05235             strncat(buf + buflen, ",", len - buflen - 1);
05236             buflen++;
05237          }
05238          strncat(buf + buflen, m->membername, len - buflen - 1);
05239          buflen += strlen(m->membername);
05240          /* Safeguard against overflow (negative length) */
05241          if (buflen >= len - 2) {
05242             ao2_ref(m, -1);
05243             ast_log(LOG_WARNING, "Truncating list\n");
05244             break;
05245          }
05246          ao2_ref(m, -1);
05247       }
05248       ao2_unlock(q);
05249       queue_unref(q);
05250    } else
05251       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05252 
05253    /* We should already be terminated, but let's make sure. */
05254    buf[len - 1] = '\0';
05255 
05256    return 0;
05257 }

static int queue_function_queuewaitingcount ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue.

Definition at line 5176 of file app_queue.c.

References ao2_find, ao2_lock(), ao2_unlock(), ast_load_realtime(), ast_log(), ast_strlen_zero(), ast_variables_destroy(), LOG_ERROR, LOG_WARNING, OBJ_POINTER, queue_unref(), queues, SENTINEL, and var.

05177 {
05178    int count = 0;
05179    struct call_queue *q, tmpq = {
05180       .name = data,  
05181    };
05182    struct ast_variable *var = NULL;
05183 
05184    buf[0] = '\0';
05185    
05186    if (ast_strlen_zero(data)) {
05187       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05188       return -1;
05189    }
05190 
05191    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05192       ao2_lock(q);
05193       count = q->count;
05194       ao2_unlock(q);
05195       queue_unref(q);
05196    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05197       /* if the queue is realtime but was not found in memory, this
05198        * means that the queue had been deleted from memory since it was 
05199        * "dead." This means it has a 0 waiting count
05200        */
05201       count = 0;
05202       ast_variables_destroy(var);
05203    } else
05204       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05205 
05206    snprintf(buf, len, "%d", count);
05207 
05208    return 0;
05209 }

static int queue_function_var ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

create interface var with all queue details.

Return values:
0 on success
-1 on error

Definition at line 5035 of file app_queue.c.

References ao2_find, ao2_lock(), ao2_unlock(), ast_log(), ast_strlen_zero(), chan, int2strat(), LOG_ERROR, LOG_WARNING, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_unref(), and queues.

05036 {
05037    int res = -1;
05038    struct call_queue *q, tmpq = {
05039       .name = data,  
05040    };
05041 
05042    char interfacevar[256] = "";
05043    float sl = 0;
05044 
05045    if (ast_strlen_zero(data)) {
05046       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05047       return -1;
05048    }
05049 
05050    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05051       ao2_lock(q);
05052       if (q->setqueuevar) {
05053          sl = 0;
05054          res = 0;
05055 
05056          if (q->callscompleted > 0) {
05057             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05058          }
05059 
05060          snprintf(interfacevar, sizeof(interfacevar),
05061             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05062             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
05063 
05064          pbx_builtin_setvar_multiple(chan, interfacevar);
05065       }
05066 
05067       ao2_unlock(q);
05068       queue_unref(q);
05069    } else {
05070       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05071    }
05072 
05073    snprintf(buf, len, "%d", res);
05074 
05075    return 0;
05076 }

static int queue_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 590 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

00591 {
00592    const struct call_queue *q = obj;
00593 
00594    return ast_str_case_hash(q->name);
00595 }

static struct call_queue* queue_ref ( struct call_queue q  )  [inline, static]

Definition at line 603 of file app_queue.c.

References ao2_ref.

Referenced by leave_queue(), and try_calling().

00604 {
00605    ao2_ref(q, 1);
00606    return q;
00607 }

static void queue_set_param ( struct call_queue q,
const char *  param,
const char *  val,
int  linenum,
int  failunknown 
) [static]

Configure a queue parameter.

The failunknown flag is set for config files (and static realtime) to show errors for unknown parameters. It is cleared for dynamic realtime to allow extra fields in the tables.

Note:
For error reporting, line number is passed for .conf static configuration, for Realtime queues, linenum is -1.

Definition at line 1134 of file app_queue.c.

References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, buf, queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::name, call_queue::numperiodicannounce, call_queue::periodicannouncefrequency, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RINGALL, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, strat2int(), call_queue::strategy, strsep(), call_queue::timeout, TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_queues().

01135 {
01136    if (!strcasecmp(param, "musicclass") || 
01137       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01138       ast_string_field_set(q, moh, val);
01139    } else if (!strcasecmp(param, "announce")) {
01140       ast_string_field_set(q, announce, val);
01141    } else if (!strcasecmp(param, "context")) {
01142       ast_string_field_set(q, context, val);
01143    } else if (!strcasecmp(param, "timeout")) {
01144       q->timeout = atoi(val);
01145       if (q->timeout < 0)
01146          q->timeout = DEFAULT_TIMEOUT;
01147    } else if (!strcasecmp(param, "ringinuse")) {
01148       q->ringinuse = ast_true(val);
01149    } else if (!strcasecmp(param, "setinterfacevar")) {
01150       q->setinterfacevar = ast_true(val);
01151    } else if (!strcasecmp(param, "setqueuevar")) {
01152       q->setqueuevar = ast_true(val);
01153    } else if (!strcasecmp(param, "setqueueentryvar")) {
01154       q->setqueueentryvar = ast_true(val);
01155    } else if (!strcasecmp(param, "monitor-format")) {
01156       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01157    } else if (!strcasecmp(param, "membermacro")) {
01158       ast_string_field_set(q, membermacro, val);
01159    } else if (!strcasecmp(param, "membergosub")) {
01160       ast_string_field_set(q, membergosub, val);
01161    } else if (!strcasecmp(param, "queue-youarenext")) {
01162       ast_string_field_set(q, sound_next, val);
01163    } else if (!strcasecmp(param, "queue-thereare")) {
01164       ast_string_field_set(q, sound_thereare, val);
01165    } else if (!strcasecmp(param, "queue-callswaiting")) {
01166       ast_string_field_set(q, sound_calls, val);
01167    } else if (!strcasecmp(param, "queue-quantity1")) {
01168       ast_string_field_set(q, queue_quantity1, val);
01169    } else if (!strcasecmp(param, "queue-quantity2")) {
01170       ast_string_field_set(q, queue_quantity2, val);
01171    } else if (!strcasecmp(param, "queue-holdtime")) {
01172       ast_string_field_set(q, sound_holdtime, val);
01173    } else if (!strcasecmp(param, "queue-minutes")) {
01174       ast_string_field_set(q, sound_minutes, val);
01175    } else if (!strcasecmp(param, "queue-minute")) {
01176       ast_string_field_set(q, sound_minute, val);
01177    } else if (!strcasecmp(param, "queue-seconds")) {
01178       ast_string_field_set(q, sound_seconds, val);
01179    } else if (!strcasecmp(param, "queue-thankyou")) {
01180       ast_string_field_set(q, sound_thanks, val);
01181    } else if (!strcasecmp(param, "queue-callerannounce")) {
01182       ast_string_field_set(q, sound_callerannounce, val);
01183    } else if (!strcasecmp(param, "queue-reporthold")) {
01184       ast_string_field_set(q, sound_reporthold, val);
01185    } else if (!strcasecmp(param, "announce-frequency")) {
01186       q->announcefrequency = atoi(val);
01187    } else if (!strcasecmp(param, "min-announce-frequency")) {
01188       q->minannouncefrequency = atoi(val);
01189       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01190    } else if (!strcasecmp(param, "announce-round-seconds")) {
01191       q->roundingseconds = atoi(val);
01192       /* Rounding to any other values just doesn't make sense... */
01193       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01194          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01195          if (linenum >= 0) {
01196             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01197                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01198                val, param, q->name, linenum);
01199          } else {
01200             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01201                "using 0 instead for queue '%s'\n", val, param, q->name);
01202          }
01203          q->roundingseconds=0;
01204       }
01205    } else if (!strcasecmp(param, "announce-holdtime")) {
01206       if (!strcasecmp(val, "once"))
01207          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01208       else if (ast_true(val))
01209          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01210       else
01211          q->announceholdtime = 0;
01212    } else if (!strcasecmp(param, "announce-position")) {
01213       if (!strcasecmp(val, "limit"))
01214          q->announceposition = ANNOUNCEPOSITION_LIMIT;
01215       else if (!strcasecmp(val, "more"))
01216          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01217       else if (ast_true(val))
01218          q->announceposition = ANNOUNCEPOSITION_YES;
01219       else
01220          q->announceposition = ANNOUNCEPOSITION_NO;
01221    } else if (!strcasecmp(param, "announce-position-limit")) {
01222       q->announcepositionlimit = atoi(val);
01223    } else if (!strcasecmp(param, "periodic-announce")) {
01224       if (strchr(val, ',')) {
01225          char *s, *buf = ast_strdupa(val);
01226          unsigned int i = 0;
01227 
01228          while ((s = strsep(&buf, ",|"))) {
01229             if (!q->sound_periodicannounce[i])
01230                q->sound_periodicannounce[i] = ast_str_create(16);
01231             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01232             i++;
01233             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01234                break;
01235          }
01236          q->numperiodicannounce = i;
01237       } else {
01238          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01239          q->numperiodicannounce = 1;
01240       }
01241    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01242       q->periodicannouncefrequency = atoi(val);
01243    } else if (!strcasecmp(param, "random-periodic-announce")) {
01244       q->randomperiodicannounce = ast_true(val);
01245    } else if (!strcasecmp(param, "retry")) {
01246       q->retry = atoi(val);
01247       if (q->retry <= 0)
01248          q->retry = DEFAULT_RETRY;
01249    } else if (!strcasecmp(param, "wrapuptime")) {
01250       q->wrapuptime = atoi(val);
01251    } else if (!strcasecmp(param, "autofill")) {
01252       q->autofill = ast_true(val);
01253    } else if (!strcasecmp(param, "monitor-type")) {
01254       if (!strcasecmp(val, "mixmonitor"))
01255          q->montype = 1;
01256    } else if (!strcasecmp(param, "autopause")) {
01257       q->autopause = ast_true(val);
01258    } else if (!strcasecmp(param, "maxlen")) {
01259       q->maxlen = atoi(val);
01260       if (q->maxlen < 0)
01261          q->maxlen = 0;
01262    } else if (!strcasecmp(param, "servicelevel")) {
01263       q->servicelevel= atoi(val);
01264    } else if (!strcasecmp(param, "strategy")) {
01265       int strategy;
01266 
01267       /* We are a static queue and already have set this, no need to do it again */
01268       if (failunknown) {
01269          return;
01270       }
01271       strategy = strat2int(val);
01272       if (strategy < 0) {
01273          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01274             val, q->name);
01275          q->strategy = QUEUE_STRATEGY_RINGALL;
01276       }
01277       if (strategy == q->strategy) {
01278          return;
01279       }
01280       if (strategy == QUEUE_STRATEGY_LINEAR) {
01281          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
01282          return;
01283       }
01284       q->strategy = strategy;
01285    } else if (!strcasecmp(param, "joinempty")) {
01286       if (!strcasecmp(val, "loose"))
01287          q->joinempty = QUEUE_EMPTY_LOOSE;
01288       else if (!strcasecmp(val, "strict"))
01289          q->joinempty = QUEUE_EMPTY_STRICT;
01290       else if (ast_true(val))
01291          q->joinempty = QUEUE_EMPTY_NORMAL;
01292       else
01293          q->joinempty = 0;
01294    } else if (!strcasecmp(param, "leavewhenempty")) {
01295       if (!strcasecmp(val, "loose"))
01296          q->leavewhenempty = QUEUE_EMPTY_LOOSE;
01297       else if (!strcasecmp(val, "strict"))
01298          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01299       else if (ast_true(val))
01300          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01301       else
01302          q->leavewhenempty = 0;
01303    } else if (!strcasecmp(param, "eventmemberstatus")) {
01304       q->maskmemberstatus = !ast_true(val);
01305    } else if (!strcasecmp(param, "eventwhencalled")) {
01306       if (!strcasecmp(val, "vars")) {
01307          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01308       } else {
01309          q->eventwhencalled = ast_true(val) ? 1 : 0;
01310       }
01311    } else if (!strcasecmp(param, "reportholdtime")) {
01312       q->reportholdtime = ast_true(val);
01313    } else if (!strcasecmp(param, "memberdelay")) {
01314       q->memberdelay = atoi(val);
01315    } else if (!strcasecmp(param, "weight")) {
01316       q->weight = atoi(val);
01317       if (q->weight)
01318          use_weight++;
01319       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01320          we will not see any effect on use_weight until next reload. */
01321    } else if (!strcasecmp(param, "timeoutrestart")) {
01322       q->timeoutrestart = ast_true(val);
01323    } else if (!strcasecmp(param, "defaultrule")) {
01324       ast_string_field_set(q, defaultrule, val);
01325    } else if (!strcasecmp(param, "timeoutpriority")) {
01326       if (!strcasecmp(val, "conf")) {
01327          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
01328       } else {
01329          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01330       }
01331    } else if (failunknown) {
01332       if (linenum >= 0) {
01333          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01334             q->name, param, linenum);
01335       } else {
01336          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01337       }
01338    }
01339 }

static char* queue_show ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 5833 of file app_queue.c.

References __queues_show(), ast_cli_args::argc, ast_cli_args::argv, CLI_GENERATE, CLI_INIT, ast_cli_entry::command, complete_queue_show(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

05834 {
05835    switch ( cmd ) {
05836    case CLI_INIT:
05837       e->command = "queue show";
05838       e->usage =
05839          "Usage: queue show\n"
05840          "       Provides summary information on a specified queue.\n";
05841       return NULL;
05842    case CLI_GENERATE:
05843       return complete_queue_show(a->line, a->word, a->pos, a->n); 
05844    }
05845 
05846    return __queues_show(NULL, a->fd, a->argc, a->argv);
05847 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 3164 of file app_queue.c.

References ast_free.

03165 {
03166    struct queue_transfer_ds *qtds = data;
03167    ast_free(qtds);
03168 }

static void queue_transfer_fixup ( void *  data,
struct ast_channel old_chan,
struct ast_channel new_chan 
) [static]

Log an attended transfer when a queue caller channel is masqueraded.

When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan to new_chan. This is why new_chan is referenced for exten, context, and datastore information.

At the end of this, we want to remove the datastore so that this fixup function is not called on any future masquerades of the caller during the current call.

Definition at line 3187 of file app_queue.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, queue_transfer_ds::member, member::membername, call_queue::name, queue_ent::opos, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, ast_channel::uniqueid, and update_queue().

03188 {
03189    struct queue_transfer_ds *qtds = data;
03190    struct queue_ent *qe = qtds->qe;
03191    struct member *member = qtds->member;
03192    time_t callstart = qtds->starttime;
03193    int callcompletedinsl = qtds->callcompletedinsl;
03194    struct ast_datastore *datastore;
03195 
03196    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03197             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03198             (long) (time(NULL) - callstart), qe->opos);
03199 
03200    update_queue(qe->parent, member, callcompletedinsl);
03201    
03202    /* No need to lock the channels because they are already locked in ast_do_masquerade */
03203    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03204       ast_channel_datastore_remove(old_chan, datastore);
03205    } else {
03206       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03207    }
03208 }

static struct call_queue* queue_unref ( struct call_queue q  )  [inline, static]

Definition at line 609 of file app_queue.c.

References ao2_ref.

Referenced by __queues_show(), compare_weight(), complete_queue(), complete_queue_remove_member(), end_bridge_callback(), find_queue_by_name_rt(), get_member_penalty(), interface_exists_global(), leave_queue(), manager_queues_status(), manager_queues_summary(), queue_function_qac(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_queues(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_status().

00610 {
00611    ao2_ref(q, -1);
00612    return q;
00613 }

static void recalc_holdtime ( struct queue_ent qe,
int  newholdtime 
) [static]

Definition at line 1981 of file app_queue.c.

References ao2_lock(), ao2_unlock(), call_queue::holdtime, and queue_ent::parent.

Referenced by try_calling().

01982 {
01983    int oldvalue;
01984 
01985    /* Calculate holdtime using an exponential average */
01986    /* Thanks to SRT for this contribution */
01987    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01988 
01989    ao2_lock(qe->parent);
01990    oldvalue = qe->parent->holdtime;
01991    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01992    ao2_unlock(qe->parent);
01993 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 2518 of file app_queue.c.

References ao2_lock(), ao2_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, manager_event, call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, set_queue_variables(), queue_ent::start, and ast_channel::uniqueid.

Referenced by queue_exec(), and try_calling().

02519 {
02520    ao2_lock(qe->parent);
02521    set_queue_variables(qe->parent, qe->chan);
02522    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02523       "Queue: %s\r\n"
02524       "Uniqueid: %s\r\n"
02525       "Position: %d\r\n"
02526       "OriginalPosition: %d\r\n"
02527       "HoldTime: %d\r\n",
02528       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02529 
02530    qe->parent->callsabandoned++;
02531    ao2_unlock(qe->parent);
02532 }

static int reload ( void   )  [static]

Definition at line 6726 of file app_queue.c.

References ast_unload_realtime(), and reload_queues().

06727 {
06728    ast_unload_realtime("queue_members");
06729    reload_queues(1);
06730    return 0;
06731 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 4367 of file app_queue.c.

References add_to_queue(), ao2_find, ao2_lock(), ao2_unlock(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), ast_debug, ast_log(), ast_strlen_zero(), errno, member::interface, ast_db_entry::key, load_realtime_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_unref(), queues, RES_OUTOFMEMORY, member::state_interface, and strsep().

Referenced by load_module().

04368 {
04369    char *cur_ptr;
04370    const char *queue_name;
04371    char *member;
04372    char *interface;
04373    char *membername = NULL;
04374    char *state_interface;
04375    char *penalty_tok;
04376    int penalty = 0;
04377    char *paused_tok;
04378    int paused = 0;
04379    struct ast_db_entry *db_tree;
04380    struct ast_db_entry *entry;
04381    struct call_queue *cur_queue;
04382    char queue_data[PM_MAX_LEN];
04383 
04384    ao2_lock(queues);
04385 
04386    /* Each key in 'pm_family' is the name of a queue */
04387    db_tree = ast_db_gettree(pm_family, NULL);
04388    for (entry = db_tree; entry; entry = entry->next) {
04389 
04390       queue_name = entry->key + strlen(pm_family) + 2;
04391 
04392       {
04393          struct call_queue tmpq = {
04394             .name = queue_name,
04395          };
04396          cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
04397       }  
04398 
04399       if (!cur_queue)
04400          cur_queue = load_realtime_queue(queue_name);
04401 
04402       if (!cur_queue) {
04403          /* If the queue no longer exists, remove it from the
04404           * database */
04405          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04406          ast_db_del(pm_family, queue_name);
04407          continue;
04408       } 
04409 
04410       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04411          queue_unref(cur_queue);
04412          continue;
04413       }
04414 
04415       cur_ptr = queue_data;
04416       while ((member = strsep(&cur_ptr, ",|"))) {
04417          if (ast_strlen_zero(member))
04418             continue;
04419 
04420          interface = strsep(&member, ";");
04421          penalty_tok = strsep(&member, ";");
04422          paused_tok = strsep(&member, ";");
04423          membername = strsep(&member, ";");
04424          state_interface = strsep(&member, ";");
04425 
04426          if (!penalty_tok) {
04427             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04428             break;
04429          }
04430          penalty = strtol(penalty_tok, NULL, 10);
04431          if (errno == ERANGE) {
04432             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04433             break;
04434          }
04435          
04436          if (!paused_tok) {
04437             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04438             break;
04439          }
04440          paused = strtol(paused_tok, NULL, 10);
04441          if ((errno == ERANGE) || paused < 0 || paused > 1) {
04442             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04443             break;
04444          }
04445 
04446          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
04447          
04448          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04449             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04450             break;
04451          }
04452       }
04453       queue_unref(cur_queue);
04454    }
04455 
04456    ao2_unlock(queues);
04457    if (db_tree) {
04458       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04459       ast_db_freetree(db_tree);
04460    }
04461 }

static int reload_queue_rules ( int  reload  )  [static]

Definition at line 5396 of file app_queue.c.

References ast_calloc, ast_category_browse(), ast_config_load, ast_copy_string(), ast_free, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEUNCHANGED, insert_penaltychange(), ast_variable::lineno, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, rule_list::rules, and ast_variable::value.

Referenced by handle_queue_rule_reload(), and reload_queues().

05397 {
05398    struct ast_config *cfg;
05399    struct rule_list *rl_iter, *new_rl;
05400    struct penalty_rule *pr_iter;
05401    char *rulecat = NULL;
05402    struct ast_variable *rulevar = NULL;
05403    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05404    
05405    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05406       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05407    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05408       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05409       return AST_MODULE_LOAD_SUCCESS;
05410    } else {
05411       AST_LIST_LOCK(&rule_lists);
05412       while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05413          while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05414             ast_free(pr_iter);
05415          ast_free(rl_iter);
05416       }
05417       while ((rulecat = ast_category_browse(cfg, rulecat))) {
05418          if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05419             ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05420             AST_LIST_UNLOCK(&rule_lists);
05421             return AST_MODULE_LOAD_FAILURE;
05422          } else {
05423             ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05424             AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05425             for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05426                if(!strcasecmp(rulevar->name, "penaltychange")) {
05427                   insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05428                } else {
05429                   ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05430                }
05431          }
05432       }
05433       AST_LIST_UNLOCK(&rule_lists);
05434    }
05435 
05436    ast_config_destroy(cfg);
05437 
05438    return AST_MODULE_LOAD_SUCCESS;
05439 }

static int reload_queues ( int  reload  )  [static]

Definition at line 5442 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), AST_APP_ARG, ast_category_browse(), ast_config_load, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), AST_MODULE_LOAD_FAILURE, AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEUNCHANGED, create_queue_member(), call_queue::dead, member::delme, member::dynamic, F_AO2I_DONTLOCK, call_queue::found, init_queue(), member::interface, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_unref(), queues, call_queue::realtime, reload_queue_rules(), remove_from_interfaces(), member::state_interface, strat2int(), and var.

Referenced by load_module(), and reload().

05443 {
05444    struct call_queue *q;
05445    struct ast_config *cfg;
05446    char *cat, *tmp;
05447    struct ast_variable *var;
05448    struct member *cur, *newm;
05449    struct ao2_iterator mem_iter;
05450    int new;
05451    const char *general_val = NULL;
05452    char parse[80];
05453    char *interface, *state_interface;
05454    char *membername = NULL;
05455    int penalty;
05456    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05457    struct ao2_iterator queue_iter;
05458    AST_DECLARE_APP_ARGS(args,
05459       AST_APP_ARG(interface);
05460       AST_APP_ARG(penalty);
05461       AST_APP_ARG(membername);
05462       AST_APP_ARG(state_interface);
05463    );
05464 
05465    /*First things first. Let's load queuerules.conf*/
05466    if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05467       return AST_MODULE_LOAD_FAILURE;
05468       
05469    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05470       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05471       return 0;
05472    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05473       return 0;
05474    ao2_lock(queues);
05475    use_weight=0;
05476    /* Mark all queues as dead for the moment */
05477    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05478    while ((q = ao2_iterator_next(&queue_iter))) {
05479       if (!q->realtime) {
05480          q->dead = 1;
05481          q->found = 0;
05482       }
05483       queue_unref(q);
05484    }
05485 
05486    /* Chug through config file */
05487    cat = NULL;
05488    while ((cat = ast_category_browse(cfg, cat)) ) {
05489       if (!strcasecmp(cat, "general")) {  
05490          /* Initialize global settings */
05491          queue_keep_stats = 0;
05492          if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05493             queue_keep_stats = ast_true(general_val);
05494          queue_persistent_members = 0;
05495          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05496             queue_persistent_members = ast_true(general_val);
05497          autofill_default = 0;
05498          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05499             autofill_default = ast_true(general_val);
05500          montype_default = 0;
05501          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05502             if (!strcasecmp(general_val, "mixmonitor"))
05503                montype_default = 1;
05504          }
05505          update_cdr = 0;
05506          if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05507             update_cdr = ast_true(general_val);
05508          shared_lastcall = 0;
05509          if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05510             shared_lastcall = ast_true(general_val);
05511       } else { /* Define queue */
05512          /* Look for an existing one */
05513          struct call_queue tmpq = {
05514             .name = cat,
05515          };
05516          if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05517             /* Make one then */
05518             if (!(q = alloc_queue(cat))) {
05519                /* TODO: Handle memory allocation failure */
05520             }
05521             new = 1;
05522          } else
05523             new = 0;
05524          if (q) {
05525             const char *tmpvar = NULL;
05526             if (!new)
05527                ao2_lock(q);
05528             /* Check if a queue with this name already exists */
05529             if (q->found) {
05530                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05531                if (!new) {
05532                   ao2_unlock(q);
05533                   queue_unref(q);
05534                }
05535                continue;
05536             }
05537             /* Due to the fact that the "linear" strategy will have a different allocation
05538              * scheme for queue members, we must devise the queue's strategy before other initializations
05539              */
05540             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05541                q->strategy = strat2int(tmpvar);
05542                if (q->strategy < 0) {
05543                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05544                   tmpvar, q->name);
05545                   q->strategy = QUEUE_STRATEGY_RINGALL;
05546                }
05547             } else
05548                q->strategy = QUEUE_STRATEGY_RINGALL;
05549             /* Re-initialize the queue, and clear statistics */
05550             init_queue(q);
05551             if (!queue_keep_stats) 
05552                clear_queue(q);
05553             mem_iter = ao2_iterator_init(q->members, 0);
05554             while ((cur = ao2_iterator_next(&mem_iter))) {
05555                if (!cur->dynamic) {
05556                   cur->delme = 1;
05557                }
05558                ao2_ref(cur, -1);
05559             }
05560             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05561                if (!strcasecmp(var->name, "member")) {
05562                   struct member tmpmem;
05563                   membername = NULL;
05564 
05565                   if (ast_strlen_zero(var->value)) {
05566                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05567                      continue;
05568                   }
05569 
05570                   /* Add a new member */
05571                   ast_copy_string(parse, var->value, sizeof(parse));
05572                   
05573                   AST_STANDARD_APP_ARGS(args, parse);
05574 
05575                   interface = args.interface;
05576                   if (!ast_strlen_zero(args.penalty)) {
05577                      tmp = args.penalty;
05578                      while (*tmp && *tmp < 33) tmp++;
05579                      penalty = atoi(tmp);
05580                      if (penalty < 0) {
05581                         penalty = 0;
05582                      }
05583                   } else
05584                      penalty = 0;
05585 
05586                   if (!ast_strlen_zero(args.membername)) {
05587                      membername = args.membername;
05588                      while (*membername && *membername < 33) membername++;
05589                   }
05590 
05591                   if (!ast_strlen_zero(args.state_interface)) {
05592                      state_interface = args.state_interface;
05593                      while (*state_interface && *state_interface < 33) state_interface++;
05594                   } else
05595                      state_interface = interface;
05596 
05597                   /* Find the old position in the list */
05598                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05599                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05600                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
05601                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
05602                      remove_from_interfaces(cur->state_interface, 0);
05603                   }
05604                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05605                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05606                      add_to_interfaces(state_interface);
05607                   ao2_link(q->members, newm);
05608                   ao2_ref(newm, -1);
05609                   newm = NULL;
05610 
05611                   if (cur)
05612                      ao2_ref(cur, -1);
05613                   else {
05614                      q->membercount++;
05615                   }
05616                } else {
05617                   queue_set_param(q, var->name, var->value, var->lineno, 1);
05618                }
05619             }
05620 
05621             /* Free remaining members marked as delme */
05622             mem_iter = ao2_iterator_init(q->members, 0);
05623             while ((cur = ao2_iterator_next(&mem_iter))) {
05624                if (! cur->delme) {
05625                   ao2_ref(cur, -1);
05626                   continue;
05627                }
05628                q->membercount--;
05629                ao2_unlink(q->members, cur);
05630                remove_from_interfaces(cur->interface, 0);
05631                ao2_ref(cur, -1);
05632             }
05633 
05634             if (new) {
05635                ao2_link(queues, q);
05636             } else 
05637                ao2_unlock(q);
05638             queue_unref(q);
05639          }
05640       }
05641    }
05642    ast_config_destroy(cfg);
05643    queue_iter = ao2_iterator_init(queues, 0);
05644    while ((q = ao2_iterator_next(&queue_iter))) {
05645       if (q->dead) {
05646          ao2_unlink(queues, q);
05647       } else {
05648          ao2_lock(q);
05649          mem_iter = ao2_iterator_init(q->members, 0);
05650          while ((cur = ao2_iterator_next(&mem_iter))) {
05651             if (cur->dynamic)
05652                q->membercount++;
05653             cur->status = ast_device_state(cur->state_interface);
05654             ao2_ref(cur, -1);
05655          }
05656          ao2_unlock(q);
05657       }
05658       queue_unref(q);
05659    }
05660    ao2_unlock(queues);
05661    return 1;
05662 }

static int remove_from_interfaces ( const char *  interface,
int  lock_queue_container 
) [static]

Definition at line 1014 of file app_queue.c.

References ast_debug, ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, member_interface::interface, interface_exists_global(), and member_interface::list.

Referenced by free_members(), reload_queues(), remove_from_queue(), rt_handle_member_record(), and update_realtime_members().

01015 {
01016    struct member_interface *curint;
01017 
01018    if (interface_exists_global(interface, lock_queue_container))
01019       return 0;
01020 
01021    AST_LIST_LOCK(&interfaces);
01022    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01023       if (!strcasecmp(curint->interface, interface)) {
01024          ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01025          AST_LIST_REMOVE_CURRENT(list);
01026          ast_free(curint);
01027          break;
01028       }
01029    }
01030    AST_LIST_TRAVERSE_SAFE_END;
01031    AST_LIST_UNLOCK(&interfaces);
01032 
01033    return 0;
01034 }

static int remove_from_queue ( const char *  queuename,
const char *  interface 
) [static]

Remove member from queue.

Return values:
RES_NOT_DYNAMIC when they aren't a RT member
RES_NOSUCHQUEUE queue does not exist
RES_OKAY removed member from queue
RES_EXISTS queue exists but no members

Definition at line 4104 of file app_queue.c.

References ao2_find, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), ast_copy_string(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, manager_event, call_queue::membercount, member::membername, call_queue::members, call_queue::name, OBJ_POINTER, queue_unref(), queues, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and member::state_interface.

Referenced by attempt_thread(), handle_queue_remove_member(), manager_remove_queue_member(), rqm_exec(), and scan_service().

04105 {
04106    struct call_queue *q, tmpq = {
04107       .name = queuename,   
04108    };
04109    struct member *mem, tmpmem;
04110    int res = RES_NOSUCHQUEUE;
04111 
04112    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04113    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04114       ao2_lock(queues);
04115       ao2_lock(q);
04116       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04117          /* XXX future changes should beware of this assumption!! */
04118          if (!mem->dynamic) {
04119             ao2_ref(mem, -1);
04120             ao2_unlock(q);
04121             queue_unref(q);
04122             ao2_unlock(queues);
04123             return RES_NOT_DYNAMIC;
04124          }
04125          q->membercount--;
04126          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04127             "Queue: %s\r\n"
04128             "Location: %s\r\n"
04129             "MemberName: %s\r\n",
04130             q->name, mem->interface, mem->membername);
04131          ao2_unlink(q->members, mem);
04132          remove_from_interfaces(mem->state_interface, 0);
04133          ao2_ref(mem, -1);
04134 
04135          if (queue_persistent_members)
04136             dump_queue_members(q);
04137          
04138          res = RES_OKAY;
04139       } else {
04140          res = RES_EXISTS;
04141       }
04142       ao2_unlock(q);
04143       ao2_unlock(queues);
04144       queue_unref(q);
04145    }
04146 
04147    return res;
04148 }

static int ring_entry ( struct queue_ent qe,
struct callattempt tmp,
int *  busies 
) [static]

Part 2 of ring_one.

Does error checking before attempting to request a channel and call a member. This function is only called from ring_one(). Failure can occur if:

Return values:
1 on success to reach a free agent
0 on failure to get agent.

Definition at line 2218 of file app_queue.c.

References ast_channel::adsicpe, ao2_lock(), ao2_unlock(), ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, ast_device_state(), AST_DEVICE_UNKNOWN, ast_free, ast_request(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, compare_weight(), ast_channel::context, ast_channel::data, dialcontext, do_hang(), EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, callattempt::interface, callattempt::lastcall, callattempt::lastqueue, queue_ent::linpos, manager_event, callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, member::state_interface, member::status, status, callattempt::stillgoing, ast_channel::uniqueid, update_status(), vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

02219 {
02220    int res;
02221    int status;
02222    char tech[256];
02223    char *location;
02224    const char *macrocontext, *macroexten;
02225 
02226    /* on entry here, we know that tmp->chan == NULL */
02227    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
02228       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
02229       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
02230             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
02231       if (qe->chan->cdr)
02232          ast_cdr_busy(qe->chan->cdr);
02233       tmp->stillgoing = 0;
02234       (*busies)++;
02235       return 0;
02236    }
02237 
02238    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
02239       ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
02240       if (qe->chan->cdr)
02241          ast_cdr_busy(qe->chan->cdr);
02242       tmp->stillgoing = 0;
02243       return 0;
02244    }
02245 
02246    if (tmp->member->paused) {
02247       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
02248       if (qe->chan->cdr)
02249          ast_cdr_busy(qe->chan->cdr);
02250       tmp->stillgoing = 0;
02251       return 0;
02252    }
02253    if (use_weight && compare_weight(qe->parent,tmp->member)) {
02254       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
02255       if (qe->chan->cdr)
02256          ast_cdr_busy(qe->chan->cdr);
02257       tmp->stillgoing = 0;
02258       (*busies)++;
02259       return 0;
02260    }
02261 
02262    ast_copy_string(tech, tmp->interface, sizeof(tech));
02263    if ((location = strchr(tech, '/')))
02264       *location++ = '\0';
02265    else
02266       location = "";
02267 
02268    /* Request the peer */
02269    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02270    if (!tmp->chan) {       /* If we can't, just go on to the next call */
02271       if (qe->chan->cdr)
02272          ast_cdr_busy(qe->chan->cdr);
02273       tmp->stillgoing = 0;
02274 
02275       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02276 
02277       ao2_lock(qe->parent);
02278       qe->parent->rrpos++;
02279       qe->linpos++;
02280       ao2_unlock(qe->parent);
02281 
02282 
02283       (*busies)++;
02284       return 0;
02285    }
02286    
02287    tmp->chan->appl = "AppQueue";
02288    tmp->chan->data = "(Outgoing Line)";
02289    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
02290    if (tmp->chan->cid.cid_num)
02291       ast_free(tmp->chan->cid.cid_num);
02292    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02293    if (tmp->chan->cid.cid_name)
02294       ast_free(tmp->chan->cid.cid_name);
02295    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02296    if (tmp->chan->cid.cid_ani)
02297       ast_free(tmp->chan->cid.cid_ani);
02298    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02299 
02300    /* Inherit specially named variables from parent channel */
02301    ast_channel_inherit_variables(qe->chan, tmp->chan);
02302 
02303    /* Presense of ADSI CPE on outgoing channel follows ours */
02304    tmp->chan->adsicpe = qe->chan->adsicpe;
02305 
02306    /* Inherit context and extension */
02307    ast_channel_lock(qe->chan);
02308    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02309    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
02310    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02311    if (!ast_strlen_zero(macroexten))
02312       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02313    else
02314       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02315    ast_channel_unlock(qe->chan);
02316 
02317    /* Place the call, but don't wait on the answer */
02318    if ((res = ast_call(tmp->chan, location, 0))) {
02319       /* Again, keep going even if there's an error */
02320       ast_debug(1, "ast call on peer returned %d\n", res);
02321       ast_verb(3, "Couldn't call %s\n", tmp->interface);
02322       do_hang(tmp);
02323       (*busies)++;
02324       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02325       return 0;
02326    } else if (qe->parent->eventwhencalled) {
02327       char vars[2048];
02328 
02329       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02330                "Queue: %s\r\n"
02331                "AgentCalled: %s\r\n"
02332                "AgentName: %s\r\n"
02333                "ChannelCalling: %s\r\n"
02334                "DestinationChannel: %s\r\n"
02335                "CallerIDNum: %s\r\n"
02336                "CallerIDName: %s\r\n"
02337                "Context: %s\r\n"
02338                "Extension: %s\r\n"
02339                "Priority: %d\r\n"
02340                "Uniqueid: %s\r\n"
02341                "%s",
02342                qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
02343                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02344                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02345                qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
02346                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02347       ast_verb(3, "Called %s\n", tmp->interface);
02348    }
02349 
02350    update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02351    return 1;
02352 }

static int ring_one ( struct queue_ent qe,
struct callattempt outgoing,
int *  busies 
) [static]

Place a call to a queue member.

Once metrics have been calculated for each member, this function is used to place a call to the appropriate member (or members). The low-level channel-handling and error detection is handled in ring_entry

Return values:
1 if a member was called successfully
0 otherwise

Definition at line 2380 of file app_queue.c.

References ast_debug, callattempt::chan, queue_ent::expire, find_best(), callattempt::interface, callattempt::metric, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_entry(), callattempt::stillgoing, and call_queue::strategy.

Referenced by try_calling(), and wait_for_answer().

02381 {
02382    int ret = 0;
02383 
02384    while (ret == 0) {
02385       struct callattempt *best = find_best(outgoing);
02386       if (!best) {
02387          ast_debug(1, "Nobody left to try ringing in queue\n");
02388          break;
02389       }
02390       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02391          struct callattempt *cur;
02392          /* Ring everyone who shares this best metric (for ringall) */
02393          for (cur = outgoing; cur; cur = cur->q_next) {
02394             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02395                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02396                ret |= ring_entry(qe, cur, busies);
02397             }
02398          }
02399       } else {
02400          /* Ring just the best channel */
02401          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02402          ret = ring_entry(qe, best, busies);
02403       }
02404       
02405       /* If we have timed out, break out */
02406       if (qe->expire && (time(NULL) >= qe->expire)) {
02407          ast_debug(1, "Queue timed out while ringing members.\n");
02408          ret = 0;
02409          break;
02410       }
02411    }
02412 
02413    return ret;
02414 }

static void rna ( int  rnatime,
struct queue_ent qe,
char *  interface,
char *  membername,
int  pause 
) [static]

RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.

Definition at line 2535 of file app_queue.c.

References ast_queue_log(), ast_verb, call_queue::autopause, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, manager_event, ast_channel::name, call_queue::name, queue_ent::parent, set_member_paused(), and ast_channel::uniqueid.

02536 {
02537    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02538    if (qe->parent->eventwhencalled)
02539       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
02540                   "Queue: %s\r\n"
02541                   "Uniqueid: %s\r\n"
02542                   "Channel: %s\r\n"
02543                   "Member: %s\r\n"
02544                   "MemberName: %s\r\n"
02545                   "Ringtime: %d\r\n",
02546                   qe->parent->name,
02547                   qe->chan->uniqueid,
02548                   qe->chan->name,
02549                   interface,
02550                   membername,
02551                   rnatime);
02552    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02553    if (qe->parent->autopause && pause) {
02554       if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02555          ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02556       } else {
02557          ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02558       }
02559    }
02560    return;
02561 }

static int rqm_exec ( struct ast_channel chan,
void *  data 
) [static]

RemoveQueueMember application.

Definition at line 4536 of file app_queue.c.

References AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and ast_channel::uniqueid.

Referenced by load_module().

04537 {
04538    int res=-1;
04539    char *parse, *temppos = NULL;
04540    AST_DECLARE_APP_ARGS(args,
04541       AST_APP_ARG(queuename);
04542       AST_APP_ARG(interface);
04543       AST_APP_ARG(options);
04544    );
04545 
04546 
04547    if (ast_strlen_zero(data)) {
04548       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04549       return -1;
04550    }
04551 
04552    parse = ast_strdupa(data);
04553 
04554    AST_STANDARD_APP_ARGS(args, parse);
04555 
04556    if (ast_strlen_zero(args.interface)) {
04557       args.interface = ast_strdupa(chan->name);
04558       temppos = strrchr(args.interface, '-');
04559       if (temppos)
04560          *temppos = '\0';
04561    }
04562 
04563    switch (remove_from_queue(args.queuename, args.interface)) {
04564    case RES_OKAY:
04565       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04566       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04567       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04568       res = 0;
04569       break;
04570    case RES_EXISTS:
04571       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04572       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04573       res = 0;
04574       break;
04575    case RES_NOSUCHQUEUE:
04576       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04577       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04578       res = 0;
04579       break;
04580    case RES_NOT_DYNAMIC:
04581       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04582       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04583       res = 0;
04584       break;
04585    }
04586 
04587    return res;
04588 }

static void rt_handle_member_record ( struct call_queue q,
char *  interface,
const char *  rt_uniqueid,
const char *  membername,
const char *  penalty_str,
const char *  paused_str,
const char *  state_interface 
) [static]

Find rt member record to update otherwise create one.

Search for member in queue, if found update penalty/paused state, if no memeber exists create one flag it as a RT member and add to queue member list.

Definition at line 1347 of file app_queue.c.

References add_to_interfaces(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_copy_string(), ast_queue_log(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, remove_from_interfaces(), member::rt_uniqueid, and member::state_interface.

Referenced by update_realtime_members().

01348 {
01349    struct member *m;
01350    struct ao2_iterator mem_iter;
01351    int penalty = 0;
01352    int paused  = 0;
01353    int found = 0;
01354 
01355    if (penalty_str) {
01356       penalty = atoi(penalty_str);
01357       if (penalty < 0)
01358          penalty = 0;
01359    }
01360 
01361    if (paused_str) {
01362       paused = atoi(paused_str);
01363       if (paused < 0)
01364          paused = 0;
01365    }
01366 
01367    /* Find member by realtime uniqueid and update */
01368    mem_iter = ao2_iterator_init(q->members, 0);
01369    while ((m = ao2_iterator_next(&mem_iter))) {
01370       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
01371          m->dead = 0;   /* Do not delete this one. */
01372          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01373          if (paused_str)
01374             m->paused = paused;
01375          if (strcasecmp(state_interface, m->state_interface)) {
01376             remove_from_interfaces(m->state_interface, 0);
01377             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01378             add_to_interfaces(m->state_interface);
01379          }     
01380          m->penalty = penalty;
01381          found = 1;
01382          ao2_ref(m, -1);
01383          break;
01384       }
01385       ao2_ref(m, -1);
01386    }
01387 
01388    /* Create a new member */
01389    if (!found) {
01390       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01391          m->dead = 0;
01392          m->realtime = 1;
01393          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01394          add_to_interfaces(m->state_interface);
01395          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
01396          ao2_link(q->members, m);
01397          ao2_ref(m, -1);
01398          m = NULL;
01399          q->membercount++;
01400       }
01401    }
01402 }

static int say_periodic_announcement ( struct queue_ent qe,
int  ringing 
) [static]

Playback announcement to queued members if peroid has elapsed.

Definition at line 2465 of file app_queue.c.

References AST_CONTROL_RINGING, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_random(), ast_strlen_zero(), ast_verb, queue_ent::chan, queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::moh, call_queue::numperiodicannounce, queue_ent::parent, call_queue::periodicannouncefrequency, play_file(), call_queue::randomperiodicannounce, call_queue::sound_periodicannounce, and valid_exit().

Referenced by queue_exec(), and wait_our_turn().

02466 {
02467    int res = 0;
02468    time_t now;
02469 
02470    /* Get the current time */
02471    time(&now);
02472 
02473    /* Check to see if it is time to announce */
02474    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02475       return 0;
02476 
02477    /* Stop the music on hold so we can play our own file */
02478    if (ringing)
02479       ast_indicate(qe->chan,-1);
02480    else
02481       ast_moh_stop(qe->chan);
02482 
02483    ast_verb(3, "Playing periodic announcement\n");
02484    
02485    if (qe->parent->randomperiodicannounce) {
02486       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
02487    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
02488       ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
02489       qe->last_periodic_announce_sound = 0;
02490    }
02491    
02492    /* play the announcement */
02493    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
02494 
02495    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
02496       res = 0;
02497 
02498    /* Resume Music on Hold if the caller is going to stay in the queue */
02499    if (!res) {
02500       if (ringing)
02501          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02502       else
02503          ast_moh_start(qe->chan, qe->moh, NULL);
02504    }
02505 
02506    /* update last_periodic_announce_time */
02507    qe->last_periodic_announce_time = now;
02508 
02509    /* Update the current periodic announcement to the next announcement */
02510    if (!qe->parent->randomperiodicannounce) {
02511       qe->last_periodic_announce_sound++;
02512    }
02513    
02514    return res;
02515 }

static int say_position ( struct queue_ent qe,
int  ringing 
) [static]

Definition at line 1838 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, AST_CONTROL_RINGING, AST_DIGIT_ANY, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verb, queue_ent::chan, call_queue::holdtime, ast_channel::language, queue_ent::last_pos, queue_ent::last_pos_said, call_queue::minannouncefrequency, queue_ent::moh, ast_channel::name, call_queue::name, queue_ent::parent, play_file(), queue_ent::pos, call_queue::queue_quantity1, call_queue::queue_quantity2, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, and valid_exit().

Referenced by queue_exec(), and wait_our_turn().

01839 {
01840    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
01841    int say_thanks = 1;
01842    time_t now;
01843 
01844    /* Let minannouncefrequency seconds pass between the start of each position announcement */
01845    time(&now);
01846    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
01847       return 0;
01848 
01849    /* If either our position has changed, or we are over the freq timer, say position */
01850    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01851       return 0;
01852 
01853    if (ringing) {
01854       ast_indicate(qe->chan,-1);
01855    } else {
01856       ast_moh_stop(qe->chan);
01857    }
01858 
01859    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
01860       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
01861       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
01862       qe->pos <= qe->parent->announcepositionlimit))
01863          announceposition = 1;
01864 
01865 
01866    if (announceposition == 1) {
01867       /* Say we're next, if we are */
01868       if (qe->pos == 1) {
01869          res = play_file(qe->chan, qe->parent->sound_next);
01870          if (res)
01871             goto playout;
01872          else
01873             goto posout;
01874       } else {
01875          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01876             /* More than Case*/
01877             res = play_file(qe->chan, qe->parent->queue_quantity1);
01878             if (res)
01879                goto playout;
01880             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
01881             if (res)
01882                goto playout;
01883          } else {
01884             /* Normal Case */
01885             res = play_file(qe->chan, qe->parent->sound_thereare);
01886             if (res)
01887                goto playout;
01888             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
01889             if (res)
01890                goto playout;
01891          }
01892          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01893             /* More than Case*/
01894             res = play_file(qe->chan, qe->parent->queue_quantity2);
01895             if (res)
01896                goto playout;
01897          } else {
01898             res = play_file(qe->chan, qe->parent->sound_calls);
01899             if (res)
01900                goto playout;
01901          }
01902       }
01903    }
01904    /* Round hold time to nearest minute */
01905    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01906 
01907    /* If they have specified a rounding then round the seconds as well */
01908    if (qe->parent->roundingseconds) {
01909       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01910       avgholdsecs *= qe->parent->roundingseconds;
01911    } else {
01912       avgholdsecs = 0;
01913    }
01914 
01915    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01916 
01917    /* If the hold time is >1 min, if it's enabled, and if it's not
01918       supposed to be only once and we have already said it, say it */
01919     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
01920         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
01921         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
01922       res = play_file(qe->chan, qe->parent->sound_holdtime);
01923       if (res)
01924          goto playout;
01925 
01926       if (avgholdmins >= 1) {
01927          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01928          if (res)
01929             goto playout;
01930 
01931          if (avgholdmins == 1) {
01932             res = play_file(qe->chan, qe->parent->sound_minute);
01933             if (res)
01934                goto playout;
01935          } else {
01936             res = play_file(qe->chan, qe->parent->sound_minutes);
01937             if (res)
01938                goto playout;
01939          }
01940       }
01941       if (avgholdsecs >= 1) {
01942          res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01943          if (res)
01944             goto playout;
01945 
01946          res = play_file(qe->chan, qe->parent->sound_seconds);
01947          if (res)
01948             goto playout;
01949       }
01950    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
01951       say_thanks = 0;
01952    }
01953 
01954 posout:
01955    if (qe->parent->announceposition) {
01956       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
01957          qe->chan->name, qe->parent->name, qe->pos);
01958    }
01959    if (say_thanks) {
01960       res = play_file(qe->chan, qe->parent->sound_thanks);
01961    }
01962 playout:
01963    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01964       res = 0;
01965 
01966    /* Set our last_pos indicators */
01967    qe->last_pos = now;
01968    qe->last_pos_said = qe->pos;
01969 
01970    /* Don't restart music on hold if we're about to exit the caller from the queue */
01971    if (!res) {
01972       if (ringing) {
01973          ast_indicate(qe->chan, AST_CONTROL_RINGING);
01974       } else {
01975          ast_moh_start(qe->chan, qe->moh, NULL);
01976       }
01977    }
01978    return res;
01979 }

static void send_agent_complete ( const struct queue_ent qe,
const char *  queuename,
const struct ast_channel peer,
const struct member member,
time_t  callstart,
char *  vars,
size_t  vars_len,
enum agent_complete_reason  rsn 
) [static]

Send out AMI message with member call completion status information.

Definition at line 3121 of file app_queue.c.

References AGENT, CALLER, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, member::interface, manager_event, member::membername, ast_channel::name, queue_ent::parent, QUEUE_EVENT_VARIABLES, queue_ent::start, ast_channel::uniqueid, and vars2manager().

Referenced by try_calling().

03124 {
03125    const char *reason = NULL; /* silence dumb compilers */
03126 
03127    if (!qe->parent->eventwhencalled)
03128       return;
03129 
03130    switch (rsn) {
03131    case CALLER:
03132       reason = "caller";
03133       break;
03134    case AGENT:
03135       reason = "agent";
03136       break;
03137    case TRANSFER:
03138       reason = "transfer";
03139       break;
03140    }
03141 
03142    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03143       "Queue: %s\r\n"
03144       "Uniqueid: %s\r\n"
03145       "Channel: %s\r\n"
03146       "Member: %s\r\n"
03147       "MemberName: %s\r\n"
03148       "HoldTime: %ld\r\n"
03149       "TalkTime: %ld\r\n"
03150       "Reason: %s\r\n"
03151       "%s",
03152       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03153       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03154       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03155 }

static int set_member_paused ( const char *  queuename,
const char *  interface,
const char *  reason,
int  paused 
) [static]

Definition at line 4212 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_debug, ast_log(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_WARNING, manager_event, member::membername, member::paused, queue_unref(), queues, member::realtime, RESULT_FAILURE, RESULT_SUCCESS, S_OR, and update_realtime_member_field().

Referenced by handle_queue_pause_member(), manager_pause_queue_member(), pqm_exec(), rna(), and upqm_exec().

04213 {
04214    int found = 0;
04215    struct call_queue *q;
04216    struct member *mem;
04217    struct ao2_iterator queue_iter;
04218    int failed;
04219 
04220    /* Special event for when all queues are paused - individual events still generated */
04221    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
04222    if (ast_strlen_zero(queuename))
04223       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04224 
04225    queue_iter = ao2_iterator_init(queues, 0);
04226    while ((q = ao2_iterator_next(&queue_iter))) {
04227       ao2_lock(q);
04228       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04229          if ((mem = interface_exists(q, interface))) {
04230             if (mem->paused == paused) {
04231                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04232             }
04233 
04234             failed = 0;
04235             if (mem->realtime) {
04236                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04237             }
04238          
04239             if (failed) {
04240                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04241                ao2_ref(mem, -1);
04242                ao2_unlock(q);
04243                continue;
04244             }  
04245             found++;
04246             mem->paused = paused;
04247 
04248             if (queue_persistent_members)
04249                dump_queue_members(q);
04250 
04251             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04252             
04253             if (!ast_strlen_zero(reason)) {
04254                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04255                   "Queue: %s\r\n"
04256                   "Location: %s\r\n"
04257                   "MemberName: %s\r\n"
04258                   "Paused: %d\r\n"
04259                   "Reason: %s\r\n",
04260                      q->name, mem->interface, mem->membername, paused, reason);
04261             } else {
04262                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04263                   "Queue: %s\r\n"
04264                   "Location: %s\r\n"
04265                   "MemberName: %s\r\n"
04266                   "Paused: %d\r\n",
04267                      q->name, mem->interface, mem->membername, paused);
04268             }
04269             ao2_ref(mem, -1);
04270          }
04271       }
04272       
04273       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04274          ao2_unlock(q);
04275          queue_unref(q);
04276          break;
04277       }
04278       
04279       ao2_unlock(q);
04280       queue_unref(q);
04281    }
04282 
04283    return found ? RESULT_SUCCESS : RESULT_FAILURE;
04284 }

static int set_member_penalty ( char *  queuename,
char *  interface,
int  penalty 
) [static]

Definition at line 4287 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_log(), ast_queue_log(), ast_strlen_zero(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_ERROR, manager_event, member::penalty, queue_unref(), queues, RESULT_FAILURE, and RESULT_SUCCESS.

Referenced by handle_queue_set_member_penalty(), manager_queue_member_penalty(), and queue_function_memberpenalty_write().

04288 {
04289    int foundinterface = 0, foundqueue = 0;
04290    struct call_queue *q;
04291    struct member *mem;
04292    struct ao2_iterator queue_iter;
04293 
04294    if (penalty < 0) {
04295       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04296       return RESULT_FAILURE;
04297    }
04298 
04299    queue_iter = ao2_iterator_init(queues, 0);
04300    while ((q = ao2_iterator_next(&queue_iter))) {
04301       ao2_lock(q);
04302       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04303          foundqueue++;
04304          if ((mem = interface_exists(q, interface))) {
04305             foundinterface++;
04306             mem->penalty = penalty;
04307             
04308             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04309             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04310                "Queue: %s\r\n"
04311                "Location: %s\r\n"
04312                "Penalty: %d\r\n",
04313                q->name, mem->interface, penalty);
04314             ao2_ref(mem, -1);
04315          }
04316       }
04317       ao2_unlock(q);
04318       queue_unref(q);
04319    }
04320 
04321    if (foundinterface) {
04322       return RESULT_SUCCESS;
04323    } else if (!foundqueue) {
04324       ast_log (LOG_ERROR, "Invalid queuename\n"); 
04325    } else {
04326       ast_log (LOG_ERROR, "Invalid interface\n");
04327    }  
04328 
04329    return RESULT_FAILURE;
04330 }

static void set_queue_result ( struct ast_channel chan,
enum queue_result  res 
) [static]

sets the QUEUESTATUS channel variable

Definition at line 554 of file app_queue.c.

References ARRAY_LEN, pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00555 {
00556    int i;
00557 
00558    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
00559       if (queue_results[i].id == res) {
00560          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00561          return;
00562       }
00563    }
00564 }

static void set_queue_variables ( struct call_queue q,
struct ast_channel chan 
) [static]

Set variables of queue.

Definition at line 616 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, call_queue::holdtime, int2strat(), call_queue::maxlen, call_queue::name, pbx_builtin_setvar_multiple(), call_queue::servicelevel, call_queue::setqueuevar, and call_queue::strategy.

Referenced by end_bridge_callback(), record_abandoned(), and try_calling().

00617 {
00618    char interfacevar[256]="";
00619    float sl = 0;
00620 
00621    if (q->setqueuevar) {
00622       sl = 0;
00623       if (q->callscompleted > 0) 
00624          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
00625 
00626       snprintf(interfacevar, sizeof(interfacevar),
00627          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
00628          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
00629    
00630       pbx_builtin_setvar_multiple(chan, interfacevar); 
00631    }
00632 }

static struct ast_datastore* setup_transfer_datastore ( struct queue_ent qe,
struct member member,
time_t  starttime,
int  callcompletedinsl 
) [static]

create a datastore for storing relevant info to log attended transfers in the queue_log

Definition at line 3225 of file app_queue.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc(), ast_log(), queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, and queue_transfer_info.

Referenced by try_calling().

03226 {
03227    struct ast_datastore *ds;
03228    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03229 
03230    if (!qtds) {
03231       ast_log(LOG_WARNING, "Memory allocation error!\n");
03232       return NULL;
03233    }
03234 
03235    ast_channel_lock(qe->chan);
03236    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03237       ast_channel_unlock(qe->chan);
03238       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03239       return NULL;
03240    }
03241 
03242    qtds->qe = qe;
03243    /* This member is refcounted in try_calling, so no need to add it here, too */
03244    qtds->member = member;
03245    qtds->starttime = starttime;
03246    qtds->callcompletedinsl = callcompletedinsl;
03247    ds->data = qtds;
03248    ast_channel_datastore_add(qe->chan, ds);
03249    ast_channel_unlock(qe->chan);
03250    return ds;
03251 }

static int store_next_lin ( struct queue_ent qe,
struct callattempt outgoing 
) [static]

Search for best metric and add to Linear queue.

Definition at line 2441 of file app_queue.c.

References ast_debug, find_best(), callattempt::interface, queue_ent::linpos, queue_ent::linwrapped, and callattempt::metric.

Referenced by try_calling().

02442 {
02443    struct callattempt *best = find_best(outgoing);
02444 
02445    if (best) {
02446       /* Ring just the best channel */
02447       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02448       qe->linpos = best->metric % 1000;
02449    } else {
02450       /* Just increment rrpos */
02451       if (qe->linwrapped) {
02452          /* No more channels, start over */
02453          qe->linpos = 0;
02454       } else {
02455          /* Prioritize next entry */
02456          qe->linpos++;
02457       }
02458    }
02459    qe->linwrapped = 0;
02460 
02461    return 0;
02462 }

static int store_next_rr ( struct queue_ent qe,
struct callattempt outgoing 
) [static]

Search for best metric and add to Round Robbin queue.

Definition at line 2417 of file app_queue.c.

References ast_debug, find_best(), callattempt::interface, callattempt::metric, queue_ent::parent, call_queue::rrpos, and call_queue::wrapped.

Referenced by try_calling().

02418 {
02419    struct callattempt *best = find_best(outgoing);
02420 
02421    if (best) {
02422       /* Ring just the best channel */
02423       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02424       qe->parent->rrpos = best->metric % 1000;
02425    } else {
02426       /* Just increment rrpos */
02427       if (qe->parent->wrapped) {
02428          /* No more channels, start over */
02429          qe->parent->rrpos = 0;
02430       } else {
02431          /* Prioritize next entry */
02432          qe->parent->rrpos++;
02433       }
02434    }
02435    qe->parent->wrapped = 0;
02436 
02437    return 0;
02438 }

static int strat2int ( const char *  strategy  )  [static]

Definition at line 578 of file app_queue.c.

References ARRAY_LEN, strategies, and strategy::strategy.

Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_queues().

00579 {
00580    int x;
00581 
00582    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00583       if (!strcasecmp(strategy, strategies[x].name))
00584          return strategies[x].strategy;
00585    }
00586 
00587    return -1;
00588 }

static int try_calling ( struct queue_ent qe,
const char *  options,
char *  announceoverride,
const char *  url,
int *  tries,
int *  noption,
const char *  agi,
const char *  macro,
const char *  gosub,
int  ringing 
) [static]

A large function which calls members, updates statistics, and bridges the caller and a member.

Here is the process of this function 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue() 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also during each iteration, we call calc_metric to determine which members should be rung when. 3. Call ring_one to place a call to the appropriate member(s) 4. Call wait_for_answer to wait for an answer. If no one answers, return. 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered. 6. Start the monitor or mixmonitor if the option is set 7. Remove the caller from the queue to allow other callers to advance 8. Bridge the call. 9. Do any post processing after the call has disconnected.

Parameters:
[in] qe the queue_ent structure which corresponds to the caller attempting to reach members
[in] options the options passed as the third parameter to the Queue() application
[in] announceoverride filename to play to user when waiting
[in] url the url passed as the fourth parameter to the Queue() application
[in,out] tries the number of times we have tried calling queue members
[out] noption set if the call to Queue() has the 'n' option set.
[in] agi the agi passed as the fifth parameter to the Queue() application
[in] macro the macro passed as the sixth parameter to the Queue() application
[in] gosub the gosub passed as the seventh parameter to the Queue() application
[in] ringing 1 if the 'r' option is set, otherwise 0

Definition at line 3307 of file app_queue.c.

References ast_channel::_state, AGENT, queue_ent::announce, ao2_alloc, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), asprintf, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CDR_FLAG_LOCKED, ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_datastore_alloc(), ast_datastore_free(), ast_debug, AST_DIGIT_ANY, AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_free, ast_hangup(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_pbx_run_args(), ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), CALLER, member::calls, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, member::dynamic, end_bridge_callback(), end_bridge_callback_data_fixup(), errno, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, member::interface, ast_channel::language, member::lastcall, member::lastqueue, leave_queue(), ast_dialed_interface::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, manager_event, callattempt::member, call_queue::membercount, call_queue::memberdelay, call_queue::membergosub, call_queue::membermacro, member::membername, call_queue::members, call_queue::monfmt, call_queue::montype, call_queue::name, ast_channel::name, queue_ent::opos, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_multiple(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), member::penalty, queue_ent::pending, play_file(), queue_ent::pos, ast_channel::priority, QUEUE_EVENT_VARIABLES, queue_ref(), QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, queue_transfer_info, queues, member::realtime, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), send_agent_complete(), call_queue::servicelevel, set_queue_variables(), call_queue::setinterfacevar, call_queue::setqueueentryvar, setup_transfer_datastore(), call_queue::sound_callerannounce, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_reporthold, call_queue::sound_seconds, queue_ent::start, member::status, store_next_lin(), store_next_rr(), call_queue::strategy, ast_channel::tech, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, ast_channel_tech::type, ast_cdr::uniqueid, ast_channel::uniqueid, update_queue(), vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.

Referenced by queue_exec().

03308 {
03309    struct member *cur;
03310    struct callattempt *outgoing = NULL; /* the list of calls we are building */
03311    int to, orig;
03312    char oldexten[AST_MAX_EXTENSION]="";
03313    char oldcontext[AST_MAX_CONTEXT]="";
03314    char queuename[256]="";
03315    char interfacevar[256]="";
03316    struct ast_channel *peer;
03317    struct ast_channel *which;
03318    struct callattempt *lpeer;
03319    struct member *member;
03320    struct ast_app *application;
03321    int res = 0, bridge = 0;
03322    int numbusies = 0;
03323    int x=0;
03324    char *announce = NULL;
03325    char digit = 0;
03326    time_t callstart;
03327    time_t now = time(NULL);
03328    struct ast_bridge_config bridge_config;
03329    char nondataquality = 1;
03330    char *agiexec = NULL;
03331    char *macroexec = NULL;
03332    char *gosubexec = NULL;
03333    int ret = 0;
03334    const char *monitorfilename;
03335    const char *monitor_exec;
03336    const char *monitor_options;
03337    char tmpid[256], tmpid2[256];
03338    char meid[1024], meid2[1024];
03339    char mixmonargs[1512];
03340    struct ast_app *mixmonapp = NULL;
03341    char *p;
03342    char vars[2048];
03343    int forwardsallowed = 1;
03344    int callcompletedinsl;
03345    struct ao2_iterator memi;
03346    struct ast_datastore *datastore, *transfer_ds;
03347    struct queue_end_bridge *queue_end_bridge = NULL;
03348 
03349    ast_channel_lock(qe->chan);
03350    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03351    ast_channel_unlock(qe->chan);
03352 
03353    memset(&bridge_config, 0, sizeof(bridge_config));
03354    tmpid[0] = 0;
03355    meid[0] = 0;
03356    time(&now);
03357 
03358    /* If we've already exceeded our timeout, then just stop
03359     * This should be extremely rare. queue_exec will take care
03360     * of removing the caller and reporting the timeout as the reason.
03361     */
03362    if (qe->expire && now >= qe->expire) {
03363       res = 0;
03364       goto out;
03365    }
03366       
03367    for (; options && *options; options++)
03368       switch (*options) {
03369       case 't':
03370          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03371          break;
03372       case 'T':
03373          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03374          break;
03375       case 'w':
03376          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03377          break;
03378       case 'W':
03379          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03380          break;
03381       case 'c':
03382          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
03383          break;
03384       case 'd':
03385          nondataquality = 0;
03386          break;
03387       case 'h':
03388          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03389          break;
03390       case 'H':
03391          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03392          break;
03393       case 'k':
03394          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
03395          break;
03396       case 'K':
03397          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
03398          break;
03399       case 'n':
03400          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03401             (*tries)++;
03402          else
03403             *tries = qe->parent->membercount;
03404          *noption = 1;
03405          break;
03406       case 'i':
03407          forwardsallowed = 0;
03408          break;
03409       case 'x':
03410          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
03411          break;
03412       case 'X':
03413          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
03414          break;
03415 
03416       }
03417 
03418    /* Hold the lock while we setup the outgoing calls */
03419    if (use_weight)
03420       ao2_lock(queues);
03421    ao2_lock(qe->parent);
03422    ast_debug(1, "%s is trying to call a queue member.\n",
03423                      qe->chan->name);
03424    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03425    if (!ast_strlen_zero(qe->announce))
03426       announce = qe->announce;
03427    if (!ast_strlen_zero(announceoverride))
03428       announce = announceoverride;
03429 
03430    memi = ao2_iterator_init(qe->parent->members, 0);
03431    while ((cur = ao2_iterator_next(&memi))) {
03432       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03433       struct ast_dialed_interface *di;
03434       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03435       if (!tmp) {
03436          ao2_ref(cur, -1);
03437          ao2_unlock(qe->parent);
03438          if (use_weight)
03439             ao2_unlock(queues);
03440          goto out;
03441       }
03442       if (!datastore) {
03443          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
03444             ao2_ref(cur, -1);
03445             ao2_unlock(qe->parent);
03446             if (use_weight)
03447                ao2_unlock(queues);
03448             free(tmp);
03449             goto out;
03450          }
03451          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03452          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03453             ao2_ref(cur, -1);
03454             ao2_unlock(&qe->parent);
03455             if (use_weight)
03456                ao2_unlock(queues);
03457             free(tmp);
03458             goto out;
03459          }
03460          datastore->data = dialed_interfaces;
03461          AST_LIST_HEAD_INIT(dialed_interfaces);
03462 
03463          ast_channel_lock(qe->chan);
03464          ast_channel_datastore_add(qe->chan, datastore);
03465          ast_channel_unlock(qe->chan);
03466       } else
03467          dialed_interfaces = datastore->data;
03468 
03469       AST_LIST_LOCK(dialed_interfaces);
03470       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03471          if (!strcasecmp(cur->interface, di->interface)) {
03472             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
03473                di->interface);
03474             break;
03475          }
03476       }
03477       AST_LIST_UNLOCK(dialed_interfaces);
03478       
03479       if (di) {
03480          free(tmp);
03481          continue;
03482       }
03483 
03484       /* It is always ok to dial a Local interface.  We only keep track of
03485        * which "real" interfaces have been dialed.  The Local channel will
03486        * inherit this list so that if it ends up dialing a real interface,
03487        * it won't call one that has already been called. */
03488       if (strncasecmp(cur->interface, "Local/", 6)) {
03489          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03490             ao2_ref(cur, -1);
03491             ao2_unlock(qe->parent);
03492             if (use_weight)
03493                ao2_unlock(queues);
03494             free(tmp);
03495             goto out;
03496          }
03497          strcpy(di->interface, cur->interface);
03498 
03499          AST_LIST_LOCK(dialed_interfaces);
03500          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03501          AST_LIST_UNLOCK(dialed_interfaces);
03502       }
03503 
03504       tmp->stillgoing = -1;
03505       tmp->member = cur;
03506       tmp->oldstatus = cur->status;
03507       tmp->lastcall = cur->lastcall;
03508       tmp->lastqueue = cur->lastqueue;
03509       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03510       /* Special case: If we ring everyone, go ahead and ring them, otherwise
03511          just calculate their metric for the appropriate strategy */
03512       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03513          /* Put them in the list of outgoing thingies...  We're ready now.
03514             XXX If we're forcibly removed, these outgoing calls won't get
03515             hung up XXX */
03516          tmp->q_next = outgoing;
03517          outgoing = tmp;      
03518          /* If this line is up, don't try anybody else */
03519          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03520             break;
03521       } else {
03522          ao2_ref(cur, -1);
03523          ast_free(tmp);
03524       }
03525    }
03526 
03527    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
03528       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
03529       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03530          to = (qe->expire - now) * 1000;
03531       else
03532          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03533    } else {
03534       /* Config timeout is higher priority thatn application timeout */
03535       if (qe->expire && qe->expire<=now) {
03536          to = 0;
03537       } else if (qe->parent->timeout) {
03538          to = qe->parent->timeout * 1000;
03539       } else {
03540          to = -1;
03541       }
03542    }
03543    orig = to;
03544    ++qe->pending;
03545    ao2_unlock(qe->parent);
03546    ring_one(qe, outgoing, &numbusies);
03547    if (use_weight)
03548       ao2_unlock(queues);
03549    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03550    /* The ast_channel_datastore_remove() function could fail here if the
03551     * datastore was moved to another channel during a masquerade. If this is
03552     * the case, don't free the datastore here because later, when the channel
03553     * to which the datastore was moved hangs up, it will attempt to free this
03554     * datastore again, causing a crash
03555     */
03556    ast_channel_lock(qe->chan);
03557    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03558       ast_datastore_free(datastore);
03559    }
03560    ast_channel_unlock(qe->chan);
03561    ao2_lock(qe->parent);
03562    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03563       store_next_rr(qe, outgoing);
03564    }
03565    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03566       store_next_lin(qe, outgoing);
03567    }
03568    ao2_unlock(qe->parent);
03569    peer = lpeer ? lpeer->chan : NULL;
03570    if (!peer) {
03571       qe->pending = 0;
03572       if (to) {
03573          /* Must gotten hung up */
03574          res = -1;
03575       } else {
03576          /* User exited by pressing a digit */
03577          res = digit;
03578       }
03579       if (res == -1)
03580          ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
03581    } else { /* peer is valid */
03582       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
03583          we will always return with -1 so that it is hung up properly after the
03584          conversation.  */
03585       if (!strcmp(qe->chan->tech->type, "DAHDI"))
03586          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03587       if (!strcmp(peer->tech->type, "DAHDI"))
03588          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03589       /* Update parameters for the queue */
03590       time(&now);
03591       recalc_holdtime(qe, (now - qe->start));
03592       ao2_lock(qe->parent);
03593       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03594       ao2_unlock(qe->parent);
03595       member = lpeer->member;
03596       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
03597       ao2_ref(member, 1);
03598       hangupcalls(outgoing, peer);
03599       outgoing = NULL;
03600       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03601          int res2;
03602 
03603          res2 = ast_autoservice_start(qe->chan);
03604          if (!res2) {
03605             if (qe->parent->memberdelay) {
03606                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03607                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03608             }
03609             if (!res2 && announce) {
03610                play_file(peer, announce);
03611             }
03612             if (!res2 && qe->parent->reportholdtime) {
03613                if (!play_file(peer, qe->parent->sound_reporthold)) {
03614                   int holdtime, holdtimesecs;
03615 
03616                   time(&now);
03617                   holdtime = abs((now - qe->start) / 60);
03618                   holdtimesecs = abs((now - qe->start));
03619                   if (holdtime == 1) {
03620                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03621                      play_file(peer, qe->parent->sound_minute);
03622                   } else {
03623                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03624                      play_file(peer, qe->parent->sound_minutes);
03625                   }
03626                   if (holdtimesecs > 1) {
03627                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03628                      play_file(peer, qe->parent->sound_seconds);
03629                   }
03630                }
03631             }
03632          }
03633          res2 |= ast_autoservice_stop(qe->chan);
03634          if (ast_check_hangup(peer)) {
03635             /* Agent must have hung up */
03636             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03637             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03638             if (qe->parent->eventwhencalled)
03639                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03640                      "Queue: %s\r\n"
03641                      "Uniqueid: %s\r\n"
03642                      "Channel: %s\r\n"
03643                      "Member: %s\r\n"
03644                      "MemberName: %s\r\n"
03645                      "%s",
03646                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03647                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03648             ast_hangup(peer);
03649             ao2_ref(member, -1);
03650             goto out;
03651          } else if (res2) {
03652             /* Caller must have hung up just before being connected*/
03653             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03654             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03655             record_abandoned(qe);
03656             ast_hangup(peer);
03657             ao2_ref(member, -1);
03658             return -1;
03659          }
03660       }
03661       /* Stop music on hold */
03662       if (ringing)
03663          ast_indicate(qe->chan,-1);
03664       else
03665          ast_moh_stop(qe->chan);
03666       /* If appropriate, log that we have a destination channel */
03667       if (qe->chan->cdr)
03668          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03669       /* Make sure channels are compatible */
03670       res = ast_channel_make_compatible(qe->chan, peer);
03671       if (res < 0) {
03672          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03673          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03674          record_abandoned(qe);
03675          ast_hangup(peer);
03676          ao2_ref(member, -1);
03677          return -1;
03678       }
03679 
03680       /* Play announcement to the caller telling it's his turn if defined */
03681       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
03682          if (play_file(qe->chan, qe->parent->sound_callerannounce))
03683             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
03684       }
03685 
03686       ao2_lock(qe->parent);
03687       /* if setinterfacevar is defined, make member variables available to the channel */
03688       /* use  pbx_builtin_setvar to set a load of variables with one call */
03689       if (qe->parent->setinterfacevar) {
03690          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
03691             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
03692          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03693          pbx_builtin_setvar_multiple(peer, interfacevar);
03694       }
03695       
03696       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
03697       /* use  pbx_builtin_setvar to set a load of variables with one call */
03698       if (qe->parent->setqueueentryvar) {
03699          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
03700             (long) time(NULL) - qe->start, qe->opos);
03701          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03702          pbx_builtin_setvar_multiple(peer, interfacevar);
03703       }
03704    
03705       /* try to set queue variables if configured to do so*/
03706       set_queue_variables(qe->parent, qe->chan);
03707       set_queue_variables(qe->parent, peer);
03708       ao2_unlock(qe->parent);
03709       
03710       ast_channel_lock(qe->chan);
03711       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
03712             monitorfilename = ast_strdupa(monitorfilename);
03713       }
03714       ast_channel_unlock(qe->chan);
03715       /* Begin Monitoring */
03716       if (qe->parent->monfmt && *qe->parent->monfmt) {
03717          if (!qe->parent->montype) {
03718             const char *monexec, *monargs;
03719             ast_debug(1, "Starting Monitor as requested.\n");
03720             ast_channel_lock(qe->chan);
03721             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
03722                which = qe->chan;
03723                monexec = monexec ? ast_strdupa(monexec) : NULL;
03724             }
03725             else
03726                which = peer;
03727             ast_channel_unlock(qe->chan);
03728             if (monitorfilename)
03729                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
03730             else if (qe->chan->cdr)
03731                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
03732             else {
03733                /* Last ditch effort -- no CDR, make up something */
03734                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03735                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
03736             }
03737             if (!ast_strlen_zero(monexec)) {
03738                ast_monitor_setjoinfiles(which, 1);
03739             }
03740          } else {
03741             mixmonapp = pbx_findapp("MixMonitor");
03742             
03743             if (mixmonapp) {
03744                ast_debug(1, "Starting MixMonitor as requested.\n");
03745                if (!monitorfilename) {
03746                   if (qe->chan->cdr)
03747                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
03748                   else
03749                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03750                } else {
03751                   const char *m = monitorfilename;
03752                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
03753                      switch (*m) {
03754                      case '^':
03755                         if (*(m + 1) == '{')
03756                            *p = '$';
03757                         break;
03758                      case ',':
03759                         *p++ = '\\';
03760                         /* Fall through */
03761                      default:
03762                         *p = *m;
03763                      }
03764                      if (*m == '\0')
03765                         break;
03766                   }
03767                   if (p == tmpid2 + sizeof(tmpid2))
03768                      tmpid2[sizeof(tmpid2) - 1] = '\0';
03769 
03770                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03771                }
03772 
03773                ast_channel_lock(qe->chan);
03774                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
03775                      monitor_exec = ast_strdupa(monitor_exec);
03776                }
03777                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
03778                      monitor_options = ast_strdupa(monitor_options);
03779                } else {
03780                   monitor_options = "";
03781                }
03782                ast_channel_unlock(qe->chan);
03783 
03784                if (monitor_exec) {
03785                   const char *m = monitor_exec;
03786                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
03787                      switch (*m) {
03788                      case '^':
03789                         if (*(m + 1) == '{')
03790                            *p = '$';
03791                         break;
03792                      case ',':
03793                         *p++ = '\\';
03794                         /* Fall through */
03795                      default:
03796                         *p = *m;
03797                      }
03798                      if (*m == '\0')
03799                         break;
03800                   }
03801                   if (p == meid2 + sizeof(meid2))
03802                      meid2[sizeof(meid2) - 1] = '\0';
03803 
03804                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03805                }
03806    
03807                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
03808 
03809                if (!ast_strlen_zero(monitor_exec))
03810                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
03811                else
03812                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
03813                
03814                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03815                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03816                if (qe->chan->cdr)
03817                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03818                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03819                if (qe->chan->cdr)
03820                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03821 
03822             } else {
03823                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03824             }
03825          }
03826       }
03827       /* Drop out of the queue at this point, to prepare for next caller */
03828       leave_queue(qe);        
03829       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03830          ast_debug(1, "app_queue: sendurl=%s.\n", url);
03831          ast_channel_sendurl(peer, url);
03832       }
03833       
03834       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
03835       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
03836       if (!ast_strlen_zero(macro)) {
03837             macroexec = ast_strdupa(macro);
03838       } else {
03839          if (qe->parent->membermacro)
03840             macroexec = ast_strdupa(qe->parent->membermacro);
03841       }
03842 
03843       if (!ast_strlen_zero(macroexec)) {
03844          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
03845          
03846          res = ast_autoservice_start(qe->chan);
03847          if (res) {
03848             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03849             res = -1;
03850          }
03851          
03852          application = pbx_findapp("Macro");
03853 
03854          if (application) {
03855             res = pbx_exec(peer, application, macroexec);
03856             ast_debug(1, "Macro exited with status %d\n", res);
03857             res = 0;
03858          } else {
03859             ast_log(LOG_ERROR, "Could not find application Macro\n");
03860             res = -1;
03861          }
03862 
03863          if (ast_autoservice_stop(qe->chan) < 0) {
03864             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03865             res = -1;
03866          }
03867       }
03868 
03869       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
03870       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
03871       if (!ast_strlen_zero(gosub)) {
03872             gosubexec = ast_strdupa(gosub);
03873       } else {
03874          if (qe->parent->membergosub)
03875             gosubexec = ast_strdupa(qe->parent->membergosub);
03876       }
03877 
03878       if (!ast_strlen_zero(gosubexec)) {
03879          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
03880          
03881          res = ast_autoservice_start(qe->chan);
03882          if (res) {
03883             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03884             res = -1;
03885          }
03886          
03887          application = pbx_findapp("Gosub");
03888          
03889          if (application) {
03890             char *gosub_args, *gosub_argstart;
03891 
03892             /* Set where we came from */
03893             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
03894             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
03895             peer->priority = 0;
03896 
03897             gosub_argstart = strchr(gosubexec, ',');
03898             if (gosub_argstart) {
03899                *gosub_argstart = 0;
03900                if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
03901                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03902                   gosub_args = NULL;
03903                }
03904                *gosub_argstart = ',';
03905             } else {
03906                if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
03907                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03908                   gosub_args = NULL;
03909                }
03910             }
03911             if (gosub_args) {
03912                res = pbx_exec(peer, application, gosub_args);
03913                if (!res) {
03914                   struct ast_pbx_args args;
03915                   memset(&args, 0, sizeof(args));
03916                   args.no_hangup_chan = 1;
03917                   ast_pbx_run_args(peer, &args);
03918                }
03919                ast_free(gosub_args);
03920                ast_debug(1, "Gosub exited with status %d\n", res);
03921             } else {
03922                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
03923             }
03924          } else {
03925             ast_log(LOG_ERROR, "Could not find application Gosub\n");
03926             res = -1;
03927          }
03928       
03929          if (ast_autoservice_stop(qe->chan) < 0) {
03930             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03931             res = -1;
03932          }
03933       }
03934 
03935       if (!ast_strlen_zero(agi)) {
03936          ast_debug(1, "app_queue: agi=%s.\n", agi);
03937          application = pbx_findapp("agi");
03938          if (application) {
03939             agiexec = ast_strdupa(agi);
03940             ret = pbx_exec(qe->chan, application, agiexec);
03941          } else
03942             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03943       }
03944       qe->handled++;
03945       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
03946                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
03947       if (update_cdr && qe->chan->cdr) 
03948          ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
03949       if (qe->parent->eventwhencalled)
03950          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03951                "Queue: %s\r\n"
03952                "Uniqueid: %s\r\n"
03953                "Channel: %s\r\n"
03954                "Member: %s\r\n"
03955                "MemberName: %s\r\n"
03956                "Holdtime: %ld\r\n"
03957                "BridgedChannel: %s\r\n"
03958                "Ringtime: %ld\r\n"
03959                "%s",
03960                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03961                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
03962                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03963       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03964       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03965    
03966       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
03967          queue_end_bridge->q = qe->parent;
03968          queue_end_bridge->chan = qe->chan;
03969          bridge_config.end_bridge_callback = end_bridge_callback;
03970          bridge_config.end_bridge_callback_data = queue_end_bridge;
03971          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
03972          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
03973           * to make sure to increase the refcount of this queue so it cannot be freed until we
03974           * are done with it. We remove this reference in end_bridge_callback.
03975           */
03976          queue_ref(qe->parent);
03977       }
03978 
03979       time(&callstart);
03980       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03981       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03982 
03983       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
03984        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
03985        */
03986       ast_channel_lock(qe->chan);
03987       if (!attended_transfer_occurred(qe->chan)) {
03988          struct ast_datastore *tds;
03989          if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
03990             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03991                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03992                (long) (time(NULL) - callstart), qe->opos);
03993             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
03994          } else if (ast_check_hangup(qe->chan)) {
03995             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03996                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03997             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
03998          } else {
03999             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04000                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04001             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04002          }
04003          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
04004             ast_channel_datastore_remove(qe->chan, tds);
04005          }
04006          update_queue(qe->parent, member, callcompletedinsl);
04007       }
04008 
04009       if (transfer_ds) {
04010          ast_datastore_free(transfer_ds);
04011       }
04012       ast_channel_unlock(qe->chan);
04013       ast_hangup(peer);
04014       res = bridge ? bridge : 1;
04015       ao2_ref(member, -1);
04016    }
04017 out:
04018    hangupcalls(outgoing, NULL);
04019 
04020    return res;
04021 }

static int unload_module ( void   )  [static]

Definition at line 6620 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, ast_cli_unregister_multiple(), ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), ast_custom_function_unregister(), ast_event_unsubscribe(), ast_manager_unregister(), ast_taskprocessor_unreference(), ast_unload_realtime(), ast_unregister_application(), clear_and_free_interfaces(), cli_queue, device_state_sub, devicestate_tps, queue_unref(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, and queuewaitingcount_function.

06621 {
06622    int res;
06623    struct ast_context *con;
06624    struct ao2_iterator q_iter;
06625    struct call_queue *q = NULL;
06626 
06627    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06628    res = ast_manager_unregister("QueueStatus");
06629    res |= ast_manager_unregister("Queues");
06630    res |= ast_manager_unregister("QueueRule");
06631    res |= ast_manager_unregister("QueueSummary");
06632    res |= ast_manager_unregister("QueueAdd");
06633    res |= ast_manager_unregister("QueueRemove");
06634    res |= ast_manager_unregister("QueuePause");
06635    res |= ast_manager_unregister("QueueLog");
06636    res |= ast_manager_unregister("QueuePenalty");
06637    res |= ast_unregister_application(app_aqm);
06638    res |= ast_unregister_application(app_rqm);
06639    res |= ast_unregister_application(app_pqm);
06640    res |= ast_unregister_application(app_upqm);
06641    res |= ast_unregister_application(app_ql);
06642    res |= ast_unregister_application(app);
06643    res |= ast_custom_function_unregister(&queuevar_function);
06644    res |= ast_custom_function_unregister(&queuemembercount_function);
06645    res |= ast_custom_function_unregister(&queuemembercount_dep);
06646    res |= ast_custom_function_unregister(&queuememberlist_function);
06647    res |= ast_custom_function_unregister(&queuewaitingcount_function);
06648    res |= ast_custom_function_unregister(&queuememberpenalty_function);
06649 
06650    if (device_state_sub)
06651       ast_event_unsubscribe(device_state_sub);
06652 
06653    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06654       ast_context_remove_extension2(con, "s", 1, NULL, 0);
06655       ast_context_destroy(con, "app_queue"); /* leave no trace */
06656    }
06657 
06658    clear_and_free_interfaces();
06659 
06660    q_iter = ao2_iterator_init(queues, 0);
06661    while ((q = ao2_iterator_next(&q_iter))) {
06662       ao2_unlink(queues, q);
06663       queue_unref(q);
06664    }
06665    ao2_ref(queues, -1);
06666    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
06667    ast_unload_realtime("queue_members");
06668    return res;
06669 }

static void update_qe_rule ( struct queue_ent qe  )  [static]

update rules for queues

Calculate min/max penalties making sure if relative they stay within bounds. Update queues penalty and set dialplan vars, goto next list entry.

Definition at line 2874 of file app_queue.c.

References ast_debug, AST_LIST_NEXT, queue_ent::chan, queue_ent::max_penalty, penalty_rule::max_relative, penalty_rule::max_value, queue_ent::min_penalty, penalty_rule::min_relative, penalty_rule::min_value, ast_channel::name, pbx_builtin_setvar_helper(), queue_ent::pr, and penalty_rule::time.

Referenced by join_queue(), queue_exec(), and wait_our_turn().

02875 {
02876    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02877    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02878    char max_penalty_str[20], min_penalty_str[20]; 
02879    /* a relative change to the penalty could put it below 0 */
02880    if (max_penalty < 0)
02881       max_penalty = 0;
02882    if (min_penalty < 0)
02883       min_penalty = 0;
02884    if (min_penalty > max_penalty)
02885       min_penalty = max_penalty;
02886    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02887    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02888    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02889    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02890    qe->max_penalty = max_penalty;
02891    qe->min_penalty = min_penalty;
02892    ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
02893    qe->pr = AST_LIST_NEXT(qe->pr, list);
02894 }

static int update_queue ( struct call_queue q,
struct member member,
int  callcompletedinsl 
) [static]

update the queue status

Return values:
Always 0

Definition at line 3015 of file app_queue.c.

References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, OBJ_POINTER, and queues.

Referenced by queue_transfer_fixup(), and try_calling().

03016 {
03017    struct member *mem;
03018    struct call_queue *qtmp;
03019    struct ao2_iterator queue_iter;  
03020    
03021    if (shared_lastcall) {
03022       queue_iter = ao2_iterator_init(queues, 0);
03023       while ((qtmp = ao2_iterator_next(&queue_iter))) {
03024          ao2_lock(qtmp);
03025          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03026             time(&mem->lastcall);
03027             mem->calls++;
03028             mem->lastqueue = q;
03029             ao2_ref(mem, -1);
03030          }
03031          ao2_unlock(qtmp);
03032          ao2_ref(qtmp, -1);
03033       }
03034    } else {
03035       ao2_lock(q);
03036       time(&member->lastcall);
03037       member->calls++;
03038       member->lastqueue = q;
03039       ao2_unlock(q);
03040    }  
03041    ao2_lock(q);
03042    q->callscompleted++;
03043    if (callcompletedinsl)
03044       q->callscompletedinsl++;
03045    ao2_unlock(q);
03046    return 0;
03047 }

static int update_realtime_member_field ( struct member mem,
const char *  queue_name,
const char *  field,
const char *  value 
) [static]

Definition at line 1638 of file app_queue.c.

References ast_strlen_zero(), ast_update_realtime(), member::rt_uniqueid, and SENTINEL.

Referenced by set_member_paused().

01639 {
01640    int ret = -1;
01641 
01642    if (ast_strlen_zero(mem->rt_uniqueid))
01643       return ret;
01644 
01645    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
01646       ret = 0;
01647 
01648    return ret;
01649 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1652 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_variable_retrieve(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, queues, member::realtime, remove_from_interfaces(), rt_handle_member_record(), S_OR, SENTINEL, and member::state_interface.

Referenced by load_realtime_queue(), and queue_exec().

01653 {
01654    struct ast_config *member_config = NULL;
01655    struct member *m;
01656    char *interface = NULL;
01657    struct ao2_iterator mem_iter;
01658 
01659    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
01660       /*This queue doesn't have realtime members*/
01661       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
01662       return;
01663    }
01664 
01665    ao2_lock(queues);
01666    ao2_lock(q);
01667    
01668    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01669    mem_iter = ao2_iterator_init(q->members, 0);
01670    while ((m = ao2_iterator_next(&mem_iter))) {
01671       if (m->realtime)
01672          m->dead = 1;
01673       ao2_ref(m, -1);
01674    }
01675 
01676    while ((interface = ast_category_browse(member_config, interface))) {
01677       rt_handle_member_record(q, interface,
01678          ast_variable_retrieve(member_config, interface, "uniqueid"),
01679          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01680          ast_variable_retrieve(member_config, interface, "penalty"),
01681          ast_variable_retrieve(member_config, interface, "paused"),
01682          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01683    }
01684 
01685    /* Delete all realtime members that have been deleted in DB. */
01686    mem_iter = ao2_iterator_init(q->members, 0);
01687    while ((m = ao2_iterator_next(&mem_iter))) {
01688       if (m->dead) {
01689          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01690          ao2_unlink(q->members, m);
01691          remove_from_interfaces(m->state_interface, 0);
01692          q->membercount--;
01693       }
01694       ao2_ref(m, -1);
01695    }
01696    ao2_unlock(q);
01697    ao2_unlock(queues);
01698    ast_config_destroy(member_config);
01699 }

static int update_status ( const char *  interface,
const int  status 
) [static]

set a member's status based on device state of that member's state_interface.

Lock interface list find sc, iterate through each queues queue_member list for member to update state inside queues

Definition at line 714 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_strdupa, member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, manager_event, member::membername, member::paused, member::penalty, queue_unref(), queues, member::realtime, member::state_interface, and member::status.

Referenced by handle_statechange(), and ring_entry().

00715 {
00716    struct member *cur;
00717    struct ao2_iterator mem_iter, queue_iter;
00718    struct call_queue *q;
00719 
00720    queue_iter = ao2_iterator_init(queues, 0);
00721    while ((q = ao2_iterator_next(&queue_iter))) {
00722       ao2_lock(q);
00723       mem_iter = ao2_iterator_init(q->members, 0);
00724       while ((cur = ao2_iterator_next(&mem_iter))) {
00725          char *tmp_interface;
00726          char *slash_pos;
00727          tmp_interface = ast_strdupa(cur->state_interface);
00728          if ((slash_pos = strchr(tmp_interface, '/')))
00729             if (!strncasecmp(tmp_interface, "Local", 5) && (slash_pos = strchr(slash_pos + 1, '/')))
00730                *slash_pos = '\0';
00731 
00732          if (strcasecmp(interface, tmp_interface)) {
00733             ao2_ref(cur, -1);
00734             continue;
00735          }
00736 
00737          if (cur->status != status) {
00738             cur->status = status;
00739             if (q->maskmemberstatus) {
00740                ao2_ref(cur, -1);
00741                continue;
00742             }
00743 
00744             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00745                "Queue: %s\r\n"
00746                "Location: %s\r\n"
00747                "MemberName: %s\r\n"
00748                "Membership: %s\r\n"
00749                "Penalty: %d\r\n"
00750                "CallsTaken: %d\r\n"
00751                "LastCall: %d\r\n"
00752                "Status: %d\r\n"
00753                "Paused: %d\r\n",
00754                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00755                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00756          }
00757          ao2_ref(cur, -1);
00758       }
00759       queue_unref(q);
00760       ao2_unlock(q);
00761    }
00762 
00763    return 0;
00764 }

static int upqm_exec ( struct ast_channel chan,
void *  data 
) [static]

UnPauseQueueMember application.

Definition at line 4500 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

04501 {
04502    char *parse;
04503    AST_DECLARE_APP_ARGS(args,
04504       AST_APP_ARG(queuename);
04505       AST_APP_ARG(interface);
04506       AST_APP_ARG(options);
04507       AST_APP_ARG(reason);
04508    );
04509 
04510    if (ast_strlen_zero(data)) {
04511       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04512       return -1;
04513    }
04514 
04515    parse = ast_strdupa(data);
04516 
04517    AST_STANDARD_APP_ARGS(args, parse);
04518 
04519    if (ast_strlen_zero(args.interface)) {
04520       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04521       return -1;
04522    }
04523 
04524    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04525       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04526       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04527       return 0;
04528    }
04529 
04530    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04531 
04532    return 0;
04533 }

static int valid_exit ( struct queue_ent qe,
char  digit 
) [static]

Check for valid exit from queue via goto.

Return values:
0 if failure
1 if successful

Definition at line 1805 of file app_queue.c.

References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::context, queue_ent::digits, and queue_ent::valid_digits.

Referenced by say_periodic_announcement(), say_position(), wait_a_bit(), and wait_our_turn().

01806 {
01807    int digitlen = strlen(qe->digits);
01808 
01809    /* Prevent possible buffer overflow */
01810    if (digitlen < sizeof(qe->digits) - 2) {
01811       qe->digits[digitlen] = digit;
01812       qe->digits[digitlen + 1] = '\0';
01813    } else {
01814       qe->digits[0] = '\0';
01815       return 0;
01816    }
01817 
01818    /* If there's no context to goto, short-circuit */
01819    if (ast_strlen_zero(qe->context))
01820       return 0;
01821 
01822    /* If the extension is bad, then reset the digits to blank */
01823    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01824       qe->digits[0] = '\0';
01825       return 0;
01826    }
01827 
01828    /* We have an exact match */
01829    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01830       qe->valid_digits = 1;
01831       /* Return 1 on a successful goto */
01832       return 1;
01833    }
01834 
01835    return 0;
01836 }

static char* vars2manager ( struct ast_channel chan,
char *  vars,
size_t  len 
) [static]

convert "\n" to "\nVariable: " ready for manager to use

Definition at line 2167 of file app_queue.c.

References ast_copy_string(), ast_str_alloca, buf, queue_ent::chan, and pbx_builtin_serialize_variables().

Referenced by ring_entry(), send_agent_complete(), and try_calling().

02168 {
02169    struct ast_str *buf = ast_str_alloca(len + 1);
02170    char *tmp;
02171 
02172    if (pbx_builtin_serialize_variables(chan, &buf)) {
02173       int i, j;
02174 
02175       /* convert "\n" to "\nVariable: " */
02176       strcpy(vars, "Variable: ");
02177       tmp = buf->str;
02178 
02179       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02180          vars[j] = tmp[i];
02181 
02182          if (tmp[i + 1] == '\0')
02183             break;
02184          if (tmp[i] == '\n') {
02185             vars[j++] = '\r';
02186             vars[j++] = '\n';
02187 
02188             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02189             j += 9;
02190          }
02191       }
02192       if (j > len - 3)
02193          j = len - 3;
02194       vars[j++] = '\r';
02195       vars[j++] = '\n';
02196       vars[j] = '\0';
02197    } else {
02198       /* there are no channel variables; leave it blank */
02199       *vars = '\0';
02200    }
02201    return vars;
02202 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 4023 of file app_queue.c.

References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().

Referenced by queue_exec().

04024 {
04025    /* Don't need to hold the lock while we setup the outgoing calls */
04026    int retrywait = qe->parent->retry * 1000;
04027 
04028    int res = ast_waitfordigit(qe->chan, retrywait);
04029    if (res > 0 && !valid_exit(qe, res))
04030       res = 0;
04031 
04032    return res;
04033 }

static struct callattempt* wait_for_answer ( struct queue_ent qe,
struct callattempt outgoing,
int *  to,
char *  digit,
int  prebusies,
int  caller_disconnect,
int  forwardsallowed 
) [static]

Wait for a member to answer the call.

Parameters:
[in] qe the queue_ent corresponding to the caller in the queue
[in] outgoing the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
[in] to the amount of time (in milliseconds) to wait for a response
[out] digit if a user presses a digit to exit the queue, this is the digit the caller pressed
[in] prebusies number of busy members calculated prior to calling wait_for_answer
[in] caller_disconnect if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
[in] forwardsallowed used to detect if we should allow call forwarding, based on the 'i' option to Queue()

Definition at line 2574 of file app_queue.c.

References AST_MAX_WATCHERS, ast_poll_channel_add(), callattempt::call_next, callattempt::chan, queue_ent::chan, f, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), starttime, status, callattempt::stillgoing, and call_queue::strategy.

02575 {
02576    const char *queue = qe->parent->name;
02577    struct callattempt *o, *start = NULL, *prev = NULL;
02578    int status;
02579    int numbusies = prebusies;
02580    int numnochan = 0;
02581    int stillgoing = 0;
02582    int orig = *to;
02583    struct ast_frame *f;
02584    struct callattempt *peer = NULL;
02585    struct ast_channel *winner;
02586    struct ast_channel *in = qe->chan;
02587    char on[80] = "";
02588    char membername[80] = "";
02589    long starttime = 0;
02590    long endtime = 0;
02591 #ifdef HAVE_EPOLL
02592    struct callattempt *epollo;
02593 #endif
02594 
02595    starttime = (long) time(NULL);
02596 #ifdef HAVE_EPOLL
02597    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02598       if (epollo->chan)
02599          ast_poll_channel_add(in, epollo->chan);
02600    }
02601 #endif
02602    
02603    while (*to && !peer) {
02604       int numlines, retry, pos = 1;
02605       struct ast_channel *watchers[AST_MAX_WATCHERS];
02606       watchers[0] = in;
02607       start = NULL;
02608 
02609       for (retry = 0; retry < 2; retry++) {
02610          numlines = 0;
02611          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02612             if (o->stillgoing) { /* Keep track of important channels */
02613                stillgoing = 1;
02614                if (o->chan) {
02615                   watchers[pos++] = o->chan;
02616                   if (!start)
02617                      start = o;
02618                   else
02619                      prev->call_next = o;
02620                   prev = o;
02621                }
02622             }
02623             numlines++;
02624          }
02625          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02626             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02627             break;
02628          /* On "ringall" strategy we only move to the next penalty level
02629             when *all* ringing phones are done in the current penalty level */
02630          ring_one(qe, outgoing, &numbusies);
02631          /* and retry... */
02632       }
02633       if (pos == 1 /* not found */) {
02634          if (numlines == (numbusies + numnochan)) {
02635             ast_debug(1, "Everyone is busy at this time\n");
02636          } else {
02637             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02638          }
02639          *to = 0;
02640          return NULL;
02641       }
02642       winner = ast_waitfor_n(watchers, pos, to);
02643       for (o = start; o; o = o->call_next) {
02644          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02645             if (!peer) {
02646                ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02647                peer = o;
02648             }
02649          } else if (o->chan && (o->chan == winner)) {
02650 
02651             ast_copy_string(on, o->member->interface, sizeof(on));
02652             ast_copy_string(membername, o->member->membername, sizeof(membername));
02653 
02654             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02655                ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02656                numnochan++;
02657                do_hang(o);
02658                winner = NULL;
02659                continue;
02660             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02661                char tmpchan[256];
02662                char *stuff;
02663                char *tech;
02664 
02665                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02666                if ((stuff = strchr(tmpchan, '/'))) {
02667                   *stuff++ = '\0';
02668                   tech = tmpchan;
02669                } else {
02670                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02671                   stuff = tmpchan;
02672                   tech = "Local";
02673                }
02674                /* Before processing channel, go ahead and check for forwarding */
02675                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02676                /* Setup parameters */
02677                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02678                if (!o->chan) {
02679                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02680                   o->stillgoing = 0;
02681                   numnochan++;
02682                } else {
02683                   ast_channel_inherit_variables(in, o->chan);
02684                   ast_channel_datastore_inherit(in, o->chan);
02685                   if (o->chan->cid.cid_num)
02686                      ast_free(o->chan->cid.cid_num);
02687                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02688 
02689                   if (o->chan->cid.cid_name)
02690                      ast_free(o->chan->cid.cid_name);
02691                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02692 
02693                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02694                   o->chan->cdrflags = in->cdrflags;
02695 
02696                   if (in->cid.cid_ani) {
02697                      if (o->chan->cid.cid_ani)
02698                         ast_free(o->chan->cid.cid_ani);
02699                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02700                   }
02701                   if (o->chan->cid.cid_rdnis)
02702                      ast_free(o->chan->cid.cid_rdnis);
02703                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02704                   if (ast_call(o->chan, tmpchan, 0)) {
02705                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02706                      do_hang(o);
02707                      numnochan++;
02708                   }
02709                }
02710                /* Hangup the original channel now, in case we needed it */
02711                ast_hangup(winner);
02712                continue;
02713             }
02714             f = ast_read(winner);
02715             if (f) {
02716                if (f->frametype == AST_FRAME_CONTROL) {
02717                   switch (f->subclass) {
02718                   case AST_CONTROL_ANSWER:
02719                      /* This is our guy if someone answered. */
02720                      if (!peer) {
02721                         ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02722                         peer = o;
02723                      }
02724                      break;
02725                   case AST_CONTROL_BUSY:
02726                      ast_verb(3, "%s is busy\n", o->chan->name);
02727                      if (in->cdr)
02728                         ast_cdr_busy(in->cdr);
02729                      do_hang(o);
02730                      endtime = (long) time(NULL);
02731                      endtime -= starttime;
02732                      rna(endtime * 1000, qe, on, membername, 0);
02733                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02734                         if (qe->parent->timeoutrestart)
02735                            *to = orig;
02736                         ring_one(qe, outgoing, &numbusies);
02737                      }
02738                      numbusies++;
02739                      break;
02740                   case AST_CONTROL_CONGESTION:
02741                      ast_verb(3, "%s is circuit-busy\n", o->chan->name);
02742                      if (in->cdr)
02743                         ast_cdr_busy(in->cdr);
02744                      endtime = (long) time(NULL);
02745                      endtime -= starttime;
02746                      rna(endtime * 1000, qe, on, membername, 0);
02747                      do_hang(o);
02748                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02749                         if (qe->parent->timeoutrestart)
02750                            *to = orig;
02751                         ring_one(qe, outgoing, &numbusies);
02752                      }
02753                      numbusies++;
02754                      break;
02755                   case AST_CONTROL_RINGING:
02756                      ast_verb(3, "%s is ringing\n", o->chan->name);
02757                      break;
02758                   case AST_CONTROL_OFFHOOK:
02759                      /* Ignore going off hook */
02760                      break;
02761                   default:
02762                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
02763                   }
02764                }
02765                ast_frfree(f);
02766             } else {
02767                endtime = (long) time(NULL) - starttime;
02768                rna(endtime * 1000, qe, on, membername, 1);
02769                do_hang(o);
02770                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02771                   if (qe->parent->timeoutrestart)
02772                      *to = orig;
02773                   ring_one(qe, outgoing, &numbusies);
02774                }
02775             }
02776          }
02777       }
02778       if (winner == in) {
02779          f = ast_read(in);
02780          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02781             /* Got hung up */
02782             *to = -1;
02783             if (f) {
02784                if (f->data.uint32) {
02785                   in->hangupcause = f->data.uint32;
02786                }
02787                ast_frfree(f);
02788             }
02789             return NULL;
02790          }
02791          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02792             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
02793             *to = 0;
02794             ast_frfree(f);
02795             return NULL;
02796          }
02797          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02798             ast_verb(3, "User pressed digit: %c\n", f->subclass);
02799             *to = 0;
02800             *digit = f->subclass;
02801             ast_frfree(f);
02802             return NULL;
02803          }
02804          ast_frfree(f);
02805       }
02806       if (!*to) {
02807          for (o = start; o; o = o->call_next)
02808             rna(orig, qe, o->interface, o->member->membername, 1);
02809       }
02810    }
02811 
02812 #ifdef HAVE_EPOLL
02813    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02814       if (epollo->chan)
02815          ast_poll_channel_del(in, epollo->chan);
02816    }
02817 #endif
02818 
02819    return peer;
02820 }

static int wait_our_turn ( struct queue_ent qe,
int  ringing,
enum queue_result reason 
) [static]

The waiting areas for callers who are not actively calling members.

This function is one large loop. This function will return if a caller either exits the queue or it becomes that caller's turn to attempt calling queue members. Inside the loop, we service the caller with periodic announcements, holdtime announcements, etc. as configured in queues.conf

Return values:
0 if the caller's turn has arrived
-1 if the caller should exit the queue.

Definition at line 2906 of file app_queue.c.

References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, queue_ent::expire, get_member_status(), is_our_turn(), leave_queue(), call_queue::leavewhenempty, queue_ent::max_penalty, queue_ent::min_penalty, call_queue::name, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, penalty_rule::time, ast_channel::uniqueid, update_qe_rule(), and valid_exit().

Referenced by queue_exec().

02907 {
02908    int res = 0;
02909 
02910    /* This is the holding pen for callers 2 through maxlen */
02911    for (;;) {
02912       enum queue_member_status status = QUEUE_NORMAL;
02913       int exit = 0;
02914 
02915       if (is_our_turn(qe))
02916          break;
02917 
02918       /* If we have timed out, break out */
02919       if (qe->expire && (time(NULL) >= qe->expire)) {
02920          *reason = QUEUE_TIMEOUT;
02921          break;
02922       }
02923 
02924       /* If we are going to exit due to a leavewhenempty condition, we should
02925        * actually attempt to keep the caller in the queue until we have
02926        * exhausted all penalty rules.
02927        */
02928       for (; !exit || qe->pr; update_qe_rule(qe)) {
02929          status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
02930   
02931          if (!qe->pr || status == QUEUE_NORMAL) {
02932             break;
02933          }
02934 
02935          /* leave the queue if no agents, if enabled */
02936          if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
02937                ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
02938                ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
02939             continue;
02940          } else {
02941             exit = 1;
02942          }
02943       }
02944 
02945       if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
02946          *reason = QUEUE_LEAVEEMPTY;
02947          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02948          leave_queue(qe);
02949          break;
02950       }
02951 
02952       /* leave the queue if no reachable agents, if enabled */
02953       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
02954          *reason = QUEUE_LEAVEUNAVAIL;
02955          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02956          leave_queue(qe);
02957          break;
02958       }
02959       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
02960          *reason = QUEUE_LEAVEUNAVAIL;
02961          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02962          leave_queue(qe);
02963          break;
02964       }
02965 
02966       /* Make a position announcement, if enabled */
02967       if (qe->parent->announcefrequency &&
02968          (res = say_position(qe,ringing)))
02969          break;
02970 
02971       /* If we have timed out, break out */
02972       if (qe->expire && (time(NULL) >= qe->expire)) {
02973          *reason = QUEUE_TIMEOUT;
02974          break;
02975       }
02976 
02977       /* Make a periodic announcement, if enabled */
02978       if (qe->parent->periodicannouncefrequency &&
02979          (res = say_periodic_announcement(qe,ringing)))
02980          break;
02981       
02982       /* see if we need to move to the next penalty level for this queue */
02983       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
02984          update_qe_rule(qe);
02985       }
02986 
02987       /* If we have timed out, break out */
02988       if (qe->expire && (time(NULL) >= qe->expire)) {
02989          *reason = QUEUE_TIMEOUT;
02990          break;
02991       }
02992       
02993       /* Wait a second before checking again */
02994       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02995          if (res > 0 && !valid_exit(qe, res))
02996             res = 0;
02997          else
02998             break;
02999       }
03000       
03001       /* If we have timed out, break out */
03002       if (qe->expire && (time(NULL) >= qe->expire)) {
03003          *reason = QUEUE_TIMEOUT;
03004          break;
03005       }
03006    }
03007 
03008    return res;
03009 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 6737 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 152 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 211 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 213 of file app_queue.c.

char* app_aqm_synopsis = "Dynamically adds queue members" [static]

Definition at line 212 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 237 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 239 of file app_queue.c.

char* app_pqm_synopsis = "Pauses a queue member" [static]

Definition at line 238 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 270 of file app_queue.c.

char* app_ql_descrip [static]

Initial value:

"   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"

Definition at line 272 of file app_queue.c.

char* app_ql_synopsis = "Writes to the queue_log" [static]

Definition at line 271 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 224 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 226 of file app_queue.c.

char* app_rqm_synopsis = "Dynamically removes queue members" [static]

Definition at line 225 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 255 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 257 of file app_queue.c.

char* app_upqm_synopsis = "Unpauses a queue member" [static]

Definition at line 256 of file app_queue.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 6737 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 292 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 6610 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* descrip [static]

Definition at line 156 of file app_queue.c.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 301 of file app_queue.c.

struct ast_taskprocessor* devicestate_tps [static]

Definition at line 136 of file app_queue.c.

Referenced by device_state_cb(), load_module(), and unload_module().

enum queue_result id

Definition at line 318 of file app_queue.c.

Referenced by _sip_show_peers(), amixer_max(), idemodulator(), and setamixer().

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 295 of file app_queue.c.

const char* pm_family = "Queue/PersistentMembers" [static]

Persistent Members astdb family.

Definition at line 278 of file app_queue.c.

const char qpm_cmd_usage[] [static]

Initial value:

 
"Usage: queue pause member <channel> in <queue> reason <reason>\n"

Definition at line 6601 of file app_queue.c.

const char qsmp_cmd_usage[] [static]

Initial value:

"Usage: queue set member penalty <channel> from <queue> <penalty>\n"

Definition at line 6607 of file app_queue.c.

int queue_keep_stats = 0 [static]

queues.conf [general] option

Definition at line 283 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 286 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

struct ast_datastore_info queue_transfer_info [static]

Initial value:

 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}
a datastore used to help correctly log attended transfers of queue callers

Definition at line 3172 of file app_queue.c.

Referenced by attended_transfer_occurred(), queue_transfer_fixup(), setup_transfer_datastore(), and try_calling().

struct ast_custom_function queuemembercount_dep [static]

Definition at line 5358 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 5345 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 5377 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberpenalty_function [static]

Definition at line 5386 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Definition at line 545 of file app_queue.c.

Referenced by __queues_show(), add_to_queue(), compare_weight(), complete_queue(), complete_queue_remove_member(), find_queue_by_name_rt(), get_member_penalty(), interface_exists_global(), join_queue(), leave_queue(), load_module(), load_realtime_queue(), manager_queues_status(), manager_queues_summary(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_queues(), remove_from_queue(), set_member_paused(), set_member_penalty(), try_calling(), unload_module(), update_queue(), update_realtime_members(), and update_status().

struct ast_custom_function queuevar_function [static]

Definition at line 5327 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Definition at line 5368 of file app_queue.c.

Referenced by load_module(), and unload_module().

const char qum_cmd_usage[] [static]

Initial value:

"Usage: queue unpause member <channel> in <queue> reason <reason>\n"

Definition at line 6604 of file app_queue.c.

int shared_lastcall = 0 [static]

queues.conf [general] option

Definition at line 298 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* synopsis = "Queue a call for a call queue" [static]

Definition at line 154 of file app_queue.c.

char* text

Definition at line 319 of file app_queue.c.

Referenced by _sip_show_peer(), build_reply_digest(), check_auth(), festival_exec(), handle_response(), iconv_read(), method_match(), parse_sip_options(), process_sdp(), reqprep(), sendtext_exec(), set_queue_result(), and sip_new().

int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 304 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 289 of file app_queue.c.


Generated on Fri Jun 19 12:10:01 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7