Fri Nov 12 11:51:01 2010

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"
Include dependency graph for app_queue.c:

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  member
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_EVENT_VARIABLES   3
#define queue_t_ref(a, b)   queue_ref(a)
#define queue_t_unref(a, b)   queue_unref(a)
#define queues_t_link(c, q, tag)   ao2_t_link(c,q,tag)
#define queues_t_unlink(c, q, tag)   ao2_t_unlink(c,q,tag)
#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  empty_conditions {
  QUEUE_EMPTY_PENALTY = (1 << 0), QUEUE_EMPTY_PAUSED = (1 << 1), QUEUE_EMPTY_INUSE = (1 << 2), QUEUE_EMPTY_RINGING = (1 << 3),
  QUEUE_EMPTY_UNAVAILABLE = (1 << 4), QUEUE_EMPTY_INVALID = (1 << 5), QUEUE_EMPTY_UNKNOWN = (1 << 6), QUEUE_EMPTY_WRAPUP = (1 << 7)
}
enum  queue_reload_mask { QUEUE_RELOAD_PARAMETERS = (1 << 0), QUEUE_RELOAD_MEMBER = (1 << 1), QUEUE_RELOAD_RULES = (1 << 2), QUEUE_RESET_STATS = (1 << 3) }
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_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_queue (struct call_queue *q)
static int clear_stats (const char *queuename)
 Facilitates resetting statistics for a queue.
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 int get_member_status (struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions)
 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_reload (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_reset (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, int cancel_answered_elsewhere)
 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 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)
static int kill_dead_members (void *obj, void *arg, int flags)
static int kill_dead_queues (void *obj, void *arg, int flags)
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_reload (struct mansession *s, const struct message *m)
static int manager_queue_reset (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 mark_dead_and_unfound (void *obj, void *arg, int flags)
static int mark_member_dead (void *obj, void *arg, int flags)
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 void parse_empty_options (const char *value, enum empty_conditions *empty, int joinempty)
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_global_params (struct ast_config *cfg)
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 int reload_handler (int reload, struct ast_flags *mask, const char *queuename)
 The command center for all reload operations.
static void reload_queue_members (void)
 Reload dynamic queue members persisted into the astdb.
static int reload_queue_rules (int reload)
 Reload the rules defined in queuerules.conf.
static int reload_queues (int reload, struct ast_flags *mask, const char *queuename)
 reload the queues.conf file
static void reload_single_member (const char *memberdata, struct call_queue *q)
 reload information pertaining to a single member
static void reload_single_queue (struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
 Reload information pertaining to a particular queue.
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, int newtalktime)
 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 (struct call_queue *q, struct member *m, const int status)
 set a member's status based on device state of that member's state_interface.
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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_pqm = "PauseQueueMember"
static char * app_ql = "QueueLog"
static char * app_rqm = "RemoveQueueMember"
static char * app_upqm = "UnpauseQueueMember"
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 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_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 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>:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered

Patch Version 1.07 2003-12-24 01

Added servicelevel statistic by Michiel Betel <michiel@betel.nl> 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 692 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 693 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 708 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 707 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 706 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 705 of file app_queue.c.

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

#define AST_MAX_WATCHERS   256

Definition at line 2834 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 530 of file app_queue.c.

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 526 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 527 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 529 of file app_queue.c.

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 532 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 555 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 694 of file app_queue.c.

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

#define queue_t_ref ( a,
 )     queue_ref(a)

Definition at line 884 of file app_queue.c.

Referenced by leave_queue(), and try_calling().

#define queue_t_unref ( a,
 )     queue_unref(a)
#define queues_t_link ( c,
q,
tag   )     ao2_t_link(c,q,tag)

Definition at line 886 of file app_queue.c.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

#define queues_t_unlink ( c,
q,
tag   )     ao2_t_unlink(c,q,tag)

Definition at line 887 of file app_queue.c.

Referenced by find_queue_by_name_rt(), leave_queue(), and unload_module().

#define RECHECK   1

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

Definition at line 528 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

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 493 of file app_queue.c.

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 3376 of file app_queue.c.

03376                            {
03377    CALLER,
03378    AGENT,
03379    TRANSFER
03380 };

Enumerator:
QUEUE_EMPTY_PENALTY 
QUEUE_EMPTY_PAUSED 
QUEUE_EMPTY_INUSE 
QUEUE_EMPTY_RINGING 
QUEUE_EMPTY_UNAVAILABLE 
QUEUE_EMPTY_INVALID 
QUEUE_EMPTY_UNKNOWN 
QUEUE_EMPTY_WRAPUP 

Definition at line 680 of file app_queue.c.

00680                       {
00681    QUEUE_EMPTY_PENALTY = (1 << 0),
00682    QUEUE_EMPTY_PAUSED = (1 << 1),
00683    QUEUE_EMPTY_INUSE = (1 << 2),
00684    QUEUE_EMPTY_RINGING = (1 << 3),
00685    QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
00686    QUEUE_EMPTY_INVALID = (1 << 5),
00687    QUEUE_EMPTY_UNKNOWN = (1 << 6),
00688    QUEUE_EMPTY_WRAPUP = (1 << 7),
00689 };

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 503 of file app_queue.c.

00503                        {
00504    QUEUE_RELOAD_PARAMETERS = (1 << 0),
00505    QUEUE_RELOAD_MEMBER = (1 << 1),
00506    QUEUE_RELOAD_RULES = (1 << 2),
00507    QUEUE_RESET_STATS = (1 << 3),
00508 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 578 of file app_queue.c.

00578                   {
00579    QUEUE_UNKNOWN = 0,
00580    QUEUE_TIMEOUT = 1,
00581    QUEUE_JOINEMPTY = 2,
00582    QUEUE_LEAVEEMPTY = 3,
00583    QUEUE_JOINUNAVAIL = 4,
00584    QUEUE_LEAVEUNAVAIL = 5,
00585    QUEUE_FULL = 6,
00586    QUEUE_CONTINUE = 7,
00587 };

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 603 of file app_queue.c.

00603                             {
00604    TIMEOUT_PRIORITY_APP,
00605    TIMEOUT_PRIORITY_CONF,
00606 };


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 6083 of file app_queue.c.

References ao2_container_count(), ao2_iterator_destroy(), AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_devstate2str(), ast_load_realtime_multientry(), ast_str_alloca, ast_str_append(), ast_str_buffer(), 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, do_print(), member::dynamic, 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_t_unref, queues, member::realtime, call_queue::realtime, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, call_queue::talktime, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

06084 {
06085    struct call_queue *q;
06086    struct ast_str *out = ast_str_alloca(240);
06087    int found = 0;
06088    time_t now = time(NULL);
06089    struct ao2_iterator queue_iter;
06090    struct ao2_iterator mem_iter;
06091 
06092    if (argc != 2 && argc != 3)
06093       return CLI_SHOWUSAGE;
06094 
06095    if (argc == 3) { /* specific queue */
06096       if ((q = load_realtime_queue(argv[2]))) {
06097          queue_t_unref(q, "Done with temporary pointer");
06098       }
06099    } else if (ast_check_realtime("queues")) {
06100       /* This block is to find any queues which are defined in realtime but
06101        * which have not yet been added to the in-core container
06102        */
06103       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
06104       char *queuename;
06105       if (cfg) {
06106          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
06107             if ((q = load_realtime_queue(queuename))) {
06108                queue_t_unref(q, "Done with temporary pointer");
06109             }
06110          }
06111          ast_config_destroy(cfg);
06112       }
06113    }
06114 
06115    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
06116    ao2_lock(queues);
06117    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06118       float sl;
06119       struct call_queue *realtime_queue = NULL;
06120 
06121       ao2_lock(q);
06122       /* This check is to make sure we don't print information for realtime
06123        * queues which have been deleted from realtime but which have not yet
06124        * been deleted from the in-core container
06125        */
06126       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
06127          ao2_unlock(q);
06128          queue_t_unref(q, "Done with iterator");
06129          continue;
06130       } else if (q->realtime) {
06131          queue_t_unref(realtime_queue, "Queue is already in memory");
06132       }
06133       if (argc == 3 && strcasecmp(q->name, argv[2])) {
06134          ao2_unlock(q);
06135          queue_t_unref(q, "Done with iterator");
06136          continue;
06137       }
06138       found = 1;
06139 
06140       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
06141       if (q->maxlen)
06142          ast_str_append(&out, 0, "%d", q->maxlen);
06143       else
06144          ast_str_append(&out, 0, "unlimited");
06145       sl = 0;
06146       if (q->callscompleted > 0)
06147          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06148       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
06149          int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
06150          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
06151       do_print(s, fd, ast_str_buffer(out));
06152       if (!ao2_container_count(q->members))
06153          do_print(s, fd, "   No Members");
06154       else {
06155          struct member *mem;
06156 
06157          do_print(s, fd, "   Members: ");
06158          mem_iter = ao2_iterator_init(q->members, 0);
06159          while ((mem = ao2_iterator_next(&mem_iter))) {
06160             ast_str_set(&out, 0, "      %s", mem->membername);
06161             if (strcasecmp(mem->membername, mem->interface)) {
06162                ast_str_append(&out, 0, " (%s)", mem->interface);
06163             }
06164             if (mem->penalty)
06165                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
06166             ast_str_append(&out, 0, "%s%s%s (%s)",
06167                mem->dynamic ? " (dynamic)" : "",
06168                mem->realtime ? " (realtime)" : "",
06169                mem->paused ? " (paused)" : "",
06170                ast_devstate2str(mem->status));
06171             if (mem->calls)
06172                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
06173                   mem->calls, (long) (time(NULL) - mem->lastcall));
06174             else
06175                ast_str_append(&out, 0, " has taken no calls yet");
06176             do_print(s, fd, ast_str_buffer(out));
06177             ao2_ref(mem, -1);
06178          }
06179          ao2_iterator_destroy(&mem_iter);
06180       }
06181       if (!q->head)
06182          do_print(s, fd, "   No Callers");
06183       else {
06184          struct queue_ent *qe;
06185          int pos = 1;
06186 
06187          do_print(s, fd, "   Callers: ");
06188          for (qe = q->head; qe; qe = qe->next) {
06189             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
06190                pos++, qe->chan->name, (long) (now - qe->start) / 60,
06191                (long) (now - qe->start) % 60, qe->prio);
06192             do_print(s, fd, ast_str_buffer(out));
06193          }
06194       }
06195       do_print(s, fd, ""); /* blank line between entries */
06196       ao2_unlock(q);
06197       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
06198    }
06199    ao2_iterator_destroy(&queue_iter);
06200    ao2_unlock(queues);
06201    if (!found) {
06202       if (argc == 3)
06203          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
06204       else
06205          ast_str_set(&out, 0, "No queues.");
06206       do_print(s, fd, ast_str_buffer(out));
06207    }
06208    return CLI_SUCCESS;
06209 }

static void __reg_module ( void   )  [static]

Definition at line 7292 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 7292 of file app_queue.c.

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 4455 of file app_queue.c.

References 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, and member::status.

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

04456 {
04457    struct call_queue *q;
04458    struct member *new_member, *old_member;
04459    int res = RES_NOSUCHQUEUE;
04460 
04461    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
04462     * short-circuits if the queue is already in memory. */
04463    if (!(q = load_realtime_queue(queuename)))
04464       return res;
04465 
04466    ao2_lock(queues);
04467 
04468    ao2_lock(q);
04469    if ((old_member = interface_exists(q, interface)) == NULL) {
04470       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04471          new_member->dynamic = 1;
04472          ao2_link(q->members, new_member);
04473          q->membercount++;
04474          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04475             "Queue: %s\r\n"
04476             "Location: %s\r\n"
04477             "MemberName: %s\r\n"
04478             "Membership: %s\r\n"
04479             "Penalty: %d\r\n"
04480             "CallsTaken: %d\r\n"
04481             "LastCall: %d\r\n"
04482             "Status: %d\r\n"
04483             "Paused: %d\r\n",
04484             q->name, new_member->interface, new_member->membername,
04485             "dynamic",
04486             new_member->penalty, new_member->calls, (int) new_member->lastcall,
04487             new_member->status, new_member->paused);
04488          
04489          ao2_ref(new_member, -1);
04490          new_member = NULL;
04491 
04492          if (dump)
04493             dump_queue_members(q);
04494          
04495          res = RES_OKAY;
04496       } else {
04497          res = RES_OUTOFMEMORY;
04498       }
04499    } else {
04500       ao2_ref(old_member, -1);
04501       res = RES_EXISTS;
04502    }
04503    ao2_unlock(q);
04504    ao2_unlock(queues);
04505 
04506    return res;
04507 }

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

Definition at line 1672 of file app_queue.c.

References ao2_t_alloc, ast_string_field_init, ast_string_field_set, destroy_queue(), and queue_t_unref.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

01673 {
01674    struct call_queue *q;
01675 
01676    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
01677       if (ast_string_field_init(q, 64)) {
01678          queue_t_unref(q, "String field allocation failed");
01679          return NULL;
01680       }
01681       ast_string_field_set(q, name, queuename);
01682    }
01683    return q;
01684 }

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

AddQueueMember application.

Definition at line 4893 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_strlen_zero(), 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().

04894 {
04895    int res=-1;
04896    char *parse, *temppos = NULL;
04897    AST_DECLARE_APP_ARGS(args,
04898       AST_APP_ARG(queuename);
04899       AST_APP_ARG(interface);
04900       AST_APP_ARG(penalty);
04901       AST_APP_ARG(options);
04902       AST_APP_ARG(membername);
04903       AST_APP_ARG(state_interface);
04904    );
04905    int penalty = 0;
04906 
04907    if (ast_strlen_zero(data)) {
04908       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04909       return -1;
04910    }
04911 
04912    parse = ast_strdupa(data);
04913 
04914    AST_STANDARD_APP_ARGS(args, parse);
04915 
04916    if (ast_strlen_zero(args.interface)) {
04917       args.interface = ast_strdupa(chan->name);
04918       temppos = strrchr(args.interface, '-');
04919       if (temppos)
04920          *temppos = '\0';
04921    }
04922 
04923    if (!ast_strlen_zero(args.penalty)) {
04924       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04925          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04926          penalty = 0;
04927       }
04928    }
04929 
04930    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04931    case RES_OKAY:
04932       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04933       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04934       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04935       res = 0;
04936       break;
04937    case RES_EXISTS:
04938       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04939       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04940       res = 0;
04941       break;
04942    case RES_NOSUCHQUEUE:
04943       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04944       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04945       res = 0;
04946       break;
04947    case RES_OUTOFMEMORY:
04948       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04949       break;
04950    }
04951 
04952    return res;
04953 }

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 3480 of file app_queue.c.

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

03481 {
03482    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03483 }

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 3319 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, QUEUE_STRATEGY_WRANDOM, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

03320 {
03321    if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03322       return -1;
03323 
03324    switch (q->strategy) {
03325    case QUEUE_STRATEGY_RINGALL:
03326       /* Everyone equal, except for penalty */
03327       tmp->metric = mem->penalty * 1000000;
03328       break;
03329    case QUEUE_STRATEGY_LINEAR:
03330       if (pos < qe->linpos) {
03331          tmp->metric = 1000 + pos;
03332       } else {
03333          if (pos > qe->linpos)
03334             /* Indicate there is another priority */
03335             qe->linwrapped = 1;
03336          tmp->metric = pos;
03337       }
03338       tmp->metric += mem->penalty * 1000000;
03339       break;
03340    case QUEUE_STRATEGY_RRMEMORY:
03341       if (pos < q->rrpos) {
03342          tmp->metric = 1000 + pos;
03343       } else {
03344          if (pos > q->rrpos)
03345             /* Indicate there is another priority */
03346             q->wrapped = 1;
03347          tmp->metric = pos;
03348       }
03349       tmp->metric += mem->penalty * 1000000;
03350       break;
03351    case QUEUE_STRATEGY_RANDOM:
03352       tmp->metric = ast_random() % 1000;
03353       tmp->metric += mem->penalty * 1000000;
03354       break;
03355    case QUEUE_STRATEGY_WRANDOM:
03356       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03357       break;
03358    case QUEUE_STRATEGY_FEWESTCALLS:
03359       tmp->metric = mem->calls;
03360       tmp->metric += mem->penalty * 1000000;
03361       break;
03362    case QUEUE_STRATEGY_LEASTRECENT:
03363       if (!mem->lastcall)
03364          tmp->metric = 0;
03365       else
03366          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03367       tmp->metric += mem->penalty * 1000000;
03368       break;
03369    default:
03370       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03371       break;
03372    }
03373    return 0;
03374 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 1243 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, call_queue::members, call_queue::talktime, and call_queue::wrapuptime.

Referenced by clear_stats(), and find_queue_by_name_rt().

01244 {
01245    q->holdtime = 0;
01246    q->callscompleted = 0;
01247    q->callsabandoned = 0;
01248    q->callscompletedinsl = 0;
01249    q->wrapuptime = 0;
01250    q->talktime = 0;
01251 
01252    if (q->members) {
01253       struct member *mem;
01254       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01255       while ((mem = ao2_iterator_next(&mem_iter))) {
01256          mem->calls = 0;
01257          ao2_ref(mem, -1);
01258       }
01259       ao2_iterator_destroy(&mem_iter);
01260    }
01261 }

static int clear_stats ( const char *  queuename  )  [static]

Facilitates resetting statistics for a queue.

This function actually does not reset any statistics, but rather finds a call_queue struct which corresponds to the passed-in queue name and passes that structure to the clear_queue function. If no queuename is passed in, then all queues will have their statistics reset.

Parameters:
queuename The name of the queue to reset the statistics for. If this is NULL or zero-length, then this means to reset the statistics for all queues
Return values:
void 

Definition at line 6024 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_t_iterator_next, ao2_unlock(), ast_strlen_zero(), clear_queue(), call_queue::name, queue_t_unref, and queues.

Referenced by reload_handler().

06025 {
06026    struct call_queue *q;
06027    struct ao2_iterator queue_iter = ao2_iterator_init(queues, 0);
06028    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06029       ao2_lock(q);
06030       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
06031          clear_queue(q);
06032       ao2_unlock(q);
06033       queue_t_unref(q, "Done with iterator");
06034    }
06035    ao2_iterator_destroy(&queue_iter);
06036    return 0;
06037 }

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

Definition at line 2371 of file app_queue.c.

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

Referenced by ring_entry().

02372 {
02373    struct call_queue *q;
02374    struct member *mem;
02375    int found = 0;
02376    struct ao2_iterator queue_iter;
02377    
02378    /* q's lock and rq's lock already set by try_calling()
02379     * to solve deadlock */
02380    queue_iter = ao2_iterator_init(queues, 0);
02381    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
02382       if (q == rq) { /* don't check myself, could deadlock */
02383          queue_t_unref(q, "Done with iterator");
02384          continue;
02385       }
02386       ao2_lock(q);
02387       if (q->count && q->members) {
02388          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02389             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02390             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02391                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);
02392                found = 1;
02393             }
02394             ao2_ref(mem, -1);
02395          }
02396       }
02397       ao2_unlock(q);
02398       queue_t_unref(q, "Done with iterator");
02399       if (found) {
02400          break;
02401       }
02402    }
02403    ao2_iterator_destroy(&queue_iter);
02404    return found;
02405 }

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

Definition at line 6211 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_strdup, call_queue::name, queue_t_unref, and queues.

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

06212 {
06213    struct call_queue *q;
06214    char *ret = NULL;
06215    int which = 0;
06216    int wordlen = strlen(word);
06217    struct ao2_iterator queue_iter;
06218 
06219    queue_iter = ao2_iterator_init(queues, 0);
06220    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06221       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
06222          ret = ast_strdup(q->name);
06223          queue_t_unref(q, "Done with iterator");
06224          break;
06225       }
06226       queue_t_unref(q, "Done with iterator");
06227    }
06228    ao2_iterator_destroy(&queue_iter);
06229 
06230    return ret;
06231 }

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

Definition at line 6643 of file app_queue.c.

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

Referenced by handle_queue_add_member().

06644 {
06645    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
06646    switch (pos) {
06647    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
06648       return NULL;
06649    case 4: /* only one possible match, "to" */
06650       return state == 0 ? ast_strdup("to") : NULL;
06651    case 5: /* <queue> */
06652       return complete_queue(line, word, pos, state);
06653    case 6: /* only one possible match, "penalty" */
06654       return state == 0 ? ast_strdup("penalty") : NULL;
06655    case 7:
06656       if (state < 100) {      /* 0-99 */
06657          char *num;
06658          if ((num = ast_malloc(3))) {
06659             sprintf(num, "%d", state);
06660          }
06661          return num;
06662       } else {
06663          return NULL;
06664       }
06665    case 8: /* only one possible match, "as" */
06666       return state == 0 ? ast_strdup("as") : NULL;
06667    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
06668       return NULL;
06669    default:
06670       return NULL;
06671    }
06672 }

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

Definition at line 6864 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

06865 {
06866    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
06867    switch (pos) {
06868    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
06869       return NULL;
06870    case 4:  /* only one possible match, "queue" */
06871       return state == 0 ? ast_strdup("queue") : NULL;
06872    case 5:  /* <queue> */
06873       return complete_queue(line, word, pos, state);
06874    case 6: /* "reason" */
06875       return state == 0 ? ast_strdup("reason") : NULL;
06876    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
06877       return NULL;
06878    default:
06879       return NULL;
06880    }
06881 }

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

Definition at line 6773 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_strdup, complete_queue(), member::interface, member::membername, call_queue::members, queue_t_unref, and queues.

Referenced by handle_queue_remove_member().

06774 {
06775    int which = 0;
06776    struct call_queue *q;
06777    struct member *m;
06778    struct ao2_iterator queue_iter;
06779    struct ao2_iterator mem_iter;
06780    int wordlen = strlen(word);
06781 
06782    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
06783    if (pos > 5 || pos < 3)
06784       return NULL;
06785    if (pos == 4)   /* only one possible match, 'from' */
06786       return (state == 0 ? ast_strdup("from") : NULL);
06787 
06788    if (pos == 5)   /* No need to duplicate code */
06789       return complete_queue(line, word, pos, state);
06790 
06791    /* here is the case for 3, <member> */
06792    queue_iter = ao2_iterator_init(queues, 0);
06793    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06794       ao2_lock(q);
06795       mem_iter = ao2_iterator_init(q->members, 0);
06796       while ((m = ao2_iterator_next(&mem_iter))) {
06797          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06798             char *tmp;
06799             ao2_unlock(q);
06800             tmp = ast_strdup(m->interface);
06801             ao2_ref(m, -1);
06802             queue_t_unref(q, "Done with iterator, returning interface name");
06803             ao2_iterator_destroy(&mem_iter);
06804             ao2_iterator_destroy(&queue_iter);
06805             return tmp;
06806          }
06807          ao2_ref(m, -1);
06808       }
06809       ao2_iterator_destroy(&mem_iter);
06810       ao2_unlock(q);
06811       queue_t_unref(q, "Done with iterator");
06812    }
06813    ao2_iterator_destroy(&queue_iter);
06814 
06815    return NULL;
06816 }

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

Definition at line 6997 of file app_queue.c.

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

Referenced by handle_queue_rule_show().

06998 {
06999    int which = 0;
07000    struct rule_list *rl_iter;
07001    int wordlen = strlen(word);
07002    char *ret = NULL;
07003    if (pos != 3) /* Wha? */ {
07004       return NULL;
07005    }
07006 
07007    AST_LIST_LOCK(&rule_lists);
07008    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07009       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
07010          ret = ast_strdup(rl_iter->name);
07011          break;
07012       }
07013    }
07014    AST_LIST_UNLOCK(&rule_lists);
07015 
07016    return ret;
07017 }

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

Definition at line 6934 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

06935 {
06936    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
06937    switch (pos) {
06938    case 4:
06939       if (state == 0) {
06940          return ast_strdup("on");
06941       } else {
06942          return NULL;
06943       }
06944    case 6:
06945       if (state == 0) {
06946          return ast_strdup("in");
06947       } else {
06948          return NULL;
06949       }
06950    case 7:
06951       return complete_queue(line, word, pos, state);
06952    default:
06953       return NULL;
06954    }
06955 }

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

Definition at line 6233 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

06234 {
06235    if (pos == 2)
06236       return complete_queue(line, word, pos, state);
06237    return NULL;
06238 }

static int compress_char ( const char  c  )  [static]

Definition at line 1142 of file app_queue.c.

Referenced by member_hash_fn().

01143 {
01144    if (c < 32)
01145       return 0;
01146    else if (c > 96)
01147       return c - 64;
01148    else
01149       return c - 32;
01150 }

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

Copy rule from global list into specified queue.

Definition at line 4990 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(), call_queue::defaultrule, LOG_ERROR, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, queue_ent::parent, queue_ent::qe_rules, rule_list::rules, and penalty_rule::time.

Referenced by queue_exec().

04991 {
04992    struct penalty_rule *pr_iter;
04993    struct rule_list *rl_iter;
04994    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
04995    AST_LIST_LOCK(&rule_lists);
04996    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04997       if (!strcasecmp(rl_iter->name, tmp))
04998          break;
04999    }
05000    if (rl_iter) {
05001       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05002          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
05003          if (!new_pr) {
05004             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
05005             AST_LIST_UNLOCK(&rule_lists);
05006             break;
05007          }
05008          new_pr->time = pr_iter->time;
05009          new_pr->max_value = pr_iter->max_value;
05010          new_pr->min_value = pr_iter->min_value;
05011          new_pr->max_relative = pr_iter->max_relative;
05012          new_pr->min_relative = pr_iter->min_relative;
05013          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
05014       }
05015    }
05016    AST_LIST_UNLOCK(&rule_lists);
05017 }

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

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

Definition at line 1117 of file app_queue.c.

References ao2_alloc, ast_copy_string(), ast_log(), ast_strlen_zero(), member::interface, LOG_WARNING, member::membername, member::paused, member::penalty, member::state_interface, and member::status.

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

01118 {
01119    struct member *cur;
01120    
01121    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01122       cur->penalty = penalty;
01123       cur->paused = paused;
01124       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01125       if (!ast_strlen_zero(state_interface))
01126          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01127       else
01128          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01129       if (!ast_strlen_zero(membername))
01130          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01131       else
01132          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01133       if (!strchr(cur->interface, '/'))
01134          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01135       cur->status = ast_device_state(cur->state_interface);
01136    }
01137 
01138    return cur;
01139 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

Definition at line 1658 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().

01659 {
01660    struct call_queue *q = obj;
01661    int i;
01662 
01663    free_members(q, 1);
01664    ast_string_field_free_memory(q);
01665    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01666       if (q->sound_periodicannounce[i])
01667          free(q->sound_periodicannounce[i]);
01668    }
01669    ao2_ref(q->members, -1);
01670 }

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

Definition at line 1090 of file app_queue.c.

References ast_calloc, 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, handle_statechange(), LOG_ERROR, and statechange::state.

Referenced by load_module().

01091 {
01092    enum ast_device_state state;
01093    const char *device;
01094    struct statechange *sc;
01095    size_t datapsize;
01096 
01097    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01098    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01099 
01100    if (ast_strlen_zero(device)) {
01101       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01102       return;
01103    }
01104    datapsize = sizeof(*sc) + strlen(device) + 1;
01105    if (!(sc = ast_calloc(1, datapsize))) {
01106       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01107       return;
01108    }
01109    sc->state = state;
01110    strcpy(sc->dev, device);
01111    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01112       ast_free(sc);
01113    }
01114 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2408 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

02409 {
02410    o->stillgoing = 0;
02411    ast_hangup(o->chan);
02412    o->chan = NULL;
02413 }

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 6069 of file app_queue.c.

References ast_cli(), and astman_append().

Referenced by __queues_show().

06070 {
06071    if (s)
06072       astman_append(s, "%s\r\n", str);
06073    else
06074       ast_cli(fd, "%s\n", str);
06075 }

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 4356 of file app_queue.c.

References ao2_iterator_destroy(), 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().

04357 {
04358    struct member *cur_member;
04359    char value[PM_MAX_LEN];
04360    int value_len = 0;
04361    int res;
04362    struct ao2_iterator mem_iter;
04363 
04364    memset(value, 0, sizeof(value));
04365 
04366    if (!pm_queue)
04367       return;
04368 
04369    mem_iter = ao2_iterator_init(pm_queue->members, 0);
04370    while ((cur_member = ao2_iterator_next(&mem_iter))) {
04371       if (!cur_member->dynamic) {
04372          ao2_ref(cur_member, -1);
04373          continue;
04374       }
04375 
04376       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04377          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04378 
04379       ao2_ref(cur_member, -1);
04380 
04381       if (res != strlen(value + value_len)) {
04382          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04383          break;
04384       }
04385       value_len += res;
04386    }
04387    ao2_iterator_destroy(&mem_iter);
04388    
04389    if (value_len && !cur_member) {
04390       if (ast_db_put(pm_family, pm_queue->name, value))
04391          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04392    } else
04393       /* Delete the entry if the queue is empty or there is an error */
04394       ast_db_del(pm_family, pm_queue->name);
04395 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 3527 of file app_queue.c.

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

Referenced by try_calling().

03528 {
03529    struct queue_end_bridge *qeb = data;
03530    struct call_queue *q = qeb->q;
03531    struct ast_channel *chan = qeb->chan;
03532 
03533    if (ao2_ref(qeb, -1) == 1) {
03534       ao2_lock(q);
03535       set_queue_variables(q, chan);
03536       ao2_unlock(q);
03537       /* This unrefs the reference we made in try_calling when we allocated qeb */
03538       queue_t_unref(q, "Expire bridge_config reference");
03539    }
03540 }

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

Definition at line 3520 of file app_queue.c.

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

Referenced by try_calling().

03521 {
03522    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03523    ao2_ref(qeb, +1);
03524    qeb->chan = originator;
03525 }

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

find the entry with the best metric, or NULL

Definition at line 2621 of file app_queue.c.

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

Referenced by ring_one(), store_next_lin(), and store_next_rr().

02622 {
02623    struct callattempt *best = NULL, *cur;
02624 
02625    for (cur = outgoing; cur; cur = cur->q_next) {
02626       if (cur->stillgoing &&              /* Not already done */
02627          !cur->chan &&              /* Isn't already going */
02628          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02629          best = cur;
02630       }
02631    }
02632 
02633    return best;
02634 }

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

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 1696 of file app_queue.c.

References alloc_queue(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_find, ao2_unlink, ao2_unlock(), ast_category_browse(), ast_copy_string(), ast_debug, ast_log(), ast_queue_log(), ast_strlen_zero(), ast_variable_retrieve(), clear_queue(), member::dead, call_queue::dead, init_queue(), member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_link, queues_t_unlink, member::realtime, call_queue::realtime, rt_handle_member_record(), S_OR, strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

01697 {
01698    struct ast_variable *v;
01699    struct call_queue *q, tmpq = {
01700       .name = queuename,   
01701    };
01702    struct member *m;
01703    struct ao2_iterator mem_iter;
01704    char *interface = NULL;
01705    const char *tmp_name;
01706    char *tmp;
01707    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01708 
01709    /* Static queues override realtime. */
01710    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
01711       ao2_lock(q);
01712       if (!q->realtime) {
01713          if (q->dead) {
01714             ao2_unlock(q);
01715             queue_t_unref(q, "Queue is dead; can't return it");
01716             return NULL;
01717          } else {
01718             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01719             ao2_unlock(q);
01720             return q;
01721          }
01722       }
01723    } else if (!member_config)
01724       /* Not found in the list, and it's not realtime ... */
01725       return NULL;
01726 
01727    /* Check if queue is defined in realtime. */
01728    if (!queue_vars) {
01729       /* Delete queue from in-core list if it has been deleted in realtime. */
01730       if (q) {
01731          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01732             found condition... So we might delete an in-core queue
01733             in case of DB failure. */
01734          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
01735 
01736          q->dead = 1;
01737          /* Delete if unused (else will be deleted when last caller leaves). */
01738          queues_t_unlink(queues, q, "Unused; removing from container");
01739          ao2_unlock(q);
01740          queue_t_unref(q, "Queue is dead; can't return it");
01741       }
01742       return NULL;
01743    }
01744 
01745    /* Create a new queue if an in-core entry does not exist yet. */
01746    if (!q) {
01747       struct ast_variable *tmpvar = NULL;
01748       if (!(q = alloc_queue(queuename)))
01749          return NULL;
01750       ao2_lock(q);
01751       clear_queue(q);
01752       q->realtime = 1;
01753       q->membercount = 0;
01754       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
01755        * will allocate the members properly
01756        */
01757       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
01758          if (!strcasecmp(tmpvar->name, "strategy")) {
01759             q->strategy = strat2int(tmpvar->value);
01760             if (q->strategy < 0) {
01761                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01762                tmpvar->value, q->name);
01763                q->strategy = QUEUE_STRATEGY_RINGALL;
01764             }
01765             break;
01766          }
01767       }
01768       /* We traversed all variables and didn't find a strategy */
01769       if (!tmpvar)
01770          q->strategy = QUEUE_STRATEGY_RINGALL;
01771       queues_t_link(queues, q, "Add queue to container");
01772    }
01773    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01774 
01775    memset(tmpbuf, 0, sizeof(tmpbuf));
01776    for (v = queue_vars; v; v = v->next) {
01777       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01778       if ((tmp = strchr(v->name, '_'))) {
01779          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01780          tmp_name = tmpbuf;
01781          tmp = tmpbuf;
01782          while ((tmp = strchr(tmp, '_')))
01783             *tmp++ = '-';
01784       } else
01785          tmp_name = v->name;
01786 
01787       if (!ast_strlen_zero(v->value)) {
01788          /* Don't want to try to set the option if the value is empty */
01789          queue_set_param(q, tmp_name, v->value, -1, 0);
01790       }
01791    }
01792 
01793    /* Temporarily set realtime members dead so we can detect deleted ones. 
01794     * Also set the membercount correctly for realtime*/
01795    mem_iter = ao2_iterator_init(q->members, 0);
01796    while ((m = ao2_iterator_next(&mem_iter))) {
01797       q->membercount++;
01798       if (m->realtime)
01799          m->dead = 1;
01800       ao2_ref(m, -1);
01801    }
01802    ao2_iterator_destroy(&mem_iter);
01803 
01804    while ((interface = ast_category_browse(member_config, interface))) {
01805       rt_handle_member_record(q, interface,
01806          ast_variable_retrieve(member_config, interface, "uniqueid"),
01807          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
01808          ast_variable_retrieve(member_config, interface, "penalty"),
01809          ast_variable_retrieve(member_config, interface, "paused"),
01810          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01811    }
01812 
01813    /* Delete all realtime members that have been deleted in DB. */
01814    mem_iter = ao2_iterator_init(q->members, 0);
01815    while ((m = ao2_iterator_next(&mem_iter))) {
01816       if (m->dead) {
01817          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01818          ao2_unlink(q->members, m);
01819          q->membercount--;
01820       }
01821       ao2_ref(m, -1);
01822    }
01823    ao2_iterator_destroy(&mem_iter);
01824 
01825    ao2_unlock(q);
01826 
01827    return q;
01828 }

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

Iterate through queue's member list and delete them.

Definition at line 1641 of file app_queue.c.

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

Referenced by destroy_queue().

01642 {
01643    /* Free non-dynamic members */
01644    struct member *cur;
01645    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01646 
01647    while ((cur = ao2_iterator_next(&mem_iter))) {
01648       if (all || !cur->dynamic) {
01649          ao2_unlink(q->members, cur);
01650          q->membercount--;
01651       }
01652       ao2_ref(cur, -1);
01653    }
01654    ao2_iterator_destroy(&mem_iter);
01655 }

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

Definition at line 4635 of file app_queue.c.

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

Referenced by queue_function_memberpenalty_read().

04636 {
04637    int foundqueue = 0, penalty;
04638    struct call_queue *q, tmpq = {
04639       .name = queuename,   
04640    };
04641    struct member *mem;
04642    
04643    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
04644       foundqueue = 1;
04645       ao2_lock(q);
04646       if ((mem = interface_exists(q, interface))) {
04647          penalty = mem->penalty;
04648          ao2_ref(mem, -1);
04649          ao2_unlock(q);
04650          queue_t_unref(q, "Search complete");
04651          return penalty;
04652       }
04653       ao2_unlock(q);
04654       queue_t_unref(q, "Search complete");
04655    }
04656 
04657    /* some useful debuging */
04658    if (foundqueue) 
04659       ast_log (LOG_ERROR, "Invalid queuename\n");
04660    else 
04661       ast_log (LOG_ERROR, "Invalid interface\n");
04662 
04663    return RESULT_FAILURE;
04664 }

static int get_member_status ( struct call_queue q,
int  max_penalty,
int  min_penalty,
enum empty_conditions  conditions 
) [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 0. If no members are available, then -1 is returned.

Definition at line 951 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, member::lastcall, member::membername, call_queue::members, member::paused, member::penalty, QUEUE_EMPTY_INUSE, QUEUE_EMPTY_INVALID, QUEUE_EMPTY_PAUSED, QUEUE_EMPTY_PENALTY, QUEUE_EMPTY_UNAVAILABLE, QUEUE_EMPTY_UNKNOWN, QUEUE_EMPTY_WRAPUP, member::status, and call_queue::wrapuptime.

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

00952 {
00953    struct member *member;
00954    struct ao2_iterator mem_iter;
00955 
00956    ao2_lock(q);
00957    mem_iter = ao2_iterator_init(q->members, 0);
00958    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
00959       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
00960          if (conditions & QUEUE_EMPTY_PENALTY) {
00961             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
00962             continue;
00963          }
00964       }
00965 
00966       switch (member->status) {
00967       case AST_DEVICE_INVALID:
00968          if (conditions & QUEUE_EMPTY_INVALID) {
00969             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
00970             break;
00971          }
00972       case AST_DEVICE_UNAVAILABLE:
00973          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
00974             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
00975             break;
00976          }
00977       case AST_DEVICE_INUSE:
00978          if (conditions & QUEUE_EMPTY_INUSE) {
00979             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
00980             break;
00981          }
00982       case AST_DEVICE_UNKNOWN:
00983          if (conditions & QUEUE_EMPTY_UNKNOWN) {
00984             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
00985             break;
00986          }
00987       default:
00988          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
00989             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
00990             break;
00991          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
00992             ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
00993             break;
00994          } else {
00995             ao2_unlock(q);
00996             ao2_ref(member, -1);
00997             ao2_iterator_destroy(&mem_iter);
00998             ast_debug(4, "%s is available.\n", member->membername);
00999             return 0;
01000          }
01001          break;
01002       }
01003    }
01004    ao2_iterator_destroy(&mem_iter);
01005 
01006    ao2_unlock(q);
01007    return -1;
01008 }

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

Definition at line 6699 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.

06700 {
06701    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06702    int penalty;
06703 
06704    switch ( cmd ) {
06705    case CLI_INIT:
06706       e->command = "queue add member";
06707       e->usage =
06708          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06709          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
06710       return NULL;
06711    case CLI_GENERATE:
06712       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06713    }
06714 
06715    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06716       return CLI_SHOWUSAGE;
06717    } else if (strcmp(a->argv[4], "to")) {
06718       return CLI_SHOWUSAGE;
06719    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06720       return CLI_SHOWUSAGE;
06721    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06722       return CLI_SHOWUSAGE;
06723    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06724       return CLI_SHOWUSAGE;
06725    }
06726 
06727    queuename = a->argv[5];
06728    interface = a->argv[3];
06729    if (a->argc >= 8) {
06730       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
06731          if (penalty < 0) {
06732             ast_cli(a->fd, "Penalty must be >= 0\n");
06733             penalty = 0;
06734          }
06735       } else {
06736          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06737          penalty = 0;
06738       }
06739    } else {
06740       penalty = 0;
06741    }
06742 
06743    if (a->argc >= 10) {
06744       membername = a->argv[9];
06745    }
06746 
06747    if (a->argc >= 12) {
06748       state_interface = a->argv[11];
06749    }
06750 
06751    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06752    case RES_OKAY:
06753       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06754       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06755       return CLI_SUCCESS;
06756    case RES_EXISTS:
06757       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06758       return CLI_FAILURE;
06759    case RES_NOSUCHQUEUE:
06760       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06761       return CLI_FAILURE;
06762    case RES_OUTOFMEMORY:
06763       ast_cli(a->fd, "Out of memory\n");
06764       return CLI_FAILURE;
06765    case RES_NOT_DYNAMIC:
06766       ast_cli(a->fd, "Member not dynamic\n");
06767       return CLI_FAILURE;
06768    default:
06769       return CLI_FAILURE;
06770    }
06771 }

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

Definition at line 6883 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(), ast_cli_entry::usage, and word.

06884 {
06885    char *queuename, *interface, *reason;
06886    int paused;
06887 
06888    switch (cmd) {
06889    case CLI_INIT:
06890       e->command = "queue {pause|unpause} member";
06891       e->usage = 
06892          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06893          "  Pause or unpause a queue member. Not specifying a particular queue\n"
06894          "  will pause or unpause a member across all queues to which the member\n"
06895          "  belongs.\n";
06896       return NULL;
06897    case CLI_GENERATE:
06898       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06899    }
06900 
06901    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06902       return CLI_SHOWUSAGE;
06903    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06904       return CLI_SHOWUSAGE;
06905    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06906       return CLI_SHOWUSAGE;
06907    }
06908 
06909 
06910    interface = a->argv[3];
06911    queuename = a->argc >= 6 ? a->argv[5] : NULL;
06912    reason = a->argc == 8 ? a->argv[7] : NULL;
06913    paused = !strcasecmp(a->argv[1], "pause");
06914 
06915    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06916       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06917       if (!ast_strlen_zero(queuename))
06918          ast_cli(a->fd, " in queue '%s'", queuename);
06919       if (!ast_strlen_zero(reason))
06920          ast_cli(a->fd, " for reason '%s'", reason);
06921       ast_cli(a->fd, "\n");
06922       return CLI_SUCCESS;
06923    } else {
06924       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06925       if (!ast_strlen_zero(queuename))
06926          ast_cli(a->fd, " in queue '%s'", queuename);
06927       if (!ast_strlen_zero(reason))
06928          ast_cli(a->fd, " for reason '%s'", reason);
06929       ast_cli(a->fd, "\n");
06930       return CLI_FAILURE;
06931    }
06932 }

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

Definition at line 7092 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, AST_FLAGS_ALL, ast_set_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue(), ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, reload_handler(), ast_cli_entry::usage, and ast_cli_args::word.

07093 {
07094    struct ast_flags mask = {0,};
07095    int i;
07096 
07097    switch (cmd) {
07098       case CLI_INIT:
07099          e->command = "queue reload {parameters|members|rules|all}";
07100          e->usage =
07101             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
07102             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
07103             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
07104             "specified in order to know what information to reload. Below is an explanation\n"
07105             "of each of these qualifiers.\n"
07106             "\n"
07107             "\t'members' - reload queue members from queues.conf\n"
07108             "\t'parameters' - reload all queue options except for queue members\n"
07109             "\t'rules' - reload the queuerules.conf file\n"
07110             "\t'all' - reload queue rules, parameters, and members\n"
07111             "\n"
07112             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
07113             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
07114             "one queue is specified when using this command, reloading queue rules may cause\n"
07115             "other queues to be affected\n";
07116          return NULL;
07117       case CLI_GENERATE:
07118          if (a->pos >= 3) {
07119             return complete_queue(a->line, a->word, a->pos, a->n);
07120          } else {
07121             return NULL;
07122          }
07123    }
07124 
07125    if (a->argc < 3)
07126       return CLI_SHOWUSAGE;
07127 
07128    if (!strcasecmp(a->argv[2], "rules")) {
07129       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07130    } else if (!strcasecmp(a->argv[2], "members")) {
07131       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07132    } else if (!strcasecmp(a->argv[2], "parameters")) {
07133       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07134    } else if (!strcasecmp(a->argv[2], "all")) {
07135       ast_set_flag(&mask, AST_FLAGS_ALL);
07136    }
07137 
07138    if (a->argc == 3) {
07139       reload_handler(1, &mask, NULL);
07140       return CLI_SUCCESS;
07141    }
07142 
07143    for (i = 3; i < a->argc; ++i) {
07144       reload_handler(1, &mask, a->argv[i]);
07145    }
07146 
07147    return CLI_SUCCESS;
07148 }

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

Definition at line 6818 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_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, ast_cli_entry::usage, and ast_cli_args::word.

06819 {
06820    char *queuename, *interface;
06821 
06822    switch (cmd) {
06823    case CLI_INIT:
06824       e->command = "queue remove member";
06825       e->usage = 
06826          "Usage: queue remove member <channel> from <queue>\n"
06827          "       Remove a specific channel from a queue.\n";
06828       return NULL;
06829    case CLI_GENERATE:
06830       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06831    }
06832 
06833    if (a->argc != 6) {
06834       return CLI_SHOWUSAGE;
06835    } else if (strcmp(a->argv[4], "from")) {
06836       return CLI_SHOWUSAGE;
06837    }
06838 
06839    queuename = a->argv[5];
06840    interface = a->argv[3];
06841 
06842    switch (remove_from_queue(queuename, interface)) {
06843    case RES_OKAY:
06844       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06845       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06846       return CLI_SUCCESS;
06847    case RES_EXISTS:
06848       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06849       return CLI_FAILURE;
06850    case RES_NOSUCHQUEUE:
06851       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06852       return CLI_FAILURE;
06853    case RES_OUTOFMEMORY:
06854       ast_cli(a->fd, "Out of memory\n");
06855       return CLI_FAILURE;
06856    case RES_NOT_DYNAMIC:
06857       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
06858       return CLI_FAILURE;
06859    default:
06860       return CLI_FAILURE;
06861    }
06862 }

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

Definition at line 7053 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue(), ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, QUEUE_RESET_STATS, reload_handler(), ast_cli_entry::usage, and ast_cli_args::word.

07054 {
07055    struct ast_flags mask = {QUEUE_RESET_STATS,};
07056    int i;
07057 
07058    switch (cmd) {
07059       case CLI_INIT:
07060          e->command = "queue reset stats";
07061          e->usage =
07062             "Usage: queue reset stats [<queuenames>]\n"
07063             "\n"
07064             "Issuing this command will reset statistics for\n"
07065             "<queuenames>, or for all queues if no queue is\n"
07066             "specified.\n";
07067          return NULL;
07068       case CLI_GENERATE:
07069          if (a->pos >= 3) {
07070             return complete_queue(a->line, a->word, a->pos, a->n);
07071          } else {
07072             return NULL;
07073          }
07074    }
07075 
07076    if (a->argc < 3) {
07077       return CLI_SHOWUSAGE;
07078    }
07079 
07080    if (a->argc == 3) {
07081       reload_handler(1, &mask, NULL);
07082       return CLI_SUCCESS;
07083    }
07084 
07085    for (i = 3; i < a->argc; ++i) {
07086       reload_handler(1, &mask, a->argv[i]);
07087    }
07088 
07089    return CLI_SUCCESS;
07090 }

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

Definition at line 7019 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::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.

07020 {
07021    char *rule;
07022    struct rule_list *rl_iter;
07023    struct penalty_rule *pr_iter;
07024    switch (cmd) {
07025    case CLI_INIT:
07026       e->command = "queue show rules";
07027       e->usage =
07028       "Usage: queue show rules [rulename]\n"
07029       "  Show the list of rules associated with rulename. If no\n"
07030       "  rulename is specified, list all rules defined in queuerules.conf\n";
07031       return NULL;
07032    case CLI_GENERATE:
07033       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
07034    }
07035 
07036    if (a->argc != 3 && a->argc != 4)
07037       return CLI_SHOWUSAGE;
07038 
07039    rule = a->argc == 4 ? a->argv[3] : "";
07040    AST_LIST_LOCK(&rule_lists);
07041    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07042       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
07043          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
07044          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07045             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);
07046          }
07047       }
07048    }
07049    AST_LIST_UNLOCK(&rule_lists);
07050    return CLI_SUCCESS; 
07051 }

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

Definition at line 6957 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.

06958 {
06959    char *queuename = NULL, *interface;
06960    int penalty = 0;
06961 
06962    switch (cmd) {
06963    case CLI_INIT:
06964       e->command = "queue set penalty";
06965       e->usage = 
06966       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06967       "  Set a member's penalty in the queue specified. If no queue is specified\n"
06968       "  then that interface's penalty is set in all queues to which that interface is a member\n";
06969       return NULL;
06970    case CLI_GENERATE:
06971       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06972    }
06973 
06974    if (a->argc != 6 && a->argc != 8) {
06975       return CLI_SHOWUSAGE;
06976    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06977       return CLI_SHOWUSAGE;
06978    }
06979 
06980    if (a->argc == 8)
06981       queuename = a->argv[7];
06982    interface = a->argv[5];
06983    penalty = atoi(a->argv[3]);
06984 
06985    switch (set_member_penalty(queuename, interface, penalty)) {
06986    case RESULT_SUCCESS:
06987       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06988       return CLI_SUCCESS;
06989    case RESULT_FAILURE:
06990       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06991       return CLI_FAILURE;
06992    default:
06993       return CLI_FAILURE;
06994    }
06995 }

static int handle_statechange ( void *  datap  )  [static]

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

Definition at line 1046 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_copy_string(), ast_debug, ast_devstate2str(), ast_free, statechange::dev, call_queue::found, call_queue::members, queue_t_unref, queues, statechange::state, member::state_interface, and update_status().

Referenced by device_state_cb().

01047 {
01048    struct statechange *sc = datap;
01049    struct ao2_iterator miter, qiter;
01050    struct member *m;
01051    struct call_queue *q;
01052    char interface[80], *slash_pos;
01053    int found = 0;
01054 
01055    qiter = ao2_iterator_init(queues, 0);
01056    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01057       ao2_lock(q);
01058 
01059       miter = ao2_iterator_init(q->members, 0);
01060       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01061          ast_copy_string(interface, m->state_interface, sizeof(interface));
01062 
01063          if ((slash_pos = strchr(interface, '/')))
01064             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01065                *slash_pos = '\0';
01066 
01067          if (!strcasecmp(interface, sc->dev)) {
01068             found = 1;
01069             update_status(q, m, sc->state);
01070             ao2_ref(m, -1);
01071             break;
01072          }
01073       }
01074       ao2_iterator_destroy(&miter);
01075 
01076       ao2_unlock(q);
01077       queue_t_unref(q, "Done with iterator");
01078    }
01079    ao2_iterator_destroy(&qiter);
01080 
01081    if (found)
01082       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01083    else
01084       ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01085 
01086    ast_free(sc);
01087    return 0;
01088 }

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

Hang up a list of outgoing calls.

Definition at line 2300 of file app_queue.c.

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

Referenced by try_calling().

02301 {
02302    struct callattempt *oo;
02303 
02304    while (outgoing) {
02305       /* If someone else answered the call we should indicate this in the CANCEL */
02306       /* Hangup any existing lines we have open */
02307       if (outgoing->chan && (outgoing->chan != exception)) {
02308          if (exception || cancel_answered_elsewhere)
02309             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02310          ast_hangup(outgoing->chan);
02311       }
02312       oo = outgoing;
02313       outgoing = outgoing->q_next;
02314       if (oo->member)
02315          ao2_ref(oo->member, -1);
02316       ast_free(oo);
02317    }
02318 }

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 1174 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, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), 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 find_queue_by_name_rt(), and reload_single_queue().

01175 {
01176    int i;
01177    struct penalty_rule *pr_iter;
01178 
01179    q->dead = 0;
01180    q->retry = DEFAULT_RETRY;
01181    q->timeout = DEFAULT_TIMEOUT;
01182    q->maxlen = 0;
01183    q->announcefrequency = 0;
01184    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01185    q->announceholdtime = 1;
01186    q->announcepositionlimit = 10; /* Default 10 positions */
01187    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
01188    q->roundingseconds = 0; /* Default - don't announce seconds */
01189    q->servicelevel = 0;
01190    q->ringinuse = 1;
01191    q->setinterfacevar = 0;
01192    q->setqueuevar = 0;
01193    q->setqueueentryvar = 0;
01194    q->autofill = autofill_default;
01195    q->montype = montype_default;
01196    q->monfmt[0] = '\0';
01197    q->reportholdtime = 0;
01198    q->wrapuptime = 0;
01199    q->joinempty = 0;
01200    q->leavewhenempty = 0;
01201    q->memberdelay = 0;
01202    q->maskmemberstatus = 0;
01203    q->eventwhencalled = 0;
01204    q->weight = 0;
01205    q->timeoutrestart = 0;
01206    q->periodicannouncefrequency = 0;
01207    q->randomperiodicannounce = 0;
01208    q->numperiodicannounce = 0;
01209    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01210    if (!q->members) {
01211       if (q->strategy == QUEUE_STRATEGY_LINEAR)
01212          /* linear strategy depends on order, so we have to place all members in a single bucket */
01213          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01214       else
01215          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01216    }
01217    q->found = 1;
01218 
01219    ast_string_field_set(q, sound_next, "queue-youarenext");
01220    ast_string_field_set(q, sound_thereare, "queue-thereare");
01221    ast_string_field_set(q, sound_calls, "queue-callswaiting");
01222    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01223    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01224    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01225    ast_string_field_set(q, sound_minutes, "queue-minutes");
01226    ast_string_field_set(q, sound_minute, "queue-minute");
01227    ast_string_field_set(q, sound_seconds, "queue-seconds");
01228    ast_string_field_set(q, sound_thanks, "queue-thankyou");
01229    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01230 
01231    if ((q->sound_periodicannounce[0] = ast_str_create(32)))
01232       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01233 
01234    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01235       if (q->sound_periodicannounce[i])
01236          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01237    }
01238 
01239    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01240       ast_free(pr_iter);
01241 }

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 921 of file app_queue.c.

References call_queue::head, queue_ent::next, and queue_ref().

Referenced by join_queue().

00922 {
00923    struct queue_ent *cur;
00924 
00925    if (!q || !new)
00926       return;
00927    if (prev) {
00928       cur = prev->next;
00929       prev->next = new;
00930    } else {
00931       cur = q->head;
00932       q->head = new;
00933    }
00934    new->next = cur;
00935 
00936    /* every queue_ent must have a reference to it's parent call_queue, this
00937     * reference does not go away until the end of the queue_ent's life, meaning
00938     * that even when the queue_ent leaves the call_queue this ref must remain. */
00939    queue_ref(q);
00940    new->parent = q;
00941    new->pos = ++(*pos);
00942    new->opos = *pos;
00943 }

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 1272 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_strlen_zero(), LOG_WARNING, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, rule_list::rules, and penalty_rule::time.

Referenced by reload_queue_rules().

01273 {
01274    char *timestr, *maxstr, *minstr, *contentdup;
01275    struct penalty_rule *rule = NULL, *rule_iter;
01276    struct rule_list *rl_iter;
01277    int penaltychangetime, inserted = 0;
01278 
01279    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01280       return -1;
01281    }
01282 
01283    contentdup = ast_strdupa(content);
01284    
01285    if (!(maxstr = strchr(contentdup, ','))) {
01286       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01287       ast_free(rule);
01288       return -1;
01289    }
01290 
01291    *maxstr++ = '\0';
01292    timestr = contentdup;
01293 
01294    if ((penaltychangetime = atoi(timestr)) < 0) {
01295       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01296       ast_free(rule);
01297       return -1;
01298    }
01299 
01300    rule->time = penaltychangetime;
01301 
01302    if ((minstr = strchr(maxstr,',')))
01303       *minstr++ = '\0';
01304    
01305    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01306     * OR if a min penalty change is indicated but no max penalty change is */
01307    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01308       rule->max_relative = 1;
01309    }
01310 
01311    rule->max_value = atoi(maxstr);
01312 
01313    if (!ast_strlen_zero(minstr)) {
01314       if (*minstr == '+' || *minstr == '-')
01315          rule->min_relative = 1;
01316       rule->min_value = atoi(minstr);
01317    } else /*there was no minimum specified, so assume this means no change*/
01318       rule->min_relative = 1;
01319 
01320    /*We have the rule made, now we need to insert it where it belongs*/
01321    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01322       if (strcasecmp(rl_iter->name, list_name))
01323          continue;
01324 
01325       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01326          if (rule->time < rule_iter->time) {
01327             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01328             inserted = 1;
01329             break;
01330          }
01331       }
01332       AST_LIST_TRAVERSE_SAFE_END;
01333    
01334       if (!inserted) {
01335          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01336       }
01337    }
01338 
01339    return 0;
01340 }

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

Definition at line 839 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().

00840 {
00841    int x;
00842 
00843    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00844       if (strategy == strategies[x].strategy)
00845          return strategies[x].name;
00846    }
00847 
00848    return "<unknown>";
00849 }

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

Definition at line 4330 of file app_queue.c.

References ao2_iterator_destroy(), 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().

04331 {
04332    struct member *mem;
04333    struct ao2_iterator mem_iter;
04334 
04335    if (!q)
04336       return NULL;
04337 
04338    mem_iter = ao2_iterator_init(q->members, 0);
04339    while ((mem = ao2_iterator_next(&mem_iter))) {
04340       if (!strcasecmp(interface, mem->interface)) {
04341          ao2_iterator_destroy(&mem_iter);
04342          return mem;
04343       }
04344       ao2_ref(mem, -1);
04345    }
04346    ao2_iterator_destroy(&mem_iter);
04347 
04348    return NULL;
04349 }

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 3120 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

03121 {
03122    struct queue_ent *ch;
03123    int res;
03124    int avl;
03125    int idx = 0;
03126    /* This needs a lock. How many members are available to be served? */
03127    ao2_lock(qe->parent);
03128 
03129    avl = num_available_members(qe->parent);
03130 
03131    ch = qe->parent->head;
03132 
03133    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
03134 
03135    while ((idx < avl) && (ch) && (ch != qe)) {
03136       if (!ch->pending)
03137          idx++;
03138       ch = ch->next;       
03139    }
03140 
03141    ao2_unlock(qe->parent);
03142    /* If the queue entry is within avl [the number of available members] calls from the top ... 
03143     * Autofill and position check added to support autofill=no (as only calls
03144     * from the front of the queue are valid when autofill is disabled)
03145     */
03146    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
03147       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
03148       res = 1;
03149    } else {
03150       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
03151       res = 0;
03152    }
03153 
03154    return res;
03155 }

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

Definition at line 1956 of file app_queue.c.

References call_queue::announce, queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), ast_debug, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::context, queue_ent::context, call_queue::count, 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, call_queue::moh, queue_ent::moh, call_queue::name, ast_channel::name, queue_ent::next, queue_ent::pos, queue_ent::prio, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_UNKNOWN, queues, S_OR, status, and ast_channel::uniqueid.

Referenced by queue_exec().

01957 {
01958    struct call_queue *q;
01959    struct queue_ent *cur, *prev = NULL;
01960    int res = -1;
01961    int pos = 0;
01962    int inserted = 0;
01963 
01964    if (!(q = load_realtime_queue(queuename)))
01965       return res;
01966 
01967    ao2_lock(queues);
01968    ao2_lock(q);
01969 
01970    /* This is our one */
01971    if (q->joinempty) {
01972       int status = 0;
01973       if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
01974          *reason = QUEUE_JOINEMPTY;
01975          ao2_unlock(q);
01976          ao2_unlock(queues);
01977          return res;
01978       }
01979    }
01980    if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
01981       *reason = QUEUE_FULL;
01982    else if (*reason == QUEUE_UNKNOWN) {
01983       /* There's space for us, put us at the right position inside
01984        * the queue.
01985        * Take into account the priority of the calling user */
01986       inserted = 0;
01987       prev = NULL;
01988       cur = q->head;
01989       while (cur) {
01990          /* We have higher priority than the current user, enter
01991           * before him, after all the other users with priority
01992           * higher or equal to our priority. */
01993          if ((!inserted) && (qe->prio > cur->prio)) {
01994             insert_entry(q, prev, qe, &pos);
01995             inserted = 1;
01996          }
01997          cur->pos = ++pos;
01998          prev = cur;
01999          cur = cur->next;
02000       }
02001       /* No luck, join at the end of the queue */
02002       if (!inserted)
02003          insert_entry(q, prev, qe, &pos);
02004       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02005       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02006       ast_copy_string(qe->context, q->context, sizeof(qe->context));
02007       q->count++;
02008       res = 0;
02009       manager_event(EVENT_FLAG_CALL, "Join",
02010          "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
02011          qe->chan->name,
02012          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
02013          S_OR(qe->chan->cid.cid_name, "unknown"),
02014          q->name, qe->pos, q->count, qe->chan->uniqueid );
02015       ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02016    }
02017    ao2_unlock(q);
02018    ao2_unlock(queues);
02019 
02020    return res;
02021 }

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

Definition at line 5803 of file app_queue.c.

References CMP_MATCH, member::delme, member::dynamic, call_queue::membercount, member::state_interface, and member::status.

Referenced by reload_single_queue().

05804 {
05805    struct member *member = obj;
05806    struct call_queue *q = arg;
05807 
05808    if (!member->delme) {
05809       if (member->dynamic) {
05810          /* dynamic members were not counted toward the member count
05811           * when reloading members from queues.conf, so we do that here
05812           */
05813          q->membercount++;
05814       }
05815       member->status = ast_device_state(member->state_interface);
05816       return 0;
05817    } else {
05818       q->membercount--;
05819       return CMP_MATCH;
05820    }
05821 }

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

Definition at line 5941 of file app_queue.c.

References ast_strlen_zero(), CMP_MATCH, call_queue::dead, and call_queue::name.

Referenced by reload_queues().

05942 {
05943    struct call_queue *q = obj;
05944    char *queuename = arg;
05945    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
05946       return CMP_MATCH;
05947    } else {
05948       return 0;
05949    }
05950 }

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 2243 of file app_queue.c.

References ao2_lock(), 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, manager_event, call_queue::name, ast_channel::name, queue_ent::next, queue_ent::parent, queue_ent::pos, queue_ent::qe_rules, queue_t_ref, queue_t_unref, queues, queues_t_unlink, call_queue::realtime, SENTINEL, ast_channel::uniqueid, and var.

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

02244 {
02245    struct call_queue *q;
02246    struct queue_ent *current, *prev = NULL;
02247    struct penalty_rule *pr_iter;
02248    int pos = 0;
02249 
02250    if (!(q = qe->parent))
02251       return;
02252    queue_t_ref(q, "Copy queue pointer from queue entry");
02253    ao2_lock(q);
02254 
02255    prev = NULL;
02256    for (current = q->head; current; current = current->next) {
02257       if (current == qe) {
02258          q->count--;
02259 
02260          /* Take us out of the queue */
02261          manager_event(EVENT_FLAG_CALL, "Leave",
02262             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02263             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
02264          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02265          /* Take us out of the queue */
02266          if (prev)
02267             prev->next = current->next;
02268          else
02269             q->head = current->next;
02270          /* Free penalty rules */
02271          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02272             ast_free(pr_iter);
02273       } else {
02274          /* Renumber the people after us in the queue based on a new count */
02275          current->pos = ++pos;
02276          prev = current;
02277       }
02278    }
02279    ao2_unlock(q);
02280 
02281    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02282    if (q->realtime) {
02283       struct ast_variable *var;
02284       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02285          q->dead = 1;
02286       } else {
02287          ast_variables_destroy(var);
02288       }
02289    }
02290 
02291    if (q->dead) { 
02292       /* It's dead and nobody is in it, so kill it */
02293       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02294    }
02295    /* unref the explicit ref earlier in the function */
02296    queue_t_unref(q, "Expire copied reference");
02297 }

static int load_module ( void   )  [static]

Definition at line 7220 of file app_queue.c.

References ao2_container_alloc, aqm_exec(), ARRAY_LEN, 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_FLAGS_ALL, ast_free_ptr(), ast_log(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application_xml, ast_strdup, ast_taskprocessor_get(), cli_queue, device_state_cb(), 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_reload(), manager_queue_reset(), 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_handler(), reload_queue_members(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().

07221 {
07222    int res;
07223    struct ast_context *con;
07224    struct ast_flags mask = {AST_FLAGS_ALL, };
07225 
07226    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
07227 
07228    use_weight = 0;
07229 
07230    if (reload_handler(0, &mask, NULL))
07231       return AST_MODULE_LOAD_DECLINE;
07232 
07233    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
07234    if (!con)
07235       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
07236    else
07237       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
07238 
07239    if (queue_persistent_members)
07240       reload_queue_members();
07241 
07242    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
07243    res = ast_register_application_xml(app, queue_exec);
07244    res |= ast_register_application_xml(app_aqm, aqm_exec);
07245    res |= ast_register_application_xml(app_rqm, rqm_exec);
07246    res |= ast_register_application_xml(app_pqm, pqm_exec);
07247    res |= ast_register_application_xml(app_upqm, upqm_exec);
07248    res |= ast_register_application_xml(app_ql, ql_exec);
07249    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
07250    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
07251    res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
07252    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
07253    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
07254    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
07255    res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
07256    res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 
07257    res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
07258    res |= ast_manager_register("QueueReload", 0, manager_queue_reload, "Reload a queue, queues, or any sub-section of a queue or queues");
07259    res |= ast_manager_register("QueueReset", 0, manager_queue_reset, "Reset queue statistics");
07260    res |= ast_custom_function_register(&queuevar_function);
07261    res |= ast_custom_function_register(&queuemembercount_function);
07262    res |= ast_custom_function_register(&queuemembercount_dep);
07263    res |= ast_custom_function_register(&queuememberlist_function);
07264    res |= ast_custom_function_register(&queuewaitingcount_function);
07265    res |= ast_custom_function_register(&queuememberpenalty_function);
07266 
07267    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
07268       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
07269    }
07270 
07271    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
07272       res = -1;
07273    }
07274 
07275    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
07276 
07277    return res ? AST_MODULE_LOAD_DECLINE : 0;
07278 }

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

Note:
Load from realtime before taking the "queues" container lock, to avoid blocking all queue operations while waiting for the DB.

This will be two separate database transactions, so we might see queue parameters as they were before another process changed the queue and member list as it was after the change. Thus we might see an empty member list when a queue is deleted. In practise, this is unlikely to cause a problem.

Definition at line 1830 of file app_queue.c.

References ao2_lock(), ao2_t_find, ao2_unlock(), ast_atomic_fetchadd_int(), 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, update_realtime_members(), and call_queue::weight.

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

01831 {
01832    struct ast_variable *queue_vars;
01833    struct ast_config *member_config = NULL;
01834    struct call_queue *q = NULL, tmpq = {
01835       .name = queuename,   
01836    };
01837    int prev_weight = 0;
01838 
01839    /* Find the queue in the in-core list first. */
01840    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
01841 
01842    if (!q || q->realtime) {
01843       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
01844          queue operations while waiting for the DB.
01845 
01846          This will be two separate database transactions, so we might
01847          see queue parameters as they were before another process
01848          changed the queue and member list as it was after the change.
01849          Thus we might see an empty member list when a queue is
01850          deleted. In practise, this is unlikely to cause a problem. */
01851 
01852       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
01853       if (queue_vars) {
01854          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
01855          if (!member_config) {
01856             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01857             ast_variables_destroy(queue_vars);
01858             return NULL;
01859          }
01860       }
01861       if (q) {
01862          prev_weight = q->weight ? 1 : 0;
01863       }
01864 
01865       ao2_lock(queues);
01866 
01867       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01868       if (member_config) {
01869          ast_config_destroy(member_config);
01870       }
01871       if (queue_vars) {
01872          ast_variables_destroy(queue_vars);
01873       }
01874       /* update the use_weight value if the queue's has gained or lost a weight */ 
01875       if (q) {
01876          if (!q->weight && prev_weight) {
01877             ast_atomic_fetchadd_int(&use_weight, -1);
01878          }
01879          if (q->weight && !prev_weight) {
01880             ast_atomic_fetchadd_int(&use_weight, +1);
01881          }
01882       }
01883       /* Other cases will end up with the proper value for use_weight */
01884       ao2_unlock(queues);
01885 
01886    } else {
01887       update_realtime_members(q);
01888    }
01889    return q;
01890 }

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

Definition at line 6466 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, and RES_OUTOFMEMORY.

Referenced by load_module().

06467 {
06468    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06469    int paused, penalty = 0;
06470 
06471    queuename = astman_get_header(m, "Queue");
06472    interface = astman_get_header(m, "Interface");
06473    penalty_s = astman_get_header(m, "Penalty");
06474    paused_s = astman_get_header(m, "Paused");
06475    membername = astman_get_header(m, "MemberName");
06476    state_interface = astman_get_header(m, "StateInterface");
06477 
06478    if (ast_strlen_zero(queuename)) {
06479       astman_send_error(s, m, "'Queue' not specified.");
06480       return 0;
06481    }
06482 
06483    if (ast_strlen_zero(interface)) {
06484       astman_send_error(s, m, "'Interface' not specified.");
06485       return 0;
06486    }
06487 
06488    if (ast_strlen_zero(penalty_s))
06489       penalty = 0;
06490    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
06491       penalty = 0;
06492 
06493    if (ast_strlen_zero(paused_s))
06494       paused = 0;
06495    else
06496       paused = abs(ast_true(paused_s));
06497 
06498    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06499    case RES_OKAY:
06500       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06501       astman_send_ack(s, m, "Added interface to queue");
06502       break;
06503    case RES_EXISTS:
06504       astman_send_error(s, m, "Unable to add interface: Already there");
06505       break;
06506    case RES_NOSUCHQUEUE:
06507       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06508       break;
06509    case RES_OUTOFMEMORY:
06510       astman_send_error(s, m, "Out of memory");
06511       break;
06512    }
06513 
06514    return 0;
06515 }

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

Definition at line 6551 of file app_queue.c.

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

Referenced by load_module().

06552 {
06553    const char *queuename, *interface, *paused_s, *reason;
06554    int paused;
06555 
06556    interface = astman_get_header(m, "Interface");
06557    paused_s = astman_get_header(m, "Paused");
06558    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
06559    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
06560 
06561    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06562       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06563       return 0;
06564    }
06565 
06566    paused = abs(ast_true(paused_s));
06567 
06568    if (set_member_paused(queuename, interface, reason, paused))
06569       astman_send_error(s, m, "Interface not found");
06570    else
06571       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06572    return 0;
06573 }

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

Definition at line 6575 of file app_queue.c.

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

Referenced by load_module().

06576 {
06577    const char *queuename, *event, *message, *interface, *uniqueid;
06578 
06579    queuename = astman_get_header(m, "Queue");
06580    uniqueid = astman_get_header(m, "UniqueId");
06581    interface = astman_get_header(m, "Interface");
06582    event = astman_get_header(m, "Event");
06583    message = astman_get_header(m, "Message");
06584 
06585    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06586       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06587       return 0;
06588    }
06589 
06590    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06591    astman_send_ack(s, m, "Event added successfully");
06592 
06593    return 0;
06594 }

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

Definition at line 6674 of file app_queue.c.

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

Referenced by load_module().

06675 {
06676    const char *queuename, *interface, *penalty_s;
06677    int penalty;
06678 
06679    interface = astman_get_header(m, "Interface");
06680    penalty_s = astman_get_header(m, "Penalty");
06681    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
06682    queuename = astman_get_header(m, "Queue");
06683 
06684    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06685       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06686       return 0;
06687    }
06688  
06689    penalty = atoi(penalty_s);
06690 
06691    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06692       astman_send_error(s, m, "Invalid interface, queuename or penalty");
06693    else
06694       astman_send_ack(s, m, "Interface penalty set successfully");
06695 
06696    return 0;
06697 }

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

Definition at line 6596 of file app_queue.c.

References AST_FLAGS_ALL, ast_set_flag, astman_get_header(), astman_send_ack(), astman_send_error(), QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, reload_handler(), and S_OR.

Referenced by load_module().

06597 {
06598    struct ast_flags mask = {0,};
06599    const char *queuename = NULL;
06600    int header_found = 0;
06601 
06602    queuename = astman_get_header(m, "Queue");
06603    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
06604       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
06605       header_found = 1;
06606    }
06607    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
06608       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
06609       header_found = 1;
06610    }
06611    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
06612       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
06613       header_found = 1;
06614    }
06615 
06616    if (!header_found) {
06617       ast_set_flag(&mask, AST_FLAGS_ALL);
06618    }
06619 
06620    if (!reload_handler(1, &mask, queuename)) {
06621       astman_send_ack(s, m, "Queue reloaded successfully");
06622    } else {
06623       astman_send_error(s, m, "Error encountered while reloading queue");
06624    }
06625    return 0;
06626 }

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

Definition at line 6628 of file app_queue.c.

References astman_get_header(), astman_send_ack(), astman_send_error(), QUEUE_RESET_STATS, and reload_handler().

Referenced by load_module().

06629 {
06630    const char *queuename = NULL;
06631    struct ast_flags mask = {QUEUE_RESET_STATS,};
06632    
06633    queuename = astman_get_header(m, "Queue");
06634 
06635    if (!reload_handler(1, &mask, queuename)) {
06636       astman_send_ack(s, m, "Queue stats reset successfully");
06637    } else {
06638       astman_send_error(s, m, "Error encountered while resetting queue stats");
06639    }
06640    return 0;
06641 }

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

Definition at line 6269 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::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, RESULT_SUCCESS, rule_list::rules, and penalty_rule::time.

Referenced by load_module().

06270 {
06271    const char *rule = astman_get_header(m, "Rule");
06272    struct rule_list *rl_iter;
06273    struct penalty_rule *pr_iter;
06274 
06275    AST_LIST_LOCK(&rule_lists);
06276    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06277       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
06278          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
06279          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06280             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 );
06281          }
06282          if (!ast_strlen_zero(rule))
06283             break;
06284       }
06285    }
06286    AST_LIST_UNLOCK(&rule_lists);
06287 
06288    astman_append(s, "\r\n\r\n");
06289 
06290    return RESULT_SUCCESS;
06291 }

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

Definition at line 6259 of file app_queue.c.

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

Referenced by load_module().

06260 {
06261    char *a[] = { "queue", "show" };
06262 
06263    __queues_show(s, -1, 2, a);
06264    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
06265 
06266    return RESULT_SUCCESS;
06267 }

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

Queue status info via AMI.

Definition at line 6369 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, 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_t_unref, queues, RESULT_SUCCESS, S_OR, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, call_queue::talktime, ast_channel::uniqueid, and call_queue::weight.

Referenced by load_module().

06370 {
06371    time_t now;
06372    int pos;
06373    const char *id = astman_get_header(m,"ActionID");
06374    const char *queuefilter = astman_get_header(m,"Queue");
06375    const char *memberfilter = astman_get_header(m,"Member");
06376    char idText[256] = "";
06377    struct call_queue *q;
06378    struct queue_ent *qe;
06379    float sl = 0;
06380    struct member *mem;
06381    struct ao2_iterator queue_iter;
06382    struct ao2_iterator mem_iter;
06383 
06384    astman_send_ack(s, m, "Queue status will follow");
06385    time(&now);
06386    if (!ast_strlen_zero(id))
06387       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
06388 
06389    queue_iter = ao2_iterator_init(queues, 0);
06390    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06391       ao2_lock(q);
06392 
06393       /* List queue properties */
06394       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06395          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
06396          astman_append(s, "Event: QueueParams\r\n"
06397             "Queue: %s\r\n"
06398             "Max: %d\r\n"
06399             "Strategy: %s\r\n"
06400             "Calls: %d\r\n"
06401             "Holdtime: %d\r\n"
06402             "TalkTime: %d\r\n"
06403             "Completed: %d\r\n"
06404             "Abandoned: %d\r\n"
06405             "ServiceLevel: %d\r\n"
06406             "ServicelevelPerf: %2.1f\r\n"
06407             "Weight: %d\r\n"
06408             "%s"
06409             "\r\n",
06410             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
06411             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06412          /* List Queue Members */
06413          mem_iter = ao2_iterator_init(q->members, 0);
06414          while ((mem = ao2_iterator_next(&mem_iter))) {
06415             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06416                astman_append(s, "Event: QueueMember\r\n"
06417                   "Queue: %s\r\n"
06418                   "Name: %s\r\n"
06419                   "Location: %s\r\n"
06420                   "Membership: %s\r\n"
06421                   "Penalty: %d\r\n"
06422                   "CallsTaken: %d\r\n"
06423                   "LastCall: %d\r\n"
06424                   "Status: %d\r\n"
06425                   "Paused: %d\r\n"
06426                   "%s"
06427                   "\r\n",
06428                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06429                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06430             }
06431             ao2_ref(mem, -1);
06432          }
06433          ao2_iterator_destroy(&mem_iter);
06434          /* List Queue Entries */
06435          pos = 1;
06436          for (qe = q->head; qe; qe = qe->next) {
06437             astman_append(s, "Event: QueueEntry\r\n"
06438                "Queue: %s\r\n"
06439                "Position: %d\r\n"
06440                "Channel: %s\r\n"
06441                "Uniqueid: %s\r\n"
06442                "CallerIDNum: %s\r\n"
06443                "CallerIDName: %s\r\n"
06444                "Wait: %ld\r\n"
06445                "%s"
06446                "\r\n",
06447                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
06448                S_OR(qe->chan->cid.cid_num, "unknown"),
06449                S_OR(qe->chan->cid.cid_name, "unknown"),
06450                (long) (now - qe->start), idText);
06451          }
06452       }
06453       ao2_unlock(q);
06454       queue_t_unref(q, "Done with iterator");
06455    }
06456    ao2_iterator_destroy(&queue_iter);
06457 
06458    astman_append(s,
06459       "Event: QueueStatusComplete\r\n"
06460       "%s"
06461       "\r\n",idText);
06462 
06463    return RESULT_SUCCESS;
06464 }

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

Summary of queue info via the AMI.

Definition at line 6294 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, 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(), call_queue::head, call_queue::holdtime, call_queue::members, call_queue::name, queue_ent::next, member::paused, queue_t_unref, queues, RESULT_SUCCESS, queue_ent::start, member::status, and call_queue::talktime.

Referenced by load_module().

06295 {
06296    time_t now;
06297    int qmemcount = 0;
06298    int qmemavail = 0;
06299    int qchancount = 0;
06300    int qlongestholdtime = 0;
06301    const char *id = astman_get_header(m, "ActionID");
06302    const char *queuefilter = astman_get_header(m, "Queue");
06303    char idText[256] = "";
06304    struct call_queue *q;
06305    struct queue_ent *qe;
06306    struct member *mem;
06307    struct ao2_iterator queue_iter;
06308    struct ao2_iterator mem_iter;
06309 
06310    astman_send_ack(s, m, "Queue summary will follow");
06311    time(&now);
06312    if (!ast_strlen_zero(id))
06313       snprintf(idText, 256, "ActionID: %s\r\n", id);
06314    queue_iter = ao2_iterator_init(queues, 0);
06315    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06316       ao2_lock(q);
06317 
06318       /* List queue properties */
06319       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06320          /* Reset the necessary local variables if no queuefilter is set*/
06321          qmemcount = 0;
06322          qmemavail = 0;
06323          qchancount = 0;
06324          qlongestholdtime = 0;
06325 
06326          /* List Queue Members */
06327          mem_iter = ao2_iterator_init(q->members, 0);
06328          while ((mem = ao2_iterator_next(&mem_iter))) {
06329             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
06330                ++qmemcount;
06331                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
06332                   ++qmemavail;
06333                }
06334             }
06335             ao2_ref(mem, -1);
06336          }
06337          ao2_iterator_destroy(&mem_iter);
06338          for (qe = q->head; qe; qe = qe->next) {
06339             if ((now - qe->start) > qlongestholdtime) {
06340                qlongestholdtime = now - qe->start;
06341             }
06342             ++qchancount;
06343          }
06344          astman_append(s, "Event: QueueSummary\r\n"
06345             "Queue: %s\r\n"
06346             "LoggedIn: %d\r\n"
06347             "Available: %d\r\n"
06348             "Callers: %d\r\n" 
06349             "HoldTime: %d\r\n"
06350             "TalkTime: %d\r\n"
06351             "LongestHoldTime: %d\r\n"
06352             "%s"
06353             "\r\n",
06354             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
06355       }
06356       ao2_unlock(q);
06357       queue_t_unref(q, "Done with iterator");
06358    }
06359    ao2_iterator_destroy(&queue_iter);
06360    astman_append(s,
06361       "Event: QueueSummaryComplete\r\n"
06362       "%s"
06363       "\r\n", idText);
06364 
06365    return RESULT_SUCCESS;
06366 }

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

Definition at line 6517 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, and RES_OUTOFMEMORY.

Referenced by load_module().

06518 {
06519    const char *queuename, *interface;
06520 
06521    queuename = astman_get_header(m, "Queue");
06522    interface = astman_get_header(m, "Interface");
06523 
06524    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06525       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06526       return 0;
06527    }
06528 
06529    switch (remove_from_queue(queuename, interface)) {
06530    case RES_OKAY:
06531       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06532       astman_send_ack(s, m, "Removed interface from queue");
06533       break;
06534    case RES_EXISTS:
06535       astman_send_error(s, m, "Unable to remove interface: Not there");
06536       break;
06537    case RES_NOSUCHQUEUE:
06538       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06539       break;
06540    case RES_OUTOFMEMORY:
06541       astman_send_error(s, m, "Out of memory");
06542       break;
06543    case RES_NOT_DYNAMIC:
06544       astman_send_error(s, m, "Member not dynamic");
06545       break;
06546    }
06547 
06548    return 0;
06549 }

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

Definition at line 5930 of file app_queue.c.

References ast_strlen_zero(), call_queue::dead, call_queue::found, call_queue::name, and call_queue::realtime.

Referenced by reload_queues().

05931 {
05932    struct call_queue *q = obj;
05933    char *queuename = arg;
05934    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
05935       q->dead = 1;
05936       q->found = 0;
05937    }
05938    return 0;
05939 }

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

Definition at line 5794 of file app_queue.c.

References member::delme, and member::dynamic.

Referenced by reload_single_queue().

05795 {
05796    struct member *member = obj;
05797    if (!member->dynamic) {
05798       member->delme = 1;
05799    }
05800    return 0;
05801 }

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

Definition at line 1164 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

01165 {
01166    struct member *mem1 = obj1, *mem2 = obj2;
01167    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01168 }

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

Definition at line 1152 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

01153 {
01154    const struct member *mem = obj;
01155    const char *chname = strchr(mem->interface, '/');
01156    int ret = 0, i;
01157    if (!chname)
01158       chname = mem->interface;
01159    for (i = 0; i < 5 && chname[i]; i++)
01160       ret += compress_char(chname[i]) << (i * 6);
01161    return ret;
01162 }

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 2328 of file app_queue.c.

References ao2_iterator_destroy(), 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().

02329 {
02330    struct member *mem;
02331    int avl = 0;
02332    struct ao2_iterator mem_iter;
02333 
02334    mem_iter = ao2_iterator_init(q->members, 0);
02335    while ((mem = ao2_iterator_next(&mem_iter))) {
02336       switch (mem->status) {
02337       case AST_DEVICE_INUSE:
02338          if (!q->ringinuse)
02339             break;
02340          /* else fall through */
02341       case AST_DEVICE_NOT_INUSE:
02342       case AST_DEVICE_UNKNOWN:
02343          if (!mem->paused) {
02344             avl++;
02345          }
02346          break;
02347       }
02348       ao2_ref(mem, -1);
02349 
02350       /* If autofill is not enabled or if the queue's strategy is ringall, then
02351        * we really don't care about the number of available members so much as we
02352        * do that there is at least one available.
02353        *
02354        * In fact, we purposely will return from this function stating that only
02355        * one member is available if either of those conditions hold. That way,
02356        * functions which determine what action to take based on the number of available
02357        * members will operate properly. The reasoning is that even if multiple
02358        * members are available, only the head caller can actually be serviced.
02359        */
02360       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02361          break;
02362       }
02363    }
02364    ao2_iterator_destroy(&mem_iter);
02365 
02366    return avl;
02367 }

static void parse_empty_options ( const char *  value,
enum empty_conditions empty,
int  joinempty 
) [static]

Definition at line 1342 of file app_queue.c.

References ast_false(), ast_log(), ast_true(), LOG_WARNING, QUEUE_EMPTY_INUSE, QUEUE_EMPTY_INVALID, QUEUE_EMPTY_PAUSED, QUEUE_EMPTY_PENALTY, QUEUE_EMPTY_RINGING, QUEUE_EMPTY_UNAVAILABLE, QUEUE_EMPTY_UNKNOWN, and QUEUE_EMPTY_WRAPUP.

Referenced by queue_set_param().

01343 {
01344    char *value_copy = ast_strdupa(value);
01345    char *option = NULL;
01346    while ((option = strsep(&value_copy, ","))) {
01347       if (!strcasecmp(option, "paused")) {
01348          *empty |= QUEUE_EMPTY_PAUSED;
01349       } else if (!strcasecmp(option, "penalty")) {
01350          *empty |= QUEUE_EMPTY_PENALTY;
01351       } else if (!strcasecmp(option, "inuse")) {
01352          *empty |= QUEUE_EMPTY_INUSE;
01353       } else if (!strcasecmp(option, "ringing")) {
01354          *empty |= QUEUE_EMPTY_RINGING;
01355       } else if (!strcasecmp(option, "invalid")) {
01356          *empty |= QUEUE_EMPTY_INVALID;
01357       } else if (!strcasecmp(option, "wrapup")) {
01358          *empty |= QUEUE_EMPTY_WRAPUP;
01359       } else if (!strcasecmp(option, "unavailable")) {
01360          *empty |= QUEUE_EMPTY_UNAVAILABLE;
01361       } else if (!strcasecmp(option, "unknown")) {
01362          *empty |= QUEUE_EMPTY_UNKNOWN;
01363       } else if (!strcasecmp(option, "loose")) {
01364          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01365       } else if (!strcasecmp(option, "strict")) {
01366          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01367       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01368          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01369       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01370          *empty = 0;
01371       } else {
01372          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01373       }
01374    }
01375 }

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

Definition at line 2023 of file app_queue.c.

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

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

02024 {
02025    int res;
02026 
02027    if (ast_strlen_zero(filename)) {
02028       return 0;
02029    }
02030 
02031    ast_stopstream(chan);
02032 
02033    res = ast_streamfile(chan, filename, chan->language);
02034    if (!res)
02035       res = ast_waitstream(chan, AST_DIGIT_ANY);
02036 
02037    ast_stopstream(chan);
02038 
02039    return res;
02040 }

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

PauseQueueMember application.

Definition at line 4764 of file app_queue.c.

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

Referenced by load_module().

04765 {
04766    char *parse;
04767    AST_DECLARE_APP_ARGS(args,
04768       AST_APP_ARG(queuename);
04769       AST_APP_ARG(interface);
04770       AST_APP_ARG(options);
04771       AST_APP_ARG(reason);
04772    );
04773 
04774    if (ast_strlen_zero(data)) {
04775       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04776       return -1;
04777    }
04778 
04779    parse = ast_strdupa(data);
04780 
04781    AST_STANDARD_APP_ARGS(args, parse);
04782 
04783    if (ast_strlen_zero(args.interface)) {
04784       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04785       return -1;
04786    }
04787 
04788    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04789       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04790       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04791       return 0;
04792    }
04793 
04794    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04795 
04796    return 0;
04797 }

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

QueueLog application.

Definition at line 4956 of file app_queue.c.

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

Referenced by load_module().

04957 {
04958    char *parse;
04959 
04960    AST_DECLARE_APP_ARGS(args,
04961       AST_APP_ARG(queuename);
04962       AST_APP_ARG(uniqueid);
04963       AST_APP_ARG(membername);
04964       AST_APP_ARG(event);
04965       AST_APP_ARG(params);
04966    );
04967 
04968    if (ast_strlen_zero(data)) {
04969       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04970       return -1;
04971    }
04972 
04973    parse = ast_strdupa(data);
04974 
04975    AST_STANDARD_APP_ARGS(args, parse);
04976 
04977    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04978        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04979       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04980       return -1;
04981    }
04982 
04983    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04984       "%s", args.params ? args.params : "");
04985 
04986    return 0;
04987 }

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

Definition at line 870 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

00871 {
00872    struct call_queue *q = obj, *q2 = arg;
00873    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
00874 }

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 5031 of file app_queue.c.

References call_queue::announcefrequency, AST_APP_ARG, ast_cdr_noanswer(), ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), AST_LIST_FIRST, ast_log(), ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strlen_zero(), ast_verb, ast_channel::cdr, queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, copy_rules(), queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::min_penalty, queue_ent::moh, call_queue::name, ast_channel::name, queue_ent::opos, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, queue_ent::prio, queue_ent::qe_rules, QUEUE_CONTINUE, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, QUEUE_UNKNOWN, queue_unref(), record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), set_queue_variables(), queue_ent::start, status, stop, penalty_rule::time, try_calling(), ast_channel::uniqueid, update_qe_rule(), update_realtime_members(), url, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

05032 {
05033    int res=-1;
05034    int ringing=0;
05035    const char *user_priority;
05036    const char *max_penalty_str;
05037    const char *min_penalty_str;
05038    int prio;
05039    int qcontinue = 0;
05040    int max_penalty, min_penalty;
05041    enum queue_result reason = QUEUE_UNKNOWN;
05042    /* whether to exit Queue application after the timeout hits */
05043    int tries = 0;
05044    int noption = 0;
05045    char *parse;
05046    int makeannouncement = 0;
05047    AST_DECLARE_APP_ARGS(args,
05048       AST_APP_ARG(queuename);
05049       AST_APP_ARG(options);
05050       AST_APP_ARG(url);
05051       AST_APP_ARG(announceoverride);
05052       AST_APP_ARG(queuetimeoutstr);
05053       AST_APP_ARG(agi);
05054       AST_APP_ARG(macro);
05055       AST_APP_ARG(gosub);
05056       AST_APP_ARG(rule);
05057    );
05058    /* Our queue entry */
05059    struct queue_ent qe = { 0 };
05060    
05061    if (ast_strlen_zero(data)) {
05062       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n");
05063       return -1;
05064    }
05065    
05066    parse = ast_strdupa(data);
05067    AST_STANDARD_APP_ARGS(args, parse);
05068 
05069    /* Setup our queue entry */
05070    qe.start = time(NULL);
05071 
05072    /* set the expire time based on the supplied timeout; */
05073    if (!ast_strlen_zero(args.queuetimeoutstr))
05074       qe.expire = qe.start + atoi(args.queuetimeoutstr);
05075    else
05076       qe.expire = 0;
05077 
05078    /* Get the priority from the variable ${QUEUE_PRIO} */
05079    ast_channel_lock(chan);
05080    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
05081    if (user_priority) {
05082       if (sscanf(user_priority, "%30d", &prio) == 1) {
05083          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
05084       } else {
05085          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
05086             user_priority, chan->name);
05087          prio = 0;
05088       }
05089    } else {
05090       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
05091       prio = 0;
05092    }
05093 
05094    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
05095 
05096    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
05097       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
05098          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
05099       } else {
05100          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
05101             max_penalty_str, chan->name);
05102          max_penalty = 0;
05103       }
05104    } else {
05105       max_penalty = 0;
05106    }
05107 
05108    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
05109       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
05110          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
05111       } else {
05112          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
05113             min_penalty_str, chan->name);
05114          min_penalty = 0;
05115       }
05116    } else {
05117       min_penalty = 0;
05118    }
05119    ast_channel_unlock(chan);
05120 
05121    if (args.options && (strchr(args.options, 'r')))
05122       ringing = 1;
05123 
05124    if (args.options && (strchr(args.options, 'c')))
05125       qcontinue = 1;
05126 
05127    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
05128       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
05129 
05130    qe.chan = chan;
05131    qe.prio = prio;
05132    qe.max_penalty = max_penalty;
05133    qe.min_penalty = min_penalty;
05134    qe.last_pos_said = 0;
05135    qe.last_pos = 0;
05136    qe.last_periodic_announce_time = time(NULL);
05137    qe.last_periodic_announce_sound = 0;
05138    qe.valid_digits = 0;
05139    if (join_queue(args.queuename, &qe, &reason)) {
05140       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
05141       set_queue_result(chan, reason);
05142       return 0;
05143    }
05144    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
05145       S_OR(chan->cid.cid_num, ""));
05146    copy_rules(&qe, args.rule);
05147    qe.pr = AST_LIST_FIRST(&qe.qe_rules);
05148 check_turns:
05149    if (ringing) {
05150       ast_indicate(chan, AST_CONTROL_RINGING);
05151    } else {
05152       ast_moh_start(chan, qe.moh, NULL);
05153    }
05154 
05155    /* This is the wait loop for callers 2 through maxlen */
05156    res = wait_our_turn(&qe, ringing, &reason);
05157    if (res) {
05158       goto stop;
05159    }
05160 
05161    makeannouncement = 0;
05162 
05163    for (;;) {
05164       /* This is the wait loop for the head caller*/
05165       /* To exit, they may get their call answered; */
05166       /* they may dial a digit from the queue context; */
05167       /* or, they may timeout. */
05168 
05169       /* Leave if we have exceeded our queuetimeout */
05170       if (qe.expire && (time(NULL) >= qe.expire)) {
05171          record_abandoned(&qe);
05172          ast_cdr_noanswer(qe.chan->cdr);
05173          reason = QUEUE_TIMEOUT;
05174          res = 0;
05175          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
05176             qe.pos, qe.opos, (long) time(NULL) - qe.start);
05177          break;
05178       }
05179 
05180       if (makeannouncement) {
05181          /* Make a position announcement, if enabled */
05182          if (qe.parent->announcefrequency)
05183             if ((res = say_position(&qe,ringing)))
05184                goto stop;
05185       }
05186       makeannouncement = 1;
05187 
05188       /* Make a periodic announcement, if enabled */
05189       if (qe.parent->periodicannouncefrequency)
05190          if ((res = say_periodic_announcement(&qe,ringing)))
05191             goto stop;
05192    
05193       /* Leave if we have exceeded our queuetimeout */
05194       if (qe.expire && (time(NULL) >= qe.expire)) {
05195          record_abandoned(&qe);
05196          ast_cdr_noanswer(qe.chan->cdr);
05197          reason = QUEUE_TIMEOUT;
05198          res = 0;
05199          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05200          break;
05201       }
05202 
05203       /* see if we need to move to the next penalty level for this queue */
05204       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
05205          update_qe_rule(&qe);
05206       }
05207 
05208       /* Try calling all queue members for 'timeout' seconds */
05209       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
05210       if (res) {
05211          goto stop;
05212       }
05213 
05214       if (qe.parent->leavewhenempty) {
05215          int status = 0;
05216          if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
05217             record_abandoned(&qe);
05218             ast_cdr_noanswer(qe.chan->cdr);
05219             reason = QUEUE_LEAVEEMPTY;
05220             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05221             res = 0;
05222             break;
05223          }
05224       }
05225 
05226       /* exit after 'timeout' cycle if 'n' option enabled */
05227       if (noption && tries >= qe.parent->membercount) {
05228          ast_verb(3, "Exiting on time-out cycle\n");
05229          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05230          record_abandoned(&qe);
05231          ast_cdr_noanswer(qe.chan->cdr);
05232          reason = QUEUE_TIMEOUT;
05233          res = 0;
05234          break;
05235       }
05236 
05237       
05238       /* Leave if we have exceeded our queuetimeout */
05239       if (qe.expire && (time(NULL) >= qe.expire)) {
05240          record_abandoned(&qe);
05241          ast_cdr_noanswer(qe.chan->cdr);
05242          reason = QUEUE_TIMEOUT;
05243          res = 0;
05244          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
05245          break;
05246       }
05247 
05248       /* If using dynamic realtime members, we should regenerate the member list for this queue */
05249       update_realtime_members(qe.parent);
05250       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
05251       res = wait_a_bit(&qe);
05252       if (res)
05253          goto stop;
05254 
05255       /* Since this is a priority queue and
05256        * it is not sure that we are still at the head
05257        * of the queue, go and check for our turn again.
05258        */
05259       if (!is_our_turn(&qe)) {
05260          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
05261          goto check_turns;
05262       }
05263    }
05264 
05265 stop:
05266    if (res) {
05267       if (res < 0) {
05268          if (!qe.handled) {
05269             record_abandoned(&qe);
05270             ast_cdr_noanswer(qe.chan->cdr);
05271             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
05272                "%d|%d|%ld", qe.pos, qe.opos,
05273                (long) time(NULL) - qe.start);
05274             res = -1;
05275          } else if (qcontinue) {
05276             reason = QUEUE_CONTINUE;
05277             res = 0;
05278          }
05279       } else if (qe.valid_digits) {
05280          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05281             "%s|%d", qe.digits, qe.pos);
05282       }
05283    }
05284 
05285    /* Don't allow return code > 0 */
05286    if (res >= 0) {
05287       res = 0; 
05288       if (ringing) {
05289          ast_indicate(chan, -1);
05290       } else {
05291          ast_moh_stop(chan);
05292       }        
05293       ast_stopstream(chan);
05294    }
05295 
05296    set_queue_variables(qe.parent, qe.chan);
05297 
05298    leave_queue(&qe);
05299    if (reason != QUEUE_UNKNOWN)
05300       set_queue_result(chan, reason);
05301 
05302    if (qe.parent) {
05303       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
05304        * This ref must be taken away right before the queue_ent is destroyed.  In this case
05305        * the queue_ent is about to be returned on the stack */
05306       qe.parent = queue_unref(qe.parent);
05307    }
05308 
05309    return res;
05310 }

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 5546 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.

05547 {
05548    int penalty;
05549    AST_DECLARE_APP_ARGS(args,
05550       AST_APP_ARG(queuename);
05551       AST_APP_ARG(interface);
05552    );
05553    /* Make sure the returned value on error is NULL. */
05554    buf[0] = '\0';
05555 
05556    if (ast_strlen_zero(data)) {
05557       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05558       return -1;
05559    }
05560 
05561    AST_STANDARD_APP_ARGS(args, data);
05562 
05563    if (args.argc < 2) {
05564       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05565       return -1;
05566    }
05567 
05568    penalty = get_member_penalty (args.queuename, args.interface);
05569    
05570    if (penalty >= 0) /* remember that buf is already '\0' */
05571       snprintf (buf, len, "%d", penalty);
05572 
05573    return 0;
05574 }

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 5577 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().

05578 {
05579    int penalty;
05580    AST_DECLARE_APP_ARGS(args,
05581       AST_APP_ARG(queuename);
05582       AST_APP_ARG(interface);
05583    );
05584 
05585    if (ast_strlen_zero(data)) {
05586       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05587       return -1;
05588    }
05589 
05590    AST_STANDARD_APP_ARGS(args, data);
05591 
05592    if (args.argc < 2) {
05593       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05594       return -1;
05595    }
05596 
05597    penalty = atoi(value);
05598 
05599    if (ast_strlen_zero(args.interface)) {
05600       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05601       return -1;
05602    }
05603 
05604    /* if queuename = NULL then penalty will be set for interface in all the queues. */
05605    if (set_member_penalty(args.queuename, args.interface, penalty)) {
05606       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05607       return -1;
05608    }
05609 
05610    return 0;
05611 }

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 5365 of file app_queue.c.

References ao2_iterator_destroy(), 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, call_queue::membercount, call_queue::members, member::paused, queue_t_unref, and member::status.

05366 {
05367    int count = 0;
05368    struct member *m;
05369    struct ao2_iterator mem_iter;
05370    struct call_queue *q;
05371    char *option;
05372 
05373    if (ast_strlen_zero(data)) {
05374       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05375       return -1;
05376    }
05377 
05378    if ((option = strchr(data, ',')))
05379       *option++ = '\0';
05380    else
05381       option = "logged";
05382    if ((q = load_realtime_queue(data))) {
05383       ao2_lock(q);
05384       if (!strcasecmp(option, "logged")) {
05385          mem_iter = ao2_iterator_init(q->members, 0);
05386          while ((m = ao2_iterator_next(&mem_iter))) {
05387             /* Count the agents who are logged in and presently answering calls */
05388             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05389                count++;
05390             }
05391             ao2_ref(m, -1);
05392          }
05393          ao2_iterator_destroy(&mem_iter);
05394       } else if (!strcasecmp(option, "free")) {
05395          mem_iter = ao2_iterator_init(q->members, 0);
05396          while ((m = ao2_iterator_next(&mem_iter))) {
05397             /* Count the agents who are logged in and presently answering calls */
05398             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05399                count++;
05400             }
05401             ao2_ref(m, -1);
05402          }
05403          ao2_iterator_destroy(&mem_iter);
05404       } else /* must be "count" */
05405          count = q->membercount;
05406       ao2_unlock(q);
05407       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
05408    } else
05409       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05410 
05411    snprintf(buf, len, "%d", count);
05412 
05413    return 0;
05414 }

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 5421 of file app_queue.c.

References ao2_iterator_destroy(), 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, call_queue::members, queue_t_unref, and member::status.

05422 {
05423    int count = 0;
05424    struct member *m;
05425    struct call_queue *q;
05426    struct ao2_iterator mem_iter;
05427    static int depflag = 1;
05428 
05429    if (depflag) {
05430       depflag = 0;
05431       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");
05432    }
05433 
05434    if (ast_strlen_zero(data)) {
05435       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05436       return -1;
05437    }
05438    
05439    if ((q = load_realtime_queue(data))) {
05440       ao2_lock(q);
05441       mem_iter = ao2_iterator_init(q->members, 0);
05442       while ((m = ao2_iterator_next(&mem_iter))) {
05443          /* Count the agents who are logged in and presently answering calls */
05444          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05445             count++;
05446          }
05447          ao2_ref(m, -1);
05448       }
05449       ao2_iterator_destroy(&mem_iter);
05450       ao2_unlock(q);
05451       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
05452    } else
05453       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05454 
05455    snprintf(buf, len, "%d", count);
05456 
05457    return 0;
05458 }

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 5497 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_find, ao2_unlock(), ast_log(), ast_strlen_zero(), member::interface, LOG_ERROR, LOG_WARNING, call_queue::members, OBJ_POINTER, queue_t_unref, and queues.

05498 {
05499    struct call_queue *q, tmpq = {
05500       .name = data,  
05501    };
05502    struct member *m;
05503 
05504    /* Ensure an otherwise empty list doesn't return garbage */
05505    buf[0] = '\0';
05506 
05507    if (ast_strlen_zero(data)) {
05508       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05509       return -1;
05510    }
05511 
05512    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
05513       int buflen = 0, count = 0;
05514       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05515 
05516       ao2_lock(q);
05517       while ((m = ao2_iterator_next(&mem_iter))) {
05518          /* strcat() is always faster than printf() */
05519          if (count++) {
05520             strncat(buf + buflen, ",", len - buflen - 1);
05521             buflen++;
05522          }
05523          strncat(buf + buflen, m->interface, len - buflen - 1);
05524          buflen += strlen(m->interface);
05525          /* Safeguard against overflow (negative length) */
05526          if (buflen >= len - 2) {
05527             ao2_ref(m, -1);
05528             ast_log(LOG_WARNING, "Truncating list\n");
05529             break;
05530          }
05531          ao2_ref(m, -1);
05532       }
05533       ao2_iterator_destroy(&mem_iter);
05534       ao2_unlock(q);
05535       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
05536    } else
05537       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05538 
05539    /* We should already be terminated, but let's make sure. */
05540    buf[len - 1] = '\0';
05541 
05542    return 0;
05543 }

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 5461 of file app_queue.c.

References ao2_lock(), ao2_t_find, ao2_unlock(), ast_load_realtime(), ast_log(), ast_strlen_zero(), ast_variables_destroy(), call_queue::count, LOG_ERROR, LOG_WARNING, OBJ_POINTER, queue_t_unref, queues, SENTINEL, and var.

05462 {
05463    int count = 0;
05464    struct call_queue *q, tmpq = {
05465       .name = data,  
05466    };
05467    struct ast_variable *var = NULL;
05468 
05469    buf[0] = '\0';
05470    
05471    if (ast_strlen_zero(data)) {
05472       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05473       return -1;
05474    }
05475 
05476    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
05477       ao2_lock(q);
05478       count = q->count;
05479       ao2_unlock(q);
05480       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
05481    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05482       /* if the queue is realtime but was not found in memory, this
05483        * means that the queue had been deleted from memory since it was 
05484        * "dead." This means it has a 0 waiting count
05485        */
05486       count = 0;
05487       ast_variables_destroy(var);
05488    } else
05489       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05490 
05491    snprintf(buf, len, "%d", count);
05492 
05493    return 0;
05494 }

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 5317 of file app_queue.c.

References ao2_lock(), ao2_t_find, ao2_unlock(), ast_log(), ast_strlen_zero(), call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::count, call_queue::holdtime, int2strat(), LOG_ERROR, LOG_WARNING, call_queue::maxlen, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_t_unref, queues, call_queue::servicelevel, call_queue::setqueuevar, call_queue::strategy, and call_queue::talktime.

05318 {
05319    int res = -1;
05320    struct call_queue *q, tmpq = {
05321       .name = data,  
05322    };
05323 
05324    char interfacevar[256] = "";
05325    float sl = 0;
05326 
05327    if (ast_strlen_zero(data)) {
05328       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05329       return -1;
05330    }
05331 
05332    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
05333       ao2_lock(q);
05334       if (q->setqueuevar) {
05335          sl = 0;
05336          res = 0;
05337 
05338          if (q->callscompleted > 0) {
05339             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05340          }
05341 
05342          snprintf(interfacevar, sizeof(interfacevar),
05343             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05344             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
05345 
05346          pbx_builtin_setvar_multiple(chan, interfacevar);
05347       }
05348 
05349       ao2_unlock(q);
05350       queue_t_unref(q, "Done with QUEUE() function");
05351    } else {
05352       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05353    }
05354 
05355    snprintf(buf, len, "%d", res);
05356 
05357    return 0;
05358 }

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

Definition at line 863 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

00864 {
00865    const struct call_queue *q = obj;
00866 
00867    return ast_str_case_hash(q->name);
00868 }

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

Definition at line 888 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

00889 {
00890    ao2_ref(q, 1);
00891    return q;
00892 }

static void queue_set_global_params ( struct ast_config cfg  )  [static]

Set the global queue parameters as defined in the "general" section of queues.conf

Definition at line 5698 of file app_queue.c.

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

05699 {
05700    const char *general_val = NULL;
05701    queue_persistent_members = 0;
05702    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05703       queue_persistent_members = ast_true(general_val);
05704    autofill_default = 0;
05705    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05706       autofill_default = ast_true(general_val);
05707    montype_default = 0;
05708    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05709       if (!strcasecmp(general_val, "mixmonitor"))
05710          montype_default = 1;
05711    }
05712    update_cdr = 0;
05713    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05714       update_cdr = ast_true(general_val);
05715    shared_lastcall = 0;
05716    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05717       shared_lastcall = ast_true(general_val);
05718 }

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 1385 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_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, queue_ent::moh, call_queue::monfmt, call_queue::montype, call_queue::name, call_queue::numperiodicannounce, parse_empty_options(), call_queue::periodicannouncefrequency, 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, call_queue::timeout, TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

01386 {
01387    if (!strcasecmp(param, "musicclass") || 
01388       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01389       ast_string_field_set(q, moh, val);
01390    } else if (!strcasecmp(param, "announce")) {
01391       ast_string_field_set(q, announce, val);
01392    } else if (!strcasecmp(param, "context")) {
01393       ast_string_field_set(q, context, val);
01394    } else if (!strcasecmp(param, "timeout")) {
01395       q->timeout = atoi(val);
01396       if (q->timeout < 0)
01397          q->timeout = DEFAULT_TIMEOUT;
01398    } else if (!strcasecmp(param, "ringinuse")) {
01399       q->ringinuse = ast_true(val);
01400    } else if (!strcasecmp(param, "setinterfacevar")) {
01401       q->setinterfacevar = ast_true(val);
01402    } else if (!strcasecmp(param, "setqueuevar")) {
01403       q->setqueuevar = ast_true(val);
01404    } else if (!strcasecmp(param, "setqueueentryvar")) {
01405       q->setqueueentryvar = ast_true(val);
01406    } else if (!strcasecmp(param, "monitor-format")) {
01407       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01408    } else if (!strcasecmp(param, "membermacro")) {
01409       ast_string_field_set(q, membermacro, val);
01410    } else if (!strcasecmp(param, "membergosub")) {
01411       ast_string_field_set(q, membergosub, val);
01412    } else if (!strcasecmp(param, "queue-youarenext")) {
01413       ast_string_field_set(q, sound_next, val);
01414    } else if (!strcasecmp(param, "queue-thereare")) {
01415       ast_string_field_set(q, sound_thereare, val);
01416    } else if (!strcasecmp(param, "queue-callswaiting")) {
01417       ast_string_field_set(q, sound_calls, val);
01418    } else if (!strcasecmp(param, "queue-quantity1")) {
01419       ast_string_field_set(q, queue_quantity1, val);
01420    } else if (!strcasecmp(param, "queue-quantity2")) {
01421       ast_string_field_set(q, queue_quantity2, val);
01422    } else if (!strcasecmp(param, "queue-holdtime")) {
01423       ast_string_field_set(q, sound_holdtime, val);
01424    } else if (!strcasecmp(param, "queue-minutes")) {
01425       ast_string_field_set(q, sound_minutes, val);
01426    } else if (!strcasecmp(param, "queue-minute")) {
01427       ast_string_field_set(q, sound_minute, val);
01428    } else if (!strcasecmp(param, "queue-seconds")) {
01429       ast_string_field_set(q, sound_seconds, val);
01430    } else if (!strcasecmp(param, "queue-thankyou")) {
01431       ast_string_field_set(q, sound_thanks, val);
01432    } else if (!strcasecmp(param, "queue-callerannounce")) {
01433       ast_string_field_set(q, sound_callerannounce, val);
01434    } else if (!strcasecmp(param, "queue-reporthold")) {
01435       ast_string_field_set(q, sound_reporthold, val);
01436    } else if (!strcasecmp(param, "announce-frequency")) {
01437       q->announcefrequency = atoi(val);
01438    } else if (!strcasecmp(param, "min-announce-frequency")) {
01439       q->minannouncefrequency = atoi(val);
01440       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01441    } else if (!strcasecmp(param, "announce-round-seconds")) {
01442       q->roundingseconds = atoi(val);
01443       /* Rounding to any other values just doesn't make sense... */
01444       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01445          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01446          if (linenum >= 0) {
01447             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01448                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01449                val, param, q->name, linenum);
01450          } else {
01451             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01452                "using 0 instead for queue '%s'\n", val, param, q->name);
01453          }
01454          q->roundingseconds=0;
01455       }
01456    } else if (!strcasecmp(param, "announce-holdtime")) {
01457       if (!strcasecmp(val, "once"))
01458          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01459       else if (ast_true(val))
01460          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01461       else
01462          q->announceholdtime = 0;
01463    } else if (!strcasecmp(param, "announce-position")) {
01464       if (!strcasecmp(val, "limit"))
01465          q->announceposition = ANNOUNCEPOSITION_LIMIT;
01466       else if (!strcasecmp(val, "more"))
01467          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01468       else if (ast_true(val))
01469          q->announceposition = ANNOUNCEPOSITION_YES;
01470       else
01471          q->announceposition = ANNOUNCEPOSITION_NO;
01472    } else if (!strcasecmp(param, "announce-position-limit")) {
01473       q->announcepositionlimit = atoi(val);
01474    } else if (!strcasecmp(param, "periodic-announce")) {
01475       if (strchr(val, ',')) {
01476          char *s, *buf = ast_strdupa(val);
01477          unsigned int i = 0;
01478 
01479          while ((s = strsep(&buf, ",|"))) {
01480             if (!q->sound_periodicannounce[i])
01481                q->sound_periodicannounce[i] = ast_str_create(16);
01482             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01483             i++;
01484             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01485                break;
01486          }
01487          q->numperiodicannounce = i;
01488       } else {
01489          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01490          q->numperiodicannounce = 1;
01491       }
01492    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01493       q->periodicannouncefrequency = atoi(val);
01494    } else if (!strcasecmp(param, "random-periodic-announce")) {
01495       q->randomperiodicannounce = ast_true(val);
01496    } else if (!strcasecmp(param, "retry")) {
01497       q->retry = atoi(val);
01498       if (q->retry <= 0)
01499          q->retry = DEFAULT_RETRY;
01500    } else if (!strcasecmp(param, "wrapuptime")) {
01501       q->wrapuptime = atoi(val);
01502    } else if (!strcasecmp(param, "autofill")) {
01503       q->autofill = ast_true(val);
01504    } else if (!strcasecmp(param, "monitor-type")) {
01505       if (!strcasecmp(val, "mixmonitor"))
01506          q->montype = 1;
01507    } else if (!strcasecmp(param, "autopause")) {
01508       q->autopause = ast_true(val);
01509    } else if (!strcasecmp(param, "maxlen")) {
01510       q->maxlen = atoi(val);
01511       if (q->maxlen < 0)
01512          q->maxlen = 0;
01513    } else if (!strcasecmp(param, "servicelevel")) {
01514       q->servicelevel= atoi(val);
01515    } else if (!strcasecmp(param, "strategy")) {
01516       int strategy;
01517 
01518       /* We are a static queue and already have set this, no need to do it again */
01519       if (failunknown) {
01520          return;
01521       }
01522       strategy = strat2int(val);
01523       if (strategy < 0) {
01524          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01525             val, q->name);
01526          q->strategy = QUEUE_STRATEGY_RINGALL;
01527       }
01528       if (strategy == q->strategy) {
01529          return;
01530       }
01531       if (strategy == QUEUE_STRATEGY_LINEAR) {
01532          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
01533          return;
01534       }
01535       q->strategy = strategy;
01536    } else if (!strcasecmp(param, "joinempty")) {
01537       parse_empty_options(val, &q->joinempty, 1);
01538    } else if (!strcasecmp(param, "leavewhenempty")) {
01539       parse_empty_options(val, &q->leavewhenempty, 0);
01540    } else if (!strcasecmp(param, "eventmemberstatus")) {
01541       q->maskmemberstatus = !ast_true(val);
01542    } else if (!strcasecmp(param, "eventwhencalled")) {
01543       if (!strcasecmp(val, "vars")) {
01544          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01545       } else {
01546          q->eventwhencalled = ast_true(val) ? 1 : 0;
01547       }
01548    } else if (!strcasecmp(param, "reportholdtime")) {
01549       q->reportholdtime = ast_true(val);
01550    } else if (!strcasecmp(param, "memberdelay")) {
01551       q->memberdelay = atoi(val);
01552    } else if (!strcasecmp(param, "weight")) {
01553       q->weight = atoi(val);
01554    } else if (!strcasecmp(param, "timeoutrestart")) {
01555       q->timeoutrestart = ast_true(val);
01556    } else if (!strcasecmp(param, "defaultrule")) {
01557       ast_string_field_set(q, defaultrule, val);
01558    } else if (!strcasecmp(param, "timeoutpriority")) {
01559       if (!strcasecmp(val, "conf")) {
01560          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
01561       } else {
01562          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01563       }
01564    } else if (failunknown) {
01565       if (linenum >= 0) {
01566          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01567             q->name, param, linenum);
01568       } else {
01569          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01570       }
01571    }
01572 }

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

Definition at line 6240 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.

06241 {
06242    switch ( cmd ) {
06243    case CLI_INIT:
06244       e->command = "queue show";
06245       e->usage =
06246          "Usage: queue show\n"
06247          "       Provides summary information on a specified queue.\n";
06248       return NULL;
06249    case CLI_GENERATE:
06250       return complete_queue_show(a->line, a->word, a->pos, a->n); 
06251    }
06252 
06253    return __queues_show(NULL, a->fd, a->argc, a->argv);
06254 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 3426 of file app_queue.c.

References ast_free.

03427 {
03428    struct queue_transfer_ds *qtds = data;
03429    ast_free(qtds);
03430 }

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 3449 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().

03450 {
03451    struct queue_transfer_ds *qtds = data;
03452    struct queue_ent *qe = qtds->qe;
03453    struct member *member = qtds->member;
03454    time_t callstart = qtds->starttime;
03455    int callcompletedinsl = qtds->callcompletedinsl;
03456    struct ast_datastore *datastore;
03457 
03458    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03459             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03460             (long) (time(NULL) - callstart), qe->opos);
03461 
03462    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
03463    
03464    /* No need to lock the channels because they are already locked in ast_do_masquerade */
03465    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03466       ast_channel_datastore_remove(old_chan, datastore);
03467    } else {
03468       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03469    }
03470 }

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

Definition at line 894 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec().

00895 {
00896    ao2_ref(q, -1);
00897    return q;
00898 }

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

Definition at line 2224 of file app_queue.c.

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

Referenced by try_calling().

02225 {
02226    int oldvalue;
02227 
02228    /* Calculate holdtime using an exponential average */
02229    /* Thanks to SRT for this contribution */
02230    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02231 
02232    ao2_lock(qe->parent);
02233    oldvalue = qe->parent->holdtime;
02234    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02235    ao2_unlock(qe->parent);
02236 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 2784 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().

02785 {
02786    ao2_lock(qe->parent);
02787    set_queue_variables(qe->parent, qe->chan);
02788    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02789       "Queue: %s\r\n"
02790       "Uniqueid: %s\r\n"
02791       "Position: %d\r\n"
02792       "OriginalPosition: %d\r\n"
02793       "HoldTime: %d\r\n",
02794       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02795 
02796    qe->parent->callsabandoned++;
02797    ao2_unlock(qe->parent);
02798 }

static int reload ( void   )  [static]

Definition at line 7280 of file app_queue.c.

References AST_FLAGS_ALL, ast_unload_realtime(), and reload_handler().

07281 {
07282    struct ast_flags mask = {AST_FLAGS_ALL,};
07283    ast_unload_realtime("queue_members");
07284    reload_handler(1, &mask, NULL);
07285    return 0;
07286 }

static int reload_handler ( int  reload,
struct ast_flags mask,
const char *  queuename 
) [static]

The command center for all reload operations.

Whenever any piece of queue information is to be reloaded, this function is called. It interprets the flags set in the mask parameter and acts based on how they are set.

Parameters:
reload True if we are reloading information, false if we are loading information for the first time.
mask A bitmask which tells the handler what actions to take
queuename The name of the queue on which we wish to take action
Return values:
0 All reloads were successful
non-zero There was a failure

Definition at line 6052 of file app_queue.c.

References ast_test_flag, clear_stats(), QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, QUEUE_RESET_STATS, reload_queue_rules(), and reload_queues().

Referenced by handle_queue_reload(), handle_queue_reset(), load_module(), manager_queue_reload(), manager_queue_reset(), and reload().

06053 {
06054    int res = 0;
06055 
06056    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
06057       res |= reload_queue_rules(reload);
06058    }
06059    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
06060       res |= clear_stats(queuename);
06061    }
06062    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
06063       res |= reload_queues(reload, mask, queuename);
06064    }
06065    return res;
06066 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 4667 of file app_queue.c.

References add_to_queue(), ao2_lock(), ao2_t_find, 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, member::membername, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_t_unref, queues, RES_OUTOFMEMORY, and member::state_interface.

Referenced by load_module().

04668 {
04669    char *cur_ptr;
04670    const char *queue_name;
04671    char *member;
04672    char *interface;
04673    char *membername = NULL;
04674    char *state_interface;
04675    char *penalty_tok;
04676    int penalty = 0;
04677    char *paused_tok;
04678    int paused = 0;
04679    struct ast_db_entry *db_tree;
04680    struct ast_db_entry *entry;
04681    struct call_queue *cur_queue;
04682    char queue_data[PM_MAX_LEN];
04683 
04684    ao2_lock(queues);
04685 
04686    /* Each key in 'pm_family' is the name of a queue */
04687    db_tree = ast_db_gettree(pm_family, NULL);
04688    for (entry = db_tree; entry; entry = entry->next) {
04689 
04690       queue_name = entry->key + strlen(pm_family) + 2;
04691 
04692       {
04693          struct call_queue tmpq = {
04694             .name = queue_name,
04695          };
04696          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
04697       }  
04698 
04699       if (!cur_queue)
04700          cur_queue = load_realtime_queue(queue_name);
04701 
04702       if (!cur_queue) {
04703          /* If the queue no longer exists, remove it from the
04704           * database */
04705          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04706          ast_db_del(pm_family, queue_name);
04707          continue;
04708       } 
04709 
04710       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04711          queue_t_unref(cur_queue, "Expire reload reference");
04712          continue;
04713       }
04714 
04715       cur_ptr = queue_data;
04716       while ((member = strsep(&cur_ptr, ",|"))) {
04717          if (ast_strlen_zero(member))
04718             continue;
04719 
04720          interface = strsep(&member, ";");
04721          penalty_tok = strsep(&member, ";");
04722          paused_tok = strsep(&member, ";");
04723          membername = strsep(&member, ";");
04724          state_interface = strsep(&member, ";");
04725 
04726          if (!penalty_tok) {
04727             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04728             break;
04729          }
04730          penalty = strtol(penalty_tok, NULL, 10);
04731          if (errno == ERANGE) {
04732             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04733             break;
04734          }
04735          
04736          if (!paused_tok) {
04737             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04738             break;
04739          }
04740          paused = strtol(paused_tok, NULL, 10);
04741          if ((errno == ERANGE) || paused < 0 || paused > 1) {
04742             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04743             break;
04744          }
04745 
04746          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
04747          
04748          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04749             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04750             break;
04751          }
04752       }
04753       queue_t_unref(cur_queue, "Expire reload reference");
04754    }
04755 
04756    ao2_unlock(queues);
04757    if (db_tree) {
04758       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04759       ast_db_freetree(db_tree);
04760    }
04761 }

static int reload_queue_rules ( int  reload  )  [static]

Reload the rules defined in queuerules.conf.

Parameters:
reload If 1, then only process queuerules.conf if the file has changed since the last time we inspected it.
Returns:
Always returns AST_MODULE_LOAD_SUCCESS

Definition at line 5650 of file app_queue.c.

References ast_calloc, ast_category_browse(), ast_config_destroy(), 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_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, insert_penaltychange(), ast_variable::lineno, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, rule_list::name, ast_variable::next, rule_list::rules, and ast_variable::value.

Referenced by reload_handler().

05651 {
05652    struct ast_config *cfg;
05653    struct rule_list *rl_iter, *new_rl;
05654    struct penalty_rule *pr_iter;
05655    char *rulecat = NULL;
05656    struct ast_variable *rulevar = NULL;
05657    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05658    
05659    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05660       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05661       return AST_MODULE_LOAD_SUCCESS;
05662    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05663       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05664       return AST_MODULE_LOAD_SUCCESS;
05665    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05666       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
05667       return AST_MODULE_LOAD_SUCCESS;
05668    }
05669 
05670    AST_LIST_LOCK(&rule_lists);
05671    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05672       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05673          ast_free(pr_iter);
05674       ast_free(rl_iter);
05675    }
05676    while ((rulecat = ast_category_browse(cfg, rulecat))) {
05677       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05678          AST_LIST_UNLOCK(&rule_lists);
05679          return AST_MODULE_LOAD_FAILURE;
05680       } else {
05681          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05682          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05683          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05684             if(!strcasecmp(rulevar->name, "penaltychange"))
05685                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05686             else
05687                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05688       }
05689    }
05690    AST_LIST_UNLOCK(&rule_lists);
05691 
05692    ast_config_destroy(cfg);
05693 
05694    return AST_MODULE_LOAD_SUCCESS;
05695 }

static int reload_queues ( int  reload,
struct ast_flags mask,
const char *  queuename 
) [static]

reload the queues.conf file

This function reloads the information in the general section of the queues.conf file and potentially more, depending on the value of mask.

Parameters:
reload 0 if we are calling this the first time, 1 every other time
mask Gives flags telling us what information to actually reload
queuename If set to a non-zero string, then only reload information from that particular queue. Otherwise inspect all queues
Return values:
-1 Failure occurred
0 All clear!

Definition at line 5964 of file app_queue.c.

References ao2_callback, ao2_lock(), ao2_unlock(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_log(), ast_strlen_zero(), ast_test_flag, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, kill_dead_queues(), LOG_ERROR, LOG_NOTICE, mark_dead_and_unfound(), OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, QUEUE_RELOAD_PARAMETERS, queue_set_global_params(), queues, and reload_single_queue().

Referenced by reload_handler().

05965 {
05966    struct ast_config *cfg;
05967    char *cat;
05968    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05969    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
05970 
05971    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05972       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05973       return -1;
05974    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05975       return 0;
05976    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
05977       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
05978       return -1;
05979    }
05980 
05981    /* We've made it here, so it looks like we're doing operations on all queues. */
05982    ao2_lock(queues);
05983    
05984    /* Mark all queues as dead for the moment if we're reloading queues.
05985     * For clarity, we could just be reloading members, in which case we don't want to mess
05986     * with the other queue parameters at all*/
05987    if (queue_reload) {
05988       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
05989    }
05990 
05991    /* Chug through config file */
05992    cat = NULL;
05993    while ((cat = ast_category_browse(cfg, cat)) ) {
05994       if (!strcasecmp(cat, "general") && queue_reload) {
05995          queue_set_global_params(cfg);
05996          continue;
05997       }
05998       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
05999          reload_single_queue(cfg, mask, cat);
06000    }
06001 
06002    ast_config_destroy(cfg);
06003    /* Unref all the dead queues if we were reloading queues */
06004    if (queue_reload) {
06005       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
06006    }
06007    ao2_unlock(queues);
06008    return 0;
06009 }

static void reload_single_member ( const char *  memberdata,
struct call_queue q 
) [static]

reload information pertaining to a single member

This function is called when a member = line is encountered in queues.conf.

Parameters:
memberdata The part after member = in the config file
q The queue to which this member belongs

Definition at line 5728 of file app_queue.c.

References ao2_find, ao2_link, ao2_ref, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strip(), ast_strlen_zero(), create_queue_member(), member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, and member::penalty.

Referenced by reload_single_queue().

05729 {
05730    char *membername, *interface, *state_interface, *tmp;
05731    char *parse;
05732    struct member *cur, *newm;
05733    struct member tmpmem;
05734    int penalty;
05735    AST_DECLARE_APP_ARGS(args,
05736       AST_APP_ARG(interface);
05737       AST_APP_ARG(penalty);
05738       AST_APP_ARG(membername);
05739       AST_APP_ARG(state_interface);
05740    );
05741 
05742    if (ast_strlen_zero(memberdata)) {
05743       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
05744       return;
05745    }
05746 
05747    /* Add a new member */
05748    parse = ast_strdupa(memberdata);
05749             
05750    AST_STANDARD_APP_ARGS(args, parse);
05751 
05752    interface = args.interface;
05753    if (!ast_strlen_zero(args.penalty)) {
05754       tmp = args.penalty;
05755       ast_strip(tmp);
05756       penalty = atoi(tmp);
05757       if (penalty < 0) {
05758          penalty = 0;
05759       }
05760    } else {
05761       penalty = 0;
05762    }
05763 
05764    if (!ast_strlen_zero(args.membername)) {
05765       membername = args.membername;
05766       ast_strip(membername);
05767    } else {
05768       membername = interface;
05769    }
05770 
05771    if (!ast_strlen_zero(args.state_interface)) {
05772       state_interface = args.state_interface;
05773       ast_strip(state_interface);
05774    } else {
05775       state_interface = interface;
05776    }
05777 
05778    /* Find the old position in the list */
05779    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05780    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05781    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
05782       ao2_link(q->members, newm);
05783       ao2_ref(newm, -1);
05784    }
05785    newm = NULL;
05786 
05787    if (cur) {
05788       ao2_ref(cur, -1);
05789    } else {
05790       q->membercount++;
05791    }
05792 }

static void reload_single_queue ( struct ast_config cfg,
struct ast_flags mask,
const char *  queuename 
) [static]

Reload information pertaining to a particular queue.

Once we have isolated a queue within reload_queues, we call this. This will either reload information for the queue or if we're just reloading member information, we'll just reload that without touching other settings within the queue

Parameters:
cfg The configuration which we are reading
mask Tells us what information we need to reload
queuename The name of the queue we are reloading information from
Return values:
void 

Definition at line 5834 of file app_queue.c.

References alloc_queue(), ao2_callback, ao2_lock(), ao2_t_find, ao2_unlock(), ast_atomic_fetchadd_int(), ast_log(), ast_test_flag, ast_variable_browse(), ast_variable_retrieve(), call_queue::found, init_queue(), kill_dead_members(), ast_variable::lineno, LOG_WARNING, mark_member_dead(), call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_POINTER, OBJ_UNLINK, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_link, reload_single_member(), strat2int(), call_queue::strategy, ast_variable::value, var, and call_queue::weight.

Referenced by reload_queues().

05835 {
05836    int new;
05837    struct call_queue *q = NULL;
05838    /*We're defining a queue*/
05839    struct call_queue tmpq = {
05840       .name = queuename,
05841    };
05842    const char *tmpvar;
05843    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
05844    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
05845    int prev_weight = 0;
05846    struct ast_variable *var;
05847    if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
05848       if (queue_reload) {
05849          /* Make one then */
05850          if (!(q = alloc_queue(queuename))) {
05851             return;
05852          }
05853       } else {
05854          /* Since we're not reloading queues, this means that we found a queue
05855           * in the configuration file which we don't know about yet. Just return.
05856           */
05857          return;
05858       }
05859       new = 1;
05860    } else {
05861       new = 0;
05862    }
05863    
05864    if (!new) {
05865       ao2_lock(q);
05866       prev_weight = q->weight ? 1 : 0;
05867    }
05868    /* Check if we already found a queue with this name in the config file */
05869    if (q->found) {
05870       ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
05871       if (!new) {
05872          /* It should be impossible to *not* hit this case*/
05873          ao2_unlock(q);
05874       }
05875       queue_t_unref(q, "We exist! Expiring temporary pointer");
05876       return;
05877    }
05878    /* Due to the fact that the "linear" strategy will have a different allocation
05879     * scheme for queue members, we must devise the queue's strategy before other initializations.
05880     * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
05881     * container used will have only a single bucket instead of the typical number.
05882     */
05883    if (queue_reload) {
05884       if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
05885          q->strategy = strat2int(tmpvar);
05886          if (q->strategy < 0) {
05887             ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05888             tmpvar, q->name);
05889             q->strategy = QUEUE_STRATEGY_RINGALL;
05890          }
05891       } else {
05892          q->strategy = QUEUE_STRATEGY_RINGALL;
05893       }
05894       init_queue(q);
05895    }
05896    if (member_reload) {
05897       q->membercount = 0;
05898       ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
05899    }
05900    for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
05901       if (member_reload && !strcasecmp(var->name, "member")) {
05902          reload_single_member(var->value, q);
05903       } else if (queue_reload) {
05904          queue_set_param(q, var->name, var->value, var->lineno, 1);
05905       }
05906    }
05907    /* At this point, we've determined if the queue has a weight, so update use_weight
05908     * as appropriate
05909     */
05910    if (!q->weight && prev_weight) {
05911       ast_atomic_fetchadd_int(&use_weight, -1);
05912    }
05913    else if (q->weight && !prev_weight) {
05914       ast_atomic_fetchadd_int(&use_weight, +1);
05915    }
05916 
05917    /* Free remaining members marked as delme */
05918    if (member_reload) {
05919       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
05920    }
05921 
05922    if (new) {
05923       queues_t_link(queues, q, "Add queue to container");
05924    } else {
05925       ao2_unlock(q);
05926    }
05927    queue_t_unref(q, "Expiring creation reference");
05928 }

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 4403 of file app_queue.c.

References ao2_find, ao2_lock(), ao2_ref, ao2_t_find, 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_t_unref, queues, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

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

04404 {
04405    struct call_queue *q, tmpq = {
04406       .name = queuename,   
04407    };
04408    struct member *mem, tmpmem;
04409    int res = RES_NOSUCHQUEUE;
04410 
04411    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04412    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
04413       ao2_lock(queues);
04414       ao2_lock(q);
04415       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04416          /* XXX future changes should beware of this assumption!! */
04417          if (!mem->dynamic) {
04418             ao2_ref(mem, -1);
04419             ao2_unlock(q);
04420             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
04421             ao2_unlock(queues);
04422             return RES_NOT_DYNAMIC;
04423          }
04424          q->membercount--;
04425          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04426             "Queue: %s\r\n"
04427             "Location: %s\r\n"
04428             "MemberName: %s\r\n",
04429             q->name, mem->interface, mem->membername);
04430          ao2_unlink(q->members, mem);
04431          ao2_ref(mem, -1);
04432 
04433          if (queue_persistent_members)
04434             dump_queue_members(q);
04435          
04436          res = RES_OKAY;
04437       } else {
04438          res = RES_EXISTS;
04439       }
04440       ao2_unlock(q);
04441       ao2_unlock(queues);
04442       queue_t_unref(q, "Expiring temporary reference");
04443    }
04444 
04445    return res;
04446 }

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:

  • Agent on call
  • Agent is paused
  • Wrapup time not expired
  • Priority by another queue
Return values:
1 on success to reach a free agent
0 on failure to get agent.

Definition at line 2467 of file app_queue.c.

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ao2_lock(), ao2_unlock(), ast_channel::appl, ast_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, AST_FLAG_ANSWERED_ELSEWHERE, ast_free, ast_request(), ast_set_flag, ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_cdr::clid, compare_weight(), ast_channel::context, ast_channel::data, ast_cdr::dcontext, dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, 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, ast_cdr::src, member::state_interface, member::status, status, callattempt::stillgoing, ast_channel::uniqueid, update_status(), ast_cdr::userfield, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

02468 {
02469    int res;
02470    int status;
02471    char tech[256];
02472    char *location;
02473    const char *macrocontext, *macroexten;
02474 
02475    /* on entry here, we know that tmp->chan == NULL */
02476    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
02477       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
02478       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
02479             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
02480       if (qe->chan->cdr)
02481          ast_cdr_busy(qe->chan->cdr);
02482       tmp->stillgoing = 0;
02483       (*busies)++;
02484       return 0;
02485    }
02486 
02487    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
02488       ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
02489       if (qe->chan->cdr)
02490          ast_cdr_busy(qe->chan->cdr);
02491       tmp->stillgoing = 0;
02492       return 0;
02493    }
02494 
02495    if (tmp->member->paused) {
02496       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
02497       if (qe->chan->cdr)
02498          ast_cdr_busy(qe->chan->cdr);
02499       tmp->stillgoing = 0;
02500       return 0;
02501    }
02502    if (use_weight && compare_weight(qe->parent,tmp->member)) {
02503       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
02504       if (qe->chan->cdr)
02505          ast_cdr_busy(qe->chan->cdr);
02506       tmp->stillgoing = 0;
02507       (*busies)++;
02508       return 0;
02509    }
02510 
02511    ast_copy_string(tech, tmp->interface, sizeof(tech));
02512    if ((location = strchr(tech, '/')))
02513       *location++ = '\0';
02514    else
02515       location = "";
02516 
02517    /* Request the peer */
02518    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02519    if (!tmp->chan) {       /* If we can't, just go on to the next call */
02520       if (qe->chan->cdr)
02521          ast_cdr_busy(qe->chan->cdr);
02522       tmp->stillgoing = 0; 
02523 
02524       ao2_lock(qe->parent);
02525       update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface));
02526       qe->parent->rrpos++;
02527       qe->linpos++;
02528       ao2_unlock(qe->parent);
02529 
02530       (*busies)++;
02531       return 0;
02532    }
02533    
02534    if (qe->cancel_answered_elsewhere) {
02535       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02536    }
02537    tmp->chan->appl = "AppQueue";
02538    tmp->chan->data = "(Outgoing Line)";
02539    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
02540    if (tmp->chan->cid.cid_num)
02541       ast_free(tmp->chan->cid.cid_num);
02542    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02543    if (tmp->chan->cid.cid_name)
02544       ast_free(tmp->chan->cid.cid_name);
02545    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02546    if (tmp->chan->cid.cid_ani)
02547       ast_free(tmp->chan->cid.cid_ani);
02548    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02549 
02550    /* Inherit specially named variables from parent channel */
02551    ast_channel_inherit_variables(qe->chan, tmp->chan);
02552    ast_channel_datastore_inherit(qe->chan, tmp->chan);
02553 
02554    /* Presense of ADSI CPE on outgoing channel follows ours */
02555    tmp->chan->adsicpe = qe->chan->adsicpe;
02556 
02557    /* Inherit context and extension */
02558    ast_channel_lock(qe->chan);
02559    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02560    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
02561    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02562    if (!ast_strlen_zero(macroexten))
02563       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02564    else
02565       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02566    if (ast_cdr_isset_unanswered()) {
02567       /* they want to see the unanswered dial attempts! */
02568       /* set up the CDR fields on all the CDRs to give sensical information */
02569       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02570       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02571       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02572       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02573       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02574       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02575       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02576       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02577       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02578       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02579       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02580    }
02581    ast_channel_unlock(qe->chan);
02582 
02583    /* Place the call, but don't wait on the answer */
02584    if ((res = ast_call(tmp->chan, location, 0))) {
02585       /* Again, keep going even if there's an error */
02586       ast_debug(1, "ast call on peer returned %d\n", res);
02587       ast_verb(3, "Couldn't call %s\n", tmp->interface);
02588       do_hang(tmp);
02589       (*busies)++;
02590       update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface));
02591       return 0;
02592    } else if (qe->parent->eventwhencalled) {
02593       char vars[2048];
02594 
02595       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02596                "Queue: %s\r\n"
02597                "AgentCalled: %s\r\n"
02598                "AgentName: %s\r\n"
02599                "ChannelCalling: %s\r\n"
02600                "DestinationChannel: %s\r\n"
02601                "CallerIDNum: %s\r\n"
02602                "CallerIDName: %s\r\n"
02603                "Context: %s\r\n"
02604                "Extension: %s\r\n"
02605                "Priority: %d\r\n"
02606                "Uniqueid: %s\r\n"
02607                "%s",
02608                qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
02609                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02610                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02611                qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
02612                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02613       ast_verb(3, "Called %s\n", tmp->interface);
02614    }
02615 
02616    update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface));
02617    return 1;
02618 }

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 2646 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().

02647 {
02648    int ret = 0;
02649 
02650    while (ret == 0) {
02651       struct callattempt *best = find_best(outgoing);
02652       if (!best) {
02653          ast_debug(1, "Nobody left to try ringing in queue\n");
02654          break;
02655       }
02656       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02657          struct callattempt *cur;
02658          /* Ring everyone who shares this best metric (for ringall) */
02659          for (cur = outgoing; cur; cur = cur->q_next) {
02660             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02661                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02662                ret |= ring_entry(qe, cur, busies);
02663             }
02664          }
02665       } else {
02666          /* Ring just the best channel */
02667          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02668          ret = ring_entry(qe, best, busies);
02669       }
02670       
02671       /* If we have timed out, break out */
02672       if (qe->expire && (time(NULL) >= qe->expire)) {
02673          ast_debug(1, "Queue timed out while ringing members.\n");
02674          ret = 0;
02675          break;
02676       }
02677    }
02678 
02679    return ret;
02680 }

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 2801 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, QUEUE_EVENT_VARIABLES, set_member_paused(), ast_channel::uniqueid, and vars2manager().

Referenced by wait_for_answer().

02802 {
02803    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02804    if (qe->parent->eventwhencalled) {
02805       char vars[2048];
02806 
02807       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
02808                   "Queue: %s\r\n"
02809                   "Uniqueid: %s\r\n"
02810                   "Channel: %s\r\n"
02811                   "Member: %s\r\n"
02812                   "MemberName: %s\r\n"
02813                   "Ringtime: %d\r\n"
02814                   "%s",
02815                   qe->parent->name,
02816                   qe->chan->uniqueid,
02817                   qe->chan->name,
02818                   interface,
02819                   membername,
02820                   rnatime,
02821                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02822    }
02823    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02824    if (qe->parent->autopause && pause) {
02825       if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02826          ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02827       } else {
02828          ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02829       }
02830    }
02831    return;
02832 }

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

RemoveQueueMember application.

Definition at line 4836 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_strlen_zero(), 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().

04837 {
04838    int res=-1;
04839    char *parse, *temppos = NULL;
04840    AST_DECLARE_APP_ARGS(args,
04841       AST_APP_ARG(queuename);
04842       AST_APP_ARG(interface);
04843       AST_APP_ARG(options);
04844    );
04845 
04846 
04847    if (ast_strlen_zero(data)) {
04848       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04849       return -1;
04850    }
04851 
04852    parse = ast_strdupa(data);
04853 
04854    AST_STANDARD_APP_ARGS(args, parse);
04855 
04856    if (ast_strlen_zero(args.interface)) {
04857       args.interface = ast_strdupa(chan->name);
04858       temppos = strrchr(args.interface, '-');
04859       if (temppos)
04860          *temppos = '\0';
04861    }
04862 
04863    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
04864 
04865    switch (remove_from_queue(args.queuename, args.interface)) {
04866    case RES_OKAY:
04867       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04868       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04869       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04870       res = 0;
04871       break;
04872    case RES_EXISTS:
04873       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04874       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04875       res = 0;
04876       break;
04877    case RES_NOSUCHQUEUE:
04878       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04879       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04880       res = 0;
04881       break;
04882    case RES_NOT_DYNAMIC:
04883       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04884       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04885       res = 0;
04886       break;
04887    }
04888 
04889    return res;
04890 }

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 member exists create one flag it as a RT member and add to queue member list.

Definition at line 1580 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_copy_string(), ast_log(), ast_queue_log(), ast_strlen_zero(), create_queue_member(), member::dead, member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::rt_uniqueid, S_OR, and member::state_interface.

Referenced by find_queue_by_name_rt(), and update_realtime_members().

01581 {
01582    struct member *m;
01583    struct ao2_iterator mem_iter;
01584    int penalty = 0;
01585    int paused  = 0;
01586    int found = 0;
01587 
01588    if (ast_strlen_zero(rt_uniqueid)) {
01589       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
01590       return;
01591    }
01592 
01593    if (penalty_str) {
01594       penalty = atoi(penalty_str);
01595       if (penalty < 0)
01596          penalty = 0;
01597    }
01598 
01599    if (paused_str) {
01600       paused = atoi(paused_str);
01601       if (paused < 0)
01602          paused = 0;
01603    }
01604 
01605    /* Find member by realtime uniqueid and update */
01606    mem_iter = ao2_iterator_init(q->members, 0);
01607    while ((m = ao2_iterator_next(&mem_iter))) {
01608       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
01609          m->dead = 0;   /* Do not delete this one. */
01610          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01611          if (paused_str)
01612             m->paused = paused;
01613          if (strcasecmp(state_interface, m->state_interface)) {
01614             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01615          }     
01616          m->penalty = penalty;
01617          found = 1;
01618          ao2_ref(m, -1);
01619          break;
01620       }
01621       ao2_ref(m, -1);
01622    }
01623    ao2_iterator_destroy(&mem_iter);
01624 
01625    /* Create a new member */
01626    if (!found) {
01627       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01628          m->dead = 0;
01629          m->realtime = 1;
01630          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01631          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
01632          ao2_link(q->members, m);
01633          ao2_ref(m, -1);
01634          m = NULL;
01635          q->membercount++;
01636       }
01637    }
01638 }

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

Playback announcement to queued members if peroid has elapsed.

Definition at line 2731 of file app_queue.c.

References AST_CONTROL_RINGING, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_random(), ast_str_buffer(), ast_str_strlen(), 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().

02732 {
02733    int res = 0;
02734    time_t now;
02735 
02736    /* Get the current time */
02737    time(&now);
02738 
02739    /* Check to see if it is time to announce */
02740    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02741       return 0;
02742 
02743    /* Stop the music on hold so we can play our own file */
02744    if (ringing)
02745       ast_indicate(qe->chan,-1);
02746    else
02747       ast_moh_stop(qe->chan);
02748 
02749    ast_verb(3, "Playing periodic announcement\n");
02750    
02751    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
02752       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
02753    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
02754       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
02755       qe->last_periodic_announce_sound = 0;
02756    }
02757    
02758    /* play the announcement */
02759    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
02760 
02761    if (res > 0 && !valid_exit(qe, res))
02762       res = 0;
02763 
02764    /* Resume Music on Hold if the caller is going to stay in the queue */
02765    if (!res) {
02766       if (ringing)
02767          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02768       else
02769          ast_moh_start(qe->chan, qe->moh, NULL);
02770    }
02771 
02772    /* update last_periodic_announce_time */
02773    qe->last_periodic_announce_time = now;
02774 
02775    /* Update the current periodic announcement to the next announcement */
02776    if (!qe->parent->randomperiodicannounce) {
02777       qe->last_periodic_announce_sound++;
02778    }
02779    
02780    return res;
02781 }

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

Definition at line 2080 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().

02081 {
02082    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02083    int say_thanks = 1;
02084    time_t now;
02085 
02086    /* Let minannouncefrequency seconds pass between the start of each position announcement */
02087    time(&now);
02088    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02089       return 0;
02090 
02091    /* If either our position has changed, or we are over the freq timer, say position */
02092    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02093       return 0;
02094 
02095    if (ringing) {
02096       ast_indicate(qe->chan,-1);
02097    } else {
02098       ast_moh_stop(qe->chan);
02099    }
02100 
02101    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02102       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02103       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02104       qe->pos <= qe->parent->announcepositionlimit))
02105          announceposition = 1;
02106 
02107 
02108    if (announceposition == 1) {
02109       /* Say we're next, if we are */
02110       if (qe->pos == 1) {
02111          res = play_file(qe->chan, qe->parent->sound_next);
02112          if (res)
02113             goto playout;
02114          else
02115             goto posout;
02116       } else {
02117          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02118             /* More than Case*/
02119             res = play_file(qe->chan, qe->parent->queue_quantity1);
02120             if (res)
02121                goto playout;
02122             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02123             if (res)
02124                goto playout;
02125          } else {
02126             /* Normal Case */
02127             res = play_file(qe->chan, qe->parent->sound_thereare);
02128             if (res)
02129                goto playout;
02130             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02131             if (res)
02132                goto playout;
02133          }
02134          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02135             /* More than Case*/
02136             res = play_file(qe->chan, qe->parent->queue_quantity2);
02137             if (res)
02138                goto playout;
02139          } else {
02140             res = play_file(qe->chan, qe->parent->sound_calls);
02141             if (res)
02142                goto playout;
02143          }
02144       }
02145    }
02146    /* Round hold time to nearest minute */
02147    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02148 
02149    /* If they have specified a rounding then round the seconds as well */
02150    if (qe->parent->roundingseconds) {
02151       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02152       avgholdsecs *= qe->parent->roundingseconds;
02153    } else {
02154       avgholdsecs = 0;
02155    }
02156 
02157    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02158 
02159    /* If the hold time is >1 min, if it's enabled, and if it's not
02160       supposed to be only once and we have already said it, say it */
02161     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02162         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02163         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02164       res = play_file(qe->chan, qe->parent->sound_holdtime);
02165       if (res)
02166          goto playout;
02167 
02168       if (avgholdmins >= 1) {
02169          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02170          if (res)
02171             goto playout;
02172 
02173          if (avgholdmins == 1) {
02174             res = play_file(qe->chan, qe->parent->sound_minute);
02175             if (res)
02176                goto playout;
02177          } else {
02178             res = play_file(qe->chan, qe->parent->sound_minutes);
02179             if (res)
02180                goto playout;
02181          }
02182       }
02183       if (avgholdsecs >= 1) {
02184          res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02185          if (res)
02186             goto playout;
02187 
02188          res = play_file(qe->chan, qe->parent->sound_seconds);
02189          if (res)
02190             goto playout;
02191       }
02192    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02193       say_thanks = 0;
02194    }
02195 
02196 posout:
02197    if (qe->parent->announceposition) {
02198       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02199          qe->chan->name, qe->parent->name, qe->pos);
02200    }
02201    if (say_thanks) {
02202       res = play_file(qe->chan, qe->parent->sound_thanks);
02203    }
02204 playout:
02205 
02206    if ((res > 0 && !valid_exit(qe, res)))
02207       res = 0;
02208 
02209    /* Set our last_pos indicators */
02210    qe->last_pos = now;
02211    qe->last_pos_said = qe->pos;
02212 
02213    /* Don't restart music on hold if we're about to exit the caller from the queue */
02214    if (!res) {
02215       if (ringing) {
02216          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02217       } else {
02218          ast_moh_start(qe->chan, qe->moh, NULL);
02219       }
02220    }
02221    return res;
02222 }

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 3383 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, TRANSFER, ast_channel::uniqueid, and vars2manager().

Referenced by try_calling().

03386 {
03387    const char *reason = NULL; /* silence dumb compilers */
03388 
03389    if (!qe->parent->eventwhencalled)
03390       return;
03391 
03392    switch (rsn) {
03393    case CALLER:
03394       reason = "caller";
03395       break;
03396    case AGENT:
03397       reason = "agent";
03398       break;
03399    case TRANSFER:
03400       reason = "transfer";
03401       break;
03402    }
03403 
03404    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03405       "Queue: %s\r\n"
03406       "Uniqueid: %s\r\n"
03407       "Channel: %s\r\n"
03408       "Member: %s\r\n"
03409       "MemberName: %s\r\n"
03410       "HoldTime: %ld\r\n"
03411       "TalkTime: %ld\r\n"
03412       "Reason: %s\r\n"
03413       "%s",
03414       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03415       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03416       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03417 }

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

Definition at line 4509 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_ref, ao2_t_iterator_next, 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, call_queue::name, member::paused, queue_t_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().

04510 {
04511    int found = 0;
04512    struct call_queue *q;
04513    struct member *mem;
04514    struct ao2_iterator queue_iter;
04515    int failed;
04516 
04517    /* Special event for when all queues are paused - individual events still generated */
04518    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
04519    if (ast_strlen_zero(queuename))
04520       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04521 
04522    queue_iter = ao2_iterator_init(queues, 0);
04523    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
04524       ao2_lock(q);
04525       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04526          if ((mem = interface_exists(q, interface))) {
04527             if (mem->paused == paused) {
04528                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04529             }
04530 
04531             failed = 0;
04532             if (mem->realtime) {
04533                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04534             }
04535          
04536             if (failed) {
04537                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04538                ao2_ref(mem, -1);
04539                ao2_unlock(q);
04540                queue_t_unref(q, "Done with iterator");
04541                continue;
04542             }  
04543             found++;
04544             mem->paused = paused;
04545 
04546             if (queue_persistent_members)
04547                dump_queue_members(q);
04548 
04549             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04550             
04551             if (!ast_strlen_zero(reason)) {
04552                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04553                   "Queue: %s\r\n"
04554                   "Location: %s\r\n"
04555                   "MemberName: %s\r\n"
04556                   "Paused: %d\r\n"
04557                   "Reason: %s\r\n",
04558                      q->name, mem->interface, mem->membername, paused, reason);
04559             } else {
04560                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04561                   "Queue: %s\r\n"
04562                   "Location: %s\r\n"
04563                   "MemberName: %s\r\n"
04564                   "Paused: %d\r\n",
04565                      q->name, mem->interface, mem->membername, paused);
04566             }
04567             ao2_ref(mem, -1);
04568          }
04569       }
04570       
04571       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04572          ao2_unlock(q);
04573          queue_t_unref(q, "Done with iterator");
04574          break;
04575       }
04576       
04577       ao2_unlock(q);
04578       queue_t_unref(q, "Done with iterator");
04579    }
04580    ao2_iterator_destroy(&queue_iter);
04581 
04582    return found ? RESULT_SUCCESS : RESULT_FAILURE;
04583 }

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

Definition at line 4586 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_log(), ast_queue_log(), ast_strlen_zero(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_ERROR, manager_event, call_queue::name, member::penalty, queue_t_unref, queues, RESULT_FAILURE, and RESULT_SUCCESS.

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

04587 {
04588    int foundinterface = 0, foundqueue = 0;
04589    struct call_queue *q;
04590    struct member *mem;
04591    struct ao2_iterator queue_iter;
04592 
04593    if (penalty < 0) {
04594       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04595       return RESULT_FAILURE;
04596    }
04597 
04598    queue_iter = ao2_iterator_init(queues, 0);
04599    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04600       ao2_lock(q);
04601       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04602          foundqueue++;
04603          if ((mem = interface_exists(q, interface))) {
04604             foundinterface++;
04605             mem->penalty = penalty;
04606             
04607             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04608             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04609                "Queue: %s\r\n"
04610                "Location: %s\r\n"
04611                "Penalty: %d\r\n",
04612                q->name, mem->interface, penalty);
04613             ao2_ref(mem, -1);
04614          }
04615       }
04616       ao2_unlock(q);
04617       queue_t_unref(q, "Done with iterator");
04618    }
04619    ao2_iterator_destroy(&queue_iter);
04620 
04621    if (foundinterface) {
04622       return RESULT_SUCCESS;
04623    } else if (!foundqueue) {
04624       ast_log (LOG_ERROR, "Invalid queuename\n"); 
04625    } else {
04626       ast_log (LOG_ERROR, "Invalid interface\n");
04627    }  
04628 
04629    return RESULT_FAILURE;
04630 }

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

sets the QUEUESTATUS channel variable

Definition at line 827 of file app_queue.c.

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

Referenced by queue_exec().

00828 {
00829    int i;
00830 
00831    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
00832       if (queue_results[i].id == res) {
00833          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00834          return;
00835       }
00836    }
00837 }

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

Set variables of queue.

Definition at line 902 of file app_queue.c.

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

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

00903 {
00904    char interfacevar[256]="";
00905    float sl = 0;
00906 
00907    if (q->setqueuevar) {
00908       sl = 0;
00909       if (q->callscompleted > 0) 
00910          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
00911 
00912       snprintf(interfacevar, sizeof(interfacevar),
00913          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
00914          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
00915    
00916       pbx_builtin_setvar_multiple(chan, interfacevar); 
00917    }
00918 }

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

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

Definition at line 3487 of file app_queue.c.

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

Referenced by try_calling().

03488 {
03489    struct ast_datastore *ds;
03490    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03491 
03492    if (!qtds) {
03493       ast_log(LOG_WARNING, "Memory allocation error!\n");
03494       return NULL;
03495    }
03496 
03497    ast_channel_lock(qe->chan);
03498    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03499       ast_channel_unlock(qe->chan);
03500       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03501       return NULL;
03502    }
03503 
03504    qtds->qe = qe;
03505    /* This member is refcounted in try_calling, so no need to add it here, too */
03506    qtds->member = member;
03507    qtds->starttime = starttime;
03508    qtds->callcompletedinsl = callcompletedinsl;
03509    ds->data = qtds;
03510    ast_channel_datastore_add(qe->chan, ds);
03511    ast_channel_unlock(qe->chan);
03512    return ds;
03513 }

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 2707 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().

02708 {
02709    struct callattempt *best = find_best(outgoing);
02710 
02711    if (best) {
02712       /* Ring just the best channel */
02713       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02714       qe->linpos = best->metric % 1000;
02715    } else {
02716       /* Just increment rrpos */
02717       if (qe->linwrapped) {
02718          /* No more channels, start over */
02719          qe->linpos = 0;
02720       } else {
02721          /* Prioritize next entry */
02722          qe->linpos++;
02723       }
02724    }
02725    qe->linwrapped = 0;
02726 
02727    return 0;
02728 }

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 2683 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().

02684 {
02685    struct callattempt *best = find_best(outgoing);
02686 
02687    if (best) {
02688       /* Ring just the best channel */
02689       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02690       qe->parent->rrpos = best->metric % 1000;
02691    } else {
02692       /* Just increment rrpos */
02693       if (qe->parent->wrapped) {
02694          /* No more channels, start over */
02695          qe->parent->rrpos = 0;
02696       } else {
02697          /* Prioritize next entry */
02698          qe->parent->rrpos++;
02699       }
02700    }
02701    qe->parent->wrapped = 0;
02702 
02703    return 0;
02704 }

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

Definition at line 851 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

00852 {
00853    int x;
00854 
00855    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00856       if (!strcasecmp(strategy, strategies[x].name))
00857          return strategies[x].strategy;
00858    }
00859 
00860    return -1;
00861 }

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 3569 of file app_queue.c.

References ast_channel::_softhangup, ast_channel::_state, AGENT, queue_ent::announce, ao2_alloc, ao2_iterator_destroy(), 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_failed(), AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), ast_cdr_noanswer(), 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_FLAG_ANSWERED_ELSEWHERE, 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_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), CALLER, member::calls, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, queue_end_bridge::chan, 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(), ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, end_bridge_callback_data_fixup(), ast_bridge_config::end_bridge_callback_data_fixup, errno, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, ast_channel::language, member::lastcall, callattempt::lastcall, member::lastqueue, callattempt::lastqueue, leave_queue(), 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, ast_pbx_args::no_hangup_chan, callattempt::oldstatus, 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_end_bridge::q, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, queue_t_ref, 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_minutes, call_queue::sound_reporthold, call_queue::sound_seconds, queue_ent::start, member::status, callattempt::stillgoing, store_next_lin(), store_next_rr(), call_queue::strategy, ast_channel::tech, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, TRANSFER, 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().

03570 {
03571    struct member *cur;
03572    struct callattempt *outgoing = NULL; /* the list of calls we are building */
03573    int to, orig;
03574    char oldexten[AST_MAX_EXTENSION]="";
03575    char oldcontext[AST_MAX_CONTEXT]="";
03576    char queuename[256]="";
03577    char interfacevar[256]="";
03578    struct ast_channel *peer;
03579    struct ast_channel *which;
03580    struct callattempt *lpeer;
03581    struct member *member;
03582    struct ast_app *application;
03583    int res = 0, bridge = 0;
03584    int numbusies = 0;
03585    int x=0;
03586    char *announce = NULL;
03587    char digit = 0;
03588    time_t callstart;
03589    time_t now = time(NULL);
03590    struct ast_bridge_config bridge_config;
03591    char nondataquality = 1;
03592    char *agiexec = NULL;
03593    char *macroexec = NULL;
03594    char *gosubexec = NULL;
03595    int ret = 0;
03596    const char *monitorfilename;
03597    const char *monitor_exec;
03598    const char *monitor_options;
03599    char tmpid[256], tmpid2[256];
03600    char meid[1024], meid2[1024];
03601    char mixmonargs[1512];
03602    struct ast_app *mixmonapp = NULL;
03603    char *p;
03604    char vars[2048];
03605    int forwardsallowed = 1;
03606    int callcompletedinsl;
03607    struct ao2_iterator memi;
03608    struct ast_datastore *datastore, *transfer_ds;
03609    struct queue_end_bridge *queue_end_bridge = NULL;
03610    const int need_weight = use_weight;
03611 
03612    ast_channel_lock(qe->chan);
03613    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03614    ast_channel_unlock(qe->chan);
03615 
03616    memset(&bridge_config, 0, sizeof(bridge_config));
03617    tmpid[0] = 0;
03618    meid[0] = 0;
03619    time(&now);
03620 
03621    /* If we've already exceeded our timeout, then just stop
03622     * This should be extremely rare. queue_exec will take care
03623     * of removing the caller and reporting the timeout as the reason.
03624     */
03625    if (qe->expire && now >= qe->expire) {
03626       res = 0;
03627       goto out;
03628    }
03629       
03630    for (; options && *options; options++)
03631       switch (*options) {
03632       case 't':
03633          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03634          break;
03635       case 'T':
03636          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03637          break;
03638       case 'w':
03639          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03640          break;
03641       case 'W':
03642          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03643          break;
03644       case 'c':
03645          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
03646          break;
03647       case 'd':
03648          nondataquality = 0;
03649          break;
03650       case 'h':
03651          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03652          break;
03653       case 'H':
03654          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03655          break;
03656       case 'k':
03657          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
03658          break;
03659       case 'K':
03660          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
03661          break;
03662       case 'n':
03663          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03664             (*tries)++;
03665          else
03666             *tries = qe->parent->membercount;
03667          *noption = 1;
03668          break;
03669       case 'i':
03670          forwardsallowed = 0;
03671          break;
03672       case 'x':
03673          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
03674          break;
03675       case 'X':
03676          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
03677          break;
03678       case 'C':
03679          qe->cancel_answered_elsewhere = 1;
03680          break;
03681 
03682       }
03683 
03684    /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 
03685       (this is mainly to support chan_local)
03686    */
03687    if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
03688       qe->cancel_answered_elsewhere = 1;
03689    }
03690 
03691    /* Hold the lock while we setup the outgoing calls */
03692    if (need_weight)
03693       ao2_lock(queues);
03694    ao2_lock(qe->parent);
03695    ast_debug(1, "%s is trying to call a queue member.\n",
03696                      qe->chan->name);
03697    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03698    if (!ast_strlen_zero(qe->announce))
03699       announce = qe->announce;
03700    if (!ast_strlen_zero(announceoverride))
03701       announce = announceoverride;
03702 
03703    memi = ao2_iterator_init(qe->parent->members, 0);
03704    while ((cur = ao2_iterator_next(&memi))) {
03705       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03706       struct ast_dialed_interface *di;
03707       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03708       if (!tmp) {
03709          ao2_ref(cur, -1);
03710          ao2_unlock(qe->parent);
03711          ao2_iterator_destroy(&memi);
03712          if (need_weight)
03713             ao2_unlock(queues);
03714          goto out;
03715       }
03716       if (!datastore) {
03717          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
03718             ao2_ref(cur, -1);
03719             ao2_unlock(qe->parent);
03720             ao2_iterator_destroy(&memi);
03721             if (need_weight)
03722                ao2_unlock(queues);
03723             free(tmp);
03724             goto out;
03725          }
03726          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03727          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03728             ao2_ref(cur, -1);
03729             ao2_unlock(&qe->parent);
03730             ao2_iterator_destroy(&memi);
03731             if (need_weight)
03732                ao2_unlock(queues);
03733             free(tmp);
03734             goto out;
03735          }
03736          datastore->data = dialed_interfaces;
03737          AST_LIST_HEAD_INIT(dialed_interfaces);
03738 
03739          ast_channel_lock(qe->chan);
03740          ast_channel_datastore_add(qe->chan, datastore);
03741          ast_channel_unlock(qe->chan);
03742       } else
03743          dialed_interfaces = datastore->data;
03744 
03745       AST_LIST_LOCK(dialed_interfaces);
03746       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03747          if (!strcasecmp(cur->interface, di->interface)) {
03748             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
03749                di->interface);
03750             break;
03751          }
03752       }
03753       AST_LIST_UNLOCK(dialed_interfaces);
03754       
03755       if (di) {
03756          free(tmp);
03757          continue;
03758       }
03759 
03760       /* It is always ok to dial a Local interface.  We only keep track of
03761        * which "real" interfaces have been dialed.  The Local channel will
03762        * inherit this list so that if it ends up dialing a real interface,
03763        * it won't call one that has already been called. */
03764       if (strncasecmp(cur->interface, "Local/", 6)) {
03765          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03766             ao2_ref(cur, -1);
03767             ao2_unlock(qe->parent);
03768             ao2_iterator_destroy(&memi);
03769             if (need_weight)
03770                ao2_unlock(queues);
03771             free(tmp);
03772             goto out;
03773          }
03774          strcpy(di->interface, cur->interface);
03775 
03776          AST_LIST_LOCK(dialed_interfaces);
03777          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03778          AST_LIST_UNLOCK(dialed_interfaces);
03779       }
03780 
03781       tmp->stillgoing = -1;
03782       tmp->member = cur;
03783       tmp->oldstatus = cur->status;
03784       tmp->lastcall = cur->lastcall;
03785       tmp->lastqueue = cur->lastqueue;
03786       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03787       /* Special case: If we ring everyone, go ahead and ring them, otherwise
03788          just calculate their metric for the appropriate strategy */
03789       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03790          /* Put them in the list of outgoing thingies...  We're ready now.
03791             XXX If we're forcibly removed, these outgoing calls won't get
03792             hung up XXX */
03793          tmp->q_next = outgoing;
03794          outgoing = tmp;      
03795          /* If this line is up, don't try anybody else */
03796          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03797             break;
03798       } else {
03799          ao2_ref(cur, -1);
03800          ast_free(tmp);
03801       }
03802    }
03803    ao2_iterator_destroy(&memi);
03804 
03805    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
03806       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
03807       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03808          to = (qe->expire - now) * 1000;
03809       else
03810          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03811    } else {
03812       /* Config timeout is higher priority thatn application timeout */
03813       if (qe->expire && qe->expire<=now) {
03814          to = 0;
03815       } else if (qe->parent->timeout) {
03816          to = qe->parent->timeout * 1000;
03817       } else {
03818          to = -1;
03819       }
03820    }
03821    orig = to;
03822    ++qe->pending;
03823    ao2_unlock(qe->parent);
03824    ring_one(qe, outgoing, &numbusies);
03825    if (need_weight)
03826       ao2_unlock(queues);
03827    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03828    /* The ast_channel_datastore_remove() function could fail here if the
03829     * datastore was moved to another channel during a masquerade. If this is
03830     * the case, don't free the datastore here because later, when the channel
03831     * to which the datastore was moved hangs up, it will attempt to free this
03832     * datastore again, causing a crash
03833     */
03834    ast_channel_lock(qe->chan);
03835    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03836       ast_datastore_free(datastore);
03837    }
03838    ast_channel_unlock(qe->chan);
03839    ao2_lock(qe->parent);
03840    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03841       store_next_rr(qe, outgoing);
03842    }
03843    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03844       store_next_lin(qe, outgoing);
03845    }
03846    ao2_unlock(qe->parent);
03847    peer = lpeer ? lpeer->chan : NULL;
03848    if (!peer) {
03849       qe->pending = 0;
03850       if (to) {
03851          /* Must gotten hung up */
03852          res = -1;
03853       } else {
03854          /* User exited by pressing a digit */
03855          res = digit;
03856       }
03857       if (res == -1)
03858          ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
03859       if (ast_cdr_isset_unanswered()) {
03860          /* channel contains the name of one of the outgoing channels
03861             in its CDR; zero out this CDR to avoid a dual-posting */
03862          struct callattempt *o;
03863          for (o = outgoing; o; o = o->q_next) {
03864             if (!o->chan) {
03865                continue;
03866             }
03867             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03868                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03869                break;
03870             }
03871          }
03872       }
03873    } else { /* peer is valid */
03874       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
03875          we will always return with -1 so that it is hung up properly after the
03876          conversation.  */
03877       if (!strcmp(qe->chan->tech->type, "DAHDI"))
03878          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03879       if (!strcmp(peer->tech->type, "DAHDI"))
03880          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03881       /* Update parameters for the queue */
03882       time(&now);
03883       recalc_holdtime(qe, (now - qe->start));
03884       ao2_lock(qe->parent);
03885       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03886       ao2_unlock(qe->parent);
03887       member = lpeer->member;
03888       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
03889       ao2_ref(member, 1);
03890       hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
03891       outgoing = NULL;
03892       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03893          int res2;
03894 
03895          res2 = ast_autoservice_start(qe->chan);
03896          if (!res2) {
03897             if (qe->parent->memberdelay) {
03898                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03899                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03900             }
03901             if (!res2 && announce) {
03902                play_file(peer, announce);
03903             }
03904             if (!res2 && qe->parent->reportholdtime) {
03905                if (!play_file(peer, qe->parent->sound_reporthold)) {
03906                   int holdtime, holdtimesecs;
03907 
03908                   time(&now);
03909                   holdtime = abs((now - qe->start) / 60);
03910                   holdtimesecs = abs((now - qe->start) % 60);
03911                   if (holdtime > 0) {
03912                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03913                      play_file(peer, qe->parent->sound_minutes);
03914                   }
03915                   if (holdtimesecs > 1) {
03916                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03917                      play_file(peer, qe->parent->sound_seconds);
03918                   }
03919                }
03920             }
03921          }
03922          res2 |= ast_autoservice_stop(qe->chan);
03923          if (ast_check_hangup(peer)) {
03924             /* Agent must have hung up */
03925             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03926             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03927             if (qe->parent->eventwhencalled)
03928                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03929                      "Queue: %s\r\n"
03930                      "Uniqueid: %s\r\n"
03931                      "Channel: %s\r\n"
03932                      "Member: %s\r\n"
03933                      "MemberName: %s\r\n"
03934                      "%s",
03935                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03936                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03937             ast_hangup(peer);
03938             ao2_ref(member, -1);
03939             goto out;
03940          } else if (res2) {
03941             /* Caller must have hung up just before being connected*/
03942             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03943             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03944             record_abandoned(qe);
03945             ast_cdr_noanswer(qe->chan->cdr);
03946             ast_hangup(peer);
03947             ao2_ref(member, -1);
03948             return -1;
03949          }
03950       }
03951       /* Stop music on hold */
03952       if (ringing)
03953          ast_indicate(qe->chan,-1);
03954       else
03955          ast_moh_stop(qe->chan);
03956       /* If appropriate, log that we have a destination channel */
03957       if (qe->chan->cdr)
03958          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03959       /* Make sure channels are compatible */
03960       res = ast_channel_make_compatible(qe->chan, peer);
03961       if (res < 0) {
03962          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03963          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03964          record_abandoned(qe);
03965          ast_cdr_failed(qe->chan->cdr);
03966          ast_hangup(peer);
03967          ao2_ref(member, -1);
03968          return -1;
03969       }
03970 
03971       /* Play announcement to the caller telling it's his turn if defined */
03972       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
03973          if (play_file(qe->chan, qe->parent->sound_callerannounce))
03974             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
03975       }
03976 
03977       ao2_lock(qe->parent);
03978       /* if setinterfacevar is defined, make member variables available to the channel */
03979       /* use  pbx_builtin_setvar to set a load of variables with one call */
03980       if (qe->parent->setinterfacevar) {
03981          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
03982             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
03983          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03984          pbx_builtin_setvar_multiple(peer, interfacevar);
03985       }
03986       
03987       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
03988       /* use  pbx_builtin_setvar to set a load of variables with one call */
03989       if (qe->parent->setqueueentryvar) {
03990          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
03991             (long) time(NULL) - qe->start, qe->opos);
03992          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03993          pbx_builtin_setvar_multiple(peer, interfacevar);
03994       }
03995    
03996       /* try to set queue variables if configured to do so*/
03997       set_queue_variables(qe->parent, qe->chan);
03998       set_queue_variables(qe->parent, peer);
03999       ao2_unlock(qe->parent);
04000       
04001       ast_channel_lock(qe->chan);
04002       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
04003             monitorfilename = ast_strdupa(monitorfilename);
04004       }
04005       ast_channel_unlock(qe->chan);
04006       /* Begin Monitoring */
04007       if (qe->parent->monfmt && *qe->parent->monfmt) {
04008          if (!qe->parent->montype) {
04009             const char *monexec, *monargs;
04010             ast_debug(1, "Starting Monitor as requested.\n");
04011             ast_channel_lock(qe->chan);
04012             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
04013                which = qe->chan;
04014                monexec = monexec ? ast_strdupa(monexec) : NULL;
04015             }
04016             else
04017                which = peer;
04018             ast_channel_unlock(qe->chan);
04019             if (ast_monitor_start) {
04020                if (monitorfilename) {
04021                   ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
04022                } else if (qe->chan->cdr && ast_monitor_start) {
04023                   ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
04024                } else if (ast_monitor_start) {
04025                   /* Last ditch effort -- no CDR, make up something */
04026                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04027                   ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
04028                }
04029             }
04030             if (!ast_strlen_zero(monexec) && ast_monitor_setjoinfiles) {
04031                ast_monitor_setjoinfiles(which, 1);
04032             }
04033          } else {
04034             mixmonapp = pbx_findapp("MixMonitor");
04035             
04036             if (mixmonapp) {
04037                ast_debug(1, "Starting MixMonitor as requested.\n");
04038                if (!monitorfilename) {
04039                   if (qe->chan->cdr)
04040                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
04041                   else
04042                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04043                } else {
04044                   const char *m = monitorfilename;
04045                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
04046                      switch (*m) {
04047                      case '^':
04048                         if (*(m + 1) == '{')
04049                            *p = '$';
04050                         break;
04051                      case ',':
04052                         *p++ = '\\';
04053                         /* Fall through */
04054                      default:
04055                         *p = *m;
04056                      }
04057                      if (*m == '\0')
04058                         break;
04059                   }
04060                   if (p == tmpid2 + sizeof(tmpid2))
04061                      tmpid2[sizeof(tmpid2) - 1] = '\0';
04062 
04063                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
04064                }
04065 
04066                ast_channel_lock(qe->chan);
04067                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
04068                      monitor_exec = ast_strdupa(monitor_exec);
04069                }
04070                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
04071                      monitor_options = ast_strdupa(monitor_options);
04072                } else {
04073                   monitor_options = "";
04074                }
04075                ast_channel_unlock(qe->chan);
04076 
04077                if (monitor_exec) {
04078                   const char *m = monitor_exec;
04079                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
04080                      switch (*m) {
04081                      case '^':
04082                         if (*(m + 1) == '{')
04083                            *p = '$';
04084                         break;
04085                      case ',':
04086                         *p++ = '\\';
04087                         /* Fall through */
04088                      default:
04089                         *p = *m;
04090                      }
04091                      if (*m == '\0')
04092                         break;
04093                   }
04094                   if (p == meid2 + sizeof(meid2))
04095                      meid2[sizeof(meid2) - 1] = '\0';
04096 
04097                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
04098                }
04099    
04100                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
04101 
04102                if (!ast_strlen_zero(monitor_exec))
04103                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
04104                else
04105                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
04106                
04107                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
04108                /* We purposely lock the CDR so that pbx_exec does not update the application data */
04109                if (qe->chan->cdr)
04110                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04111                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
04112                if (qe->chan->cdr)
04113                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04114 
04115             } else {
04116                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
04117             }
04118          }
04119       }
04120       /* Drop out of the queue at this point, to prepare for next caller */
04121       leave_queue(qe);        
04122       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
04123          ast_debug(1, "app_queue: sendurl=%s.\n", url);
04124          ast_channel_sendurl(peer, url);
04125       }
04126       
04127       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
04128       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
04129       if (!ast_strlen_zero(macro)) {
04130             macroexec = ast_strdupa(macro);
04131       } else {
04132          if (qe->parent->membermacro)
04133             macroexec = ast_strdupa(qe->parent->membermacro);
04134       }
04135 
04136       if (!ast_strlen_zero(macroexec)) {
04137          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
04138          
04139          res = ast_autoservice_start(qe->chan);
04140          if (res) {
04141             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04142             res = -1;
04143          }
04144          
04145          application = pbx_findapp("Macro");
04146 
04147          if (application) {
04148             res = pbx_exec(peer, application, macroexec);
04149             ast_debug(1, "Macro exited with status %d\n", res);
04150             res = 0;
04151          } else {
04152             ast_log(LOG_ERROR, "Could not find application Macro\n");
04153             res = -1;
04154          }
04155 
04156          if (ast_autoservice_stop(qe->chan) < 0) {
04157             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04158             res = -1;
04159          }
04160       }
04161 
04162       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
04163       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
04164       if (!ast_strlen_zero(gosub)) {
04165             gosubexec = ast_strdupa(gosub);
04166       } else {
04167          if (qe->parent->membergosub)
04168             gosubexec = ast_strdupa(qe->parent->membergosub);
04169       }
04170 
04171       if (!ast_strlen_zero(gosubexec)) {
04172          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
04173          
04174          res = ast_autoservice_start(qe->chan);
04175          if (res) {
04176             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04177             res = -1;
04178          }
04179          
04180          application = pbx_findapp("Gosub");
04181          
04182          if (application) {
04183             char *gosub_args, *gosub_argstart;
04184 
04185             /* Set where we came from */
04186             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
04187             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
04188             peer->priority = 0;
04189 
04190             gosub_argstart = strchr(gosubexec, ',');
04191             if (gosub_argstart) {
04192                *gosub_argstart = 0;
04193                if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
04194                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04195                   gosub_args = NULL;
04196                }
04197                *gosub_argstart = ',';
04198             } else {
04199                if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
04200                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04201                   gosub_args = NULL;
04202                }
04203             }
04204             if (gosub_args) {
04205                res = pbx_exec(peer, application, gosub_args);
04206                if (!res) {
04207                   struct ast_pbx_args args;
04208                   memset(&args, 0, sizeof(args));
04209                   args.no_hangup_chan = 1;
04210                   ast_pbx_run_args(peer, &args);
04211                }
04212                ast_free(gosub_args);
04213                ast_debug(1, "Gosub exited with status %d\n", res);
04214             } else {
04215                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
04216             }
04217          } else {
04218             ast_log(LOG_ERROR, "Could not find application Gosub\n");
04219             res = -1;
04220          }
04221       
04222          if (ast_autoservice_stop(qe->chan) < 0) {
04223             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04224             res = -1;
04225          }
04226       }
04227 
04228       if (!ast_strlen_zero(agi)) {
04229          ast_debug(1, "app_queue: agi=%s.\n", agi);
04230          application = pbx_findapp("agi");
04231          if (application) {
04232             agiexec = ast_strdupa(agi);
04233             ret = pbx_exec(qe->chan, application, agiexec);
04234          } else
04235             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
04236       }
04237       qe->handled++;
04238       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
04239                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
04240       if (update_cdr && qe->chan->cdr) 
04241          ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
04242       if (qe->parent->eventwhencalled)
04243          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
04244                "Queue: %s\r\n"
04245                "Uniqueid: %s\r\n"
04246                "Channel: %s\r\n"
04247                "Member: %s\r\n"
04248                "MemberName: %s\r\n"
04249                "Holdtime: %ld\r\n"
04250                "BridgedChannel: %s\r\n"
04251                "Ringtime: %ld\r\n"
04252                "%s",
04253                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04254                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
04255                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04256       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
04257       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
04258    
04259       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
04260          queue_end_bridge->q = qe->parent;
04261          queue_end_bridge->chan = qe->chan;
04262          bridge_config.end_bridge_callback = end_bridge_callback;
04263          bridge_config.end_bridge_callback_data = queue_end_bridge;
04264          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
04265          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
04266           * to make sure to increase the refcount of this queue so it cannot be freed until we
04267           * are done with it. We remove this reference in end_bridge_callback.
04268           */
04269          queue_t_ref(qe->parent, "For bridge_config reference");
04270       }
04271 
04272       time(&callstart);
04273       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
04274       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
04275 
04276       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
04277        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
04278        */
04279       ast_channel_lock(qe->chan);
04280       if (!attended_transfer_occurred(qe->chan)) {
04281          struct ast_datastore *tds;
04282 
04283          /* detect a blind transfer */
04284          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
04285             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04286                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
04287                (long) (time(NULL) - callstart), qe->opos);
04288             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
04289          } else if (ast_check_hangup(qe->chan)) {
04290             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
04291                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04292             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
04293          } else {
04294             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04295                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04296             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04297          }
04298          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
04299             ast_channel_datastore_remove(qe->chan, tds);
04300          }
04301          update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04302       }
04303 
04304       if (transfer_ds) {
04305          ast_datastore_free(transfer_ds);
04306       }
04307       ast_channel_unlock(qe->chan);
04308       ast_hangup(peer);
04309       res = bridge ? bridge : 1;
04310       ao2_ref(member, -1);
04311    }
04312 out:
04313    hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
04314 
04315    return res;
04316 }

static int unload_module ( void   )  [static]

Definition at line 7170 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ao2_t_iterator_next, ARRAY_LEN, 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(), cli_queue, queue_t_unref, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queues_t_unlink, queuevar_function, and queuewaitingcount_function.

07171 {
07172    int res;
07173    struct ast_context *con;
07174    struct ao2_iterator q_iter;
07175    struct call_queue *q = NULL;
07176 
07177    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
07178    res = ast_manager_unregister("QueueStatus");
07179    res |= ast_manager_unregister("Queues");
07180    res |= ast_manager_unregister("QueueRule");
07181    res |= ast_manager_unregister("QueueSummary");
07182    res |= ast_manager_unregister("QueueAdd");
07183    res |= ast_manager_unregister("QueueRemove");
07184    res |= ast_manager_unregister("QueuePause");
07185    res |= ast_manager_unregister("QueueLog");
07186    res |= ast_manager_unregister("QueuePenalty");
07187    res |= ast_unregister_application(app_aqm);
07188    res |= ast_unregister_application(app_rqm);
07189    res |= ast_unregister_application(app_pqm);
07190    res |= ast_unregister_application(app_upqm);
07191    res |= ast_unregister_application(app_ql);
07192    res |= ast_unregister_application(app);
07193    res |= ast_custom_function_unregister(&queuevar_function);
07194    res |= ast_custom_function_unregister(&queuemembercount_function);
07195    res |= ast_custom_function_unregister(&queuemembercount_dep);
07196    res |= ast_custom_function_unregister(&queuememberlist_function);
07197    res |= ast_custom_function_unregister(&queuewaitingcount_function);
07198    res |= ast_custom_function_unregister(&queuememberpenalty_function);
07199 
07200    if (device_state_sub)
07201       ast_event_unsubscribe(device_state_sub);
07202 
07203    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
07204       ast_context_remove_extension2(con, "s", 1, NULL, 0);
07205       ast_context_destroy(con, "app_queue"); /* leave no trace */
07206    }
07207 
07208    q_iter = ao2_iterator_init(queues, 0);
07209    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
07210       queues_t_unlink(queues, q, "Remove queue from container due to unload");
07211       queue_t_unref(q, "Done with iterator");
07212    }
07213    ao2_iterator_destroy(&q_iter);
07214    ao2_ref(queues, -1);
07215    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
07216    ast_unload_realtime("queue_members");
07217    return res;
07218 }

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 3163 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 queue_exec(), and wait_our_turn().

03164 {
03165    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
03166    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
03167    char max_penalty_str[20], min_penalty_str[20]; 
03168    /* a relative change to the penalty could put it below 0 */
03169    if (max_penalty < 0)
03170       max_penalty = 0;
03171    if (min_penalty < 0)
03172       min_penalty = 0;
03173    if (min_penalty > max_penalty)
03174       min_penalty = max_penalty;
03175    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
03176    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
03177    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
03178    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
03179    qe->max_penalty = max_penalty;
03180    qe->min_penalty = min_penalty;
03181    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);
03182    qe->pr = AST_LIST_NEXT(qe->pr, list);
03183 }

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

update the queue status

Return values:
Always 0

Definition at line 3271 of file app_queue.c.

References ao2_find, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, call_queue::members, OBJ_POINTER, queue_t_unref, queues, and call_queue::talktime.

Referenced by queue_transfer_fixup(), and try_calling().

03272 {
03273    int oldtalktime;
03274 
03275    struct member *mem;
03276    struct call_queue *qtmp;
03277    struct ao2_iterator queue_iter;  
03278    
03279    if (shared_lastcall) {
03280       queue_iter = ao2_iterator_init(queues, 0);
03281       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
03282          ao2_lock(qtmp);
03283          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03284             time(&mem->lastcall);
03285             mem->calls++;
03286             mem->lastqueue = q;
03287             ao2_ref(mem, -1);
03288          }
03289          ao2_unlock(qtmp);
03290          queue_t_unref(qtmp, "Done with iterator");
03291       }
03292       ao2_iterator_destroy(&queue_iter);
03293    } else {
03294       ao2_lock(q);
03295       time(&member->lastcall);
03296       member->calls++;
03297       member->lastqueue = q;
03298       ao2_unlock(q);
03299    }  
03300    ao2_lock(q);
03301    q->callscompleted++;
03302    if (callcompletedinsl)
03303       q->callscompletedinsl++;
03304    /* Calculate talktime using the same exponential average as holdtime code*/
03305    oldtalktime = q->talktime;
03306    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
03307    ao2_unlock(q);
03308    return 0;
03309 }

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

Definition at line 1892 of file app_queue.c.

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

Referenced by set_member_paused().

01893 {
01894    int ret = -1;
01895 
01896    if (ast_strlen_zero(mem->rt_uniqueid))
01897       return ret;
01898 
01899    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
01900       ret = 0;
01901 
01902    return ret;
01903 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1906 of file app_queue.c.

References ao2_iterator_destroy(), 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, rt_handle_member_record(), S_OR, and SENTINEL.

Referenced by load_realtime_queue(), and queue_exec().

01907 {
01908    struct ast_config *member_config = NULL;
01909    struct member *m;
01910    char *interface = NULL;
01911    struct ao2_iterator mem_iter;
01912 
01913    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
01914       /*This queue doesn't have realtime members*/
01915       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
01916       return;
01917    }
01918 
01919    ao2_lock(queues);
01920    ao2_lock(q);
01921    
01922    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01923    mem_iter = ao2_iterator_init(q->members, 0);
01924    while ((m = ao2_iterator_next(&mem_iter))) {
01925       if (m->realtime)
01926          m->dead = 1;
01927       ao2_ref(m, -1);
01928    }
01929    ao2_iterator_destroy(&mem_iter);
01930 
01931    while ((interface = ast_category_browse(member_config, interface))) {
01932       rt_handle_member_record(q, interface,
01933          ast_variable_retrieve(member_config, interface, "uniqueid"),
01934          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01935          ast_variable_retrieve(member_config, interface, "penalty"),
01936          ast_variable_retrieve(member_config, interface, "paused"),
01937          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01938    }
01939 
01940    /* Delete all realtime members that have been deleted in DB. */
01941    mem_iter = ao2_iterator_init(q->members, 0);
01942    while ((m = ao2_iterator_next(&mem_iter))) {
01943       if (m->dead) {
01944          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01945          ao2_unlink(q->members, m);
01946          q->membercount--;
01947       }
01948       ao2_ref(m, -1);
01949    }
01950    ao2_iterator_destroy(&mem_iter);
01951    ao2_unlock(q);
01952    ao2_unlock(queues);
01953    ast_config_destroy(member_config);
01954 }

static int update_status ( struct call_queue q,
struct member m,
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 1021 of file app_queue.c.

References EVENT_FLAG_AGENT, and manager_event.

Referenced by handle_statechange(), and ring_entry().

01022 {
01023    m->status = status;
01024 
01025    if (q->maskmemberstatus)
01026       return 0;
01027 
01028    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01029       "Queue: %s\r\n"
01030       "Location: %s\r\n"
01031       "MemberName: %s\r\n"
01032       "Membership: %s\r\n"
01033       "Penalty: %d\r\n"
01034       "CallsTaken: %d\r\n"
01035       "LastCall: %d\r\n"
01036       "Status: %d\r\n"
01037       "Paused: %d\r\n",
01038       q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01039       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01040    );
01041 
01042    return 0;
01043 }

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

UnPauseQueueMember application.

Definition at line 4800 of file app_queue.c.

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

Referenced by load_module().

04801 {
04802    char *parse;
04803    AST_DECLARE_APP_ARGS(args,
04804       AST_APP_ARG(queuename);
04805       AST_APP_ARG(interface);
04806       AST_APP_ARG(options);
04807       AST_APP_ARG(reason);
04808    );
04809 
04810    if (ast_strlen_zero(data)) {
04811       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04812       return -1;
04813    }
04814 
04815    parse = ast_strdupa(data);
04816 
04817    AST_STANDARD_APP_ARGS(args, parse);
04818 
04819    if (ast_strlen_zero(args.interface)) {
04820       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04821       return -1;
04822    }
04823 
04824    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04825       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04826       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04827       return 0;
04828    }
04829 
04830    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04831 
04832    return 0;
04833 }

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 2047 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(), wait_for_answer(), and wait_our_turn().

02048 {
02049    int digitlen = strlen(qe->digits);
02050 
02051    /* Prevent possible buffer overflow */
02052    if (digitlen < sizeof(qe->digits) - 2) {
02053       qe->digits[digitlen] = digit;
02054       qe->digits[digitlen + 1] = '\0';
02055    } else {
02056       qe->digits[0] = '\0';
02057       return 0;
02058    }
02059 
02060    /* If there's no context to goto, short-circuit */
02061    if (ast_strlen_zero(qe->context))
02062       return 0;
02063 
02064    /* If the extension is bad, then reset the digits to blank */
02065    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
02066       qe->digits[0] = '\0';
02067       return 0;
02068    }
02069 
02070    /* We have an exact match */
02071    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02072       qe->valid_digits = 1;
02073       /* Return 1 on a successful goto */
02074       return 1;
02075    }
02076 
02077    return 0;
02078 }

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 2416 of file app_queue.c.

References ast_copy_string(), ast_str_buffer(), ast_str_thread_get(), buf, and pbx_builtin_serialize_variables().

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

02417 {
02418    struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
02419    char *tmp;
02420 
02421    if (pbx_builtin_serialize_variables(chan, &buf)) {
02422       int i, j;
02423 
02424       /* convert "\n" to "\nVariable: " */
02425       strcpy(vars, "Variable: ");
02426       tmp = ast_str_buffer(buf);
02427 
02428       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02429          vars[j] = tmp[i];
02430 
02431          if (tmp[i + 1] == '\0')
02432             break;
02433          if (tmp[i] == '\n') {
02434             vars[j++] = '\r';
02435             vars[j++] = '\n';
02436 
02437             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02438             j += 9;
02439          }
02440       }
02441       if (j > len - 3)
02442          j = len - 3;
02443       vars[j++] = '\r';
02444       vars[j++] = '\n';
02445       vars[j] = '\0';
02446    } else {
02447       /* there are no channel variables; leave it blank */
02448       *vars = '\0';
02449    }
02450    return vars;
02451 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 4318 of file app_queue.c.

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

Referenced by queue_exec().

04319 {
04320    /* Don't need to hold the lock while we setup the outgoing calls */
04321    int retrywait = qe->parent->retry * 1000;
04322 
04323    int res = ast_waitfordigit(qe->chan, retrywait);
04324    if (res > 0 && !valid_exit(qe, res))
04325       res = 0;
04326 
04327    return res;
04328 }

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, read]

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 2845 of file app_queue.c.

References ast_channel::_state, ast_channel::accountcode, accountcode, ast_call(), ast_cdr_busy(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, ast_hangup(), ast_log(), AST_MAX_WATCHERS, ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), ast_request(), AST_STATE_UP, ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_waitfor_n(), ast_channel::call_forward, callattempt::call_next, ast_channel::cdr, ast_channel::cdrflags, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_rdnis, ast_channel::context, ast_frame::data, do_hang(), ast_channel::exten, f, ast_frame::frametype, ast_channel::hangupcause, callattempt::interface, member::interface, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, queue_ent::parent, queue_ent::pos, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), rna(), S_OR, queue_ent::start, starttime, status, callattempt::stillgoing, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, ast_frame::uint32, and valid_exit().

Referenced by try_calling().

02846 {
02847    const char *queue = qe->parent->name;
02848    struct callattempt *o, *start = NULL, *prev = NULL;
02849    int status;
02850    int numbusies = prebusies;
02851    int numnochan = 0;
02852    int stillgoing = 0;
02853    int orig = *to;
02854    struct ast_frame *f;
02855    struct callattempt *peer = NULL;
02856    struct ast_channel *winner;
02857    struct ast_channel *in = qe->chan;
02858    char on[80] = "";
02859    char membername[80] = "";
02860    long starttime = 0;
02861    long endtime = 0;
02862 #ifdef HAVE_EPOLL
02863    struct callattempt *epollo;
02864 #endif
02865 
02866    starttime = (long) time(NULL);
02867 #ifdef HAVE_EPOLL
02868    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02869       if (epollo->chan)
02870          ast_poll_channel_add(in, epollo->chan);
02871    }
02872 #endif
02873    
02874    while (*to && !peer) {
02875       int numlines, retry, pos = 1;
02876       struct ast_channel *watchers[AST_MAX_WATCHERS];
02877       watchers[0] = in;
02878       start = NULL;
02879 
02880       for (retry = 0; retry < 2; retry++) {
02881          numlines = 0;
02882          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02883             if (o->stillgoing) { /* Keep track of important channels */
02884                stillgoing = 1;
02885                if (o->chan) {
02886                   watchers[pos++] = o->chan;
02887                   if (!start)
02888                      start = o;
02889                   else
02890                      prev->call_next = o;
02891                   prev = o;
02892                }
02893             }
02894             numlines++;
02895          }
02896          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02897             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02898             break;
02899          /* On "ringall" strategy we only move to the next penalty level
02900             when *all* ringing phones are done in the current penalty level */
02901          ring_one(qe, outgoing, &numbusies);
02902          /* and retry... */
02903       }
02904       if (pos == 1 /* not found */) {
02905          if (numlines == (numbusies + numnochan)) {
02906             ast_debug(1, "Everyone is busy at this time\n");
02907          } else {
02908             ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
02909          }
02910          *to = 0;
02911          return NULL;
02912       }
02913 
02914       /* Poll for events from both the incoming channel as well as any outgoing channels */
02915       winner = ast_waitfor_n(watchers, pos, to);
02916 
02917       /* Service all of the outgoing channels */
02918       for (o = start; o; o = o->call_next) {
02919          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02920             if (!peer) {
02921                ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02922                peer = o;
02923             }
02924          } else if (o->chan && (o->chan == winner)) {
02925 
02926             ast_copy_string(on, o->member->interface, sizeof(on));
02927             ast_copy_string(membername, o->member->membername, sizeof(membername));
02928 
02929             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02930                ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02931                numnochan++;
02932                do_hang(o);
02933                winner = NULL;
02934                continue;
02935             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02936                char tmpchan[256];
02937                char *stuff;
02938                char *tech;
02939 
02940                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02941                if ((stuff = strchr(tmpchan, '/'))) {
02942                   *stuff++ = '\0';
02943                   tech = tmpchan;
02944                } else {
02945                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02946                   stuff = tmpchan;
02947                   tech = "Local";
02948                }
02949                /* Before processing channel, go ahead and check for forwarding */
02950                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02951                /* Setup parameters */
02952                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02953                if (!o->chan) {
02954                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02955                   o->stillgoing = 0;
02956                   numnochan++;
02957                } else {
02958                   ast_channel_inherit_variables(in, o->chan);
02959                   ast_channel_datastore_inherit(in, o->chan);
02960                   if (o->chan->cid.cid_num)
02961                      ast_free(o->chan->cid.cid_num);
02962                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02963 
02964                   if (o->chan->cid.cid_name)
02965                      ast_free(o->chan->cid.cid_name);
02966                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02967 
02968                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02969                   o->chan->cdrflags = in->cdrflags;
02970 
02971                   if (in->cid.cid_ani) {
02972                      if (o->chan->cid.cid_ani)
02973                         ast_free(o->chan->cid.cid_ani);
02974                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02975                   }
02976                   if (o->chan->cid.cid_rdnis)
02977                      ast_free(o->chan->cid.cid_rdnis);
02978                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02979                   if (ast_call(o->chan, tmpchan, 0)) {
02980                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02981                      do_hang(o);
02982                      numnochan++;
02983                   }
02984                }
02985                /* Hangup the original channel now, in case we needed it */
02986                ast_hangup(winner);
02987                continue;
02988             }
02989             f = ast_read(winner);
02990             if (f) {
02991                if (f->frametype == AST_FRAME_CONTROL) {
02992                   switch (f->subclass) {
02993                   case AST_CONTROL_ANSWER:
02994                      /* This is our guy if someone answered. */
02995                      if (!peer) {
02996                         ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02997                         peer = o;
02998                      }
02999                      break;
03000                   case AST_CONTROL_BUSY:
03001                      ast_verb(3, "%s is busy\n", o->chan->name);
03002                      if (in->cdr)
03003                         ast_cdr_busy(in->cdr);
03004                      do_hang(o);
03005                      endtime = (long) time(NULL);
03006                      endtime -= starttime;
03007                      rna(endtime * 1000, qe, on, membername, 0);
03008                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03009                         if (qe->parent->timeoutrestart)
03010                            *to = orig;
03011                         /* Have enough time for a queue member to answer? */
03012                         if (*to > 500) {
03013                            ring_one(qe, outgoing, &numbusies);
03014                            starttime = (long) time(NULL);
03015                         }
03016                      }
03017                      numbusies++;
03018                      break;
03019                   case AST_CONTROL_CONGESTION:
03020                      ast_verb(3, "%s is circuit-busy\n", o->chan->name);
03021                      if (in->cdr)
03022                         ast_cdr_busy(in->cdr);
03023                      endtime = (long) time(NULL);
03024                      endtime -= starttime;
03025                      rna(endtime * 1000, qe, on, membername, 0);
03026                      do_hang(o);
03027                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03028                         if (qe->parent->timeoutrestart)
03029                            *to = orig;
03030                         if (*to > 500) {
03031                            ring_one(qe, outgoing, &numbusies);
03032                            starttime = (long) time(NULL);
03033                         }
03034                      }
03035                      numbusies++;
03036                      break;
03037                   case AST_CONTROL_RINGING:
03038                      ast_verb(3, "%s is ringing\n", o->chan->name);
03039                      break;
03040                   case AST_CONTROL_OFFHOOK:
03041                      /* Ignore going off hook */
03042                      break;
03043                   default:
03044                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
03045                   }
03046                }
03047                ast_frfree(f);
03048             } else { /* ast_read() returned NULL */
03049                endtime = (long) time(NULL) - starttime;
03050                rna(endtime * 1000, qe, on, membername, 1);
03051                do_hang(o);
03052                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03053                   if (qe->parent->timeoutrestart)
03054                      *to = orig;
03055                   if (*to > 500) {
03056                      ring_one(qe, outgoing, &numbusies);
03057                      starttime = (long) time(NULL);
03058                   }
03059                }
03060             }
03061          }
03062       }
03063 
03064       /* If we received an event from the caller, deal with it. */
03065       if (winner == in) {
03066          f = ast_read(in);
03067          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
03068             /* Got hung up */
03069             *to = -1;
03070             if (f) {
03071                if (f->data.uint32) {
03072                   in->hangupcause = f->data.uint32;
03073                }
03074                ast_frfree(f);
03075             }
03076             return NULL;
03077          }
03078          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
03079             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
03080             *to = 0;
03081             ast_frfree(f);
03082             return NULL;
03083          }
03084          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
03085             ast_verb(3, "User pressed digit: %c\n", f->subclass);
03086             *to = 0;
03087             *digit = f->subclass;
03088             ast_frfree(f);
03089             return NULL;
03090          }
03091          ast_frfree(f);
03092       }
03093       if (!*to) {
03094          for (o = start; o; o = o->call_next)
03095             rna(orig, qe, o->interface, o->member->membername, 1);
03096       }
03097    }
03098 
03099 #ifdef HAVE_EPOLL
03100    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03101       if (epollo->chan)
03102          ast_poll_channel_del(in, epollo->chan);
03103    }
03104 #endif
03105 
03106    return peer;
03107 }

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 3195 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_LEAVEEMPTY, 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().

03196 {
03197    int res = 0;
03198 
03199    /* This is the holding pen for callers 2 through maxlen */
03200    for (;;) {
03201 
03202       if (is_our_turn(qe))
03203          break;
03204 
03205       /* If we have timed out, break out */
03206       if (qe->expire && (time(NULL) >= qe->expire)) {
03207          *reason = QUEUE_TIMEOUT;
03208          break;
03209       }
03210 
03211       if (qe->parent->leavewhenempty) {
03212          int status = 0;
03213 
03214          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
03215             *reason = QUEUE_LEAVEEMPTY;
03216             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03217             leave_queue(qe);
03218             break;
03219          }
03220       }
03221 
03222       /* Make a position announcement, if enabled */
03223       if (qe->parent->announcefrequency &&
03224          (res = say_position(qe,ringing)))
03225          break;
03226 
03227       /* If we have timed out, break out */
03228       if (qe->expire && (time(NULL) >= qe->expire)) {
03229          *reason = QUEUE_TIMEOUT;
03230          break;
03231       }
03232 
03233       /* Make a periodic announcement, if enabled */
03234       if (qe->parent->periodicannouncefrequency &&
03235          (res = say_periodic_announcement(qe,ringing)))
03236          break;
03237       
03238       /* see if we need to move to the next penalty level for this queue */
03239       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03240          update_qe_rule(qe);
03241       }
03242 
03243       /* If we have timed out, break out */
03244       if (qe->expire && (time(NULL) >= qe->expire)) {
03245          *reason = QUEUE_TIMEOUT;
03246          break;
03247       }
03248       
03249       /* Wait a second before checking again */
03250       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03251          if (res > 0 && !valid_exit(qe, res))
03252             res = 0;
03253          else
03254             break;
03255       }
03256       
03257       /* If we have timed out, break out */
03258       if (qe->expire && (time(NULL) >= qe->expire)) {
03259          *reason = QUEUE_TIMEOUT;
03260          break;
03261       }
03262    }
03263 
03264    return res;
03265 }


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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 7292 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 540 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 542 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 546 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 550 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 544 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 548 of file app_queue.c.

Definition at line 7292 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 564 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 7159 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 573 of file app_queue.c.

Definition at line 524 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 567 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 553 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 7150 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 7156 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 558 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

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 3434 of file app_queue.c.

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

Initial value:
 {
   .name = "QUEUE_MEMBER_COUNT",
   .read = queue_function_qac_dep,
}

Definition at line 5623 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   .name = "QUEUE_MEMBER",
   .read = queue_function_qac,
}

Definition at line 5618 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   .name = "QUEUE_MEMBER_LIST",
   .read = queue_function_queuememberlist,
}

Definition at line 5633 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   .name = "QUEUE_MEMBER_PENALTY",
   .read = queue_function_memberpenalty_read,
   .write = queue_function_memberpenalty_write,
}

Definition at line 5638 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]
Initial value:
 {
   .name = "QUEUE_VARIABLES",
   .read = queue_function_var,
}

Definition at line 5613 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   .name = "QUEUE_WAITING_COUNT",
   .read = queue_function_queuewaitingcount,
}

Definition at line 5628 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 7153 of file app_queue.c.

int shared_lastcall = 0 [static]

queues.conf [general] option

Definition at line 570 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* text
int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 576 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 561 of file app_queue.c.


Generated by  doxygen 1.6.2