Mon Oct 8 12:39:08 2012

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 "asterisk/aoc.h"
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
#include "asterisk/data.h"

Go to the source code of this file.

Data Structures

struct  autopause
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 DATA_EXPORT_CALL_QUEUE(MEMBER)
#define DATA_EXPORT_MEMBER(MEMBER)
#define DATA_EXPORT_QUEUE_ENT(MEMBER)
#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, QUEUE_STRATEGY_RRORDERED
}
enum  { QUEUE_AUTOPAUSE_OFF = 0, QUEUE_AUTOPAUSE_ON, QUEUE_AUTOPAUSE_ALL }
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, const char *const *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, const char *data)
 AddQueueMember application.
 AST_DATA_STRUCTURE (queue_ent, DATA_EXPORT_QUEUE_ENT)
 AST_DATA_STRUCTURE (member, DATA_EXPORT_MEMBER)
 AST_DATA_STRUCTURE (call_queue, DATA_EXPORT_CALL_QUEUE)
static int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
static int autopause2int (const char *autopause)
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 callattempt_free (struct callattempt *doomed)
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 int extension_state_cb (char *context, char *exten, enum ast_extension_states state, void *data)
static int extensionstate2devicestate (int state)
 Helper function which converts from extension state to device state values.
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 int get_queue_member_status (struct member *cur)
 Return the current state of a member.
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, int position)
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, const char *data)
 PauseQueueMember application.
static int ql_exec (struct ast_channel *chan, const char *data)
 QueueLog application.
static int queue_cmp_cb (void *obj, void *arg, int flags)
static int queue_exec (struct ast_channel *chan, const char *data)
 The starting point for all queue calls.
static int queue_function_exists (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Check if a given queue exists.
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 / ready 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 int queues_data_provider_get (const struct ast_data_search *search, struct ast_data *data_root)
static void queues_data_provider_get_helper (const struct ast_data_search *search, struct ast_data *data_root, struct call_queue *queue)
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, const char *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 period 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 (const char *queuename, const 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, const char *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_LOAD_ORDER , .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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", }
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 = 1
 queues.conf [general] option
static struct autopause autopausesmodes []
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 *const pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage []
static const char qsmp_cmd_usage []
static struct ast_data_entry queue_data_providers []
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 queueexists_function
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_data_handler queues_data_provider
static struct ast_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 1
 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>:

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

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

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

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

#define ANNOUNCEPOSITION_MORE_THAN   3

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

Definition at line 1080 of file app_queue.c.

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

#define ANNOUNCEPOSITION_NO   2

We don't announce position

Definition at line 1079 of file app_queue.c.

Referenced by queue_set_param(), and queues_data_provider_get_helper().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 1078 of file app_queue.c.

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

#define AST_MAX_WATCHERS   256

Definition at line 3420 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 8072 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 8136 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 887 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 893 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 916 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 1067 of file app_queue.c.

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

#define queue_t_ref ( a,
 )     queue_ref(a)

Definition at line 1273 of file app_queue.c.

Referenced by leave_queue().

#define queue_t_unref ( a,
 )     queue_unref(a)

Definition at line 1274 of file app_queue.c.

Referenced by __queues_show(), add_to_queue(), alloc_queue(), clear_stats(), compare_weight(), complete_queue(), complete_queue_remove_member(), end_bridge_callback(), extension_state_cb(), find_queue_by_name_rt(), get_member_penalty(), handle_statechange(), join_queue(), leave_queue(), load_realtime_queue(), manager_queues_status(), manager_queues_summary(), queue_function_exists(), queue_function_qac(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_queue().

#define queues_t_link ( c,
q,
tag   )     ao2_t_link(c,q,tag)

Definition at line 1275 of file app_queue.c.

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

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

#define RES_EXISTS   (-1)

Entry already exists

Definition at line 896 of file app_queue.c.

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

#define RES_NOSUCHQUEUE   (-3)

No such queue

Definition at line 898 of file app_queue.c.

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

#define RES_NOT_DYNAMIC   (-4)

Member is not dynamic

Definition at line 899 of file app_queue.c.

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

#define RES_OKAY   0

Action completed

Definition at line 895 of file app_queue.c.

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

#define RES_OUTOFMEMORY   (-2)

Out of memory

Definition at line 897 of file app_queue.c.

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


Enumeration Type Documentation

anonymous enum

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

Definition at line 836 of file app_queue.c.

anonymous enum

Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 847 of file app_queue.c.

00847      {
00848      QUEUE_AUTOPAUSE_OFF = 0,
00849      QUEUE_AUTOPAUSE_ON,
00850      QUEUE_AUTOPAUSE_ALL
00851 };

enum agent_complete_reason

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 4178 of file app_queue.c.

04178                            {
04179    CALLER,
04180    AGENT,
04181    TRANSFER
04182 };

enum empty_conditions

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

01053                       {
01054    QUEUE_EMPTY_PENALTY = (1 << 0),
01055    QUEUE_EMPTY_PAUSED = (1 << 1),
01056    QUEUE_EMPTY_INUSE = (1 << 2),
01057    QUEUE_EMPTY_RINGING = (1 << 3),
01058    QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
01059    QUEUE_EMPTY_INVALID = (1 << 5),
01060    QUEUE_EMPTY_UNKNOWN = (1 << 6),
01061    QUEUE_EMPTY_WRAPUP = (1 << 7),
01062 };

enum queue_reload_mask

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 853 of file app_queue.c.

00853                        {
00854    QUEUE_RELOAD_PARAMETERS = (1 << 0),
00855    QUEUE_RELOAD_MEMBER = (1 << 1),
00856    QUEUE_RELOAD_RULES = (1 << 2),
00857    QUEUE_RESET_STATS = (1 << 3),
00858 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 939 of file app_queue.c.

00939                   {
00940    QUEUE_UNKNOWN = 0,
00941    QUEUE_TIMEOUT = 1,
00942    QUEUE_JOINEMPTY = 2,
00943    QUEUE_LEAVEEMPTY = 3,
00944    QUEUE_JOINUNAVAIL = 4,
00945    QUEUE_LEAVEUNAVAIL = 5,
00946    QUEUE_FULL = 6,
00947    QUEUE_CONTINUE = 7,
00948 };

enum queue_timeout_priority

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 964 of file app_queue.c.

00964                             {
00965    TIMEOUT_PRIORITY_APP,
00966    TIMEOUT_PRIORITY_CONF,
00967 };


Function Documentation

static char* __queues_show ( struct mansession s,
int  fd,
int  argc,
const char *const *  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 6967 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().

06968 {
06969    struct call_queue *q;
06970    struct ast_str *out = ast_str_alloca(240);
06971    int found = 0;
06972    time_t now = time(NULL);
06973    struct ao2_iterator queue_iter;
06974    struct ao2_iterator mem_iter;
06975 
06976    if (argc != 2 && argc != 3)
06977       return CLI_SHOWUSAGE;
06978 
06979    if (argc == 3) { /* specific queue */
06980       if ((q = load_realtime_queue(argv[2]))) {
06981          queue_t_unref(q, "Done with temporary pointer");
06982       }
06983    } else if (ast_check_realtime("queues")) {
06984       /* This block is to find any queues which are defined in realtime but
06985        * which have not yet been added to the in-core container
06986        */
06987       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
06988       char *queuename;
06989       if (cfg) {
06990          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
06991             if ((q = load_realtime_queue(queuename))) {
06992                queue_t_unref(q, "Done with temporary pointer");
06993             }
06994          }
06995          ast_config_destroy(cfg);
06996       }
06997    }
06998 
06999    ao2_lock(queues);
07000    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
07001    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07002       float sl;
07003       struct call_queue *realtime_queue = NULL;
07004 
07005       ao2_lock(q);
07006       /* This check is to make sure we don't print information for realtime
07007        * queues which have been deleted from realtime but which have not yet
07008        * been deleted from the in-core container
07009        */
07010       if (q->realtime) {
07011          realtime_queue = load_realtime_queue(q->name);
07012          if (!realtime_queue) {
07013             ao2_unlock(q);
07014             queue_t_unref(q, "Done with iterator");
07015             continue;
07016          }
07017          queue_t_unref(realtime_queue, "Queue is already in memory");
07018       }
07019 
07020       if (argc == 3 && strcasecmp(q->name, argv[2])) {
07021          ao2_unlock(q);
07022          queue_t_unref(q, "Done with iterator");
07023          continue;
07024       }
07025       found = 1;
07026 
07027       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
07028       if (q->maxlen)
07029          ast_str_append(&out, 0, "%d", q->maxlen);
07030       else
07031          ast_str_append(&out, 0, "unlimited");
07032       sl = 0;
07033       if (q->callscompleted > 0)
07034          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
07035       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
07036          int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
07037          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
07038       do_print(s, fd, ast_str_buffer(out));
07039       if (!ao2_container_count(q->members))
07040          do_print(s, fd, "   No Members");
07041       else {
07042          struct member *mem;
07043 
07044          do_print(s, fd, "   Members: ");
07045          mem_iter = ao2_iterator_init(q->members, 0);
07046          while ((mem = ao2_iterator_next(&mem_iter))) {
07047             ast_str_set(&out, 0, "      %s", mem->membername);
07048             if (strcasecmp(mem->membername, mem->interface)) {
07049                ast_str_append(&out, 0, " (%s)", mem->interface);
07050             }
07051             if (mem->penalty)
07052                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
07053             ast_str_append(&out, 0, "%s%s%s (%s)",
07054                mem->dynamic ? " (dynamic)" : "",
07055                mem->realtime ? " (realtime)" : "",
07056                mem->paused ? " (paused)" : "",
07057                ast_devstate2str(mem->status));
07058             if (mem->calls)
07059                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
07060                   mem->calls, (long) (time(NULL) - mem->lastcall));
07061             else
07062                ast_str_append(&out, 0, " has taken no calls yet");
07063             do_print(s, fd, ast_str_buffer(out));
07064             ao2_ref(mem, -1);
07065          }
07066          ao2_iterator_destroy(&mem_iter);
07067       }
07068       if (!q->head)
07069          do_print(s, fd, "   No Callers");
07070       else {
07071          struct queue_ent *qe;
07072          int pos = 1;
07073 
07074          do_print(s, fd, "   Callers: ");
07075          for (qe = q->head; qe; qe = qe->next) {
07076             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
07077                pos++, qe->chan->name, (long) (now - qe->start) / 60,
07078                (long) (now - qe->start) % 60, qe->prio);
07079             do_print(s, fd, ast_str_buffer(out));
07080          }
07081       }
07082       do_print(s, fd, ""); /* blank line between entries */
07083       ao2_unlock(q);
07084       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
07085    }
07086    ao2_iterator_destroy(&queue_iter);
07087    ao2_unlock(queues);
07088    if (!found) {
07089       if (argc == 3)
07090          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
07091       else
07092          ast_str_set(&out, 0, "No queues.");
07093       do_print(s, fd, ast_str_buffer(out));
07094    }
07095    return CLI_SUCCESS;
07096 }

static void __reg_module ( void   )  [static]

Definition at line 8476 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 8476 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 5299 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, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, queue_t_unref, 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().

05300 {
05301    struct call_queue *q;
05302    struct member *new_member, *old_member;
05303    int res = RES_NOSUCHQUEUE;
05304 
05305    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05306     * short-circuits if the queue is already in memory. */
05307    if (!(q = load_realtime_queue(queuename)))
05308       return res;
05309 
05310    ao2_lock(q);
05311    if ((old_member = interface_exists(q, interface)) == NULL) {
05312       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05313          new_member->dynamic = 1;
05314          ao2_link(q->members, new_member);
05315          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05316             "Queue: %s\r\n"
05317             "Location: %s\r\n"
05318             "MemberName: %s\r\n"
05319             "Membership: %s\r\n"
05320             "Penalty: %d\r\n"
05321             "CallsTaken: %d\r\n"
05322             "LastCall: %d\r\n"
05323             "Status: %d\r\n"
05324             "Paused: %d\r\n",
05325             q->name, new_member->interface, new_member->membername,
05326             "dynamic",
05327             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05328             new_member->status, new_member->paused);
05329          
05330          ao2_ref(new_member, -1);
05331          new_member = NULL;
05332 
05333          if (dump)
05334             dump_queue_members(q);
05335          
05336          res = RES_OKAY;
05337       } else {
05338          res = RES_OUTOFMEMORY;
05339       }
05340    } else {
05341       ao2_ref(old_member, -1);
05342       res = RES_EXISTS;
05343    }
05344    ao2_unlock(q);
05345    queue_t_unref(q, "Expiring temporary reference");
05346 
05347    return res;
05348 }

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

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

02180 {
02181    struct call_queue *q;
02182 
02183    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02184       if (ast_string_field_init(q, 64)) {
02185          queue_t_unref(q, "String field allocation failed");
02186          return NULL;
02187       }
02188       ast_string_field_set(q, name, queuename);
02189    }
02190    return q;
02191 }

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

AddQueueMember application.

Definition at line 5731 of file app_queue.c.

References add_to_queue(), args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, 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().

05732 {
05733    int res=-1;
05734    char *parse, *temppos = NULL;
05735    AST_DECLARE_APP_ARGS(args,
05736       AST_APP_ARG(queuename);
05737       AST_APP_ARG(interface);
05738       AST_APP_ARG(penalty);
05739       AST_APP_ARG(options);
05740       AST_APP_ARG(membername);
05741       AST_APP_ARG(state_interface);
05742    );
05743    int penalty = 0;
05744 
05745    if (ast_strlen_zero(data)) {
05746       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
05747       return -1;
05748    }
05749 
05750    parse = ast_strdupa(data);
05751 
05752    AST_STANDARD_APP_ARGS(args, parse);
05753 
05754    if (ast_strlen_zero(args.interface)) {
05755       args.interface = ast_strdupa(chan->name);
05756       temppos = strrchr(args.interface, '-');
05757       if (temppos)
05758          *temppos = '\0';
05759    }
05760 
05761    if (!ast_strlen_zero(args.penalty)) {
05762       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
05763          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
05764          penalty = 0;
05765       }
05766    }
05767 
05768    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
05769    case RES_OKAY:
05770       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
05771       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
05772       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
05773       res = 0;
05774       break;
05775    case RES_EXISTS:
05776       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
05777       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
05778       res = 0;
05779       break;
05780    case RES_NOSUCHQUEUE:
05781       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
05782       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
05783       res = 0;
05784       break;
05785    case RES_OUTOFMEMORY:
05786       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
05787       break;
05788    }
05789 
05790    return res;
05791 }

AST_DATA_STRUCTURE ( queue_ent  ,
DATA_EXPORT_QUEUE_ENT   
)

AST_DATA_STRUCTURE ( member  ,
DATA_EXPORT_MEMBER   
)

AST_DATA_STRUCTURE ( call_queue  ,
DATA_EXPORT_CALL_QUEUE   
)

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

References ast_channel_datastore_find(), and queue_transfer_info.

04283 {
04284    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04285 }

static int autopause2int ( const char *  autopause  )  [static]

Definition at line 1232 of file app_queue.c.

References ARRAY_LEN, ast_strlen_zero(), ast_true(), autopause::autopause, autopausesmodes, QUEUE_AUTOPAUSE_OFF, and QUEUE_AUTOPAUSE_ON.

Referenced by queue_set_param().

01233 {
01234    int x;
01235    /*This 'double check' that default value is OFF */
01236    if (ast_strlen_zero(autopause))
01237       return QUEUE_AUTOPAUSE_OFF;
01238 
01239    /*This 'double check' is to ensure old values works */
01240    if(ast_true(autopause))
01241       return QUEUE_AUTOPAUSE_ON;
01242 
01243    for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
01244       if (!strcasecmp(autopause, autopausesmodes[x].name))
01245          return autopausesmodes[x].autopause;
01246    }
01247 
01248    /*This 'double check' that default value is OFF */
01249    return QUEUE_AUTOPAUSE_OFF;
01250 }

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

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

04110 {
04111    /* disregarding penalty on too few members? */
04112    int membercount = ao2_container_count(q->members);
04113    unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04114 
04115    if (usepenalty) {
04116       if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
04117          (qe->min_penalty && (mem->penalty < qe->min_penalty))) {
04118          return -1;
04119       }
04120    } else {
04121       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04122            membercount, q->penaltymemberslimit);
04123    }
04124 
04125    switch (q->strategy) {
04126    case QUEUE_STRATEGY_RINGALL:
04127       /* Everyone equal, except for penalty */
04128       tmp->metric = mem->penalty * 1000000 * usepenalty;
04129       break;
04130    case QUEUE_STRATEGY_LINEAR:
04131       if (pos < qe->linpos) {
04132          tmp->metric = 1000 + pos;
04133       } else {
04134          if (pos > qe->linpos)
04135             /* Indicate there is another priority */
04136             qe->linwrapped = 1;
04137          tmp->metric = pos;
04138       }
04139       tmp->metric += mem->penalty * 1000000 * usepenalty;
04140       break;
04141    case QUEUE_STRATEGY_RRORDERED:
04142    case QUEUE_STRATEGY_RRMEMORY:
04143       if (pos < q->rrpos) {
04144          tmp->metric = 1000 + pos;
04145       } else {
04146          if (pos > q->rrpos)
04147             /* Indicate there is another priority */
04148             q->wrapped = 1;
04149          tmp->metric = pos;
04150       }
04151       tmp->metric += mem->penalty * 1000000 * usepenalty;
04152       break;
04153    case QUEUE_STRATEGY_RANDOM:
04154       tmp->metric = ast_random() % 1000;
04155       tmp->metric += mem->penalty * 1000000 * usepenalty;
04156       break;
04157    case QUEUE_STRATEGY_WRANDOM:
04158       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04159       break;
04160    case QUEUE_STRATEGY_FEWESTCALLS:
04161       tmp->metric = mem->calls;
04162       tmp->metric += mem->penalty * 1000000 * usepenalty;
04163       break;
04164    case QUEUE_STRATEGY_LEASTRECENT:
04165       if (!mem->lastcall)
04166          tmp->metric = 0;
04167       else
04168          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04169       tmp->metric += mem->penalty * 1000000 * usepenalty;
04170       break;
04171    default:
04172       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04173       break;
04174    }
04175    return 0;
04176 }

static void callattempt_free ( struct callattempt doomed  )  [static]

Definition at line 2830 of file app_queue.c.

References ao2_ref, ast_free, ast_party_connected_line_free(), callattempt::connected, and callattempt::member.

Referenced by hangupcalls().

02831 {
02832    if (doomed->member) {
02833       ao2_ref(doomed->member, -1);
02834    }
02835    ast_party_connected_line_free(&doomed->connected);
02836    ast_free(doomed);
02837 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 1738 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, member::lastcall, call_queue::members, and call_queue::talktime.

Referenced by clear_stats(), and find_queue_by_name_rt().

01739 {
01740    q->holdtime = 0;
01741    q->callscompleted = 0;
01742    q->callsabandoned = 0;
01743    q->callscompletedinsl = 0;
01744    q->talktime = 0;
01745 
01746    if (q->members) {
01747       struct member *mem;
01748       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01749       while ((mem = ao2_iterator_next(&mem_iter))) {
01750          mem->calls = 0;
01751          mem->lastcall = 0;
01752          ao2_ref(mem, -1);
01753       }
01754       ao2_iterator_destroy(&mem_iter);
01755    }
01756 }

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

06907 {
06908    struct call_queue *q;
06909    struct ao2_iterator queue_iter;
06910 
06911    queue_iter = ao2_iterator_init(queues, 0);
06912    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06913       ao2_lock(q);
06914       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
06915          clear_queue(q);
06916       ao2_unlock(q);
06917       queue_t_unref(q, "Done with iterator");
06918    }
06919    ao2_iterator_destroy(&queue_iter);
06920    return 0;
06921 }

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

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

02911 {
02912    struct call_queue *q;
02913    struct member *mem;
02914    int found = 0;
02915    struct ao2_iterator queue_iter;
02916 
02917    queue_iter = ao2_iterator_init(queues, 0);
02918    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
02919       if (q == rq) { /* don't check myself, could deadlock */
02920          queue_t_unref(q, "Done with iterator");
02921          continue;
02922       }
02923       ao2_lock(q);
02924       if (q->count && q->members) {
02925          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02926             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02927             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02928                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);
02929                found = 1;
02930             }
02931             ao2_ref(mem, -1);
02932          }
02933       }
02934       ao2_unlock(q);
02935       queue_t_unref(q, "Done with iterator");
02936       if (found) {
02937          break;
02938       }
02939    }
02940    ao2_iterator_destroy(&queue_iter);
02941    return found;
02942 }

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

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

07099 {
07100    struct call_queue *q;
07101    char *ret = NULL;
07102    int which = 0;
07103    int wordlen = strlen(word);
07104    struct ao2_iterator queue_iter;
07105 
07106    queue_iter = ao2_iterator_init(queues, 0);
07107    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07108       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
07109          ret = ast_strdup(q->name);
07110          queue_t_unref(q, "Done with iterator");
07111          break;
07112       }
07113       queue_t_unref(q, "Done with iterator");
07114    }
07115    ao2_iterator_destroy(&queue_iter);
07116 
07117    return ret;
07118 }

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

Definition at line 7544 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

07545 {
07546    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
07547    switch (pos) {
07548    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
07549       return NULL;
07550    case 4: /* only one possible match, "to" */
07551       return state == 0 ? ast_strdup("to") : NULL;
07552    case 5: /* <queue> */
07553       return complete_queue(line, word, pos, state);
07554    case 6: /* only one possible match, "penalty" */
07555       return state == 0 ? ast_strdup("penalty") : NULL;
07556    case 7:
07557       if (state < 100) {      /* 0-99 */
07558          char *num;
07559          if ((num = ast_malloc(3))) {
07560             sprintf(num, "%d", state);
07561          }
07562          return num;
07563       } else {
07564          return NULL;
07565       }
07566    case 8: /* only one possible match, "as" */
07567       return state == 0 ? ast_strdup("as") : NULL;
07568    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
07569       return NULL;
07570    default:
07571       return NULL;
07572    }
07573 }

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

Definition at line 7765 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

07766 {
07767    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
07768    switch (pos) {
07769    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
07770       return NULL;
07771    case 4:  /* only one possible match, "queue" */
07772       return state == 0 ? ast_strdup("queue") : NULL;
07773    case 5:  /* <queue> */
07774       return complete_queue(line, word, pos, state);
07775    case 6: /* "reason" */
07776       return state == 0 ? ast_strdup("reason") : NULL;
07777    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
07778       return NULL;
07779    default:
07780       return NULL;
07781    }
07782 }

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

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

07675 {
07676    int which = 0;
07677    struct call_queue *q;
07678    struct member *m;
07679    struct ao2_iterator queue_iter;
07680    struct ao2_iterator mem_iter;
07681    int wordlen = strlen(word);
07682 
07683    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
07684    if (pos > 5 || pos < 3)
07685       return NULL;
07686    if (pos == 4)   /* only one possible match, 'from' */
07687       return (state == 0 ? ast_strdup("from") : NULL);
07688 
07689    if (pos == 5)   /* No need to duplicate code */
07690       return complete_queue(line, word, pos, state);
07691 
07692    /* here is the case for 3, <member> */
07693    queue_iter = ao2_iterator_init(queues, 0);
07694    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07695       ao2_lock(q);
07696       mem_iter = ao2_iterator_init(q->members, 0);
07697       while ((m = ao2_iterator_next(&mem_iter))) {
07698          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
07699             char *tmp;
07700             tmp = ast_strdup(m->interface);
07701             ao2_ref(m, -1);
07702             ao2_iterator_destroy(&mem_iter);
07703             ao2_unlock(q);
07704             queue_t_unref(q, "Done with iterator, returning interface name");
07705             ao2_iterator_destroy(&queue_iter);
07706             return tmp;
07707          }
07708          ao2_ref(m, -1);
07709       }
07710       ao2_iterator_destroy(&mem_iter);
07711       ao2_unlock(q);
07712       queue_t_unref(q, "Done with iterator");
07713    }
07714    ao2_iterator_destroy(&queue_iter);
07715 
07716    return NULL;
07717 }

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

Definition at line 7898 of file app_queue.c.

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

Referenced by handle_queue_rule_show().

07899 {
07900    int which = 0;
07901    struct rule_list *rl_iter;
07902    int wordlen = strlen(word);
07903    char *ret = NULL;
07904    if (pos != 3) /* Wha? */ {
07905       return NULL;
07906    }
07907 
07908    AST_LIST_LOCK(&rule_lists);
07909    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07910       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
07911          ret = ast_strdup(rl_iter->name);
07912          break;
07913       }
07914    }
07915    AST_LIST_UNLOCK(&rule_lists);
07916 
07917    return ret;
07918 }

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

Definition at line 7835 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

07836 {
07837    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
07838    switch (pos) {
07839    case 4:
07840       if (state == 0) {
07841          return ast_strdup("on");
07842       } else {
07843          return NULL;
07844       }
07845    case 6:
07846       if (state == 0) {
07847          return ast_strdup("in");
07848       } else {
07849          return NULL;
07850       }
07851    case 7:
07852       return complete_queue(line, word, pos, state);
07853    default:
07854       return NULL;
07855    }
07856 }

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

Definition at line 7120 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

07121 {
07122    if (pos == 2)
07123       return complete_queue(line, word, pos, state);
07124    return NULL;
07125 }

static int compress_char ( const char  c  )  [static]

Definition at line 1630 of file app_queue.c.

Referenced by member_hash_fn().

01631 {
01632    if (c < 32)
01633       return 0;
01634    else if (c > 96)
01635       return c - 64;
01636    else
01637       return c - 32;
01638 }

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

Copy rule from global list into specified queue.

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

Referenced by queue_exec().

05829 {
05830    struct penalty_rule *pr_iter;
05831    struct rule_list *rl_iter;
05832    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
05833    AST_LIST_LOCK(&rule_lists);
05834    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05835       if (!strcasecmp(rl_iter->name, tmp))
05836          break;
05837    }
05838    if (rl_iter) {
05839       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05840          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
05841          if (!new_pr) {
05842             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
05843             break;
05844          }
05845          new_pr->time = pr_iter->time;
05846          new_pr->max_value = pr_iter->max_value;
05847          new_pr->min_value = pr_iter->min_value;
05848          new_pr->max_relative = pr_iter->max_relative;
05849          new_pr->min_relative = pr_iter->min_relative;
05850          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
05851       }
05852    }
05853    AST_LIST_UNLOCK(&rule_lists);
05854 }

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

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

Definition at line 1598 of file app_queue.c.

References ao2_alloc, ast_copy_string(), ast_log(), ast_strdupa, ast_strlen_zero(), queue_ent::context, exten, get_queue_member_status(), LOG_WARNING, member::penalty, S_OR, and strsep().

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

01599 {
01600    struct member *cur;
01601    
01602    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01603       cur->penalty = penalty;
01604       cur->paused = paused;
01605       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01606       if (!ast_strlen_zero(state_interface))
01607          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01608       else
01609          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01610       if (!ast_strlen_zero(membername))
01611          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01612       else
01613          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01614       if (!strchr(cur->interface, '/'))
01615          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01616       if (!strncmp(cur->state_interface, "hint:", 5)) {
01617          char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01618          char *exten = strsep(&context, "@") + 5;
01619 
01620          ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01621          ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01622       }
01623       cur->status = get_queue_member_status(cur);
01624    }
01625 
01626    return cur;
01627 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

02166 {
02167    struct call_queue *q = obj;
02168    int i;
02169 
02170    free_members(q, 1);
02171    ast_string_field_free_memory(q);
02172    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02173       if (q->sound_periodicannounce[i])
02174          free(q->sound_periodicannounce[i]);
02175    }
02176    ao2_ref(q->members, -1);
02177 }

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

Definition at line 1496 of file app_queue.c.

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

Referenced by load_module(), and load_pbx().

01497 {
01498    enum ast_device_state state;
01499    const char *device;
01500    struct statechange *sc;
01501    size_t datapsize;
01502 
01503    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01504    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01505 
01506    if (ast_strlen_zero(device)) {
01507       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01508       return;
01509    }
01510    datapsize = sizeof(*sc) + strlen(device) + 1;
01511    if (!(sc = ast_calloc(1, datapsize))) {
01512       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01513       return;
01514    }
01515    sc->state = state;
01516    strcpy(sc->dev, device);
01517    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01518       ast_free(sc);
01519    }
01520 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2945 of file app_queue.c.

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

Referenced by ring_entry().

02946 {
02947    o->stillgoing = 0;
02948    ast_hangup(o->chan);
02949    o->chan = NULL;
02950 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

06954 {
06955    if (s)
06956       astman_append(s, "%s\r\n", str);
06957    else
06958       ast_cli(fd, "%s\n", str);
06959 }

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 5204 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, member::state_interface, and value.

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

05205 {
05206    struct member *cur_member;
05207    char value[PM_MAX_LEN];
05208    int value_len = 0;
05209    int res;
05210    struct ao2_iterator mem_iter;
05211 
05212    memset(value, 0, sizeof(value));
05213 
05214    if (!pm_queue)
05215       return;
05216 
05217    mem_iter = ao2_iterator_init(pm_queue->members, 0);
05218    while ((cur_member = ao2_iterator_next(&mem_iter))) {
05219       if (!cur_member->dynamic) {
05220          ao2_ref(cur_member, -1);
05221          continue;
05222       }
05223 
05224       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
05225          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
05226 
05227       ao2_ref(cur_member, -1);
05228 
05229       if (res != strlen(value + value_len)) {
05230          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
05231          break;
05232       }
05233       value_len += res;
05234    }
05235    ao2_iterator_destroy(&mem_iter);
05236    
05237    if (value_len && !cur_member) {
05238       if (ast_db_put(pm_family, pm_queue->name, value))
05239          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05240    } else
05241       /* Delete the entry if the queue is empty or there is an error */
05242       ast_db_del(pm_family, pm_queue->name);
05243 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 4330 of file app_queue.c.

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

04331 {
04332    struct queue_end_bridge *qeb = data;
04333    struct call_queue *q = qeb->q;
04334    struct ast_channel *chan = qeb->chan;
04335 
04336    if (ao2_ref(qeb, -1) == 1) {
04337       set_queue_variables(q, chan);
04338       /* This unrefs the reference we made in try_calling when we allocated qeb */
04339       queue_t_unref(q, "Expire bridge_config reference");
04340    }
04341 }

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

Definition at line 4323 of file app_queue.c.

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

04324 {
04325    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04326    ao2_ref(qeb, +1);
04327    qeb->chan = originator;
04328 }

static int extension_state_cb ( char *  context,
char *  exten,
enum ast_extension_states  state,
void *  data 
) [static]

Definition at line 1554 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_debug, ast_devstate2str(), extensionstate2devicestate(), call_queue::found, call_queue::members, queue_t_unref, queues, member::state_context, member::state_exten, and update_status().

Referenced by load_module(), and unload_module().

01555 {
01556    struct ao2_iterator miter, qiter;
01557    struct member *m;
01558    struct call_queue *q;
01559    int found = 0, device_state = extensionstate2devicestate(state);
01560 
01561    qiter = ao2_iterator_init(queues, 0);
01562    while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01563       ao2_lock(q);
01564 
01565       miter = ao2_iterator_init(q->members, 0);
01566       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01567          if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01568             update_status(q, m, device_state);
01569             ao2_ref(m, -1);
01570             found = 1;
01571             break;
01572          }
01573       }
01574       ao2_iterator_destroy(&miter);
01575 
01576       ao2_unlock(q);
01577       queue_t_unref(q, "Done with iterator");
01578    }
01579    ao2_iterator_destroy(&qiter);
01580 
01581         if (found) {
01582       ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01583    } else {
01584       ast_debug(3, "Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n",
01585            exten, context, device_state, ast_devstate2str(device_state));
01586    }
01587 
01588    return 0;
01589 }

static int extensionstate2devicestate ( int  state  )  [static]

Helper function which converts from extension state to device state values.

Definition at line 1523 of file app_queue.c.

References AST_DEVICE_BUSY, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_UNAVAILABLE, AST_EXTENSION_BUSY, AST_EXTENSION_DEACTIVATED, AST_EXTENSION_INUSE, AST_EXTENSION_NOT_INUSE, AST_EXTENSION_ONHOLD, AST_EXTENSION_REMOVED, AST_EXTENSION_RINGING, and AST_EXTENSION_UNAVAILABLE.

Referenced by extension_state_cb(), and get_queue_member_status().

01524 {
01525    switch (state) {
01526    case AST_EXTENSION_NOT_INUSE:
01527       state = AST_DEVICE_NOT_INUSE;
01528       break;
01529    case AST_EXTENSION_INUSE:
01530       state = AST_DEVICE_INUSE;
01531       break;
01532    case AST_EXTENSION_BUSY:
01533       state = AST_DEVICE_BUSY;
01534       break;
01535    case AST_EXTENSION_RINGING:
01536       state = AST_DEVICE_RINGING;
01537       break;
01538    case AST_EXTENSION_ONHOLD:
01539       state = AST_DEVICE_ONHOLD;
01540       break;
01541    case AST_EXTENSION_UNAVAILABLE:
01542       state = AST_DEVICE_UNAVAILABLE;
01543       break;
01544    case AST_EXTENSION_REMOVED:
01545    case AST_EXTENSION_DEACTIVATED:
01546    default:
01547       state = AST_DEVICE_INVALID;
01548       break;
01549    }
01550 
01551    return state;
01552 }

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

find the entry with the best metric, or NULL

Definition at line 3185 of file app_queue.c.

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

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

03186 {
03187    struct callattempt *best = NULL, *cur;
03188 
03189    for (cur = outgoing; cur; cur = cur->q_next) {
03190       if (cur->stillgoing &&              /* Not already done */
03191          !cur->chan &&              /* Isn't already going */
03192          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03193          best = cur;
03194       }
03195    }
03196 
03197    return best;
03198 }

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

Reload a single queue via realtime.

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

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

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

Definition at line 2203 of file app_queue.c.

References alloc_queue(), ao2_lock, ao2_t_find, ao2_unlock, ast_debug, ast_log(), clear_queue(), call_queue::dead, LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_unlink, call_queue::realtime, strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

02204 {
02205    struct ast_variable *v;
02206    struct call_queue *q, tmpq = {
02207       .name = queuename,   
02208    };
02209    struct member *m;
02210    struct ao2_iterator mem_iter;
02211    char *interface = NULL;
02212    const char *tmp_name;
02213    char *tmp;
02214    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
02215 
02216    /* Static queues override realtime. */
02217    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
02218       ao2_lock(q);
02219       if (!q->realtime) {
02220          if (q->dead) {
02221             ao2_unlock(q);
02222             queue_t_unref(q, "Queue is dead; can't return it");
02223             return NULL;
02224          } else {
02225             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
02226             ao2_unlock(q);
02227             return q;
02228          }
02229       }
02230    } else if (!member_config)
02231       /* Not found in the list, and it's not realtime ... */
02232       return NULL;
02233 
02234    /* Check if queue is defined in realtime. */
02235    if (!queue_vars) {
02236       /* Delete queue from in-core list if it has been deleted in realtime. */
02237       if (q) {
02238          /*! \note Hmm, can't seem to distinguish a DB failure from a not
02239             found condition... So we might delete an in-core queue
02240             in case of DB failure. */
02241          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
02242 
02243          q->dead = 1;
02244          /* Delete if unused (else will be deleted when last caller leaves). */
02245          queues_t_unlink(queues, q, "Unused; removing from container");
02246          ao2_unlock(q);
02247          queue_t_unref(q, "Queue is dead; can't return it");
02248       }
02249       return NULL;
02250    }
02251 
02252    /* Create a new queue if an in-core entry does not exist yet. */
02253    if (!q) {
02254       struct ast_variable *tmpvar = NULL;
02255       if (!(q = alloc_queue(queuename)))
02256          return NULL;
02257       ao2_lock(q);
02258       clear_queue(q);
02259       q->realtime = 1;
02260       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
02261        * will allocate the members properly
02262        */
02263       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
02264          if (!strcasecmp(tmpvar->name, "strategy")) {
02265             q->strategy = strat2int(tmpvar->value);
02266             if (q->strategy < 0) {
02267                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02268                tmpvar->value, q->name);
02269                q->strategy = QUEUE_STRATEGY_RINGALL;
02270             }
02271             break;
02272          }
02273       }
02274       /* We traversed all variables and didn't find a strategy */
02275       if (!tmpvar)
02276          q->strategy = QUEUE_STRATEGY_RINGALL;
02277       queues_t_link(queues, q, "Add queue to container");
02278    }
02279    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
02280 
02281    memset(tmpbuf, 0, sizeof(tmpbuf));
02282    for (v = queue_vars; v; v = v->next) {
02283       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
02284       if (strchr(v->name, '_')) {
02285          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
02286          tmp_name = tmpbuf;
02287          tmp = tmpbuf;
02288          while ((tmp = strchr(tmp, '_')))
02289             *tmp++ = '-';
02290       } else
02291          tmp_name = v->name;
02292 
02293       /* NULL values don't get returned from realtime; blank values should
02294        * still get set.  If someone doesn't want a value to be set, they
02295        * should set the realtime column to NULL, not blank. */
02296       queue_set_param(q, tmp_name, v->value, -1, 0);
02297    }
02298 
02299    /* Temporarily set realtime members dead so we can detect deleted ones. */
02300    mem_iter = ao2_iterator_init(q->members, 0);
02301    while ((m = ao2_iterator_next(&mem_iter))) {
02302       if (m->realtime)
02303          m->dead = 1;
02304       ao2_ref(m, -1);
02305    }
02306    ao2_iterator_destroy(&mem_iter);
02307 
02308    while ((interface = ast_category_browse(member_config, interface))) {
02309       rt_handle_member_record(q, interface,
02310          ast_variable_retrieve(member_config, interface, "uniqueid"),
02311          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
02312          ast_variable_retrieve(member_config, interface, "penalty"),
02313          ast_variable_retrieve(member_config, interface, "paused"),
02314          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
02315    }
02316 
02317    /* Delete all realtime members that have been deleted in DB. */
02318    mem_iter = ao2_iterator_init(q->members, 0);
02319    while ((m = ao2_iterator_next(&mem_iter))) {
02320       if (m->dead) {
02321          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02322          ao2_unlink(q->members, m);
02323       }
02324       ao2_ref(m, -1);
02325    }
02326    ao2_iterator_destroy(&mem_iter);
02327 
02328    ao2_unlock(q);
02329 
02330    return q;
02331 }

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

Iterate through queue's member list and delete them.

Definition at line 2149 of file app_queue.c.

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

Referenced by destroy_queue().

02150 {
02151    /* Free non-dynamic members */
02152    struct member *cur;
02153    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
02154 
02155    while ((cur = ao2_iterator_next(&mem_iter))) {
02156       if (all || !cur->dynamic) {
02157          ao2_unlink(q->members, cur);
02158       }
02159       ao2_ref(cur, -1);
02160    }
02161    ao2_iterator_destroy(&mem_iter);
02162 }

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

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

05477 {
05478    int foundqueue = 0, penalty;
05479    struct call_queue *q, tmpq = {
05480       .name = queuename,   
05481    };
05482    struct member *mem;
05483    
05484    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05485       foundqueue = 1;
05486       ao2_lock(q);
05487       if ((mem = interface_exists(q, interface))) {
05488          penalty = mem->penalty;
05489          ao2_ref(mem, -1);
05490          ao2_unlock(q);
05491          queue_t_unref(q, "Search complete");
05492          return penalty;
05493       }
05494       ao2_unlock(q);
05495       queue_t_unref(q, "Search complete");
05496    }
05497 
05498    /* some useful debuging */
05499    if (foundqueue) 
05500       ast_log (LOG_ERROR, "Invalid queuename\n");
05501    else 
05502       ast_log (LOG_ERROR, "Invalid interface\n");
05503 
05504    return RESULT_FAILURE;
05505 }

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 1346 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_RINGING, 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_RINGING, 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().

01347 {
01348    struct member *member;
01349    struct ao2_iterator mem_iter;
01350 
01351    ao2_lock(q);
01352    mem_iter = ao2_iterator_init(q->members, 0);
01353    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01354       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
01355          if (conditions & QUEUE_EMPTY_PENALTY) {
01356             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01357             continue;
01358          }
01359       }
01360 
01361       switch (member->status) {
01362       case AST_DEVICE_INVALID:
01363          if (conditions & QUEUE_EMPTY_INVALID) {
01364             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01365             break;
01366          }
01367          goto default_case;
01368       case AST_DEVICE_UNAVAILABLE:
01369          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01370             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01371             break;
01372          }
01373          goto default_case;
01374       case AST_DEVICE_INUSE:
01375          if (conditions & QUEUE_EMPTY_INUSE) {
01376             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01377             break;
01378          }
01379          goto default_case;
01380       case AST_DEVICE_RINGING:
01381          if (conditions & QUEUE_EMPTY_RINGING) {
01382             ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01383             break;
01384          }
01385          goto default_case;
01386       case AST_DEVICE_UNKNOWN:
01387          if (conditions & QUEUE_EMPTY_UNKNOWN) {
01388             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01389             break;
01390          }
01391          /* Fall-through */
01392       default:
01393       default_case:
01394          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01395             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01396             break;
01397          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01398             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);
01399             break;
01400          } else {
01401             ao2_ref(member, -1);
01402             ao2_iterator_destroy(&mem_iter);
01403             ao2_unlock(q);
01404             ast_debug(4, "%s is available.\n", member->membername);
01405             return 0;
01406          }
01407          break;
01408       }
01409    }
01410    ao2_iterator_destroy(&mem_iter);
01411 
01412    ao2_unlock(q);
01413    return -1;
01414 }

static int get_queue_member_status ( struct member cur  )  [static]

Return the current state of a member.

Definition at line 1592 of file app_queue.c.

References ast_device_state(), ast_extension_state(), ast_strlen_zero(), extensionstate2devicestate(), member::state_context, member::state_exten, and member::state_interface.

Referenced by create_queue_member(), kill_dead_members(), and ring_entry().

01593 {
01594    return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten));
01595 }

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

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

07601 {
07602    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07603    int penalty;
07604 
07605    switch ( cmd ) {
07606    case CLI_INIT:
07607       e->command = "queue add member";
07608       e->usage =
07609          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07610          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
07611       return NULL;
07612    case CLI_GENERATE:
07613       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07614    }
07615 
07616    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07617       return CLI_SHOWUSAGE;
07618    } else if (strcmp(a->argv[4], "to")) {
07619       return CLI_SHOWUSAGE;
07620    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07621       return CLI_SHOWUSAGE;
07622    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07623       return CLI_SHOWUSAGE;
07624    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
07625       return CLI_SHOWUSAGE;
07626    }
07627 
07628    queuename = a->argv[5];
07629    interface = a->argv[3];
07630    if (a->argc >= 8) {
07631       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
07632          if (penalty < 0) {
07633             ast_cli(a->fd, "Penalty must be >= 0\n");
07634             penalty = 0;
07635          }
07636       } else {
07637          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
07638          penalty = 0;
07639       }
07640    } else {
07641       penalty = 0;
07642    }
07643 
07644    if (a->argc >= 10) {
07645       membername = a->argv[9];
07646    }
07647 
07648    if (a->argc >= 12) {
07649       state_interface = a->argv[11];
07650    }
07651 
07652    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
07653    case RES_OKAY:
07654       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
07655       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
07656       return CLI_SUCCESS;
07657    case RES_EXISTS:
07658       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
07659       return CLI_FAILURE;
07660    case RES_NOSUCHQUEUE:
07661       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
07662       return CLI_FAILURE;
07663    case RES_OUTOFMEMORY:
07664       ast_cli(a->fd, "Out of memory\n");
07665       return CLI_FAILURE;
07666    case RES_NOT_DYNAMIC:
07667       ast_cli(a->fd, "Member not dynamic\n");
07668       return CLI_FAILURE;
07669    default:
07670       return CLI_FAILURE;
07671    }
07672 }

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

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

07785 {
07786    const char *queuename, *interface, *reason;
07787    int paused;
07788 
07789    switch (cmd) {
07790    case CLI_INIT:
07791       e->command = "queue {pause|unpause} member";
07792       e->usage = 
07793          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
07794          "  Pause or unpause a queue member. Not specifying a particular queue\n"
07795          "  will pause or unpause a member across all queues to which the member\n"
07796          "  belongs.\n";
07797       return NULL;
07798    case CLI_GENERATE:
07799       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
07800    }
07801 
07802    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
07803       return CLI_SHOWUSAGE;
07804    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
07805       return CLI_SHOWUSAGE;
07806    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
07807       return CLI_SHOWUSAGE;
07808    }
07809 
07810 
07811    interface = a->argv[3];
07812    queuename = a->argc >= 6 ? a->argv[5] : NULL;
07813    reason = a->argc == 8 ? a->argv[7] : NULL;
07814    paused = !strcasecmp(a->argv[1], "pause");
07815 
07816    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
07817       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
07818       if (!ast_strlen_zero(queuename))
07819          ast_cli(a->fd, " in queue '%s'", queuename);
07820       if (!ast_strlen_zero(reason))
07821          ast_cli(a->fd, " for reason '%s'", reason);
07822       ast_cli(a->fd, "\n");
07823       return CLI_SUCCESS;
07824    } else {
07825       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
07826       if (!ast_strlen_zero(queuename))
07827          ast_cli(a->fd, " in queue '%s'", queuename);
07828       if (!ast_strlen_zero(reason))
07829          ast_cli(a->fd, " for reason '%s'", reason);
07830       ast_cli(a->fd, "\n");
07831       return CLI_FAILURE;
07832    }
07833 }

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

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

07994 {
07995    struct ast_flags mask = {0,};
07996    int i;
07997 
07998    switch (cmd) {
07999       case CLI_INIT:
08000          e->command = "queue reload {parameters|members|rules|all}";
08001          e->usage =
08002             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
08003             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
08004             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
08005             "specified in order to know what information to reload. Below is an explanation\n"
08006             "of each of these qualifiers.\n"
08007             "\n"
08008             "\t'members' - reload queue members from queues.conf\n"
08009             "\t'parameters' - reload all queue options except for queue members\n"
08010             "\t'rules' - reload the queuerules.conf file\n"
08011             "\t'all' - reload queue rules, parameters, and members\n"
08012             "\n"
08013             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
08014             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
08015             "one queue is specified when using this command, reloading queue rules may cause\n"
08016             "other queues to be affected\n";
08017          return NULL;
08018       case CLI_GENERATE:
08019          if (a->pos >= 3) {
08020             return complete_queue(a->line, a->word, a->pos, a->n);
08021          } else {
08022             return NULL;
08023          }
08024    }
08025 
08026    if (a->argc < 3)
08027       return CLI_SHOWUSAGE;
08028 
08029    if (!strcasecmp(a->argv[2], "rules")) {
08030       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08031    } else if (!strcasecmp(a->argv[2], "members")) {
08032       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08033    } else if (!strcasecmp(a->argv[2], "parameters")) {
08034       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08035    } else if (!strcasecmp(a->argv[2], "all")) {
08036       ast_set_flag(&mask, AST_FLAGS_ALL);
08037    }
08038 
08039    if (a->argc == 3) {
08040       reload_handler(1, &mask, NULL);
08041       return CLI_SUCCESS;
08042    }
08043 
08044    for (i = 3; i < a->argc; ++i) {
08045       reload_handler(1, &mask, a->argv[i]);
08046    }
08047 
08048    return CLI_SUCCESS;
08049 }

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

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

07720 {
07721    const char *queuename, *interface;
07722 
07723    switch (cmd) {
07724    case CLI_INIT:
07725       e->command = "queue remove member";
07726       e->usage = 
07727          "Usage: queue remove member <channel> from <queue>\n"
07728          "       Remove a specific channel from a queue.\n";
07729       return NULL;
07730    case CLI_GENERATE:
07731       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
07732    }
07733 
07734    if (a->argc != 6) {
07735       return CLI_SHOWUSAGE;
07736    } else if (strcmp(a->argv[4], "from")) {
07737       return CLI_SHOWUSAGE;
07738    }
07739 
07740    queuename = a->argv[5];
07741    interface = a->argv[3];
07742 
07743    switch (remove_from_queue(queuename, interface)) {
07744    case RES_OKAY:
07745       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
07746       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
07747       return CLI_SUCCESS;
07748    case RES_EXISTS:
07749       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
07750       return CLI_FAILURE;
07751    case RES_NOSUCHQUEUE:
07752       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
07753       return CLI_FAILURE;
07754    case RES_OUTOFMEMORY:
07755       ast_cli(a->fd, "Out of memory\n");
07756       return CLI_FAILURE;
07757    case RES_NOT_DYNAMIC:
07758       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
07759       return CLI_FAILURE;
07760    default:
07761       return CLI_FAILURE;
07762    }
07763 }

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

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

07955 {
07956    struct ast_flags mask = {QUEUE_RESET_STATS,};
07957    int i;
07958 
07959    switch (cmd) {
07960       case CLI_INIT:
07961          e->command = "queue reset stats";
07962          e->usage =
07963             "Usage: queue reset stats [<queuenames>]\n"
07964             "\n"
07965             "Issuing this command will reset statistics for\n"
07966             "<queuenames>, or for all queues if no queue is\n"
07967             "specified.\n";
07968          return NULL;
07969       case CLI_GENERATE:
07970          if (a->pos >= 3) {
07971             return complete_queue(a->line, a->word, a->pos, a->n);
07972          } else {
07973             return NULL;
07974          }
07975    }
07976 
07977    if (a->argc < 3) {
07978       return CLI_SHOWUSAGE;
07979    }
07980 
07981    if (a->argc == 3) {
07982       reload_handler(1, &mask, NULL);
07983       return CLI_SUCCESS;
07984    }
07985 
07986    for (i = 3; i < a->argc; ++i) {
07987       reload_handler(1, &mask, a->argv[i]);
07988    }
07989 
07990    return CLI_SUCCESS;
07991 }

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

Definition at line 7920 of file app_queue.c.

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

07921 {
07922    const char *rule;
07923    struct rule_list *rl_iter;
07924    struct penalty_rule *pr_iter;
07925    switch (cmd) {
07926    case CLI_INIT:
07927       e->command = "queue show rules";
07928       e->usage =
07929       "Usage: queue show rules [rulename]\n"
07930       "  Show the list of rules associated with rulename. If no\n"
07931       "  rulename is specified, list all rules defined in queuerules.conf\n";
07932       return NULL;
07933    case CLI_GENERATE:
07934       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
07935    }
07936 
07937    if (a->argc != 3 && a->argc != 4)
07938       return CLI_SHOWUSAGE;
07939 
07940    rule = a->argc == 4 ? a->argv[3] : "";
07941    AST_LIST_LOCK(&rule_lists);
07942    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07943       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
07944          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
07945          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07946             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);
07947          }
07948       }
07949    }
07950    AST_LIST_UNLOCK(&rule_lists);
07951    return CLI_SUCCESS; 
07952 }

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

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

07859 {
07860    const char *queuename = NULL, *interface;
07861    int penalty = 0;
07862 
07863    switch (cmd) {
07864    case CLI_INIT:
07865       e->command = "queue set penalty";
07866       e->usage = 
07867       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
07868       "  Set a member's penalty in the queue specified. If no queue is specified\n"
07869       "  then that interface's penalty is set in all queues to which that interface is a member\n";
07870       return NULL;
07871    case CLI_GENERATE:
07872       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
07873    }
07874 
07875    if (a->argc != 6 && a->argc != 8) {
07876       return CLI_SHOWUSAGE;
07877    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
07878       return CLI_SHOWUSAGE;
07879    }
07880 
07881    if (a->argc == 8)
07882       queuename = a->argv[7];
07883    interface = a->argv[5];
07884    penalty = atoi(a->argv[3]);
07885 
07886    switch (set_member_penalty(queuename, interface, penalty)) {
07887    case RESULT_SUCCESS:
07888       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07889       return CLI_SUCCESS;
07890    case RESULT_FAILURE:
07891       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07892       return CLI_FAILURE;
07893    default:
07894       return CLI_FAILURE;
07895    }
07896 }

static int handle_statechange ( void *  datap  )  [static]

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

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

01453 {
01454    struct statechange *sc = datap;
01455    struct ao2_iterator miter, qiter;
01456    struct member *m;
01457    struct call_queue *q;
01458    char interface[80], *slash_pos;
01459    int found = 0;
01460 
01461    qiter = ao2_iterator_init(queues, 0);
01462    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01463       ao2_lock(q);
01464 
01465       miter = ao2_iterator_init(q->members, 0);
01466       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01467          ast_copy_string(interface, m->state_interface, sizeof(interface));
01468 
01469          if ((slash_pos = strchr(interface, '/')))
01470             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01471                *slash_pos = '\0';
01472 
01473          if (!strcasecmp(interface, sc->dev)) {
01474             found = 1;
01475             update_status(q, m, sc->state);
01476             ao2_ref(m, -1);
01477             break;
01478          }
01479       }
01480       ao2_iterator_destroy(&miter);
01481 
01482       ao2_unlock(q);
01483       queue_t_unref(q, "Done with iterator");
01484    }
01485    ao2_iterator_destroy(&qiter);
01486 
01487    if (found)
01488       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01489    else
01490       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));
01491 
01492    ast_free(sc);
01493    return 0;
01494 }

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

References callattempt::aoc_s_rate_list, ast_aoc_destroy_decoded(), AST_FLAG_ANSWERED_ELSEWHERE, ast_hangup(), ast_set_flag, callattempt_free(), callattempt::chan, and callattempt::q_next.

02841 {
02842    struct callattempt *oo;
02843 
02844    while (outgoing) {
02845       /* If someone else answered the call we should indicate this in the CANCEL */
02846       /* Hangup any existing lines we have open */
02847       if (outgoing->chan && (outgoing->chan != exception)) {
02848          if (exception || cancel_answered_elsewhere)
02849             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02850          ast_hangup(outgoing->chan);
02851       }
02852       oo = outgoing;
02853       outgoing = outgoing->q_next;
02854       ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
02855       callattempt_free(oo);
02856    }
02857 }

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 1662 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::autopause, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, penalty_rule::list, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::numperiodicannounce, call_queue::penaltymemberslimit, call_queue::periodicannouncefrequency, QUEUE_AUTOPAUSE_OFF, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRORDERED, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_single_queue().

01663 {
01664    int i;
01665    struct penalty_rule *pr_iter;
01666 
01667    q->dead = 0;
01668    q->retry = DEFAULT_RETRY;
01669    q->timeout = DEFAULT_TIMEOUT;
01670    q->maxlen = 0;
01671    q->announcefrequency = 0;
01672    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01673    q->announceholdtime = 1;
01674    q->announcepositionlimit = 10; /* Default 10 positions */
01675    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
01676    q->roundingseconds = 0; /* Default - don't announce seconds */
01677    q->servicelevel = 0;
01678    q->ringinuse = 1;
01679    q->setinterfacevar = 0;
01680    q->setqueuevar = 0;
01681    q->setqueueentryvar = 0;
01682    q->autofill = autofill_default;
01683    q->montype = montype_default;
01684    q->monfmt[0] = '\0';
01685    q->reportholdtime = 0;
01686    q->wrapuptime = 0;
01687    q->penaltymemberslimit = 0;
01688    q->joinempty = 0;
01689    q->leavewhenempty = 0;
01690    q->memberdelay = 0;
01691    q->maskmemberstatus = 0;
01692    q->eventwhencalled = 0;
01693    q->weight = 0;
01694    q->timeoutrestart = 0;
01695    q->periodicannouncefrequency = 0;
01696    q->randomperiodicannounce = 0;
01697    q->numperiodicannounce = 0;
01698    q->autopause = QUEUE_AUTOPAUSE_OFF;
01699    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01700    if (!q->members) {
01701       if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
01702          /* linear strategy depends on order, so we have to place all members in a single bucket */
01703          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01704       else
01705          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01706    }
01707    q->found = 1;
01708 
01709    ast_string_field_set(q, sound_next, "queue-youarenext");
01710    ast_string_field_set(q, sound_thereare, "queue-thereare");
01711    ast_string_field_set(q, sound_calls, "queue-callswaiting");
01712    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01713    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01714    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01715    ast_string_field_set(q, sound_minutes, "queue-minutes");
01716    ast_string_field_set(q, sound_minute, "queue-minute");
01717    ast_string_field_set(q, sound_seconds, "queue-seconds");
01718    ast_string_field_set(q, sound_thanks, "queue-thankyou");
01719    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01720 
01721    if (!q->sound_periodicannounce[0]) {
01722       q->sound_periodicannounce[0] = ast_str_create(32);
01723    }
01724 
01725    if (q->sound_periodicannounce[0]) {
01726       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01727    }
01728 
01729    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01730       if (q->sound_periodicannounce[i])
01731          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01732    }
01733 
01734    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01735       ast_free(pr_iter);
01736 }

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

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

Referenced by join_queue().

01317 {
01318    struct queue_ent *cur;
01319 
01320    if (!q || !new)
01321       return;
01322    if (prev) {
01323       cur = prev->next;
01324       prev->next = new;
01325    } else {
01326       cur = q->head;
01327       q->head = new;
01328    }
01329    new->next = cur;
01330 
01331    /* every queue_ent must have a reference to it's parent call_queue, this
01332     * reference does not go away until the end of the queue_ent's life, meaning
01333     * that even when the queue_ent leaves the call_queue this ref must remain. */
01334    queue_ref(q);
01335    new->parent = q;
01336    new->pos = ++(*pos);
01337    new->opos = *pos;
01338 }

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

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

Referenced by reload_queue_rules().

01768 {
01769    char *timestr, *maxstr, *minstr, *contentdup;
01770    struct penalty_rule *rule = NULL, *rule_iter;
01771    struct rule_list *rl_iter;
01772    int penaltychangetime, inserted = 0;
01773 
01774    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01775       return -1;
01776    }
01777 
01778    contentdup = ast_strdupa(content);
01779    
01780    if (!(maxstr = strchr(contentdup, ','))) {
01781       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01782       ast_free(rule);
01783       return -1;
01784    }
01785 
01786    *maxstr++ = '\0';
01787    timestr = contentdup;
01788 
01789    if ((penaltychangetime = atoi(timestr)) < 0) {
01790       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01791       ast_free(rule);
01792       return -1;
01793    }
01794 
01795    rule->time = penaltychangetime;
01796 
01797    if ((minstr = strchr(maxstr,',')))
01798       *minstr++ = '\0';
01799    
01800    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01801     * OR if a min penalty change is indicated but no max penalty change is */
01802    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01803       rule->max_relative = 1;
01804    }
01805 
01806    rule->max_value = atoi(maxstr);
01807 
01808    if (!ast_strlen_zero(minstr)) {
01809       if (*minstr == '+' || *minstr == '-')
01810          rule->min_relative = 1;
01811       rule->min_value = atoi(minstr);
01812    } else /*there was no minimum specified, so assume this means no change*/
01813       rule->min_relative = 1;
01814 
01815    /*We have the rule made, now we need to insert it where it belongs*/
01816    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01817       if (strcasecmp(rl_iter->name, list_name))
01818          continue;
01819 
01820       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01821          if (rule->time < rule_iter->time) {
01822             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01823             inserted = 1;
01824             break;
01825          }
01826       }
01827       AST_LIST_TRAVERSE_SAFE_END;
01828    
01829       if (!inserted) {
01830          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01831          inserted = 1;
01832       }
01833 
01834       break;
01835    }
01836 
01837    if (!inserted) {
01838       ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
01839       ast_free(rule);
01840       return -1;
01841    }
01842    return 0;
01843 }

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

Definition at line 1208 of file app_queue.c.

References ARRAY_LEN, strategy::name, and strategies.

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

01209 {
01210    int x;
01211 
01212    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01213       if (strategy == strategies[x].strategy)
01214          return strategies[x].name;
01215    }
01216 
01217    return "<unknown>";
01218 }

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

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

05179 {
05180    struct member *mem;
05181    struct ao2_iterator mem_iter;
05182 
05183    if (!q)
05184       return NULL;
05185 
05186    mem_iter = ao2_iterator_init(q->members, 0);
05187    while ((mem = ao2_iterator_next(&mem_iter))) {
05188       if (!strcasecmp(interface, mem->interface)) {
05189          ao2_iterator_destroy(&mem_iter);
05190          return mem;
05191       }
05192       ao2_ref(mem, -1);
05193    }
05194    ao2_iterator_destroy(&mem_iter);
05195 
05196    return NULL;
05197 }

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

03911 {
03912    struct queue_ent *ch;
03913    int res;
03914    int avl;
03915    int idx = 0;
03916    /* This needs a lock. How many members are available to be served? */
03917    ao2_lock(qe->parent);
03918 
03919    avl = num_available_members(qe->parent);
03920 
03921    ch = qe->parent->head;
03922 
03923    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
03924 
03925    while ((idx < avl) && (ch) && (ch != qe)) {
03926       if (!ch->pending)
03927          idx++;
03928       ch = ch->next;       
03929    }
03930 
03931    ao2_unlock(qe->parent);
03932    /* If the queue entry is within avl [the number of available members] calls from the top ... 
03933     * Autofill and position check added to support autofill=no (as only calls
03934     * from the front of the queue are valid when autofill is disabled)
03935     */
03936    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
03937       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
03938       res = 1;
03939    } else {
03940       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
03941       res = 0;
03942    }
03943 
03944    return res;
03945 }

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

Definition at line 2450 of file app_queue.c.

References queue_ent::announce, ao2_lock, ao2_unlock, ast_copy_string(), ast_debug, ast_log(), ast_manager_event, ast_channel::caller, queue_ent::chan, ast_channel::connected, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, get_member_status(), call_queue::head, ast_party_connected_line::id, ast_party_caller::id, insert_entry(), call_queue::joinempty, load_realtime_queue(), LOG_NOTICE, queue_ent::max_penalty, call_queue::maxlen, queue_ent::min_penalty, queue_ent::moh, ast_party_id::name, ast_channel::name, queue_ent::next, ast_party_id::number, queue_ent::pos, queue_ent::prio, QUEUE_FULL, QUEUE_JOINEMPTY, queue_t_unref, QUEUE_UNKNOWN, S_COR, status, ast_party_name::str, ast_party_number::str, ast_channel::uniqueid, ast_party_name::valid, and ast_party_number::valid.

Referenced by queue_exec().

02451 {
02452    struct call_queue *q;
02453    struct queue_ent *cur, *prev = NULL;
02454    int res = -1;
02455    int pos = 0;
02456    int inserted = 0;
02457 
02458    if (!(q = load_realtime_queue(queuename)))
02459       return res;
02460 
02461    ao2_lock(q);
02462 
02463    /* This is our one */
02464    if (q->joinempty) {
02465       int status = 0;
02466       if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
02467          *reason = QUEUE_JOINEMPTY;
02468          ao2_unlock(q);
02469          queue_t_unref(q, "Done with realtime queue");
02470          return res;
02471       }
02472    }
02473    if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
02474       *reason = QUEUE_FULL;
02475    else if (*reason == QUEUE_UNKNOWN) {
02476       /* There's space for us, put us at the right position inside
02477        * the queue.
02478        * Take into account the priority of the calling user */
02479       inserted = 0;
02480       prev = NULL;
02481       cur = q->head;
02482       while (cur) {
02483          /* We have higher priority than the current user, enter
02484           * before him, after all the other users with priority
02485           * higher or equal to our priority. */
02486          if ((!inserted) && (qe->prio > cur->prio)) {
02487             insert_entry(q, prev, qe, &pos);
02488             inserted = 1;
02489          }
02490          /* <= is necessary for the position comparison because it may not be possible to enter
02491           * at our desired position since higher-priority callers may have taken the position we want
02492           */
02493          if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
02494             insert_entry(q, prev, qe, &pos);
02495             inserted = 1;
02496             /*pos is incremented inside insert_entry, so don't need to add 1 here*/
02497             if (position < pos) {
02498                ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
02499             }
02500          }
02501          cur->pos = ++pos;
02502          prev = cur;
02503          cur = cur->next;
02504       }
02505       /* No luck, join at the end of the queue */
02506       if (!inserted)
02507          insert_entry(q, prev, qe, &pos);
02508       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02509       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02510       ast_copy_string(qe->context, q->context, sizeof(qe->context));
02511       q->count++;
02512       res = 0;
02513       ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
02514          "Channel: %s\r\n"
02515          "CallerIDNum: %s\r\n"
02516          "CallerIDName: %s\r\n"
02517          "ConnectedLineNum: %s\r\n"
02518          "ConnectedLineName: %s\r\n"
02519          "Queue: %s\r\n"
02520          "Position: %d\r\n"
02521          "Count: %d\r\n"
02522          "Uniqueid: %s\r\n",
02523          qe->chan->name,
02524          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02525          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
02526          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02527          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
02528          q->name, qe->pos, q->count, qe->chan->uniqueid );
02529       ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02530    }
02531    ao2_unlock(q);
02532    queue_t_unref(q, "Done with realtime queue");
02533 
02534    return res;
02535 }

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

Definition at line 6694 of file app_queue.c.

References CMP_MATCH, member::delme, get_queue_member_status(), and member::status.

06695 {
06696    struct member *member = obj;
06697 
06698    if (!member->delme) {
06699       member->status = get_queue_member_status(member);
06700       return 0;
06701    } else {
06702       return CMP_MATCH;
06703    }
06704 }

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

Definition at line 6823 of file app_queue.c.

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

Referenced by reload_queues().

06824 {
06825    struct call_queue *q = obj;
06826    char *queuename = arg;
06827    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
06828       return CMP_MATCH;
06829    } else {
06830       return 0;
06831    }
06832 }

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

References ao2_lock, ao2_unlock, ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_load_realtime(), ast_manager_event, ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, penalty_rule::list, call_queue::name, ast_channel::name, queue_ent::next, queue_ent::parent, pbx_builtin_setvar_helper(), 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 wait_our_turn().

02763 {
02764    struct call_queue *q;
02765    struct queue_ent *current, *prev = NULL;
02766    struct penalty_rule *pr_iter;
02767    int pos = 0;
02768 
02769    if (!(q = qe->parent))
02770       return;
02771    queue_t_ref(q, "Copy queue pointer from queue entry");
02772    ao2_lock(q);
02773 
02774    prev = NULL;
02775    for (current = q->head; current; current = current->next) {
02776       if (current == qe) {
02777          char posstr[20];
02778          q->count--;
02779 
02780          /* Take us out of the queue */
02781          ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
02782             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
02783             qe->chan->name, q->name,  q->count, qe->pos, qe->chan->uniqueid);
02784          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02785          /* Take us out of the queue */
02786          if (prev)
02787             prev->next = current->next;
02788          else
02789             q->head = current->next;
02790          /* Free penalty rules */
02791          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02792             ast_free(pr_iter);
02793          snprintf(posstr, sizeof(posstr), "%d", qe->pos);
02794          pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
02795       } else {
02796          /* Renumber the people after us in the queue based on a new count */
02797          current->pos = ++pos;
02798          prev = current;
02799       }
02800    }
02801    ao2_unlock(q);
02802 
02803    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02804    if (q->realtime) {
02805       struct ast_variable *var;
02806       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02807          q->dead = 1;
02808       } else {
02809          ast_variables_destroy(var);
02810       }
02811    }
02812 
02813    if (q->dead) { 
02814       /* It's dead and nobody is in it, so kill it */
02815       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02816    }
02817    /* unref the explicit ref earlier in the function */
02818    queue_t_unref(q, "Expire copied reference");
02819 }

static int load_module ( void   )  [static]

Definition at line 8396 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_data_register_multiple, AST_EVENT_DEVICE_STATE, AST_EVENT_IE_END, ast_event_subscribe(), ast_extension_state_add(), AST_FLAGS_ALL, ast_free_ptr, ast_log(), ast_manager_register_xml, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application_xml, ast_strdup, ast_taskprocessor_get(), cli_queue, device_state_cb(), device_state_sub, devicestate_tps, EVENT_FLAG_AGENT, extension_state_cb(), 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_data_providers, queue_exec(), queue_hash_cb(), queueexists_function, 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().

08397 {
08398    int res;
08399    struct ast_context *con;
08400    struct ast_flags mask = {AST_FLAGS_ALL, };
08401 
08402    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08403 
08404    use_weight = 0;
08405 
08406    if (reload_handler(0, &mask, NULL))
08407       return AST_MODULE_LOAD_DECLINE;
08408 
08409    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08410    if (!con)
08411       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08412    else
08413       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08414 
08415    if (queue_persistent_members)
08416       reload_queue_members();
08417 
08418    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08419 
08420    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08421    res = ast_register_application_xml(app, queue_exec);
08422    res |= ast_register_application_xml(app_aqm, aqm_exec);
08423    res |= ast_register_application_xml(app_rqm, rqm_exec);
08424    res |= ast_register_application_xml(app_pqm, pqm_exec);
08425    res |= ast_register_application_xml(app_upqm, upqm_exec);
08426    res |= ast_register_application_xml(app_ql, ql_exec);
08427    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08428    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08429    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08430    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08431    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08432    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08433    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08434    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08435    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08436    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08437    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08438    res |= ast_custom_function_register(&queuevar_function);
08439    res |= ast_custom_function_register(&queueexists_function);
08440    res |= ast_custom_function_register(&queuemembercount_function);
08441    res |= ast_custom_function_register(&queuemembercount_dep);
08442    res |= ast_custom_function_register(&queuememberlist_function);
08443    res |= ast_custom_function_register(&queuewaitingcount_function);
08444    res |= ast_custom_function_register(&queuememberpenalty_function);
08445 
08446    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08447       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08448    }
08449 
08450    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
08451    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08452       res = -1;
08453    }
08454 
08455    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08456 
08457    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08458 
08459    return res ? AST_MODULE_LOAD_DECLINE : 0;
08460 }

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

Note:
Returns a reference to the loaded realtime queue.

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

References ao2_t_find, ast_atomic_fetchadd_int(), ast_config_destroy(), ast_config_new(), ast_debug, ast_load_realtime(), ast_load_realtime_multientry(), ast_variables_destroy(), find_queue_by_name_rt(), call_queue::name, OBJ_POINTER, queue_t_unref, queues, call_queue::realtime, SENTINEL, update_realtime_members(), and call_queue::weight.

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

02335 {
02336    struct ast_variable *queue_vars;
02337    struct ast_config *member_config = NULL;
02338    struct call_queue *q = NULL, tmpq = {
02339       .name = queuename,   
02340    };
02341    int prev_weight = 0;
02342 
02343    /* Find the queue in the in-core list first. */
02344    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
02345 
02346    if (!q || q->realtime) {
02347       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
02348          queue operations while waiting for the DB.
02349 
02350          This will be two separate database transactions, so we might
02351          see queue parameters as they were before another process
02352          changed the queue and member list as it was after the change.
02353          Thus we might see an empty member list when a queue is
02354          deleted. In practise, this is unlikely to cause a problem. */
02355 
02356       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
02357       if (queue_vars) {
02358          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
02359          if (!member_config) {
02360             ast_debug(1, "No queue_members defined in config extconfig.conf\n");
02361             member_config = ast_config_new();
02362          }
02363       }
02364       if (q) {
02365          prev_weight = q->weight ? 1 : 0;
02366          queue_t_unref(q, "Need to find realtime queue");
02367       }
02368 
02369       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
02370       ast_config_destroy(member_config);
02371       ast_variables_destroy(queue_vars);
02372 
02373       /* update the use_weight value if the queue's has gained or lost a weight */
02374       if (q) {
02375          if (!q->weight && prev_weight) {
02376             ast_atomic_fetchadd_int(&use_weight, -1);
02377          }
02378          if (q->weight && !prev_weight) {
02379             ast_atomic_fetchadd_int(&use_weight, +1);
02380          }
02381       }
02382       /* Other cases will end up with the proper value for use_weight */
02383    } else {
02384       update_realtime_members(q);
02385    }
02386    return q;
02387 }

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

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

07368 {
07369    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07370    int paused, penalty = 0;
07371 
07372    queuename = astman_get_header(m, "Queue");
07373    interface = astman_get_header(m, "Interface");
07374    penalty_s = astman_get_header(m, "Penalty");
07375    paused_s = astman_get_header(m, "Paused");
07376    membername = astman_get_header(m, "MemberName");
07377    state_interface = astman_get_header(m, "StateInterface");
07378 
07379    if (ast_strlen_zero(queuename)) {
07380       astman_send_error(s, m, "'Queue' not specified.");
07381       return 0;
07382    }
07383 
07384    if (ast_strlen_zero(interface)) {
07385       astman_send_error(s, m, "'Interface' not specified.");
07386       return 0;
07387    }
07388 
07389    if (ast_strlen_zero(penalty_s))
07390       penalty = 0;
07391    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07392       penalty = 0;
07393 
07394    if (ast_strlen_zero(paused_s))
07395       paused = 0;
07396    else
07397       paused = abs(ast_true(paused_s));
07398 
07399    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07400    case RES_OKAY:
07401       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07402       astman_send_ack(s, m, "Added interface to queue");
07403       break;
07404    case RES_EXISTS:
07405       astman_send_error(s, m, "Unable to add interface: Already there");
07406       break;
07407    case RES_NOSUCHQUEUE:
07408       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07409       break;
07410    case RES_OUTOFMEMORY:
07411       astman_send_error(s, m, "Out of memory");
07412       break;
07413    }
07414 
07415    return 0;
07416 }

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

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

07453 {
07454    const char *queuename, *interface, *paused_s, *reason;
07455    int paused;
07456 
07457    interface = astman_get_header(m, "Interface");
07458    paused_s = astman_get_header(m, "Paused");
07459    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
07460    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
07461 
07462    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07463       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07464       return 0;
07465    }
07466 
07467    paused = abs(ast_true(paused_s));
07468 
07469    if (set_member_paused(queuename, interface, reason, paused))
07470       astman_send_error(s, m, "Interface not found");
07471    else
07472       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07473    return 0;
07474 }

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

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

07477 {
07478    const char *queuename, *event, *message, *interface, *uniqueid;
07479 
07480    queuename = astman_get_header(m, "Queue");
07481    uniqueid = astman_get_header(m, "UniqueId");
07482    interface = astman_get_header(m, "Interface");
07483    event = astman_get_header(m, "Event");
07484    message = astman_get_header(m, "Message");
07485 
07486    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07487       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07488       return 0;
07489    }
07490 
07491    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07492    astman_send_ack(s, m, "Event added successfully");
07493 
07494    return 0;
07495 }

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

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

07576 {
07577    const char *queuename, *interface, *penalty_s;
07578    int penalty;
07579 
07580    interface = astman_get_header(m, "Interface");
07581    penalty_s = astman_get_header(m, "Penalty");
07582    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
07583    queuename = astman_get_header(m, "Queue");
07584 
07585    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07586       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07587       return 0;
07588    }
07589  
07590    penalty = atoi(penalty_s);
07591 
07592    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07593       astman_send_error(s, m, "Invalid interface, queuename or penalty");
07594    else
07595       astman_send_ack(s, m, "Interface penalty set successfully");
07596 
07597    return 0;
07598 }

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

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

07498 {
07499    struct ast_flags mask = {0,};
07500    const char *queuename = NULL;
07501    int header_found = 0;
07502 
07503    queuename = astman_get_header(m, "Queue");
07504    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07505       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07506       header_found = 1;
07507    }
07508    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07509       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07510       header_found = 1;
07511    }
07512    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07513       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07514       header_found = 1;
07515    }
07516 
07517    if (!header_found) {
07518       ast_set_flag(&mask, AST_FLAGS_ALL);
07519    }
07520 
07521    if (!reload_handler(1, &mask, queuename)) {
07522       astman_send_ack(s, m, "Queue reloaded successfully");
07523    } else {
07524       astman_send_error(s, m, "Error encountered while reloading queue");
07525    }
07526    return 0;
07527 }

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

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

07530 {
07531    const char *queuename = NULL;
07532    struct ast_flags mask = {QUEUE_RESET_STATS,};
07533    
07534    queuename = astman_get_header(m, "Queue");
07535 
07536    if (!reload_handler(1, &mask, queuename)) {
07537       astman_send_ack(s, m, "Queue stats reset successfully");
07538    } else {
07539       astman_send_error(s, m, "Error encountered while resetting queue stats");
07540    }
07541    return 0;
07542 }

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

Definition at line 7156 of file app_queue.c.

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

Referenced by load_module().

07157 {
07158    const char *rule = astman_get_header(m, "Rule");
07159    const char *id = astman_get_header(m, "ActionID");
07160    struct rule_list *rl_iter;
07161    struct penalty_rule *pr_iter;
07162 
07163    astman_append(s, "Response: Success\r\n");
07164    if (!ast_strlen_zero(id)) {
07165       astman_append(s, "ActionID: %s\r\n", id);
07166    }
07167 
07168    AST_LIST_LOCK(&rule_lists);
07169    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07170       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07171          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07172          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07173             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 );
07174          }
07175          if (!ast_strlen_zero(rule))
07176             break;
07177       }
07178    }
07179    AST_LIST_UNLOCK(&rule_lists);
07180 
07181    /*
07182     * Two blank lines instead of one because the Response and
07183     * ActionID headers used to not be present.
07184     */
07185    astman_append(s, "\r\n\r\n");
07186 
07187    return RESULT_SUCCESS;
07188 }

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

Definition at line 7146 of file app_queue.c.

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

Referenced by load_module().

07147 {
07148    static const char * const a[] = { "queue", "show" };
07149 
07150    __queues_show(s, -1, 2, a);
07151    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
07152 
07153    return RESULT_SUCCESS;
07154 }

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

Queue status info via AMI.

Definition at line 7266 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(), ast_channel::caller, member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::connected, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, ast_party_connected_line::id, ast_party_caller::id, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, ast_party_id::name, ast_channel::name, call_queue::name, queue_ent::next, ast_party_id::number, member::paused, member::penalty, queue_t_unref, queues, RESULT_SUCCESS, S_COR, call_queue::servicelevel, queue_ent::start, member::status, ast_party_name::str, ast_party_number::str, call_queue::strategy, call_queue::talktime, ast_channel::uniqueid, ast_party_name::valid, ast_party_number::valid, and call_queue::weight.

Referenced by load_module().

07267 {
07268    time_t now;
07269    int pos;
07270    const char *id = astman_get_header(m,"ActionID");
07271    const char *queuefilter = astman_get_header(m,"Queue");
07272    const char *memberfilter = astman_get_header(m,"Member");
07273    char idText[256] = "";
07274    struct call_queue *q;
07275    struct queue_ent *qe;
07276    float sl = 0;
07277    struct member *mem;
07278    struct ao2_iterator queue_iter;
07279    struct ao2_iterator mem_iter;
07280 
07281    astman_send_ack(s, m, "Queue status will follow");
07282    time(&now);
07283    if (!ast_strlen_zero(id))
07284       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07285 
07286    queue_iter = ao2_iterator_init(queues, 0);
07287    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07288       ao2_lock(q);
07289 
07290       /* List queue properties */
07291       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07292          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07293          astman_append(s, "Event: QueueParams\r\n"
07294             "Queue: %s\r\n"
07295             "Max: %d\r\n"
07296             "Strategy: %s\r\n"
07297             "Calls: %d\r\n"
07298             "Holdtime: %d\r\n"
07299             "TalkTime: %d\r\n"
07300             "Completed: %d\r\n"
07301             "Abandoned: %d\r\n"
07302             "ServiceLevel: %d\r\n"
07303             "ServicelevelPerf: %2.1f\r\n"
07304             "Weight: %d\r\n"
07305             "%s"
07306             "\r\n",
07307             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07308             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07309          /* List Queue Members */
07310          mem_iter = ao2_iterator_init(q->members, 0);
07311          while ((mem = ao2_iterator_next(&mem_iter))) {
07312             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07313                astman_append(s, "Event: QueueMember\r\n"
07314                   "Queue: %s\r\n"
07315                   "Name: %s\r\n"
07316                   "Location: %s\r\n"
07317                   "Membership: %s\r\n"
07318                   "Penalty: %d\r\n"
07319                   "CallsTaken: %d\r\n"
07320                   "LastCall: %d\r\n"
07321                   "Status: %d\r\n"
07322                   "Paused: %d\r\n"
07323                   "%s"
07324                   "\r\n",
07325                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07326                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07327             }
07328             ao2_ref(mem, -1);
07329          }
07330          ao2_iterator_destroy(&mem_iter);
07331          /* List Queue Entries */
07332          pos = 1;
07333          for (qe = q->head; qe; qe = qe->next) {
07334             astman_append(s, "Event: QueueEntry\r\n"
07335                "Queue: %s\r\n"
07336                "Position: %d\r\n"
07337                "Channel: %s\r\n"
07338                "Uniqueid: %s\r\n"
07339                "CallerIDNum: %s\r\n"
07340                "CallerIDName: %s\r\n"
07341                "ConnectedLineNum: %s\r\n"
07342                "ConnectedLineName: %s\r\n"
07343                "Wait: %ld\r\n"
07344                "%s"
07345                "\r\n",
07346                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07347                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07348                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07349                S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
07350                S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
07351                (long) (now - qe->start), idText);
07352          }
07353       }
07354       ao2_unlock(q);
07355       queue_t_unref(q, "Done with iterator");
07356    }
07357    ao2_iterator_destroy(&queue_iter);
07358 
07359    astman_append(s,
07360       "Event: QueueStatusComplete\r\n"
07361       "%s"
07362       "\r\n",idText);
07363 
07364    return RESULT_SUCCESS;
07365 }

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

Summary of queue info via the AMI.

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

07192 {
07193    time_t now;
07194    int qmemcount = 0;
07195    int qmemavail = 0;
07196    int qchancount = 0;
07197    int qlongestholdtime = 0;
07198    const char *id = astman_get_header(m, "ActionID");
07199    const char *queuefilter = astman_get_header(m, "Queue");
07200    char idText[256] = "";
07201    struct call_queue *q;
07202    struct queue_ent *qe;
07203    struct member *mem;
07204    struct ao2_iterator queue_iter;
07205    struct ao2_iterator mem_iter;
07206 
07207    astman_send_ack(s, m, "Queue summary will follow");
07208    time(&now);
07209    if (!ast_strlen_zero(id))
07210       snprintf(idText, 256, "ActionID: %s\r\n", id);
07211    queue_iter = ao2_iterator_init(queues, 0);
07212    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07213       ao2_lock(q);
07214 
07215       /* List queue properties */
07216       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07217          /* Reset the necessary local variables if no queuefilter is set*/
07218          qmemcount = 0;
07219          qmemavail = 0;
07220          qchancount = 0;
07221          qlongestholdtime = 0;
07222 
07223          /* List Queue Members */
07224          mem_iter = ao2_iterator_init(q->members, 0);
07225          while ((mem = ao2_iterator_next(&mem_iter))) {
07226             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07227                ++qmemcount;
07228                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
07229                   ++qmemavail;
07230                }
07231             }
07232             ao2_ref(mem, -1);
07233          }
07234          ao2_iterator_destroy(&mem_iter);
07235          for (qe = q->head; qe; qe = qe->next) {
07236             if ((now - qe->start) > qlongestholdtime) {
07237                qlongestholdtime = now - qe->start;
07238             }
07239             ++qchancount;
07240          }
07241          astman_append(s, "Event: QueueSummary\r\n"
07242             "Queue: %s\r\n"
07243             "LoggedIn: %d\r\n"
07244             "Available: %d\r\n"
07245             "Callers: %d\r\n" 
07246             "HoldTime: %d\r\n"
07247             "TalkTime: %d\r\n"
07248             "LongestHoldTime: %d\r\n"
07249             "%s"
07250             "\r\n",
07251             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07252       }
07253       ao2_unlock(q);
07254       queue_t_unref(q, "Done with iterator");
07255    }
07256    ao2_iterator_destroy(&queue_iter);
07257    astman_append(s,
07258       "Event: QueueSummaryComplete\r\n"
07259       "%s"
07260       "\r\n", idText);
07261 
07262    return RESULT_SUCCESS;
07263 }

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

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

07419 {
07420    const char *queuename, *interface;
07421 
07422    queuename = astman_get_header(m, "Queue");
07423    interface = astman_get_header(m, "Interface");
07424 
07425    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07426       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07427       return 0;
07428    }
07429 
07430    switch (remove_from_queue(queuename, interface)) {
07431    case RES_OKAY:
07432       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07433       astman_send_ack(s, m, "Removed interface from queue");
07434       break;
07435    case RES_EXISTS:
07436       astman_send_error(s, m, "Unable to remove interface: Not there");
07437       break;
07438    case RES_NOSUCHQUEUE:
07439       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07440       break;
07441    case RES_OUTOFMEMORY:
07442       astman_send_error(s, m, "Out of memory");
07443       break;
07444    case RES_NOT_DYNAMIC:
07445       astman_send_error(s, m, "Member not dynamic");
07446       break;
07447    }
07448 
07449    return 0;
07450 }

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

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

06813 {
06814    struct call_queue *q = obj;
06815    char *queuename = arg;
06816    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
06817       q->dead = 1;
06818       q->found = 0;
06819    }
06820    return 0;
06821 }

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

Definition at line 6685 of file app_queue.c.

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

Referenced by reload_single_queue().

06686 {
06687    struct member *member = obj;
06688    if (!member->dynamic) {
06689       member->delme = 1;
06690    }
06691    return 0;
06692 }

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

Definition at line 1652 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

01653 {
01654    struct member *mem1 = obj1, *mem2 = obj2;
01655    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01656 }

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

Definition at line 1640 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

01641 {
01642    const struct member *mem = obj;
01643    const char *chname = strchr(mem->interface, '/');
01644    int ret = 0, i;
01645    if (!chname)
01646       chname = mem->interface;
01647    for (i = 0; i < 5 && chname[i]; i++)
01648       ret += compress_char(chname[i]) << (i * 6);
01649    return ret;
01650 }

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

02868 {
02869    struct member *mem;
02870    int avl = 0;
02871    struct ao2_iterator mem_iter;
02872 
02873    mem_iter = ao2_iterator_init(q->members, 0);
02874    while ((mem = ao2_iterator_next(&mem_iter))) {
02875       switch (mem->status) {
02876       case AST_DEVICE_INUSE:
02877          if (!q->ringinuse)
02878             break;
02879          /* else fall through */
02880       case AST_DEVICE_NOT_INUSE:
02881       case AST_DEVICE_UNKNOWN:
02882          if (!mem->paused) {
02883             avl++;
02884          }
02885          break;
02886       }
02887       ao2_ref(mem, -1);
02888 
02889       /* If autofill is not enabled or if the queue's strategy is ringall, then
02890        * we really don't care about the number of available members so much as we
02891        * do that there is at least one available.
02892        *
02893        * In fact, we purposely will return from this function stating that only
02894        * one member is available if either of those conditions hold. That way,
02895        * functions which determine what action to take based on the number of available
02896        * members will operate properly. The reasoning is that even if multiple
02897        * members are available, only the head caller can actually be serviced.
02898        */
02899       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02900          break;
02901       }
02902    }
02903    ao2_iterator_destroy(&mem_iter);
02904 
02905    return avl;
02906 }

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

Definition at line 1845 of file app_queue.c.

References ast_false(), ast_log(), ast_strdupa, 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, QUEUE_EMPTY_WRAPUP, and strsep().

Referenced by queue_set_param().

01846 {
01847    char *value_copy = ast_strdupa(value);
01848    char *option = NULL;
01849    while ((option = strsep(&value_copy, ","))) {
01850       if (!strcasecmp(option, "paused")) {
01851          *empty |= QUEUE_EMPTY_PAUSED;
01852       } else if (!strcasecmp(option, "penalty")) {
01853          *empty |= QUEUE_EMPTY_PENALTY;
01854       } else if (!strcasecmp(option, "inuse")) {
01855          *empty |= QUEUE_EMPTY_INUSE;
01856       } else if (!strcasecmp(option, "ringing")) {
01857          *empty |= QUEUE_EMPTY_RINGING;
01858       } else if (!strcasecmp(option, "invalid")) {
01859          *empty |= QUEUE_EMPTY_INVALID;
01860       } else if (!strcasecmp(option, "wrapup")) {
01861          *empty |= QUEUE_EMPTY_WRAPUP;
01862       } else if (!strcasecmp(option, "unavailable")) {
01863          *empty |= QUEUE_EMPTY_UNAVAILABLE;
01864       } else if (!strcasecmp(option, "unknown")) {
01865          *empty |= QUEUE_EMPTY_UNKNOWN;
01866       } else if (!strcasecmp(option, "loose")) {
01867          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01868       } else if (!strcasecmp(option, "strict")) {
01869          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01870       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01871          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01872       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01873          *empty = 0;
01874       } else {
01875          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01876       }
01877    }
01878 }

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

Definition at line 2537 of file app_queue.c.

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

Referenced by say_periodic_announcement(), and say_position().

02538 {
02539    int res;
02540 
02541    if (ast_strlen_zero(filename)) {
02542       return 0;
02543    }
02544 
02545    if (!ast_fileexists(filename, NULL, chan->language)) {
02546       return 0;
02547    }
02548 
02549    ast_stopstream(chan);
02550 
02551    res = ast_streamfile(chan, filename, chan->language);
02552    if (!res)
02553       res = ast_waitstream(chan, AST_DIGIT_ANY);
02554 
02555    ast_stopstream(chan);
02556 
02557    return res;
02558 }

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

PauseQueueMember application.

Definition at line 5602 of file app_queue.c.

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

Referenced by load_module().

05603 {
05604    char *parse;
05605    AST_DECLARE_APP_ARGS(args,
05606       AST_APP_ARG(queuename);
05607       AST_APP_ARG(interface);
05608       AST_APP_ARG(options);
05609       AST_APP_ARG(reason);
05610    );
05611 
05612    if (ast_strlen_zero(data)) {
05613       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05614       return -1;
05615    }
05616 
05617    parse = ast_strdupa(data);
05618 
05619    AST_STANDARD_APP_ARGS(args, parse);
05620 
05621    if (ast_strlen_zero(args.interface)) {
05622       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05623       return -1;
05624    }
05625 
05626    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05627       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05628       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05629       return 0;
05630    }
05631 
05632    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05633 
05634    return 0;
05635 }

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

QueueLog application.

Definition at line 5794 of file app_queue.c.

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

Referenced by load_module().

05795 {
05796    char *parse;
05797 
05798    AST_DECLARE_APP_ARGS(args,
05799       AST_APP_ARG(queuename);
05800       AST_APP_ARG(uniqueid);
05801       AST_APP_ARG(membername);
05802       AST_APP_ARG(event);
05803       AST_APP_ARG(params);
05804    );
05805 
05806    if (ast_strlen_zero(data)) {
05807       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
05808       return -1;
05809    }
05810 
05811    parse = ast_strdupa(data);
05812 
05813    AST_STANDARD_APP_ARGS(args, parse);
05814 
05815    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
05816        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
05817       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
05818       return -1;
05819    }
05820 
05821    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
05822       "%s", args.params ? args.params : "");
05823 
05824    return 0;
05825 }

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

Definition at line 1259 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

01260 {
01261    struct call_queue *q = obj, *q2 = arg;
01262    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
01263 }

static int queue_exec ( struct ast_channel chan,
const char *  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 5868 of file app_queue.c.

References call_queue::announcefrequency, ao2_container_count(), args, AST_APP_ARG, ast_assert, 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_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::chan, copy_rules(), queue_ent::expire, get_member_status(), ast_party_caller::id, 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, call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::members, queue_ent::min_penalty, queue_ent::moh, call_queue::name, ast_channel::name, ast_party_id::number, 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_LEAVEEMPTY, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), queue_ent::ring_when_ringing, S_COR, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, status, stop, ast_party_number::str, penalty_rule::time, try_calling(), ast_channel::uniqueid, update_qe_rule(), update_realtime_members(), url, ast_party_number::valid, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

05869 {
05870    int res=-1;
05871    int ringing=0;
05872    const char *user_priority;
05873    const char *max_penalty_str;
05874    const char *min_penalty_str;
05875    int prio;
05876    int qcontinue = 0;
05877    int max_penalty, min_penalty;
05878    enum queue_result reason = QUEUE_UNKNOWN;
05879    /* whether to exit Queue application after the timeout hits */
05880    int tries = 0;
05881    int noption = 0;
05882    char *parse;
05883    int makeannouncement = 0;
05884    int position = 0;
05885    AST_DECLARE_APP_ARGS(args,
05886       AST_APP_ARG(queuename);
05887       AST_APP_ARG(options);
05888       AST_APP_ARG(url);
05889       AST_APP_ARG(announceoverride);
05890       AST_APP_ARG(queuetimeoutstr);
05891       AST_APP_ARG(agi);
05892       AST_APP_ARG(macro);
05893       AST_APP_ARG(gosub);
05894       AST_APP_ARG(rule);
05895       AST_APP_ARG(position);
05896    );
05897    /* Our queue entry */
05898    struct queue_ent qe = { 0 };
05899    
05900    if (ast_strlen_zero(data)) {
05901       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
05902       return -1;
05903    }
05904    
05905    parse = ast_strdupa(data);
05906    AST_STANDARD_APP_ARGS(args, parse);
05907 
05908    /* Setup our queue entry */
05909    qe.start = time(NULL);
05910 
05911    /* set the expire time based on the supplied timeout; */
05912    if (!ast_strlen_zero(args.queuetimeoutstr))
05913       qe.expire = qe.start + atoi(args.queuetimeoutstr);
05914    else
05915       qe.expire = 0;
05916 
05917    /* Get the priority from the variable ${QUEUE_PRIO} */
05918    ast_channel_lock(chan);
05919    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
05920    if (user_priority) {
05921       if (sscanf(user_priority, "%30d", &prio) == 1) {
05922          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
05923       } else {
05924          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
05925             user_priority, chan->name);
05926          prio = 0;
05927       }
05928    } else {
05929       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
05930       prio = 0;
05931    }
05932 
05933    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
05934 
05935    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
05936       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
05937          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
05938       } else {
05939          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
05940             max_penalty_str, chan->name);
05941          max_penalty = 0;
05942       }
05943    } else {
05944       max_penalty = 0;
05945    }
05946 
05947    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
05948       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
05949          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
05950       } else {
05951          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
05952             min_penalty_str, chan->name);
05953          min_penalty = 0;
05954       }
05955    } else {
05956       min_penalty = 0;
05957    }
05958    ast_channel_unlock(chan);
05959 
05960    if (args.options && (strchr(args.options, 'r')))
05961       ringing = 1;
05962 
05963    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
05964       qe.ring_when_ringing = 1;
05965    }
05966 
05967    if (args.options && (strchr(args.options, 'c')))
05968       qcontinue = 1;
05969 
05970    if (args.position) {
05971       position = atoi(args.position);
05972       if (position < 0) {
05973          ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
05974          position = 0;
05975       }
05976    }
05977 
05978    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
05979       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
05980 
05981    qe.chan = chan;
05982    qe.prio = prio;
05983    qe.max_penalty = max_penalty;
05984    qe.min_penalty = min_penalty;
05985    qe.last_pos_said = 0;
05986    qe.last_pos = 0;
05987    qe.last_periodic_announce_time = time(NULL);
05988    qe.last_periodic_announce_sound = 0;
05989    qe.valid_digits = 0;
05990    if (join_queue(args.queuename, &qe, &reason, position)) {
05991       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
05992       set_queue_result(chan, reason);
05993       return 0;
05994    }
05995    ast_assert(qe.parent != NULL);
05996 
05997    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d",
05998       S_OR(args.url, ""),
05999       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
06000       qe.opos);
06001    copy_rules(&qe, args.rule);
06002    qe.pr = AST_LIST_FIRST(&qe.qe_rules);
06003 check_turns:
06004    if (ringing) {
06005       ast_indicate(chan, AST_CONTROL_RINGING);
06006    } else {
06007       ast_moh_start(chan, qe.moh, NULL);
06008    }
06009 
06010    /* This is the wait loop for callers 2 through maxlen */
06011    res = wait_our_turn(&qe, ringing, &reason);
06012    if (res) {
06013       goto stop;
06014    }
06015 
06016    makeannouncement = 0;
06017 
06018    for (;;) {
06019       /* This is the wait loop for the head caller*/
06020       /* To exit, they may get their call answered; */
06021       /* they may dial a digit from the queue context; */
06022       /* or, they may timeout. */
06023 
06024       /* Leave if we have exceeded our queuetimeout */
06025       if (qe.expire && (time(NULL) >= qe.expire)) {
06026          record_abandoned(&qe);
06027          reason = QUEUE_TIMEOUT;
06028          res = 0;
06029          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
06030             qe.pos, qe.opos, (long) time(NULL) - qe.start);
06031          break;
06032       }
06033 
06034       if (makeannouncement) {
06035          /* Make a position announcement, if enabled */
06036          if (qe.parent->announcefrequency)
06037             if ((res = say_position(&qe,ringing)))
06038                goto stop;
06039       }
06040       makeannouncement = 1;
06041 
06042       /* Make a periodic announcement, if enabled */
06043       if (qe.parent->periodicannouncefrequency)
06044          if ((res = say_periodic_announcement(&qe,ringing)))
06045             goto stop;
06046    
06047       /* Leave if we have exceeded our queuetimeout */
06048       if (qe.expire && (time(NULL) >= qe.expire)) {
06049          record_abandoned(&qe);
06050          reason = QUEUE_TIMEOUT;
06051          res = 0;
06052          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06053          break;
06054       }
06055 
06056       /* see if we need to move to the next penalty level for this queue */
06057       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
06058          update_qe_rule(&qe);
06059       }
06060 
06061       /* Try calling all queue members for 'timeout' seconds */
06062       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
06063       if (res) {
06064          goto stop;
06065       }
06066 
06067       if (qe.parent->leavewhenempty) {
06068          int status = 0;
06069          if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
06070             record_abandoned(&qe);
06071             reason = QUEUE_LEAVEEMPTY;
06072             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
06073             res = 0;
06074             break;
06075          }
06076       }
06077 
06078       /* exit after 'timeout' cycle if 'n' option enabled */
06079       if (noption && tries >= ao2_container_count(qe.parent->members)) {
06080          ast_verb(3, "Exiting on time-out cycle\n");
06081          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06082          record_abandoned(&qe);
06083          reason = QUEUE_TIMEOUT;
06084          res = 0;
06085          break;
06086       }
06087 
06088       
06089       /* Leave if we have exceeded our queuetimeout */
06090       if (qe.expire && (time(NULL) >= qe.expire)) {
06091          record_abandoned(&qe);
06092          reason = QUEUE_TIMEOUT;
06093          res = 0;
06094          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
06095          break;
06096       }
06097 
06098       /* If using dynamic realtime members, we should regenerate the member list for this queue */
06099       update_realtime_members(qe.parent);
06100       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
06101       res = wait_a_bit(&qe);
06102       if (res)
06103          goto stop;
06104 
06105       /* Since this is a priority queue and
06106        * it is not sure that we are still at the head
06107        * of the queue, go and check for our turn again.
06108        */
06109       if (!is_our_turn(&qe)) {
06110          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
06111          goto check_turns;
06112       }
06113    }
06114 
06115 stop:
06116    if (res) {
06117       if (res < 0) {
06118          if (!qe.handled) {
06119             record_abandoned(&qe);
06120             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
06121                "%d|%d|%ld", qe.pos, qe.opos,
06122                (long) time(NULL) - qe.start);
06123             res = -1;
06124          } else if (qcontinue) {
06125             reason = QUEUE_CONTINUE;
06126             res = 0;
06127          }
06128       } else if (qe.valid_digits) {
06129          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
06130             "%s|%d", qe.digits, qe.pos);
06131       }
06132    }
06133 
06134    /* Don't allow return code > 0 */
06135    if (res >= 0) {
06136       res = 0; 
06137       if (ringing) {
06138          ast_indicate(chan, -1);
06139       } else {
06140          ast_moh_stop(chan);
06141       }        
06142       ast_stopstream(chan);
06143    }
06144 
06145    set_queue_variables(qe.parent, qe.chan);
06146 
06147    leave_queue(&qe);
06148    if (reason != QUEUE_UNKNOWN)
06149       set_queue_result(chan, reason);
06150 
06151    /*
06152     * every queue_ent is given a reference to it's parent
06153     * call_queue when it joins the queue.  This ref must be taken
06154     * away right before the queue_ent is destroyed.  In this case
06155     * the queue_ent is about to be returned on the stack
06156     */
06157    qe.parent = queue_unref(qe.parent);
06158 
06159    return res;
06160 }

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

Check if a given queue exists.

Definition at line 6214 of file app_queue.c.

References ast_log(), ast_strlen_zero(), load_realtime_queue(), LOG_ERROR, and queue_t_unref.

06215 {
06216    struct call_queue *q;
06217 
06218    buf[0] = '\0';
06219 
06220    if (ast_strlen_zero(data)) {
06221       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06222       return -1;
06223    }
06224    q = load_realtime_queue(data);
06225    snprintf(buf, len, "%d", q != NULL? 1 : 0);
06226    if (q) {
06227       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06228    }
06229 
06230    return 0;
06231 }

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

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

06434 {
06435    int penalty;
06436    AST_DECLARE_APP_ARGS(args,
06437       AST_APP_ARG(queuename);
06438       AST_APP_ARG(interface);
06439    );
06440    /* Make sure the returned value on error is NULL. */
06441    buf[0] = '\0';
06442 
06443    if (ast_strlen_zero(data)) {
06444       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06445       return -1;
06446    }
06447 
06448    AST_STANDARD_APP_ARGS(args, data);
06449 
06450    if (args.argc < 2) {
06451       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06452       return -1;
06453    }
06454 
06455    penalty = get_member_penalty (args.queuename, args.interface);
06456    
06457    if (penalty >= 0) /* remember that buf is already '\0' */
06458       snprintf (buf, len, "%d", penalty);
06459 
06460    return 0;
06461 }

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

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

06465 {
06466    int penalty;
06467    AST_DECLARE_APP_ARGS(args,
06468       AST_APP_ARG(queuename);
06469       AST_APP_ARG(interface);
06470    );
06471 
06472    if (ast_strlen_zero(data)) {
06473       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06474       return -1;
06475    }
06476 
06477    AST_STANDARD_APP_ARGS(args, data);
06478 
06479    if (args.argc < 2) {
06480       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06481       return -1;
06482    }
06483 
06484    penalty = atoi(value);
06485 
06486    if (ast_strlen_zero(args.interface)) {
06487       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06488       return -1;
06489    }
06490 
06491    /* if queuename = NULL then penalty will be set for interface in all the queues. */
06492    if (set_member_penalty(args.queuename, args.interface, penalty)) {
06493       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06494       return -1;
06495    }
06496 
06497    return 0;
06498 }

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 / ready or total members of a specific queue.

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

Definition at line 6238 of file app_queue.c.

References ao2_container_count(), 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, member::lastcall, load_realtime_queue(), LOG_ERROR, LOG_WARNING, member::paused, queue_t_unref, and member::status.

06239 {
06240    int count = 0;
06241    struct member *m;
06242    struct ao2_iterator mem_iter;
06243    struct call_queue *q;
06244    char *option;
06245 
06246    if (ast_strlen_zero(data)) {
06247       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06248       return -1;
06249    }
06250 
06251    if ((option = strchr(data, ',')))
06252       *option++ = '\0';
06253    else
06254       option = "logged";
06255    if ((q = load_realtime_queue(data))) {
06256       ao2_lock(q);
06257       if (!strcasecmp(option, "logged")) {
06258          mem_iter = ao2_iterator_init(q->members, 0);
06259          while ((m = ao2_iterator_next(&mem_iter))) {
06260             /* Count the agents who are logged in and presently answering calls */
06261             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06262                count++;
06263             }
06264             ao2_ref(m, -1);
06265          }
06266          ao2_iterator_destroy(&mem_iter);
06267       } else if (!strcasecmp(option, "free")) {
06268          mem_iter = ao2_iterator_init(q->members, 0);
06269          while ((m = ao2_iterator_next(&mem_iter))) {
06270             /* Count the agents who are logged in and presently answering calls */
06271             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06272                count++;
06273             }
06274             ao2_ref(m, -1);
06275          }
06276          ao2_iterator_destroy(&mem_iter);
06277       } else if (!strcasecmp(option, "ready")) {
06278          time_t now;
06279          time(&now);
06280          mem_iter = ao2_iterator_init(q->members, 0);
06281          while ((m = ao2_iterator_next(&mem_iter))) {
06282             /* Count the agents who are logged in, not paused and not wrapping up */
06283             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06284                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06285                count++;
06286             }
06287             ao2_ref(m, -1);
06288          }
06289          ao2_iterator_destroy(&mem_iter);
06290       } else /* must be "count" */
06291          count = ao2_container_count(q->members);
06292       ao2_unlock(q);
06293       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06294    } else
06295       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06296 
06297    snprintf(buf, len, "%d", count);
06298 
06299    return 0;
06300 }

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

06308 {
06309    int count = 0;
06310    struct member *m;
06311    struct call_queue *q;
06312    struct ao2_iterator mem_iter;
06313    static int depflag = 1;
06314 
06315    if (depflag) {
06316       depflag = 0;
06317       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");
06318    }
06319 
06320    if (ast_strlen_zero(data)) {
06321       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06322       return -1;
06323    }
06324    
06325    if ((q = load_realtime_queue(data))) {
06326       ao2_lock(q);
06327       mem_iter = ao2_iterator_init(q->members, 0);
06328       while ((m = ao2_iterator_next(&mem_iter))) {
06329          /* Count the agents who are logged in and presently answering calls */
06330          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06331             count++;
06332          }
06333          ao2_ref(m, -1);
06334       }
06335       ao2_iterator_destroy(&mem_iter);
06336       ao2_unlock(q);
06337       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06338    } else
06339       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06340 
06341    snprintf(buf, len, "%d", count);
06342 
06343    return 0;
06344 }

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

06384 {
06385    struct call_queue *q, tmpq = {
06386       .name = data,  
06387    };
06388    struct member *m;
06389 
06390    /* Ensure an otherwise empty list doesn't return garbage */
06391    buf[0] = '\0';
06392 
06393    if (ast_strlen_zero(data)) {
06394       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06395       return -1;
06396    }
06397 
06398    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06399       int buflen = 0, count = 0;
06400       struct ao2_iterator mem_iter;
06401 
06402       ao2_lock(q);
06403       mem_iter = ao2_iterator_init(q->members, 0);
06404       while ((m = ao2_iterator_next(&mem_iter))) {
06405          /* strcat() is always faster than printf() */
06406          if (count++) {
06407             strncat(buf + buflen, ",", len - buflen - 1);
06408             buflen++;
06409          }
06410          strncat(buf + buflen, m->interface, len - buflen - 1);
06411          buflen += strlen(m->interface);
06412          /* Safeguard against overflow (negative length) */
06413          if (buflen >= len - 2) {
06414             ao2_ref(m, -1);
06415             ast_log(LOG_WARNING, "Truncating list\n");
06416             break;
06417          }
06418          ao2_ref(m, -1);
06419       }
06420       ao2_iterator_destroy(&mem_iter);
06421       ao2_unlock(q);
06422       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06423    } else
06424       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06425 
06426    /* We should already be terminated, but let's make sure. */
06427    buf[len - 1] = '\0';
06428 
06429    return 0;
06430 }

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

06348 {
06349    int count = 0;
06350    struct call_queue *q, tmpq = {
06351       .name = data,  
06352    };
06353    struct ast_variable *var = NULL;
06354 
06355    buf[0] = '\0';
06356    
06357    if (ast_strlen_zero(data)) {
06358       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06359       return -1;
06360    }
06361 
06362    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06363       ao2_lock(q);
06364       count = q->count;
06365       ao2_unlock(q);
06366       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06367    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06368       /* if the queue is realtime but was not found in memory, this
06369        * means that the queue had been deleted from memory since it was 
06370        * "dead." This means it has a 0 waiting count
06371        */
06372       count = 0;
06373       ast_variables_destroy(var);
06374    } else
06375       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06376 
06377    snprintf(buf, len, "%d", count);
06378 
06379    return 0;
06380 }

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

06168 {
06169    int res = -1;
06170    struct call_queue *q, tmpq = {
06171       .name = data,  
06172    };
06173 
06174    char interfacevar[256] = "";
06175    float sl = 0;
06176 
06177    if (ast_strlen_zero(data)) {
06178       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06179       return -1;
06180    }
06181 
06182    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06183       ao2_lock(q);
06184       if (q->setqueuevar) {
06185          sl = 0;
06186          res = 0;
06187 
06188          if (q->callscompleted > 0) {
06189             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06190          }
06191 
06192          snprintf(interfacevar, sizeof(interfacevar),
06193             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06194             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
06195 
06196          pbx_builtin_setvar_multiple(chan, interfacevar);
06197       }
06198 
06199       ao2_unlock(q);
06200       queue_t_unref(q, "Done with QUEUE() function");
06201    } else {
06202       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06203    }
06204 
06205    snprintf(buf, len, "%d", res);
06206 
06207    return 0;
06208 }

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

Definition at line 1252 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

01253 {
01254    const struct call_queue *q = obj;
01255 
01256    return ast_str_case_hash(q->name);
01257 }

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

Definition at line 1277 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

01278 {
01279    ao2_ref(q, 1);
01280    return q;
01281 }

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

06592 {
06593    const char *general_val = NULL;
06594    queue_persistent_members = 0;
06595    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06596       queue_persistent_members = ast_true(general_val);
06597    autofill_default = 0;
06598    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06599       autofill_default = ast_true(general_val);
06600    montype_default = 0;
06601    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06602       if (!strcasecmp(general_val, "mixmonitor"))
06603          montype_default = 1;
06604    }
06605    update_cdr = 0;
06606    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06607       update_cdr = ast_true(general_val);
06608    shared_lastcall = 0;
06609    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06610       shared_lastcall = ast_true(general_val);
06611 }

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

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

Referenced by reload_single_queue().

01889 {
01890    if (!strcasecmp(param, "musicclass") || 
01891       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01892       ast_string_field_set(q, moh, val);
01893    } else if (!strcasecmp(param, "announce")) {
01894       ast_string_field_set(q, announce, val);
01895    } else if (!strcasecmp(param, "context")) {
01896       ast_string_field_set(q, context, val);
01897    } else if (!strcasecmp(param, "timeout")) {
01898       q->timeout = atoi(val);
01899       if (q->timeout < 0)
01900          q->timeout = DEFAULT_TIMEOUT;
01901    } else if (!strcasecmp(param, "ringinuse")) {
01902       q->ringinuse = ast_true(val);
01903    } else if (!strcasecmp(param, "setinterfacevar")) {
01904       q->setinterfacevar = ast_true(val);
01905    } else if (!strcasecmp(param, "setqueuevar")) {
01906       q->setqueuevar = ast_true(val);
01907    } else if (!strcasecmp(param, "setqueueentryvar")) {
01908       q->setqueueentryvar = ast_true(val);
01909    } else if (!strcasecmp(param, "monitor-format")) {
01910       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01911    } else if (!strcasecmp(param, "membermacro")) {
01912       ast_string_field_set(q, membermacro, val);
01913    } else if (!strcasecmp(param, "membergosub")) {
01914       ast_string_field_set(q, membergosub, val);
01915    } else if (!strcasecmp(param, "queue-youarenext")) {
01916       ast_string_field_set(q, sound_next, val);
01917    } else if (!strcasecmp(param, "queue-thereare")) {
01918       ast_string_field_set(q, sound_thereare, val);
01919    } else if (!strcasecmp(param, "queue-callswaiting")) {
01920       ast_string_field_set(q, sound_calls, val);
01921    } else if (!strcasecmp(param, "queue-quantity1")) {
01922       ast_string_field_set(q, queue_quantity1, val);
01923    } else if (!strcasecmp(param, "queue-quantity2")) {
01924       ast_string_field_set(q, queue_quantity2, val);
01925    } else if (!strcasecmp(param, "queue-holdtime")) {
01926       ast_string_field_set(q, sound_holdtime, val);
01927    } else if (!strcasecmp(param, "queue-minutes")) {
01928       ast_string_field_set(q, sound_minutes, val);
01929    } else if (!strcasecmp(param, "queue-minute")) {
01930       ast_string_field_set(q, sound_minute, val);
01931    } else if (!strcasecmp(param, "queue-seconds")) {
01932       ast_string_field_set(q, sound_seconds, val);
01933    } else if (!strcasecmp(param, "queue-thankyou")) {
01934       ast_string_field_set(q, sound_thanks, val);
01935    } else if (!strcasecmp(param, "queue-callerannounce")) {
01936       ast_string_field_set(q, sound_callerannounce, val);
01937    } else if (!strcasecmp(param, "queue-reporthold")) {
01938       ast_string_field_set(q, sound_reporthold, val);
01939    } else if (!strcasecmp(param, "announce-frequency")) {
01940       q->announcefrequency = atoi(val);
01941    } else if (!strcasecmp(param, "min-announce-frequency")) {
01942       q->minannouncefrequency = atoi(val);
01943       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01944    } else if (!strcasecmp(param, "announce-round-seconds")) {
01945       q->roundingseconds = atoi(val);
01946       /* Rounding to any other values just doesn't make sense... */
01947       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01948          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01949          if (linenum >= 0) {
01950             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01951                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01952                val, param, q->name, linenum);
01953          } else {
01954             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01955                "using 0 instead for queue '%s'\n", val, param, q->name);
01956          }
01957          q->roundingseconds=0;
01958       }
01959    } else if (!strcasecmp(param, "announce-holdtime")) {
01960       if (!strcasecmp(val, "once"))
01961          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01962       else if (ast_true(val))
01963          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01964       else
01965          q->announceholdtime = 0;
01966    } else if (!strcasecmp(param, "announce-position")) {
01967       if (!strcasecmp(val, "limit"))
01968          q->announceposition = ANNOUNCEPOSITION_LIMIT;
01969       else if (!strcasecmp(val, "more"))
01970          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01971       else if (ast_true(val))
01972          q->announceposition = ANNOUNCEPOSITION_YES;
01973       else
01974          q->announceposition = ANNOUNCEPOSITION_NO;
01975    } else if (!strcasecmp(param, "announce-position-limit")) {
01976       q->announcepositionlimit = atoi(val);
01977    } else if (!strcasecmp(param, "periodic-announce")) {
01978       if (strchr(val, ',')) {
01979          char *s, *buf = ast_strdupa(val);
01980          unsigned int i = 0;
01981 
01982          while ((s = strsep(&buf, ",|"))) {
01983             if (!q->sound_periodicannounce[i])
01984                q->sound_periodicannounce[i] = ast_str_create(16);
01985             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01986             i++;
01987             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01988                break;
01989          }
01990          q->numperiodicannounce = i;
01991       } else {
01992          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01993          q->numperiodicannounce = 1;
01994       }
01995    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01996       q->periodicannouncefrequency = atoi(val);
01997    } else if (!strcasecmp(param, "relative-periodic-announce")) {
01998       q->relativeperiodicannounce = ast_true(val);
01999    } else if (!strcasecmp(param, "random-periodic-announce")) {
02000       q->randomperiodicannounce = ast_true(val);
02001    } else if (!strcasecmp(param, "retry")) {
02002       q->retry = atoi(val);
02003       if (q->retry <= 0)
02004          q->retry = DEFAULT_RETRY;
02005    } else if (!strcasecmp(param, "wrapuptime")) {
02006       q->wrapuptime = atoi(val);
02007    } else if (!strcasecmp(param, "penaltymemberslimit")) {
02008       if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
02009          q->penaltymemberslimit = 0;
02010       }
02011    } else if (!strcasecmp(param, "autofill")) {
02012       q->autofill = ast_true(val);
02013    } else if (!strcasecmp(param, "monitor-type")) {
02014       if (!strcasecmp(val, "mixmonitor"))
02015          q->montype = 1;
02016    } else if (!strcasecmp(param, "autopause")) {
02017       q->autopause = autopause2int(val);
02018    } else if (!strcasecmp(param, "maxlen")) {
02019       q->maxlen = atoi(val);
02020       if (q->maxlen < 0)
02021          q->maxlen = 0;
02022    } else if (!strcasecmp(param, "servicelevel")) {
02023       q->servicelevel= atoi(val);
02024    } else if (!strcasecmp(param, "strategy")) {
02025       int strategy;
02026 
02027       /* We are a static queue and already have set this, no need to do it again */
02028       if (failunknown) {
02029          return;
02030       }
02031       strategy = strat2int(val);
02032       if (strategy < 0) {
02033          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02034             val, q->name);
02035          q->strategy = QUEUE_STRATEGY_RINGALL;
02036       }
02037       if (strategy == q->strategy) {
02038          return;
02039       }
02040       if (strategy == QUEUE_STRATEGY_LINEAR) {
02041          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
02042          return;
02043       }
02044       q->strategy = strategy;
02045    } else if (!strcasecmp(param, "joinempty")) {
02046       parse_empty_options(val, &q->joinempty, 1);
02047    } else if (!strcasecmp(param, "leavewhenempty")) {
02048       parse_empty_options(val, &q->leavewhenempty, 0);
02049    } else if (!strcasecmp(param, "eventmemberstatus")) {
02050       q->maskmemberstatus = !ast_true(val);
02051    } else if (!strcasecmp(param, "eventwhencalled")) {
02052       if (!strcasecmp(val, "vars")) {
02053          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
02054       } else {
02055          q->eventwhencalled = ast_true(val) ? 1 : 0;
02056       }
02057    } else if (!strcasecmp(param, "reportholdtime")) {
02058       q->reportholdtime = ast_true(val);
02059    } else if (!strcasecmp(param, "memberdelay")) {
02060       q->memberdelay = atoi(val);
02061    } else if (!strcasecmp(param, "weight")) {
02062       q->weight = atoi(val);
02063    } else if (!strcasecmp(param, "timeoutrestart")) {
02064       q->timeoutrestart = ast_true(val);
02065    } else if (!strcasecmp(param, "defaultrule")) {
02066       ast_string_field_set(q, defaultrule, val);
02067    } else if (!strcasecmp(param, "timeoutpriority")) {
02068       if (!strcasecmp(val, "conf")) {
02069          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
02070       } else {
02071          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
02072       }
02073    } else if (failunknown) {
02074       if (linenum >= 0) {
02075          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
02076             q->name, param, linenum);
02077       } else {
02078          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
02079       }
02080    }
02081 }

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

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

07128 {
07129    switch ( cmd ) {
07130    case CLI_INIT:
07131       e->command = "queue show";
07132       e->usage =
07133          "Usage: queue show\n"
07134          "       Provides summary information on a specified queue.\n";
07135       return NULL;
07136    case CLI_GENERATE:
07137       return complete_queue_show(a->line, a->word, a->pos, a->n); 
07138    }
07139 
07140    return __queues_show(NULL, a->fd, a->argc, a->argv);
07141 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 4228 of file app_queue.c.

References ast_free.

04229 {
04230    struct queue_transfer_ds *qtds = data;
04231    ast_free(qtds);
04232 }

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

04252 {
04253    struct queue_transfer_ds *qtds = data;
04254    struct queue_ent *qe = qtds->qe;
04255    struct member *member = qtds->member;
04256    time_t callstart = qtds->starttime;
04257    int callcompletedinsl = qtds->callcompletedinsl;
04258    struct ast_datastore *datastore;
04259 
04260    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04261             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04262             (long) (time(NULL) - callstart), qe->opos);
04263 
04264    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04265    
04266    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04267    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04268       ast_channel_datastore_remove(old_chan, datastore);
04269    } else {
04270       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04271    }
04272 }

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

Definition at line 1283 of file app_queue.c.

References ao2_ref.

Referenced by queues_data_provider_get().

01284 {
01285    ao2_ref(q, -1);
01286    return NULL;
01287 }

static int queues_data_provider_get ( const struct ast_data_search search,
struct ast_data data_root 
) [static]

Definition at line 8288 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_load_realtime_multientry(), ast_strlen_zero(), load_realtime_queue(), queue_unref(), queues, queues_data_provider_get_helper(), and SENTINEL.

08290 {
08291    struct ao2_iterator i;
08292    struct call_queue *queue, *queue_realtime = NULL;
08293    struct ast_config *cfg;
08294    char *queuename;
08295 
08296    /* load realtime queues. */
08297    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08298    if (cfg) {
08299       for (queuename = ast_category_browse(cfg, NULL);
08300             !ast_strlen_zero(queuename);
08301             queuename = ast_category_browse(cfg, queuename)) {
08302          if ((queue = load_realtime_queue(queuename))) {
08303             queue_unref(queue);
08304          }
08305       }
08306       ast_config_destroy(cfg);
08307    }
08308 
08309    /* static queues. */
08310    i = ao2_iterator_init(queues, 0);
08311    while ((queue = ao2_iterator_next(&i))) {
08312       ao2_lock(queue);
08313       if (queue->realtime) {
08314          queue_realtime = load_realtime_queue(queue->name);
08315          if (!queue_realtime) {
08316             ao2_unlock(queue);
08317             queue_unref(queue);
08318             continue;
08319          }
08320          queue_unref(queue_realtime);
08321       }
08322 
08323       queues_data_provider_get_helper(search, data_root, queue);
08324       ao2_unlock(queue);
08325       queue_unref(queue);
08326    }
08327    ao2_iterator_destroy(&i);
08328 
08329    return 0;
08330 }

static void queues_data_provider_get_helper ( const struct ast_data_search search,
struct ast_data data_root,
struct call_queue queue 
) [static]

Definition at line 8182 of file app_queue.c.

References call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_data_add_structure(), ast_data_add_int(), ast_data_add_node(), ast_data_add_str(), ast_data_add_structure, ast_data_remove_node(), ast_data_search_match(), queue_ent::chan, call_queue::head, int2strat(), call_queue::members, queue_ent::next, and call_queue::strategy.

Referenced by queues_data_provider_get().

08184 {
08185    struct ao2_iterator im;
08186    struct member *member;
08187    struct queue_ent *qe;
08188    struct ast_data *data_queue, *data_members = NULL, *enum_node;
08189    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08190 
08191    data_queue = ast_data_add_node(data_root, "queue");
08192    if (!data_queue) {
08193       return;
08194    }
08195 
08196    ast_data_add_structure(call_queue, data_queue, queue);
08197 
08198    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08199    ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08200 
08201    /* announce position */
08202    enum_node = ast_data_add_node(data_queue, "announceposition");
08203    if (!enum_node) {
08204       return;
08205    }
08206    switch (queue->announceposition) {
08207    case ANNOUNCEPOSITION_LIMIT:
08208       ast_data_add_str(enum_node, "text", "limit");
08209       break;
08210    case ANNOUNCEPOSITION_MORE_THAN:
08211       ast_data_add_str(enum_node, "text", "more");
08212       break;
08213    case ANNOUNCEPOSITION_YES:
08214       ast_data_add_str(enum_node, "text", "yes");
08215       break;
08216    case ANNOUNCEPOSITION_NO:
08217       ast_data_add_str(enum_node, "text", "no");
08218       break;
08219    default:
08220       ast_data_add_str(enum_node, "text", "unknown");
08221       break;
08222    }
08223    ast_data_add_int(enum_node, "value", queue->announceposition);
08224 
08225    /* add queue members */
08226    im = ao2_iterator_init(queue->members, 0);
08227    while ((member = ao2_iterator_next(&im))) {
08228       if (!data_members) {
08229          data_members = ast_data_add_node(data_queue, "members");
08230          if (!data_members) {
08231             ao2_ref(member, -1);
08232             continue;
08233          }
08234       }
08235 
08236       data_member = ast_data_add_node(data_members, "member");
08237       if (!data_member) {
08238          ao2_ref(member, -1);
08239          continue;
08240       }
08241 
08242       ast_data_add_structure(member, data_member, member);
08243 
08244       ao2_ref(member, -1);
08245    }
08246    ao2_iterator_destroy(&im);
08247 
08248    /* include the callers inside the result. */
08249    if (queue->head) {
08250       for (qe = queue->head; qe; qe = qe->next) {
08251          if (!data_callers) {
08252             data_callers = ast_data_add_node(data_queue, "callers");
08253             if (!data_callers) {
08254                continue;
08255             }
08256          }
08257 
08258          data_caller = ast_data_add_node(data_callers, "caller");
08259          if (!data_caller) {
08260             continue;
08261          }
08262 
08263          ast_data_add_structure(queue_ent, data_caller, qe);
08264 
08265          /* add the caller channel. */
08266          data_caller_channel = ast_data_add_node(data_caller, "channel");
08267          if (!data_caller_channel) {
08268             continue;
08269          }
08270 
08271          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08272       }
08273    }
08274 
08275    /* if this queue doesn't match remove the added queue. */
08276    if (!ast_data_search_match(search, data_queue)) {
08277       ast_data_remove_node(data_root, data_queue);
08278    }
08279 }

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

Definition at line 2743 of file app_queue.c.

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

02744 {
02745    int oldvalue;
02746 
02747    /* Calculate holdtime using an exponential average */
02748    /* Thanks to SRT for this contribution */
02749    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02750 
02751    ao2_lock(qe->parent);
02752    oldvalue = qe->parent->holdtime;
02753    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02754    ao2_unlock(qe->parent);
02755 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

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

03352 {
03353    set_queue_variables(qe->parent, qe->chan);
03354    ao2_lock(qe->parent);
03355    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03356       "Queue: %s\r\n"
03357       "Uniqueid: %s\r\n"
03358       "Position: %d\r\n"
03359       "OriginalPosition: %d\r\n"
03360       "HoldTime: %d\r\n",
03361       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03362 
03363    qe->parent->callsabandoned++;
03364    ao2_unlock(qe->parent);
03365 }

static int reload ( void   )  [static]

Definition at line 8462 of file app_queue.c.

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

08463 {
08464    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08465    ast_unload_realtime("queue_members");
08466    reload_handler(1, &mask, NULL);
08467    return 0;
08468 }

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

06937 {
06938    int res = 0;
06939 
06940    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
06941       res |= reload_queue_rules(reload);
06942    }
06943    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
06944       res |= clear_stats(queuename);
06945    }
06946    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
06947       res |= reload_queues(reload, mask, queuename);
06948    }
06949    return res;
06950 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 5508 of file app_queue.c.

References add_to_queue(), ao2_t_find, 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, member::state_interface, and strsep().

Referenced by load_module().

05509 {
05510    char *cur_ptr;
05511    const char *queue_name;
05512    char *member;
05513    char *interface;
05514    char *membername = NULL;
05515    char *state_interface;
05516    char *penalty_tok;
05517    int penalty = 0;
05518    char *paused_tok;
05519    int paused = 0;
05520    struct ast_db_entry *db_tree;
05521    struct ast_db_entry *entry;
05522    struct call_queue *cur_queue;
05523    char queue_data[PM_MAX_LEN];
05524 
05525    /* Each key in 'pm_family' is the name of a queue */
05526    db_tree = ast_db_gettree(pm_family, NULL);
05527    for (entry = db_tree; entry; entry = entry->next) {
05528 
05529       queue_name = entry->key + strlen(pm_family) + 2;
05530 
05531       {
05532          struct call_queue tmpq = {
05533             .name = queue_name,
05534          };
05535          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05536       }  
05537 
05538       if (!cur_queue)
05539          cur_queue = load_realtime_queue(queue_name);
05540 
05541       if (!cur_queue) {
05542          /* If the queue no longer exists, remove it from the
05543           * database */
05544          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05545          ast_db_del(pm_family, queue_name);
05546          continue;
05547       } 
05548 
05549       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
05550          queue_t_unref(cur_queue, "Expire reload reference");
05551          continue;
05552       }
05553 
05554       cur_ptr = queue_data;
05555       while ((member = strsep(&cur_ptr, ",|"))) {
05556          if (ast_strlen_zero(member))
05557             continue;
05558 
05559          interface = strsep(&member, ";");
05560          penalty_tok = strsep(&member, ";");
05561          paused_tok = strsep(&member, ";");
05562          membername = strsep(&member, ";");
05563          state_interface = strsep(&member, ";");
05564 
05565          if (!penalty_tok) {
05566             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05567             break;
05568          }
05569          penalty = strtol(penalty_tok, NULL, 10);
05570          if (errno == ERANGE) {
05571             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05572             break;
05573          }
05574          
05575          if (!paused_tok) {
05576             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05577             break;
05578          }
05579          paused = strtol(paused_tok, NULL, 10);
05580          if ((errno == ERANGE) || paused < 0 || paused > 1) {
05581             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05582             break;
05583          }
05584 
05585          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
05586          
05587          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05588             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05589             break;
05590          }
05591       }
05592       queue_t_unref(cur_queue, "Expire reload reference");
05593    }
05594 
05595    if (db_tree) {
05596       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05597       ast_db_freetree(db_tree);
05598    }
05599 }

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 6542 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, ast_variable::next, rule_list::rules, and ast_variable::value.

Referenced by reload_handler().

06543 {
06544    struct ast_config *cfg;
06545    struct rule_list *rl_iter, *new_rl;
06546    struct penalty_rule *pr_iter;
06547    char *rulecat = NULL;
06548    struct ast_variable *rulevar = NULL;
06549    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06550    
06551    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06552       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06553       return AST_MODULE_LOAD_SUCCESS;
06554    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06555       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06556       return AST_MODULE_LOAD_SUCCESS;
06557    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06558       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
06559       return AST_MODULE_LOAD_SUCCESS;
06560    }
06561 
06562    AST_LIST_LOCK(&rule_lists);
06563    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06564       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06565          ast_free(pr_iter);
06566       ast_free(rl_iter);
06567    }
06568    while ((rulecat = ast_category_browse(cfg, rulecat))) {
06569       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06570          AST_LIST_UNLOCK(&rule_lists);
06571          ast_config_destroy(cfg);
06572          return AST_MODULE_LOAD_FAILURE;
06573       } else {
06574          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06575          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06576          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06577             if(!strcasecmp(rulevar->name, "penaltychange"))
06578                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06579             else
06580                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06581       }
06582    }
06583    AST_LIST_UNLOCK(&rule_lists);
06584 
06585    ast_config_destroy(cfg);
06586 
06587    return AST_MODULE_LOAD_SUCCESS;
06588 }

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

06847 {
06848    struct ast_config *cfg;
06849    char *cat;
06850    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06851    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06852 
06853    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
06854       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
06855       return -1;
06856    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06857       return 0;
06858    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06859       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
06860       return -1;
06861    }
06862 
06863    /* We've made it here, so it looks like we're doing operations on all queues. */
06864    ao2_lock(queues);
06865 
06866    /* Mark all queues as dead for the moment if we're reloading queues.
06867     * For clarity, we could just be reloading members, in which case we don't want to mess
06868     * with the other queue parameters at all*/
06869    if (queue_reload) {
06870       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
06871    }
06872 
06873    /* Chug through config file */
06874    cat = NULL;
06875    while ((cat = ast_category_browse(cfg, cat)) ) {
06876       if (!strcasecmp(cat, "general") && queue_reload) {
06877          queue_set_global_params(cfg);
06878          continue;
06879       }
06880       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
06881          reload_single_queue(cfg, mask, cat);
06882    }
06883 
06884    ast_config_destroy(cfg);
06885    /* Unref all the dead queues if we were reloading queues */
06886    if (queue_reload) {
06887       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
06888    }
06889    ao2_unlock(queues);
06890    return 0;
06891 }

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

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

Referenced by reload_single_queue().

06622 {
06623    char *membername, *interface, *state_interface, *tmp;
06624    char *parse;
06625    struct member *cur, *newm;
06626    struct member tmpmem;
06627    int penalty;
06628    AST_DECLARE_APP_ARGS(args,
06629       AST_APP_ARG(interface);
06630       AST_APP_ARG(penalty);
06631       AST_APP_ARG(membername);
06632       AST_APP_ARG(state_interface);
06633    );
06634 
06635    if (ast_strlen_zero(memberdata)) {
06636       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06637       return;
06638    }
06639 
06640    /* Add a new member */
06641    parse = ast_strdupa(memberdata);
06642             
06643    AST_STANDARD_APP_ARGS(args, parse);
06644 
06645    interface = args.interface;
06646    if (!ast_strlen_zero(args.penalty)) {
06647       tmp = args.penalty;
06648       ast_strip(tmp);
06649       penalty = atoi(tmp);
06650       if (penalty < 0) {
06651          penalty = 0;
06652       }
06653    } else {
06654       penalty = 0;
06655    }
06656 
06657    if (!ast_strlen_zero(args.membername)) {
06658       membername = args.membername;
06659       ast_strip(membername);
06660    } else {
06661       membername = interface;
06662    }
06663 
06664    if (!ast_strlen_zero(args.state_interface)) {
06665       state_interface = args.state_interface;
06666       ast_strip(state_interface);
06667    } else {
06668       state_interface = interface;
06669    }
06670 
06671    /* Find the old position in the list */
06672    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06673    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
06674    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06675       ao2_link(q->members, newm);
06676       ao2_ref(newm, -1);
06677    }
06678    newm = NULL;
06679 
06680    if (cur) {
06681       ao2_ref(cur, -1);
06682    }
06683 }

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

References alloc_queue(), ao2_callback, ao2_lock, ao2_t_find, ao2_unlock, ast_log(), ast_test_flag, ast_variable_browse(), ast_variable_retrieve(), call_queue::found, init_queue(), LOG_WARNING, mark_member_dead(), call_queue::members, call_queue::name, OBJ_NODATA, OBJ_POINTER, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, reload_single_member(), strat2int(), call_queue::strategy, and var.

Referenced by reload_queues().

06718 {
06719    int new;
06720    struct call_queue *q = NULL;
06721    /*We're defining a queue*/
06722    struct call_queue tmpq = {
06723       .name = queuename,
06724    };
06725    const char *tmpvar;
06726    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06727    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
06728    int prev_weight = 0;
06729    struct ast_variable *var;
06730    if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
06731       if (queue_reload) {
06732          /* Make one then */
06733          if (!(q = alloc_queue(queuename))) {
06734             return;
06735          }
06736       } else {
06737          /* Since we're not reloading queues, this means that we found a queue
06738           * in the configuration file which we don't know about yet. Just return.
06739           */
06740          return;
06741       }
06742       new = 1;
06743    } else {
06744       new = 0;
06745    }
06746    
06747    if (!new) {
06748       ao2_lock(q);
06749       prev_weight = q->weight ? 1 : 0;
06750    }
06751    /* Check if we already found a queue with this name in the config file */
06752    if (q->found) {
06753       ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
06754       if (!new) {
06755          /* It should be impossible to *not* hit this case*/
06756          ao2_unlock(q);
06757       }
06758       queue_t_unref(q, "We exist! Expiring temporary pointer");
06759       return;
06760    }
06761    /* Due to the fact that the "linear" strategy will have a different allocation
06762     * scheme for queue members, we must devise the queue's strategy before other initializations.
06763     * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
06764     * container used will have only a single bucket instead of the typical number.
06765     */
06766    if (queue_reload) {
06767       if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
06768          q->strategy = strat2int(tmpvar);
06769          if (q->strategy < 0) {
06770             ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
06771             tmpvar, q->name);
06772             q->strategy = QUEUE_STRATEGY_RINGALL;
06773          }
06774       } else {
06775          q->strategy = QUEUE_STRATEGY_RINGALL;
06776       }
06777       init_queue(q);
06778    }
06779    if (member_reload) {
06780       ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
06781    }
06782    for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
06783       if (member_reload && !strcasecmp(var->name, "member")) {
06784          reload_single_member(var->value, q);
06785       } else if (queue_reload) {
06786          queue_set_param(q, var->name, var->value, var->lineno, 1);
06787       }
06788    }
06789    /* At this point, we've determined if the queue has a weight, so update use_weight
06790     * as appropriate
06791     */
06792    if (!q->weight && prev_weight) {
06793       ast_atomic_fetchadd_int(&use_weight, -1);
06794    }
06795    else if (q->weight && !prev_weight) {
06796       ast_atomic_fetchadd_int(&use_weight, +1);
06797    }
06798 
06799    /* Free remaining members marked as delme */
06800    if (member_reload) {
06801       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
06802    }
06803 
06804    if (new) {
06805       queues_t_link(queues, q, "Add queue to container");
06806    } else {
06807       ao2_unlock(q);
06808    }
06809    queue_t_unref(q, "Expiring creation reference");
06810 }

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 5251 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, 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 attempt_thread(), handle_queue_remove_member(), manager_remove_queue_member(), rqm_exec(), and scan_service().

05252 {
05253    struct call_queue *q, tmpq = {
05254       .name = queuename,   
05255    };
05256    struct member *mem, tmpmem;
05257    int res = RES_NOSUCHQUEUE;
05258 
05259    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05260    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05261       ao2_lock(q);
05262       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05263          /* XXX future changes should beware of this assumption!! */
05264          if (!mem->dynamic) {
05265             ao2_ref(mem, -1);
05266             ao2_unlock(q);
05267             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05268             return RES_NOT_DYNAMIC;
05269          }
05270          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05271             "Queue: %s\r\n"
05272             "Location: %s\r\n"
05273             "MemberName: %s\r\n",
05274             q->name, mem->interface, mem->membername);
05275          ao2_unlink(q->members, mem);
05276          ao2_ref(mem, -1);
05277 
05278          if (queue_persistent_members)
05279             dump_queue_members(q);
05280          
05281          res = RES_OKAY;
05282       } else {
05283          res = RES_EXISTS;
05284       }
05285       ao2_unlock(q);
05286       queue_t_unref(q, "Expiring temporary reference");
05287    }
05288 
05289    return res;
05290 }

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

Part 2 of ring_one.

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

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

Definition at line 3004 of file app_queue.c.

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ast_party_connected_line::ani, ast_party_caller::ani, 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_both, ast_channel_set_caller_event(), ast_channel_unlock, ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, AST_FLAG_ANSWERED_ELSEWHERE, ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_request(), ast_set_callerid(), ast_set_flag, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_cdr::clid, compare_weight(), ast_channel::connected, ast_channel::context, ast_channel::data, ast_cdr::dcontext, callattempt::dial_callerid_absent, dialcontext, ast_channel::dialed, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, get_queue_member_status(), ast_party_connected_line::id, ast_party_caller::id, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, queue_ent::linpos, ast_channel::macroexten, manager_event, callattempt::member, member::membername, ast_party_id::name, ast_channel::name, call_queue::name, ast_channel::nativeformats, ast_party_dialed::number, ast_party_id::number, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, ast_channel::redirecting, call_queue::ringinuse, call_queue::rrpos, S_COR, S_OR, ast_cdr::src, member::status, status, callattempt::stillgoing, ast_party_name::str, ast_party_number::str, ast_party_dialed::str, ast_party_dialed::transit_network_select, ast_channel::uniqueid, update_status(), ast_cdr::userfield, ast_party_name::valid, ast_party_number::valid, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

03005 {
03006    int res;
03007    int status;
03008    char tech[256];
03009    char *location;
03010    const char *macrocontext, *macroexten;
03011 
03012    /* on entry here, we know that tmp->chan == NULL */
03013    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
03014       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
03015       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
03016             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
03017       if (qe->chan->cdr)
03018          ast_cdr_busy(qe->chan->cdr);
03019       tmp->stillgoing = 0;
03020       (*busies)++;
03021       return 0;
03022    }
03023 
03024    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
03025       ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
03026       if (qe->chan->cdr)
03027          ast_cdr_busy(qe->chan->cdr);
03028       tmp->stillgoing = 0;
03029       return 0;
03030    }
03031 
03032    if (tmp->member->paused) {
03033       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
03034       if (qe->chan->cdr)
03035          ast_cdr_busy(qe->chan->cdr);
03036       tmp->stillgoing = 0;
03037       return 0;
03038    }
03039    if (use_weight && compare_weight(qe->parent,tmp->member)) {
03040       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
03041       if (qe->chan->cdr)
03042          ast_cdr_busy(qe->chan->cdr);
03043       tmp->stillgoing = 0;
03044       (*busies)++;
03045       return 0;
03046    }
03047 
03048    ast_copy_string(tech, tmp->interface, sizeof(tech));
03049    if ((location = strchr(tech, '/')))
03050       *location++ = '\0';
03051    else
03052       location = "";
03053 
03054    /* Request the peer */
03055    tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
03056    if (!tmp->chan) {       /* If we can't, just go on to the next call */
03057       if (qe->chan->cdr)
03058          ast_cdr_busy(qe->chan->cdr);
03059       tmp->stillgoing = 0; 
03060 
03061       ao2_lock(qe->parent);
03062       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03063       qe->parent->rrpos++;
03064       qe->linpos++;
03065       ao2_unlock(qe->parent);
03066 
03067       (*busies)++;
03068       return 0;
03069    }
03070 
03071    ast_channel_lock_both(tmp->chan, qe->chan);
03072 
03073    if (qe->cancel_answered_elsewhere) {
03074       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03075    }
03076    tmp->chan->appl = "AppQueue";
03077    tmp->chan->data = "(Outgoing Line)";
03078    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
03079 
03080    /* If the new channel has no callerid, try to guess what it should be */
03081    if (!tmp->chan->caller.id.number.valid) {
03082       if (qe->chan->connected.id.number.valid) {
03083          struct ast_party_caller caller;
03084 
03085          ast_party_caller_set_init(&caller, &tmp->chan->caller);
03086          caller.id = qe->chan->connected.id;
03087          caller.ani = qe->chan->connected.ani;
03088          ast_channel_set_caller_event(tmp->chan, &caller, NULL);
03089       } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
03090          ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
03091       } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
03092          ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 
03093       }
03094       tmp->dial_callerid_absent = 1;
03095    }
03096 
03097    ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
03098 
03099    tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
03100 
03101    ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
03102 
03103    /* Inherit specially named variables from parent channel */
03104    ast_channel_inherit_variables(qe->chan, tmp->chan);
03105    ast_channel_datastore_inherit(qe->chan, tmp->chan);
03106 
03107    /* Presense of ADSI CPE on outgoing channel follows ours */
03108    tmp->chan->adsicpe = qe->chan->adsicpe;
03109 
03110    /* Inherit context and extension */
03111    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
03112    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
03113    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
03114    if (!ast_strlen_zero(macroexten))
03115       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
03116    else
03117       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
03118    if (ast_cdr_isset_unanswered()) {
03119       /* they want to see the unanswered dial attempts! */
03120       /* set up the CDR fields on all the CDRs to give sensical information */
03121       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
03122       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
03123       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
03124       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
03125       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
03126       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
03127       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
03128       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
03129       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
03130       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
03131       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
03132    }
03133 
03134    ast_channel_unlock(tmp->chan);
03135    ast_channel_unlock(qe->chan);
03136 
03137    /* Place the call, but don't wait on the answer */
03138    if ((res = ast_call(tmp->chan, location, 0))) {
03139       /* Again, keep going even if there's an error */
03140       ast_debug(1, "ast call on peer returned %d\n", res);
03141       ast_verb(3, "Couldn't call %s\n", tmp->interface);
03142       do_hang(tmp);
03143       (*busies)++;
03144       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03145       return 0;
03146    } else if (qe->parent->eventwhencalled) {
03147       char vars[2048];
03148 
03149       ast_channel_lock_both(tmp->chan, qe->chan);
03150 
03151       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
03152          "Queue: %s\r\n"
03153          "AgentCalled: %s\r\n"
03154          "AgentName: %s\r\n"
03155          "ChannelCalling: %s\r\n"
03156          "DestinationChannel: %s\r\n"
03157          "CallerIDNum: %s\r\n"
03158          "CallerIDName: %s\r\n"
03159          "ConnectedLineNum: %s\r\n"
03160          "ConnectedLineName: %s\r\n"
03161          "Context: %s\r\n"
03162          "Extension: %s\r\n"
03163          "Priority: %d\r\n"
03164          "Uniqueid: %s\r\n"
03165          "%s",
03166          qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
03167          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
03168          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
03169          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
03170          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
03171          qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
03172          qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03173 
03174       ast_channel_unlock(tmp->chan);
03175       ast_channel_unlock(qe->chan);
03176 
03177       ast_verb(3, "Called %s\n", tmp->interface);
03178    }
03179 
03180    update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03181    return 1;
03182 }

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 3210 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 wait_for_answer().

03211 {
03212    int ret = 0;
03213 
03214    while (ret == 0) {
03215       struct callattempt *best = find_best(outgoing);
03216       if (!best) {
03217          ast_debug(1, "Nobody left to try ringing in queue\n");
03218          break;
03219       }
03220       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03221          struct callattempt *cur;
03222          /* Ring everyone who shares this best metric (for ringall) */
03223          for (cur = outgoing; cur; cur = cur->q_next) {
03224             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03225                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03226                ret |= ring_entry(qe, cur, busies);
03227             }
03228          }
03229       } else {
03230          /* Ring just the best channel */
03231          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03232          ret = ring_entry(qe, best, busies);
03233       }
03234       
03235       /* If we have timed out, break out */
03236       if (qe->expire && (time(NULL) >= qe->expire)) {
03237          ast_debug(1, "Queue timed out while ringing members.\n");
03238          ret = 0;
03239          break;
03240       }
03241    }
03242 
03243    return ret;
03244 }

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

References ast_indicate(), ast_moh_start(), ast_queue_log(), ast_verb, call_queue::autopause, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, manager_event, queue_ent::moh, ast_channel::name, call_queue::name, queue_ent::parent, QUEUE_AUTOPAUSE_OFF, QUEUE_AUTOPAUSE_ON, QUEUE_EVENT_VARIABLES, queue_ent::ring_when_ringing, set_member_paused(), ast_channel::uniqueid, and vars2manager().

03369 {
03370    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03371 
03372    /* Stop ringing, and resume MOH if specified */
03373    if (qe->ring_when_ringing) {
03374       ast_indicate(qe->chan, -1);
03375       ast_moh_start(qe->chan, qe->moh, NULL);
03376    }
03377 
03378    if (qe->parent->eventwhencalled) {
03379       char vars[2048];
03380 
03381       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03382                   "Queue: %s\r\n"
03383                   "Uniqueid: %s\r\n"
03384                   "Channel: %s\r\n"
03385                   "Member: %s\r\n"
03386                   "MemberName: %s\r\n"
03387                   "Ringtime: %d\r\n"
03388                   "%s",
03389                   qe->parent->name,
03390                   qe->chan->uniqueid,
03391                   qe->chan->name,
03392                   interface,
03393                   membername,
03394                   rnatime,
03395                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03396    }
03397    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
03398    if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03399       if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03400          if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03401             ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03402                interface, qe->parent->name);
03403          } else {
03404             ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03405          }
03406       } else {
03407          /* If queue autopause is mode all, just don't send any queue to stop.
03408          * the function will stop in all queues */
03409          if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03410             ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03411                   interface, qe->parent->name);
03412          } else {
03413                ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03414          }
03415       }
03416    }
03417    return;
03418 }

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

RemoveQueueMember application.

Definition at line 5674 of file app_queue.c.

References args, AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, 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().

05675 {
05676    int res=-1;
05677    char *parse, *temppos = NULL;
05678    AST_DECLARE_APP_ARGS(args,
05679       AST_APP_ARG(queuename);
05680       AST_APP_ARG(interface);
05681       AST_APP_ARG(options);
05682    );
05683 
05684 
05685    if (ast_strlen_zero(data)) {
05686       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
05687       return -1;
05688    }
05689 
05690    parse = ast_strdupa(data);
05691 
05692    AST_STANDARD_APP_ARGS(args, parse);
05693 
05694    if (ast_strlen_zero(args.interface)) {
05695       args.interface = ast_strdupa(chan->name);
05696       temppos = strrchr(args.interface, '-');
05697       if (temppos)
05698          *temppos = '\0';
05699    }
05700 
05701    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05702 
05703    switch (remove_from_queue(args.queuename, args.interface)) {
05704    case RES_OKAY:
05705       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
05706       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05707       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05708       res = 0;
05709       break;
05710    case RES_EXISTS:
05711       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05712       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05713       res = 0;
05714       break;
05715    case RES_NOSUCHQUEUE:
05716       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05717       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05718       res = 0;
05719       break;
05720    case RES_NOT_DYNAMIC:
05721       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05722       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05723       res = 0;
05724       break;
05725    }
05726 
05727    return res;
05728 }

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 2089 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::members, call_queue::name, member::paused, member::penalty, member::realtime, member::rt_uniqueid, S_OR, and member::state_interface.

Referenced by update_realtime_members().

02090 {
02091    struct member *m;
02092    struct ao2_iterator mem_iter;
02093    int penalty = 0;
02094    int paused  = 0;
02095    int found = 0;
02096 
02097    if (ast_strlen_zero(rt_uniqueid)) {
02098       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
02099       return;
02100    }
02101 
02102    if (penalty_str) {
02103       penalty = atoi(penalty_str);
02104       if (penalty < 0)
02105          penalty = 0;
02106    }
02107 
02108    if (paused_str) {
02109       paused = atoi(paused_str);
02110       if (paused < 0)
02111          paused = 0;
02112    }
02113 
02114    /* Find member by realtime uniqueid and update */
02115    mem_iter = ao2_iterator_init(q->members, 0);
02116    while ((m = ao2_iterator_next(&mem_iter))) {
02117       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
02118          m->dead = 0;   /* Do not delete this one. */
02119          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02120          if (paused_str)
02121             m->paused = paused;
02122          if (strcasecmp(state_interface, m->state_interface)) {
02123             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
02124          }     
02125          m->penalty = penalty;
02126          found = 1;
02127          ao2_ref(m, -1);
02128          break;
02129       }
02130       ao2_ref(m, -1);
02131    }
02132    ao2_iterator_destroy(&mem_iter);
02133 
02134    /* Create a new member */
02135    if (!found) {
02136       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
02137          m->dead = 0;
02138          m->realtime = 1;
02139          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02140          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
02141          ao2_link(q->members, m);
02142          ao2_ref(m, -1);
02143          m = NULL;
02144       }
02145    }
02146 }

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

Playback announcement to queued members if period has elapsed.

Definition at line 3295 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::relativeperiodicannounce, call_queue::sound_periodicannounce, and valid_exit().

Referenced by queue_exec().

03296 {
03297    int res = 0;
03298    time_t now;
03299 
03300    /* Get the current time */
03301    time(&now);
03302 
03303    /* Check to see if it is time to announce */
03304    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03305       return 0;
03306 
03307    /* Stop the music on hold so we can play our own file */
03308    if (ringing)
03309       ast_indicate(qe->chan,-1);
03310    else
03311       ast_moh_stop(qe->chan);
03312 
03313    ast_verb(3, "Playing periodic announcement\n");
03314    
03315    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03316       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03317    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
03318       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03319       qe->last_periodic_announce_sound = 0;
03320    }
03321    
03322    /* play the announcement */
03323    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03324 
03325    if (res > 0 && !valid_exit(qe, res))
03326       res = 0;
03327 
03328    /* Resume Music on Hold if the caller is going to stay in the queue */
03329    if (!res) {
03330       if (ringing)
03331          ast_indicate(qe->chan, AST_CONTROL_RINGING);
03332       else
03333          ast_moh_start(qe->chan, qe->moh, NULL);
03334    }
03335 
03336    /* update last_periodic_announce_time */
03337    if (qe->parent->relativeperiodicannounce)
03338       time(&qe->last_periodic_announce_time);
03339    else
03340       qe->last_periodic_announce_time = now;
03341 
03342    /* Update the current periodic announcement to the next announcement */
03343    if (!qe->parent->randomperiodicannounce) {
03344       qe->last_periodic_announce_sound++;
03345    }
03346    
03347    return res;
03348 }

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

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

02600 {
02601    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02602    int say_thanks = 1;
02603    time_t now;
02604 
02605    /* Let minannouncefrequency seconds pass between the start of each position announcement */
02606    time(&now);
02607    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02608       return 0;
02609 
02610    /* If either our position has changed, or we are over the freq timer, say position */
02611    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02612       return 0;
02613 
02614    if (ringing) {
02615       ast_indicate(qe->chan,-1);
02616    } else {
02617       ast_moh_stop(qe->chan);
02618    }
02619 
02620    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02621       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02622       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02623       qe->pos <= qe->parent->announcepositionlimit))
02624          announceposition = 1;
02625 
02626 
02627    if (announceposition == 1) {
02628       /* Say we're next, if we are */
02629       if (qe->pos == 1) {
02630          res = play_file(qe->chan, qe->parent->sound_next);
02631          if (res)
02632             goto playout;
02633          else
02634             goto posout;
02635       } else {
02636          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02637             /* More than Case*/
02638             res = play_file(qe->chan, qe->parent->queue_quantity1);
02639             if (res)
02640                goto playout;
02641             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02642             if (res)
02643                goto playout;
02644          } else {
02645             /* Normal Case */
02646             res = play_file(qe->chan, qe->parent->sound_thereare);
02647             if (res)
02648                goto playout;
02649             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02650             if (res)
02651                goto playout;
02652          }
02653          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02654             /* More than Case*/
02655             res = play_file(qe->chan, qe->parent->queue_quantity2);
02656             if (res)
02657                goto playout;
02658          } else {
02659             res = play_file(qe->chan, qe->parent->sound_calls);
02660             if (res)
02661                goto playout;
02662          }
02663       }
02664    }
02665    /* Round hold time to nearest minute */
02666    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02667 
02668    /* If they have specified a rounding then round the seconds as well */
02669    if (qe->parent->roundingseconds) {
02670       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02671       avgholdsecs *= qe->parent->roundingseconds;
02672    } else {
02673       avgholdsecs = 0;
02674    }
02675 
02676    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02677 
02678    /* If the hold time is >1 min, if it's enabled, and if it's not
02679       supposed to be only once and we have already said it, say it */
02680     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02681         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02682         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02683       res = play_file(qe->chan, qe->parent->sound_holdtime);
02684       if (res)
02685          goto playout;
02686 
02687       if (avgholdmins >= 1) {
02688          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02689          if (res)
02690             goto playout;
02691 
02692          if (avgholdmins == 1) {
02693             res = play_file(qe->chan, qe->parent->sound_minute);
02694             if (res)
02695                goto playout;
02696          } else {
02697             res = play_file(qe->chan, qe->parent->sound_minutes);
02698             if (res)
02699                goto playout;
02700          }
02701       }
02702       if (avgholdsecs >= 1) {
02703          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02704          if (res)
02705             goto playout;
02706 
02707          res = play_file(qe->chan, qe->parent->sound_seconds);
02708          if (res)
02709             goto playout;
02710       }
02711    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02712       say_thanks = 0;
02713    }
02714 
02715 posout:
02716    if (qe->parent->announceposition) {
02717       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02718          qe->chan->name, qe->parent->name, qe->pos);
02719    }
02720    if (say_thanks) {
02721       res = play_file(qe->chan, qe->parent->sound_thanks);
02722    }
02723 playout:
02724 
02725    if ((res > 0 && !valid_exit(qe, res)))
02726       res = 0;
02727 
02728    /* Set our last_pos indicators */
02729    qe->last_pos = now;
02730    qe->last_pos_said = qe->pos;
02731 
02732    /* Don't restart music on hold if we're about to exit the caller from the queue */
02733    if (!res) {
02734       if (ringing) {
02735          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02736       } else {
02737          ast_moh_start(qe->chan, qe->moh, NULL);
02738       }
02739    }
02740    return res;
02741 }

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

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

04188 {
04189    const char *reason = NULL; /* silence dumb compilers */
04190 
04191    if (!qe->parent->eventwhencalled)
04192       return;
04193 
04194    switch (rsn) {
04195    case CALLER:
04196       reason = "caller";
04197       break;
04198    case AGENT:
04199       reason = "agent";
04200       break;
04201    case TRANSFER:
04202       reason = "transfer";
04203       break;
04204    }
04205 
04206    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04207       "Queue: %s\r\n"
04208       "Uniqueid: %s\r\n"
04209       "Channel: %s\r\n"
04210       "Member: %s\r\n"
04211       "MemberName: %s\r\n"
04212       "HoldTime: %ld\r\n"
04213       "TalkTime: %ld\r\n"
04214       "Reason: %s\r\n"
04215       "%s",
04216       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04217       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04218       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04219 }

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

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

05351 {
05352    int found = 0;
05353    struct call_queue *q;
05354    struct member *mem;
05355    struct ao2_iterator queue_iter;
05356    int failed;
05357 
05358    /* Special event for when all queues are paused - individual events still generated */
05359    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05360    if (ast_strlen_zero(queuename))
05361       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05362 
05363    queue_iter = ao2_iterator_init(queues, 0);
05364    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05365       ao2_lock(q);
05366       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05367          if ((mem = interface_exists(q, interface))) {
05368             if (mem->paused == paused) {
05369                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05370             }
05371 
05372             failed = 0;
05373             if (mem->realtime) {
05374                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05375             }
05376          
05377             if (failed) {
05378                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05379                ao2_ref(mem, -1);
05380                ao2_unlock(q);
05381                queue_t_unref(q, "Done with iterator");
05382                continue;
05383             }  
05384             found++;
05385             mem->paused = paused;
05386 
05387             if (queue_persistent_members)
05388                dump_queue_members(q);
05389 
05390             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05391             
05392             if (!ast_strlen_zero(reason)) {
05393                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05394                   "Queue: %s\r\n"
05395                   "Location: %s\r\n"
05396                   "MemberName: %s\r\n"
05397                   "Paused: %d\r\n"
05398                   "Reason: %s\r\n",
05399                      q->name, mem->interface, mem->membername, paused, reason);
05400             } else {
05401                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05402                   "Queue: %s\r\n"
05403                   "Location: %s\r\n"
05404                   "MemberName: %s\r\n"
05405                   "Paused: %d\r\n",
05406                      q->name, mem->interface, mem->membername, paused);
05407             }
05408             ao2_ref(mem, -1);
05409          }
05410       }
05411       
05412       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05413          ao2_unlock(q);
05414          queue_t_unref(q, "Done with iterator");
05415          break;
05416       }
05417       
05418       ao2_unlock(q);
05419       queue_t_unref(q, "Done with iterator");
05420    }
05421    ao2_iterator_destroy(&queue_iter);
05422 
05423    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05424 }

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

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

05428 {
05429    int foundinterface = 0, foundqueue = 0;
05430    struct call_queue *q;
05431    struct member *mem;
05432    struct ao2_iterator queue_iter;
05433 
05434    if (penalty < 0) {
05435       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05436       return RESULT_FAILURE;
05437    }
05438 
05439    queue_iter = ao2_iterator_init(queues, 0);
05440    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05441       ao2_lock(q);
05442       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05443          foundqueue++;
05444          if ((mem = interface_exists(q, interface))) {
05445             foundinterface++;
05446             mem->penalty = penalty;
05447             
05448             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05449             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05450                "Queue: %s\r\n"
05451                "Location: %s\r\n"
05452                "Penalty: %d\r\n",
05453                q->name, mem->interface, penalty);
05454             ao2_ref(mem, -1);
05455          }
05456       }
05457       ao2_unlock(q);
05458       queue_t_unref(q, "Done with iterator");
05459    }
05460    ao2_iterator_destroy(&queue_iter);
05461 
05462    if (foundinterface) {
05463       return RESULT_SUCCESS;
05464    } else if (!foundqueue) {
05465       ast_log (LOG_ERROR, "Invalid queuename\n"); 
05466    } else {
05467       ast_log (LOG_ERROR, "Invalid interface\n");
05468    }  
05469 
05470    return RESULT_FAILURE;
05471 }

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

sets the QUEUESTATUS channel variable

Definition at line 1196 of file app_queue.c.

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

Referenced by queue_exec().

01197 {
01198    int i;
01199 
01200    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
01201       if (queue_results[i].id == res) {
01202          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
01203          return;
01204       }
01205    }
01206 }

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

Set variables of queue.

Definition at line 1291 of file app_queue.c.

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

Referenced by end_bridge_callback(), and record_abandoned().

01292 {
01293    char interfacevar[256]="";
01294    float sl = 0;
01295 
01296    ao2_lock(q);
01297 
01298    if (q->setqueuevar) {
01299       sl = 0;
01300       if (q->callscompleted > 0) 
01301          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01302 
01303       snprintf(interfacevar, sizeof(interfacevar),
01304          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01305          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
01306 
01307       ao2_unlock(q);
01308    
01309       pbx_builtin_setvar_multiple(chan, interfacevar); 
01310    } else {
01311       ao2_unlock(q);
01312    }
01313 }

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

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

Definition at line 4289 of file app_queue.c.

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

04290 {
04291    struct ast_datastore *ds;
04292    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04293 
04294    if (!qtds) {
04295       ast_log(LOG_WARNING, "Memory allocation error!\n");
04296       return NULL;
04297    }
04298 
04299    ast_channel_lock(qe->chan);
04300    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04301       ast_channel_unlock(qe->chan);
04302       ast_free(qtds);
04303       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04304       return NULL;
04305    }
04306 
04307    qtds->qe = qe;
04308    /* This member is refcounted in try_calling, so no need to add it here, too */
04309    qtds->member = member;
04310    qtds->starttime = starttime;
04311    qtds->callcompletedinsl = callcompletedinsl;
04312    ds->data = qtds;
04313    ast_channel_datastore_add(qe->chan, ds);
04314    ast_channel_unlock(qe->chan);
04315    return ds;
04316 }

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

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

03272 {
03273    struct callattempt *best = find_best(outgoing);
03274 
03275    if (best) {
03276       /* Ring just the best channel */
03277       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03278       qe->linpos = best->metric % 1000;
03279    } else {
03280       /* Just increment rrpos */
03281       if (qe->linwrapped) {
03282          /* No more channels, start over */
03283          qe->linpos = 0;
03284       } else {
03285          /* Prioritize next entry */
03286          qe->linpos++;
03287       }
03288    }
03289    qe->linwrapped = 0;
03290 
03291    return 0;
03292 }

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

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

03248 {
03249    struct callattempt *best = find_best(outgoing);
03250 
03251    if (best) {
03252       /* Ring just the best channel */
03253       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03254       qe->parent->rrpos = best->metric % 1000;
03255    } else {
03256       /* Just increment rrpos */
03257       if (qe->parent->wrapped) {
03258          /* No more channels, start over */
03259          qe->parent->rrpos = 0;
03260       } else {
03261          /* Prioritize next entry */
03262          qe->parent->rrpos++;
03263       }
03264    }
03265    qe->parent->wrapped = 0;
03266 
03267    return 0;
03268 }

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

Definition at line 1220 of file app_queue.c.

References ARRAY_LEN, strategies, and strategy::strategy.

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

01221 {
01222    int x;
01223 
01224    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01225       if (!strcasecmp(strategy, strategies[x].name))
01226          return strategies[x].strategy;
01227    }
01228 
01229    return -1;
01230 }

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

References ao2_container_count(), ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, 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_MAX_CONTEXT, AST_MAX_EXTENSION, ast_set_flag, ast_test_flag, queue_ent::cancel_answered_elsewhere, queue_ent::chan, dialed_interface_info, queue_ent::expire, call_queue::members, queue_ent::parent, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, and call_queue::strategy.

Referenced by queue_exec().

04371 {
04372    struct member *cur;
04373    struct callattempt *outgoing = NULL; /* the list of calls we are building */
04374    int to, orig;
04375    char oldexten[AST_MAX_EXTENSION]="";
04376    char oldcontext[AST_MAX_CONTEXT]="";
04377    char queuename[256]="";
04378    char interfacevar[256]="";
04379    struct ast_channel *peer;
04380    struct ast_channel *which;
04381    struct callattempt *lpeer;
04382    struct member *member;
04383    struct ast_app *application;
04384    int res = 0, bridge = 0;
04385    int numbusies = 0;
04386    int x=0;
04387    char *announce = NULL;
04388    char digit = 0;
04389    time_t callstart;
04390    time_t now = time(NULL);
04391    struct ast_bridge_config bridge_config;
04392    char nondataquality = 1;
04393    char *agiexec = NULL;
04394    char *macroexec = NULL;
04395    char *gosubexec = NULL;
04396    const char *monitorfilename;
04397    const char *monitor_exec;
04398    const char *monitor_options;
04399    char tmpid[256], tmpid2[256];
04400    char meid[1024], meid2[1024];
04401    char mixmonargs[1512];
04402    struct ast_app *mixmonapp = NULL;
04403    char *p;
04404    char vars[2048];
04405    int forwardsallowed = 1;
04406    int block_connected_line = 0;
04407    int callcompletedinsl;
04408    struct ao2_iterator memi;
04409    struct ast_datastore *datastore, *transfer_ds;
04410    struct queue_end_bridge *queue_end_bridge = NULL;
04411 
04412    ast_channel_lock(qe->chan);
04413    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
04414    ast_channel_unlock(qe->chan);
04415 
04416    memset(&bridge_config, 0, sizeof(bridge_config));
04417    tmpid[0] = 0;
04418    meid[0] = 0;
04419    time(&now);
04420 
04421    /* If we've already exceeded our timeout, then just stop
04422     * This should be extremely rare. queue_exec will take care
04423     * of removing the caller and reporting the timeout as the reason.
04424     */
04425    if (qe->expire && now >= qe->expire) {
04426       res = 0;
04427       goto out;
04428    }
04429       
04430    for (; options && *options; options++)
04431       switch (*options) {
04432       case 't':
04433          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
04434          break;
04435       case 'T':
04436          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
04437          break;
04438       case 'w':
04439          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
04440          break;
04441       case 'W':
04442          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
04443          break;
04444       case 'c':
04445          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
04446          break;
04447       case 'd':
04448          nondataquality = 0;
04449          break;
04450       case 'h':
04451          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
04452          break;
04453       case 'H':
04454          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
04455          break;
04456       case 'k':
04457          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
04458          break;
04459       case 'K':
04460          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
04461          break;
04462       case 'n':
04463          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
04464             (*tries)++;
04465          else
04466             *tries = ao2_container_count(qe->parent->members);
04467          *noption = 1;
04468          break;
04469       case 'i':
04470          forwardsallowed = 0;
04471          break;
04472       case 'I':
04473          block_connected_line = 1;
04474          break;
04475       case 'x':
04476          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
04477          break;
04478       case 'X':
04479          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
04480          break;
04481       case 'C':
04482          qe->cancel_answered_elsewhere = 1;
04483          break;
04484       }
04485 
04486    /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 
04487       (this is mainly to support chan_local)
04488    */
04489    if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
04490       qe->cancel_answered_elsewhere = 1;
04491    }
04492 
04493    ao2_lock(qe->parent);
04494    ast_debug(1, "%s is trying to call a queue member.\n",
04495                      qe->chan->name);
04496    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
04497    if (!ast_strlen_zero(qe->announce))
04498       announce = qe->announce;
04499    if (!ast_strlen_zero(announceoverride))
04500       announce = announceoverride;
04501 
04502    memi = ao2_iterator_init(qe->parent->members, 0);
04503    while ((cur = ao2_iterator_next(&memi))) {
04504       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
04505       struct ast_dialed_interface *di;
04506       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
04507       if (!tmp) {
04508          ao2_ref(cur, -1);
04509          ao2_iterator_destroy(&memi);
04510          ao2_unlock(qe->parent);
04511          goto out;
04512       }
04513       if (!datastore) {
04514          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
04515             callattempt_free(tmp);
04516             ao2_ref(cur, -1);
04517             ao2_iterator_destroy(&memi);
04518             ao2_unlock(qe->parent);
04519             goto out;
04520          }
04521          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
04522          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
04523             callattempt_free(tmp);
04524             ao2_ref(cur, -1);
04525             ao2_iterator_destroy(&memi);
04526             ao2_unlock(&qe->parent);
04527             goto out;
04528          }
04529          datastore->data = dialed_interfaces;
04530          AST_LIST_HEAD_INIT(dialed_interfaces);
04531 
04532          ast_channel_lock(qe->chan);
04533          ast_channel_datastore_add(qe->chan, datastore);
04534          ast_channel_unlock(qe->chan);
04535       } else
04536          dialed_interfaces = datastore->data;
04537 
04538       AST_LIST_LOCK(dialed_interfaces);
04539       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
04540          if (!strcasecmp(cur->interface, di->interface)) {
04541             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
04542                di->interface);
04543             break;
04544          }
04545       }
04546       AST_LIST_UNLOCK(dialed_interfaces);
04547 
04548       if (di) {
04549          callattempt_free(tmp);
04550          ao2_ref(cur, -1);
04551          continue;
04552       }
04553 
04554       /* It is always ok to dial a Local interface.  We only keep track of
04555        * which "real" interfaces have been dialed.  The Local channel will
04556        * inherit this list so that if it ends up dialing a real interface,
04557        * it won't call one that has already been called. */
04558       if (strncasecmp(cur->interface, "Local/", 6)) {
04559          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
04560             callattempt_free(tmp);
04561             ao2_ref(cur, -1);
04562             ao2_iterator_destroy(&memi);
04563             ao2_unlock(qe->parent);
04564             goto out;
04565          }
04566          strcpy(di->interface, cur->interface);
04567 
04568          AST_LIST_LOCK(dialed_interfaces);
04569          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
04570          AST_LIST_UNLOCK(dialed_interfaces);
04571       }
04572 
04573       /*
04574        * Seed the callattempt's connected line information with previously
04575        * acquired connected line info from the queued channel.  The
04576        * previously acquired connected line info could have been set
04577        * through the CONNECTED_LINE dialplan function.
04578        */
04579       ast_channel_lock(qe->chan);
04580       ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected);
04581       ast_channel_unlock(qe->chan);
04582 
04583       tmp->block_connected_update = block_connected_line;
04584       tmp->stillgoing = 1;
04585       tmp->member = cur;/* Place the reference for cur into callattempt. */
04586       tmp->lastcall = cur->lastcall;
04587       tmp->lastqueue = cur->lastqueue;
04588       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
04589       /* Special case: If we ring everyone, go ahead and ring them, otherwise
04590          just calculate their metric for the appropriate strategy */
04591       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
04592          /* Put them in the list of outgoing thingies...  We're ready now.
04593             XXX If we're forcibly removed, these outgoing calls won't get
04594             hung up XXX */
04595          tmp->q_next = outgoing;
04596          outgoing = tmp;      
04597          /* If this line is up, don't try anybody else */
04598          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
04599             break;
04600       } else {
04601          callattempt_free(tmp);
04602       }
04603    }
04604    ao2_iterator_destroy(&memi);
04605 
04606    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
04607       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
04608       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
04609          to = (qe->expire - now) * 1000;
04610       else
04611          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
04612    } else {
04613       /* Config timeout is higher priority thatn application timeout */
04614       if (qe->expire && qe->expire<=now) {
04615          to = 0;
04616       } else if (qe->parent->timeout) {
04617          to = qe->parent->timeout * 1000;
04618       } else {
04619          to = -1;
04620       }
04621    }
04622    orig = to;
04623    ++qe->pending;
04624    ao2_unlock(qe->parent);
04625    ring_one(qe, outgoing, &numbusies);
04626    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
04627       ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
04628       forwardsallowed);
04629    /* The ast_channel_datastore_remove() function could fail here if the
04630     * datastore was moved to another channel during a masquerade. If this is
04631     * the case, don't free the datastore here because later, when the channel
04632     * to which the datastore was moved hangs up, it will attempt to free this
04633     * datastore again, causing a crash
04634     */
04635    ast_channel_lock(qe->chan);
04636    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
04637       ast_datastore_free(datastore);
04638    }
04639    ast_channel_unlock(qe->chan);
04640    ao2_lock(qe->parent);
04641    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
04642       store_next_rr(qe, outgoing);
04643 
04644    }
04645    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
04646       store_next_lin(qe, outgoing);
04647    }
04648    ao2_unlock(qe->parent);
04649    peer = lpeer ? lpeer->chan : NULL;
04650    if (!peer) {
04651       qe->pending = 0;
04652       if (to) {
04653          /* Must gotten hung up */
04654          res = -1;
04655       } else {
04656          /* User exited by pressing a digit */
04657          res = digit;
04658       }
04659       if (res == -1)
04660          ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
04661       if (ast_cdr_isset_unanswered()) {
04662          /* channel contains the name of one of the outgoing channels
04663             in its CDR; zero out this CDR to avoid a dual-posting */
04664          struct callattempt *o;
04665          for (o = outgoing; o; o = o->q_next) {
04666             if (!o->chan) {
04667                continue;
04668             }
04669             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
04670                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
04671                break;
04672             }
04673          }
04674       }
04675    } else { /* peer is valid */
04676       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
04677          we will always return with -1 so that it is hung up properly after the
04678          conversation.  */
04679       if (!strcmp(qe->chan->tech->type, "DAHDI"))
04680          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04681       if (!strcmp(peer->tech->type, "DAHDI"))
04682          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04683       /* Update parameters for the queue */
04684       time(&now);
04685       recalc_holdtime(qe, (now - qe->start));
04686       ao2_lock(qe->parent);
04687       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
04688       ao2_unlock(qe->parent);
04689       member = lpeer->member;
04690       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
04691       ao2_ref(member, 1);
04692       hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
04693       outgoing = NULL;
04694       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
04695          int res2;
04696 
04697          res2 = ast_autoservice_start(qe->chan);
04698          if (!res2) {
04699             if (qe->parent->memberdelay) {
04700                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
04701                res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
04702             }
04703             if (!res2 && announce) {
04704                if (play_file(peer, announce) < 0) {
04705                   ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, peer->name);
04706                }
04707             }
04708             if (!res2 && qe->parent->reportholdtime) {
04709                if (!play_file(peer, qe->parent->sound_reporthold)) {
04710                   int holdtime, holdtimesecs;
04711 
04712                   time(&now);
04713                   holdtime = abs((now - qe->start) / 60);
04714                   holdtimesecs = abs((now - qe->start) % 60);
04715                   if (holdtime > 0) {
04716                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
04717                      if (play_file(peer, qe->parent->sound_minutes) < 0) {
04718                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, peer->name);
04719                      }
04720                   }
04721                   if (holdtimesecs > 1) {
04722                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
04723                      if (play_file(peer, qe->parent->sound_seconds) < 0) {
04724                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, peer->name);
04725                      }
04726                   }
04727                }
04728             }
04729             ast_autoservice_stop(qe->chan);
04730          }
04731          if (ast_check_hangup(peer)) {
04732             /* Agent must have hung up */
04733             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
04734             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
04735             if (qe->parent->eventwhencalled)
04736                manager_event(EVENT_FLAG_AGENT, "AgentDump",
04737                      "Queue: %s\r\n"
04738                      "Uniqueid: %s\r\n"
04739                      "Channel: %s\r\n"
04740                      "Member: %s\r\n"
04741                      "MemberName: %s\r\n"
04742                      "%s",
04743                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04744                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04745             ast_hangup(peer);
04746             ao2_ref(member, -1);
04747             goto out;
04748          } else if (ast_check_hangup(qe->chan)) {
04749             /* Caller must have hung up just before being connected */
04750             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
04751             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04752             record_abandoned(qe);
04753             ast_hangup(peer);
04754             ao2_ref(member, -1);
04755             return -1;
04756          }
04757       }
04758       /* Stop music on hold */
04759       if (ringing)
04760          ast_indicate(qe->chan,-1);
04761       else
04762          ast_moh_stop(qe->chan);
04763       /* If appropriate, log that we have a destination channel */
04764       if (qe->chan->cdr)
04765          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
04766       /* Make sure channels are compatible */
04767       res = ast_channel_make_compatible(qe->chan, peer);
04768       if (res < 0) {
04769          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
04770          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
04771          record_abandoned(qe);
04772          ast_cdr_failed(qe->chan->cdr);
04773          ast_hangup(peer);
04774          ao2_ref(member, -1);
04775          return -1;
04776       }
04777 
04778       /* Play announcement to the caller telling it's his turn if defined */
04779       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
04780          if (play_file(qe->chan, qe->parent->sound_callerannounce))
04781             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
04782       }
04783 
04784       ao2_lock(qe->parent);
04785       /* if setinterfacevar is defined, make member variables available to the channel */
04786       /* use  pbx_builtin_setvar to set a load of variables with one call */
04787       if (qe->parent->setinterfacevar) {
04788          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
04789             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
04790          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04791          pbx_builtin_setvar_multiple(peer, interfacevar);
04792       }
04793       
04794       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
04795       /* use  pbx_builtin_setvar to set a load of variables with one call */
04796       if (qe->parent->setqueueentryvar) {
04797          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
04798             (long) time(NULL) - qe->start, qe->opos);
04799          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04800          pbx_builtin_setvar_multiple(peer, interfacevar);
04801       }
04802    
04803       ao2_unlock(qe->parent);
04804 
04805       /* try to set queue variables if configured to do so*/
04806       set_queue_variables(qe->parent, qe->chan);
04807       set_queue_variables(qe->parent, peer);
04808       
04809       ast_channel_lock(qe->chan);
04810       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
04811             monitorfilename = ast_strdupa(monitorfilename);
04812       }
04813       ast_channel_unlock(qe->chan);
04814       /* Begin Monitoring */
04815       if (qe->parent->monfmt && *qe->parent->monfmt) {
04816          if (!qe->parent->montype) {
04817             const char *monexec;
04818             ast_debug(1, "Starting Monitor as requested.\n");
04819             ast_channel_lock(qe->chan);
04820             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) {
04821                which = qe->chan;
04822                monexec = monexec ? ast_strdupa(monexec) : NULL;
04823             }
04824             else
04825                which = peer;
04826             ast_channel_unlock(qe->chan);
04827             if (monitorfilename) {
04828                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
04829             } else if (qe->chan->cdr) {
04830                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
04831             } else {
04832                /* Last ditch effort -- no CDR, make up something */
04833                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04834                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
04835             }
04836             if (!ast_strlen_zero(monexec)) {
04837                ast_monitor_setjoinfiles(which, 1);
04838             }
04839          } else {
04840             mixmonapp = pbx_findapp("MixMonitor");
04841             
04842             if (mixmonapp) {
04843                ast_debug(1, "Starting MixMonitor as requested.\n");
04844                if (!monitorfilename) {
04845                   if (qe->chan->cdr)
04846                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
04847                   else
04848                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04849                } else {
04850                   const char *m = monitorfilename;
04851                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
04852                      switch (*m) {
04853                      case '^':
04854                         if (*(m + 1) == '{')
04855                            *p = '$';
04856                         break;
04857                      case ',':
04858                         *p++ = '\\';
04859                         /* Fall through */
04860                      default:
04861                         *p = *m;
04862                      }
04863                      if (*m == '\0')
04864                         break;
04865                   }
04866                   if (p == tmpid2 + sizeof(tmpid2))
04867                      tmpid2[sizeof(tmpid2) - 1] = '\0';
04868 
04869                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
04870                }
04871 
04872                ast_channel_lock(qe->chan);
04873                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
04874                      monitor_exec = ast_strdupa(monitor_exec);
04875                }
04876                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
04877                      monitor_options = ast_strdupa(monitor_options);
04878                } else {
04879                   monitor_options = "";
04880                }
04881                ast_channel_unlock(qe->chan);
04882 
04883                if (monitor_exec) {
04884                   const char *m = monitor_exec;
04885                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
04886                      switch (*m) {
04887                      case '^':
04888                         if (*(m + 1) == '{')
04889                            *p = '$';
04890                         break;
04891                      case ',':
04892                         *p++ = '\\';
04893                         /* Fall through */
04894                      default:
04895                         *p = *m;
04896                      }
04897                      if (*m == '\0')
04898                         break;
04899                   }
04900                   if (p == meid2 + sizeof(meid2))
04901                      meid2[sizeof(meid2) - 1] = '\0';
04902 
04903                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
04904                }
04905    
04906                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
04907 
04908                if (!ast_strlen_zero(monitor_exec))
04909                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
04910                else
04911                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
04912                
04913                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
04914                /* We purposely lock the CDR so that pbx_exec does not update the application data */
04915                if (qe->chan->cdr)
04916                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04917                pbx_exec(qe->chan, mixmonapp, mixmonargs);
04918                if (qe->chan->cdr)
04919                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04920 
04921             } else {
04922                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
04923             }
04924          }
04925       }
04926       /* Drop out of the queue at this point, to prepare for next caller */
04927       leave_queue(qe);        
04928       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
04929          ast_debug(1, "app_queue: sendurl=%s.\n", url);
04930          ast_channel_sendurl(peer, url);
04931       }
04932       
04933       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
04934       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
04935       if (!ast_strlen_zero(macro)) {
04936             macroexec = ast_strdupa(macro);
04937       } else {
04938          if (qe->parent->membermacro)
04939             macroexec = ast_strdupa(qe->parent->membermacro);
04940       }
04941 
04942       if (!ast_strlen_zero(macroexec)) {
04943          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
04944          
04945          res = ast_autoservice_start(qe->chan);
04946          if (res) {
04947             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04948             res = -1;
04949          }
04950          
04951          application = pbx_findapp("Macro");
04952 
04953          if (application) {
04954             res = pbx_exec(peer, application, macroexec);
04955             ast_debug(1, "Macro exited with status %d\n", res);
04956             res = 0;
04957          } else {
04958             ast_log(LOG_ERROR, "Could not find application Macro\n");
04959             res = -1;
04960          }
04961 
04962          if (ast_autoservice_stop(qe->chan) < 0) {
04963             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04964             res = -1;
04965          }
04966       }
04967 
04968       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
04969       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
04970       if (!ast_strlen_zero(gosub)) {
04971             gosubexec = ast_strdupa(gosub);
04972       } else {
04973          if (qe->parent->membergosub)
04974             gosubexec = ast_strdupa(qe->parent->membergosub);
04975       }
04976 
04977       if (!ast_strlen_zero(gosubexec)) {
04978          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
04979          
04980          res = ast_autoservice_start(qe->chan);
04981          if (res) {
04982             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04983             res = -1;
04984          }
04985          
04986          application = pbx_findapp("Gosub");
04987          
04988          if (application) {
04989             char *gosub_args, *gosub_argstart;
04990 
04991             /* Set where we came from */
04992             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
04993             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
04994             peer->priority = 0;
04995 
04996             gosub_argstart = strchr(gosubexec, ',');
04997             if (gosub_argstart) {
04998                const char *what_is_s = "s";
04999                *gosub_argstart = 0;
05000                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05001                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05002                   what_is_s = "~~s~~";
05003                }
05004                if (asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
05005                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
05006                   gosub_args = NULL;
05007                }
05008                *gosub_argstart = ',';
05009             } else {
05010                const char *what_is_s = "s";
05011                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05012                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05013                   what_is_s = "~~s~~";
05014                }
05015                if (asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
05016                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
05017                   gosub_args = NULL;
05018                }
05019             }
05020             if (gosub_args) {
05021                res = pbx_exec(peer, application, gosub_args);
05022                if (!res) {
05023                   struct ast_pbx_args args;
05024                   memset(&args, 0, sizeof(args));
05025                   args.no_hangup_chan = 1;
05026                   ast_pbx_run_args(peer, &args);
05027                }
05028                ast_free(gosub_args);
05029                ast_debug(1, "Gosub exited with status %d\n", res);
05030             } else {
05031                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
05032             }
05033          } else {
05034             ast_log(LOG_ERROR, "Could not find application Gosub\n");
05035             res = -1;
05036          }
05037       
05038          if (ast_autoservice_stop(qe->chan) < 0) {
05039             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05040             res = -1;
05041          }
05042       }
05043 
05044       if (!ast_strlen_zero(agi)) {
05045          ast_debug(1, "app_queue: agi=%s.\n", agi);
05046          application = pbx_findapp("agi");
05047          if (application) {
05048             agiexec = ast_strdupa(agi);
05049             pbx_exec(qe->chan, application, agiexec);
05050          } else
05051             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
05052       }
05053       qe->handled++;
05054       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
05055                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
05056 
05057       if (qe->chan->cdr) {
05058          struct ast_cdr *cdr;
05059          struct ast_cdr *newcdr;
05060 
05061          /* Only work with the last CDR in the stack*/
05062          cdr = qe->chan->cdr;
05063          while (cdr->next) {
05064             cdr = cdr->next;
05065          }
05066 
05067          /* If this CDR is not related to us add new one*/
05068          if ((strcasecmp(cdr->uniqueid, qe->chan->uniqueid)) &&
05069              (strcasecmp(cdr->linkedid, qe->chan->uniqueid)) &&
05070              (newcdr = ast_cdr_dup(cdr))) {
05071             ast_channel_lock(qe->chan);
05072             ast_cdr_init(newcdr, qe->chan);
05073             ast_cdr_reset(newcdr, 0);
05074             cdr = ast_cdr_append(cdr, newcdr);
05075             cdr = cdr->next;
05076             ast_channel_unlock(qe->chan);
05077          }
05078 
05079          if (update_cdr) {
05080             ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
05081          }
05082       }
05083 
05084       if (qe->parent->eventwhencalled)
05085          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
05086                "Queue: %s\r\n"
05087                "Uniqueid: %s\r\n"
05088                "Channel: %s\r\n"
05089                "Member: %s\r\n"
05090                "MemberName: %s\r\n"
05091                "Holdtime: %ld\r\n"
05092                "BridgedChannel: %s\r\n"
05093                "Ringtime: %ld\r\n"
05094                "%s",
05095                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
05096                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
05097                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
05098       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
05099       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
05100    
05101       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
05102          queue_end_bridge->q = qe->parent;
05103          queue_end_bridge->chan = qe->chan;
05104          bridge_config.end_bridge_callback = end_bridge_callback;
05105          bridge_config.end_bridge_callback_data = queue_end_bridge;
05106          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
05107          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
05108           * to make sure to increase the refcount of this queue so it cannot be freed until we
05109           * are done with it. We remove this reference in end_bridge_callback.
05110           */
05111          queue_t_ref(qe->parent, "For bridge_config reference");
05112       }
05113 
05114       time(&callstart);
05115       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
05116       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
05117 
05118       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
05119        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
05120        * the AgentComplete manager event
05121        */
05122       ast_channel_lock(qe->chan);
05123       if (!attended_transfer_occurred(qe->chan)) {
05124          struct ast_datastore *tds;
05125 
05126          /* detect a blind transfer */
05127          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
05128             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
05129                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
05130                (long) (time(NULL) - callstart), qe->opos);
05131             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05132          } else if (ast_check_hangup(qe->chan)) {
05133             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
05134                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05135             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
05136          } else {
05137             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
05138                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05139             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
05140          }
05141          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
05142             ast_channel_datastore_remove(qe->chan, tds);
05143          }
05144          ast_channel_unlock(qe->chan);
05145          update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
05146       } else {
05147          ast_channel_unlock(qe->chan);
05148 
05149          /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
05150          send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05151       }
05152 
05153       if (transfer_ds) {
05154          ast_datastore_free(transfer_ds);
05155       }
05156       ast_hangup(peer);
05157       res = bridge ? bridge : 1;
05158       ao2_ref(member, -1);
05159    }
05160 out:
05161    hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
05162 
05163    return res;
05164 }

static int unload_module ( void   )  [static]

Definition at line 8341 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_data_unregister, ast_event_unsubscribe(), ast_extension_state_del(), ast_manager_unregister(), ast_taskprocessor_unreference(), ast_unload_realtime(), ast_unregister_application(), cli_queue, device_state_sub, devicestate_tps, extension_state_cb(), queue_t_unref, queueexists_function, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queues_t_unlink, queuevar_function, and queuewaitingcount_function.

08342 {
08343    int res;
08344    struct ast_context *con;
08345    struct ao2_iterator q_iter;
08346    struct call_queue *q = NULL;
08347 
08348    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08349    res = ast_manager_unregister("QueueStatus");
08350    res |= ast_manager_unregister("Queues");
08351    res |= ast_manager_unregister("QueueRule");
08352    res |= ast_manager_unregister("QueueSummary");
08353    res |= ast_manager_unregister("QueueAdd");
08354    res |= ast_manager_unregister("QueueRemove");
08355    res |= ast_manager_unregister("QueuePause");
08356    res |= ast_manager_unregister("QueueLog");
08357    res |= ast_manager_unregister("QueuePenalty");
08358    res |= ast_unregister_application(app_aqm);
08359    res |= ast_unregister_application(app_rqm);
08360    res |= ast_unregister_application(app_pqm);
08361    res |= ast_unregister_application(app_upqm);
08362    res |= ast_unregister_application(app_ql);
08363    res |= ast_unregister_application(app);
08364    res |= ast_custom_function_unregister(&queueexists_function);
08365    res |= ast_custom_function_unregister(&queuevar_function);
08366    res |= ast_custom_function_unregister(&queuemembercount_function);
08367    res |= ast_custom_function_unregister(&queuemembercount_dep);
08368    res |= ast_custom_function_unregister(&queuememberlist_function);
08369    res |= ast_custom_function_unregister(&queuewaitingcount_function);
08370    res |= ast_custom_function_unregister(&queuememberpenalty_function);
08371 
08372    res |= ast_data_unregister(NULL);
08373 
08374    if (device_state_sub)
08375       ast_event_unsubscribe(device_state_sub);
08376 
08377    ast_extension_state_del(0, extension_state_cb);
08378 
08379    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08380       ast_context_remove_extension2(con, "s", 1, NULL, 0);
08381       ast_context_destroy(con, "app_queue"); /* leave no trace */
08382    }
08383 
08384    q_iter = ao2_iterator_init(queues, 0);
08385    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08386       queues_t_unlink(queues, q, "Remove queue from container due to unload");
08387       queue_t_unref(q, "Done with iterator");
08388    }
08389    ao2_iterator_destroy(&q_iter);
08390    ao2_ref(queues, -1);
08391    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08392    ast_unload_realtime("queue_members");
08393    return res;
08394 }

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

03954 {
03955    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
03956    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
03957    char max_penalty_str[20], min_penalty_str[20]; 
03958    /* a relative change to the penalty could put it below 0 */
03959    if (max_penalty < 0)
03960       max_penalty = 0;
03961    if (min_penalty < 0)
03962       min_penalty = 0;
03963    if (min_penalty > max_penalty)
03964       min_penalty = max_penalty;
03965    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
03966    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
03967    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
03968    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
03969    qe->max_penalty = max_penalty;
03970    qe->min_penalty = min_penalty;
03971    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);
03972    qe->pr = AST_LIST_NEXT(qe->pr, list);
03973 }

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

04062 {
04063    int oldtalktime;
04064 
04065    struct member *mem;
04066    struct call_queue *qtmp;
04067    struct ao2_iterator queue_iter;  
04068    
04069    if (shared_lastcall) {
04070       queue_iter = ao2_iterator_init(queues, 0);
04071       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04072          ao2_lock(qtmp);
04073          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04074             time(&mem->lastcall);
04075             mem->calls++;
04076             mem->lastqueue = q;
04077             ao2_ref(mem, -1);
04078          }
04079          ao2_unlock(qtmp);
04080          queue_t_unref(qtmp, "Done with iterator");
04081       }
04082       ao2_iterator_destroy(&queue_iter);
04083    } else {
04084       ao2_lock(q);
04085       time(&member->lastcall);
04086       member->calls++;
04087       member->lastqueue = q;
04088       ao2_unlock(q);
04089    }  
04090    ao2_lock(q);
04091    q->callscompleted++;
04092    if (callcompletedinsl)
04093       q->callscompletedinsl++;
04094    /* Calculate talktime using the same exponential average as holdtime code*/
04095    oldtalktime = q->talktime;
04096    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04097    ao2_unlock(q);
04098    return 0;
04099 }

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

Definition at line 2389 of file app_queue.c.

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

Referenced by set_member_paused().

02390 {
02391    int ret = -1;
02392 
02393    if (ast_strlen_zero(mem->rt_uniqueid))
02394       return ret;
02395 
02396    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02397       ret = 0;
02398 
02399    return ret;
02400 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 2403 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::members, call_queue::name, member::realtime, rt_handle_member_record(), S_OR, and SENTINEL.

Referenced by load_realtime_queue(), and queue_exec().

02404 {
02405    struct ast_config *member_config = NULL;
02406    struct member *m;
02407    char *interface = NULL;
02408    struct ao2_iterator mem_iter;
02409 
02410    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02411       /*This queue doesn't have realtime members*/
02412       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02413       return;
02414    }
02415 
02416    ao2_lock(q);
02417 
02418    /* Temporarily set realtime  members dead so we can detect deleted ones.*/
02419    mem_iter = ao2_iterator_init(q->members, 0);
02420    while ((m = ao2_iterator_next(&mem_iter))) {
02421       if (m->realtime)
02422          m->dead = 1;
02423       ao2_ref(m, -1);
02424    }
02425    ao2_iterator_destroy(&mem_iter);
02426 
02427    while ((interface = ast_category_browse(member_config, interface))) {
02428       rt_handle_member_record(q, interface,
02429          ast_variable_retrieve(member_config, interface, "uniqueid"),
02430          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
02431          ast_variable_retrieve(member_config, interface, "penalty"),
02432          ast_variable_retrieve(member_config, interface, "paused"),
02433          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
02434    }
02435 
02436    /* Delete all realtime members that have been deleted in DB. */
02437    mem_iter = ao2_iterator_init(q->members, 0);
02438    while ((m = ao2_iterator_next(&mem_iter))) {
02439       if (m->dead) {
02440          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02441          ao2_unlink(q->members, m);
02442       }
02443       ao2_ref(m, -1);
02444    }
02445    ao2_iterator_destroy(&mem_iter);
02446    ao2_unlock(q);
02447    ast_config_destroy(member_config);
02448 }

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

References EVENT_FLAG_AGENT, and manager_event.

Referenced by extension_state_cb(), handle_statechange(), and ring_entry().

01428 {
01429    m->status = status;
01430 
01431    if (q->maskmemberstatus)
01432       return 0;
01433 
01434    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01435       "Queue: %s\r\n"
01436       "Location: %s\r\n"
01437       "MemberName: %s\r\n"
01438       "Membership: %s\r\n"
01439       "Penalty: %d\r\n"
01440       "CallsTaken: %d\r\n"
01441       "LastCall: %d\r\n"
01442       "Status: %d\r\n"
01443       "Paused: %d\r\n",
01444       q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01445       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01446    );
01447 
01448    return 0;
01449 }

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

UnPauseQueueMember application.

Definition at line 5638 of file app_queue.c.

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

Referenced by load_module().

05639 {
05640    char *parse;
05641    AST_DECLARE_APP_ARGS(args,
05642       AST_APP_ARG(queuename);
05643       AST_APP_ARG(interface);
05644       AST_APP_ARG(options);
05645       AST_APP_ARG(reason);
05646    );
05647 
05648    if (ast_strlen_zero(data)) {
05649       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05650       return -1;
05651    }
05652 
05653    parse = ast_strdupa(data);
05654 
05655    AST_STANDARD_APP_ARGS(args, parse);
05656 
05657    if (ast_strlen_zero(args.interface)) {
05658       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05659       return -1;
05660    }
05661 
05662    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05663       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05664       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05665       return 0;
05666    }
05667 
05668    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05669 
05670    return 0;
05671 }

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

References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), ast_channel::caller, queue_ent::chan, queue_ent::context, queue_ent::digits, ast_party_caller::id, ast_party_id::number, S_COR, ast_party_number::str, ast_party_number::valid, and queue_ent::valid_digits.

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

02566 {
02567    int digitlen = strlen(qe->digits);
02568 
02569    /* Prevent possible buffer overflow */
02570    if (digitlen < sizeof(qe->digits) - 2) {
02571       qe->digits[digitlen] = digit;
02572       qe->digits[digitlen + 1] = '\0';
02573    } else {
02574       qe->digits[0] = '\0';
02575       return 0;
02576    }
02577 
02578    /* If there's no context to goto, short-circuit */
02579    if (ast_strlen_zero(qe->context))
02580       return 0;
02581 
02582    /* If the extension is bad, then reset the digits to blank */
02583    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02584       S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02585       qe->digits[0] = '\0';
02586       return 0;
02587    }
02588 
02589    /* We have an exact match */
02590    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02591       qe->valid_digits = 1;
02592       /* Return 1 on a successful goto */
02593       return 1;
02594    }
02595 
02596    return 0;
02597 }

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

References ast_copy_string(), ast_str_buffer(), ast_str_thread_get(), queue_ent::chan, and pbx_builtin_serialize_variables().

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

02954 {
02955    struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
02956    const char *tmp;
02957 
02958    if (pbx_builtin_serialize_variables(chan, &buf)) {
02959       int i, j;
02960 
02961       /* convert "\n" to "\nVariable: " */
02962       strcpy(vars, "Variable: ");
02963       tmp = ast_str_buffer(buf);
02964 
02965       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02966          vars[j] = tmp[i];
02967 
02968          if (tmp[i + 1] == '\0')
02969             break;
02970          if (tmp[i] == '\n') {
02971             vars[j++] = '\r';
02972             vars[j++] = '\n';
02973 
02974             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02975             j += 9;
02976          }
02977       }
02978       if (j > len - 3)
02979          j = len - 3;
02980       vars[j++] = '\r';
02981       vars[j++] = '\n';
02982       vars[j] = '\0';
02983    } else {
02984       /* there are no channel variables; leave it blank */
02985       *vars = '\0';
02986    }
02987    return vars;
02988 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 5166 of file app_queue.c.

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

Referenced by queue_exec().

05167 {
05168    /* Don't need to hold the lock while we setup the outgoing calls */
05169    int retrywait = qe->parent->retry * 1000;
05170 
05171    int res = ast_waitfordigit(qe->chan, retrywait);
05172    if (res > 0 && !valid_exit(qe, res))
05173       res = 0;
05174 
05175    return res;
05176 }

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

Wait for a member to answer the call.

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

Todo:
XXX Queue like Dial really should send any connected line updates (AST_CONTROL_CONNECTED_LINE) from the caller to each ringing queue member.

Definition at line 3434 of file app_queue.c.

References ast_channel_lock, ast_channel_unlock, AST_MAX_WATCHERS, ast_party_connected_line_init(), ast_poll_channel_add(), ast_strdupa, callattempt::call_next, callattempt::chan, queue_ent::chan, f, ast_channel::name, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), status, callattempt::stillgoing, and call_queue::strategy.

03435 {
03436    const char *queue = qe->parent->name;
03437    struct callattempt *o, *start = NULL, *prev = NULL;
03438    int status;
03439    int numbusies = prebusies;
03440    int numnochan = 0;
03441    int stillgoing = 0;
03442    int orig = *to;
03443    struct ast_frame *f;
03444    struct callattempt *peer = NULL;
03445    struct ast_channel *winner;
03446    struct ast_channel *in = qe->chan;
03447    char on[80] = "";
03448    char membername[80] = "";
03449    long starttime = 0;
03450    long endtime = 0;
03451 #ifdef HAVE_EPOLL
03452    struct callattempt *epollo;
03453 #endif
03454    struct ast_party_connected_line connected_caller;
03455    char *inchan_name;
03456 
03457    ast_party_connected_line_init(&connected_caller);
03458 
03459    ast_channel_lock(qe->chan);
03460    inchan_name = ast_strdupa(qe->chan->name);
03461    ast_channel_unlock(qe->chan);
03462 
03463    starttime = (long) time(NULL);
03464 #ifdef HAVE_EPOLL
03465    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03466       if (epollo->chan)
03467          ast_poll_channel_add(in, epollo->chan);
03468    }
03469 #endif
03470    
03471    while (*to && !peer) {
03472       int numlines, retry, pos = 1;
03473       struct ast_channel *watchers[AST_MAX_WATCHERS];
03474       watchers[0] = in;
03475       start = NULL;
03476 
03477       for (retry = 0; retry < 2; retry++) {
03478          numlines = 0;
03479          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
03480             if (o->stillgoing) { /* Keep track of important channels */
03481                stillgoing = 1;
03482                if (o->chan) {
03483                   if (pos < AST_MAX_WATCHERS) {
03484                      watchers[pos++] = o->chan;
03485                   }
03486                   if (!start)
03487                      start = o;
03488                   else
03489                      prev->call_next = o;
03490                   prev = o;
03491                }
03492             }
03493             numlines++;
03494          }
03495          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
03496             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
03497             break;
03498          /* On "ringall" strategy we only move to the next penalty level
03499             when *all* ringing phones are done in the current penalty level */
03500          ring_one(qe, outgoing, &numbusies);
03501          /* and retry... */
03502       }
03503       if (pos == 1 /* not found */) {
03504          if (numlines == (numbusies + numnochan)) {
03505             ast_debug(1, "Everyone is busy at this time\n");
03506          } else {
03507             ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
03508          }
03509          *to = 0;
03510          return NULL;
03511       }
03512 
03513       /* Poll for events from both the incoming channel as well as any outgoing channels */
03514       winner = ast_waitfor_n(watchers, pos, to);
03515 
03516       /* Service all of the outgoing channels */
03517       for (o = start; o; o = o->call_next) {
03518          /* We go with a fixed buffer here instead of using ast_strdupa. Using
03519           * ast_strdupa in a loop like this one can cause a stack overflow
03520           */
03521          char ochan_name[AST_CHANNEL_NAME];
03522 
03523          if (o->chan) {
03524             ast_channel_lock(o->chan);
03525             ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name));
03526             ast_channel_unlock(o->chan);
03527          }
03528          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
03529             if (!peer) {
03530                ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03531                if (!o->block_connected_update) {
03532                   if (o->pending_connected_update) {
03533                      if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03534                         ast_channel_update_connected_line(in, &o->connected, NULL);
03535                      }
03536                   } else if (!o->dial_callerid_absent) {
03537                      ast_channel_lock(o->chan);
03538                      ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03539                      ast_channel_unlock(o->chan);
03540                      connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03541                      if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
03542                         ast_channel_update_connected_line(in, &connected_caller, NULL);
03543                      }
03544                      ast_party_connected_line_free(&connected_caller);
03545                   }
03546                }
03547                if (o->aoc_s_rate_list) {
03548                   size_t encoded_size;
03549                   struct ast_aoc_encoded *encoded;
03550                   if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03551                      ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03552                      ast_aoc_destroy_encoded(encoded);
03553                   }
03554                }
03555                peer = o;
03556             }
03557          } else if (o->chan && (o->chan == winner)) {
03558 
03559             ast_copy_string(on, o->member->interface, sizeof(on));
03560             ast_copy_string(membername, o->member->membername, sizeof(membername));
03561 
03562             /* Before processing channel, go ahead and check for forwarding */
03563             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
03564                ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward);
03565                numnochan++;
03566                do_hang(o);
03567                winner = NULL;
03568                continue;
03569             } else if (!ast_strlen_zero(o->chan->call_forward)) {
03570                struct ast_channel *original = o->chan;
03571                char tmpchan[256];
03572                char *stuff;
03573                char *tech;
03574 
03575                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
03576                if ((stuff = strchr(tmpchan, '/'))) {
03577                   *stuff++ = '\0';
03578                   tech = tmpchan;
03579                } else {
03580                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
03581                   stuff = tmpchan;
03582                   tech = "Local";
03583                }
03584                if (!strcasecmp(tech, "Local")) {
03585                   /*
03586                    * Drop the connected line update block for local channels since
03587                    * this is going to run dialplan and the user can change his
03588                    * mind about what connected line information he wants to send.
03589                    */
03590                   o->block_connected_update = 0;
03591                }
03592 
03593                ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
03594 
03595                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03596                /* Setup parameters */
03597                o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03598                if (!o->chan) {
03599                   ast_log(LOG_NOTICE,
03600                      "Forwarding failed to create channel to dial '%s/%s'\n",
03601                      tech, stuff);
03602                   o->stillgoing = 0;
03603                   numnochan++;
03604                } else {
03605                   ast_channel_lock_both(o->chan, original);
03606                   ast_party_redirecting_copy(&o->chan->redirecting, &original->redirecting);
03607                   ast_channel_unlock(o->chan);
03608                   ast_channel_unlock(original);
03609 
03610                   ast_channel_lock_both(o->chan, in);
03611                   ast_channel_inherit_variables(in, o->chan);
03612                   ast_channel_datastore_inherit(in, o->chan);
03613 
03614                   if (o->pending_connected_update) {
03615                      /*
03616                       * Re-seed the callattempt's connected line information with
03617                       * previously acquired connected line info from the queued
03618                       * channel.  The previously acquired connected line info could
03619                       * have been set through the CONNECTED_LINE dialplan function.
03620                       */
03621                      o->pending_connected_update = 0;
03622                      ast_party_connected_line_copy(&o->connected, &in->connected);
03623                   }
03624 
03625                   ast_string_field_set(o->chan, accountcode, in->accountcode);
03626 
03627                   if (!o->chan->redirecting.from.number.valid
03628                      || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03629                      /*
03630                       * The call was not previously redirected so it is
03631                       * now redirected from this number.
03632                       */
03633                      ast_party_number_free(&o->chan->redirecting.from.number);
03634                      ast_party_number_init(&o->chan->redirecting.from.number);
03635                      o->chan->redirecting.from.number.valid = 1;
03636                      o->chan->redirecting.from.number.str =
03637                         ast_strdup(S_OR(in->macroexten, in->exten));
03638                   }
03639 
03640                   o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03641 
03642                   o->dial_callerid_absent = !o->chan->caller.id.number.valid
03643                      || ast_strlen_zero(o->chan->caller.id.number.str);
03644                   ast_connected_line_copy_from_caller(&o->chan->connected, &in->caller);
03645 
03646                   ast_channel_unlock(in);
03647                   if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
03648                      && !o->block_connected_update) {
03649                      struct ast_party_redirecting redirecting;
03650 
03651                      /*
03652                       * Redirecting updates to the caller make sense only on single
03653                       * call at a time strategies.
03654                       *
03655                       * We must unlock o->chan before calling
03656                       * ast_channel_redirecting_macro, because we put o->chan into
03657                       * autoservice there.  That is pretty much a guaranteed
03658                       * deadlock.  This is why the handling of o->chan's lock may
03659                       * seem a bit unusual here.
03660                       */
03661                      ast_party_redirecting_init(&redirecting);
03662                      ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
03663                      ast_channel_unlock(o->chan);
03664                      if (ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
03665                         ast_channel_update_redirecting(in, &redirecting, NULL);
03666                      }
03667                      ast_party_redirecting_free(&redirecting);
03668                   } else {
03669                      ast_channel_unlock(o->chan);
03670                   }
03671 
03672                   if (ast_call(o->chan, stuff, 0)) {
03673                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
03674                         tech, stuff);
03675                      do_hang(o);
03676                      numnochan++;
03677                   }
03678                }
03679                /* Hangup the original channel now, in case we needed it */
03680                ast_hangup(winner);
03681                continue;
03682             }
03683             f = ast_read(winner);
03684             if (f) {
03685                if (f->frametype == AST_FRAME_CONTROL) {
03686                   switch (f->subclass.integer) {
03687                   case AST_CONTROL_ANSWER:
03688                      /* This is our guy if someone answered. */
03689                      if (!peer) {
03690                         ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03691                         if (!o->block_connected_update) {
03692                            if (o->pending_connected_update) {
03693                               if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03694                                  ast_channel_update_connected_line(in, &o->connected, NULL);
03695                               }
03696                            } else if (!o->dial_callerid_absent) {
03697                               ast_channel_lock(o->chan);
03698                               ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03699                               ast_channel_unlock(o->chan);
03700                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03701                               if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
03702                                  ast_channel_update_connected_line(in, &connected_caller, NULL);
03703                               }
03704                               ast_party_connected_line_free(&connected_caller);
03705                            }
03706                         }
03707                         if (o->aoc_s_rate_list) {
03708                            size_t encoded_size;
03709                            struct ast_aoc_encoded *encoded;
03710                            if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03711                               ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03712                               ast_aoc_destroy_encoded(encoded);
03713                            }
03714                         }
03715                         peer = o;
03716                      }
03717                      break;
03718                   case AST_CONTROL_BUSY:
03719                      ast_verb(3, "%s is busy\n", ochan_name);
03720                      if (in->cdr)
03721                         ast_cdr_busy(in->cdr);
03722                      do_hang(o);
03723                      endtime = (long) time(NULL);
03724                      endtime -= starttime;
03725                      rna(endtime * 1000, qe, on, membername, 0);
03726                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03727                         if (qe->parent->timeoutrestart)
03728                            *to = orig;
03729                         /* Have enough time for a queue member to answer? */
03730                         if (*to > 500) {
03731                            ring_one(qe, outgoing, &numbusies);
03732                            starttime = (long) time(NULL);
03733                         }
03734                      }
03735                      numbusies++;
03736                      break;
03737                   case AST_CONTROL_CONGESTION:
03738                      ast_verb(3, "%s is circuit-busy\n", ochan_name);
03739                      if (in->cdr)
03740                         ast_cdr_busy(in->cdr);
03741                      endtime = (long) time(NULL);
03742                      endtime -= starttime;
03743                      rna(endtime * 1000, qe, on, membername, 0);
03744                      do_hang(o);
03745                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03746                         if (qe->parent->timeoutrestart)
03747                            *to = orig;
03748                         if (*to > 500) {
03749                            ring_one(qe, outgoing, &numbusies);
03750                            starttime = (long) time(NULL);
03751                         }
03752                      }
03753                      numbusies++;
03754                      break;
03755                   case AST_CONTROL_RINGING:
03756                      ast_verb(3, "%s is ringing\n", ochan_name);
03757 
03758                      /* Start ring indication when the channel is ringing, if specified */
03759                      if (qe->ring_when_ringing) {
03760                         ast_moh_stop(qe->chan);
03761                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
03762                      }
03763                      break;
03764                   case AST_CONTROL_OFFHOOK:
03765                      /* Ignore going off hook */
03766                      break;
03767                   case AST_CONTROL_CONNECTED_LINE:
03768                      if (o->block_connected_update) {
03769                         ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
03770                         break;
03771                      }
03772                      if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03773                         struct ast_party_connected_line connected;
03774 
03775                         ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
03776                         ast_party_connected_line_set_init(&connected, &o->connected);
03777                         ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
03778                         ast_party_connected_line_set(&o->connected, &connected, NULL);
03779                         ast_party_connected_line_free(&connected);
03780                         o->pending_connected_update = 1;
03781                         break;
03782                      }
03783 
03784                      /*
03785                       * Prevent using the CallerID from the outgoing channel since we
03786                       * got a connected line update from it.
03787                       */
03788                      o->dial_callerid_absent = 1;
03789 
03790                      if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
03791                         ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
03792                      }
03793                      break;
03794                   case AST_CONTROL_AOC:
03795                      {
03796                         struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
03797                         if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
03798                            ast_aoc_destroy_decoded(o->aoc_s_rate_list);
03799                            o->aoc_s_rate_list = decoded;
03800                         } else {
03801                            ast_aoc_destroy_decoded(decoded);
03802                         }
03803                      }
03804                      break;
03805                   case AST_CONTROL_REDIRECTING:
03806                      if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03807                         /*
03808                          * Redirecting updates to the caller make sense only on single
03809                          * call at a time strategies.
03810                          */
03811                         break;
03812                      }
03813                      if (o->block_connected_update) {
03814                         ast_verb(3, "Redirecting update to %s prevented\n",
03815                            inchan_name);
03816                         break;
03817                      }
03818                      ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
03819                         ochan_name, inchan_name);
03820                      if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
03821                         ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
03822                      }
03823                      break;
03824                   default:
03825                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
03826                      break;
03827                   }
03828                }
03829                ast_frfree(f);
03830             } else { /* ast_read() returned NULL */
03831                endtime = (long) time(NULL) - starttime;
03832                rna(endtime * 1000, qe, on, membername, 1);
03833                do_hang(o);
03834                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03835                   if (qe->parent->timeoutrestart)
03836                      *to = orig;
03837                   if (*to > 500) {
03838                      ring_one(qe, outgoing, &numbusies);
03839                      starttime = (long) time(NULL);
03840                   }
03841                }
03842             }
03843          }
03844       }
03845 
03846       /* If we received an event from the caller, deal with it. */
03847       if (winner == in) {
03848          f = ast_read(in);
03849          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
03850             /* Got hung up */
03851             *to = -1;
03852             if (f) {
03853                if (f->data.uint32) {
03854                   in->hangupcause = f->data.uint32;
03855                }
03856                ast_frfree(f);
03857             }
03858             return NULL;
03859          }
03860 
03861          /*!
03862           * \todo
03863           * XXX Queue like Dial really should send any connected line
03864           * updates (AST_CONTROL_CONNECTED_LINE) from the caller to each
03865           * ringing queue member.
03866           */
03867 
03868          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
03869             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
03870             *to = 0;
03871             ast_frfree(f);
03872             return NULL;
03873          }
03874          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
03875             ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
03876             *to = 0;
03877             *digit = f->subclass.integer;
03878             ast_frfree(f);
03879             return NULL;
03880          }
03881          ast_frfree(f);
03882       }
03883       if (!*to) {
03884          for (o = start; o; o = o->call_next)
03885             rna(orig, qe, o->interface, o->member->membername, 1);
03886       }
03887    }
03888 
03889 #ifdef HAVE_EPOLL
03890    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03891       if (epollo->chan)
03892          ast_poll_channel_del(in, epollo->chan);
03893    }
03894 #endif
03895 
03896    return peer;
03897 }

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

References ast_queue_log(), queue_ent::chan, 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, queue_ent::pos, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, queue_ent::start, status, and ast_channel::uniqueid.

Referenced by queue_exec().

03986 {
03987    int res = 0;
03988 
03989    /* This is the holding pen for callers 2 through maxlen */
03990    for (;;) {
03991 
03992       if (is_our_turn(qe))
03993          break;
03994 
03995       /* If we have timed out, break out */
03996       if (qe->expire && (time(NULL) >= qe->expire)) {
03997          *reason = QUEUE_TIMEOUT;
03998          break;
03999       }
04000 
04001       if (qe->parent->leavewhenempty) {
04002          int status = 0;
04003 
04004          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
04005             *reason = QUEUE_LEAVEEMPTY;
04006             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04007             leave_queue(qe);
04008             break;
04009          }
04010       }
04011 
04012       /* Make a position announcement, if enabled */
04013       if (qe->parent->announcefrequency &&
04014          (res = say_position(qe,ringing)))
04015          break;
04016 
04017       /* If we have timed out, break out */
04018       if (qe->expire && (time(NULL) >= qe->expire)) {
04019          *reason = QUEUE_TIMEOUT;
04020          break;
04021       }
04022 
04023       /* Make a periodic announcement, if enabled */
04024       if (qe->parent->periodicannouncefrequency &&
04025          (res = say_periodic_announcement(qe,ringing)))
04026          break;
04027       
04028       /* see if we need to move to the next penalty level for this queue */
04029       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
04030          update_qe_rule(qe);
04031       }
04032 
04033       /* If we have timed out, break out */
04034       if (qe->expire && (time(NULL) >= qe->expire)) {
04035          *reason = QUEUE_TIMEOUT;
04036          break;
04037       }
04038       
04039       /* Wait a second before checking again */
04040       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
04041          if (res > 0 && !valid_exit(qe, res))
04042             res = 0;
04043          else
04044             break;
04045       }
04046       
04047       /* If we have timed out, break out */
04048       if (qe->expire && (time(NULL) >= qe->expire)) {
04049          *reason = QUEUE_TIMEOUT;
04050          break;
04051       }
04052    }
04053 
04054    return res;
04055 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", } [static]

Definition at line 8476 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 901 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 903 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 907 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 911 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 905 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 909 of file app_queue.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 8476 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 925 of file app_queue.c.

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

struct ast_cli_entry cli_queue[] [static]

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

Referenced by aji_init_event_distribution(), load_module(), load_pbx(), and unload_module().

struct ast_taskprocessor* devicestate_tps [static]

Definition at line 885 of file app_queue.c.

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

enum queue_result id

Definition at line 951 of file app_queue.c.

Referenced by _sip_show_peers(), _skinny_show_devices(), _skinny_show_lines(), ast_cc_extension_monitor_add_dialstring(), ast_party_id_presentation(), frame_trace_helper(), idemodulator(), misdn_queue_connected_line_update(), misdn_update_caller_id(), party_id_build_data(), party_id_read(), party_id_write(), and pidf_validate_tuple().

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 928 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 914 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 8051 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 8057 of file app_queue.c.

struct ast_data_entry queue_data_providers[] [static]

Initial value:

 {
   AST_DATA_ENTRY("asterisk/application/queue/list", &queues_data_provider),
}

Definition at line 8337 of file app_queue.c.

Referenced by load_module().

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 919 of file app_queue.c.

struct { ... } queue_results[] [static]

Referenced by set_queue_result().

struct ast_datastore_info queue_transfer_info [static]

Initial value:

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

Definition at line 4236 of file app_queue.c.

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

struct ast_custom_function queueexists_function [static]

Initial value:

 {
   .name = "QUEUE_EXISTS",
   .read = queue_function_exists,
}

Definition at line 6500 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_dep [static]

Initial value:

 {
   .name = "QUEUE_MEMBER_COUNT",
   .read = queue_function_qac_dep,
}

Definition at line 6515 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Initial value:

 {
   .name = "QUEUE_MEMBER",
   .read = queue_function_qac,
}

Definition at line 6510 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Initial value:

 {
   .name = "QUEUE_MEMBER_LIST",
   .read = queue_function_queuememberlist,
}

Definition at line 6525 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberpenalty_function [static]

Initial value:

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

Definition at line 6530 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Definition at line 1189 of file app_queue.c.

Referenced by __queues_show(), clear_stats(), compare_weight(), complete_queue(), complete_queue_remove_member(), extension_state_cb(), find_queue_by_name_rt(), get_member_penalty(), handle_statechange(), leave_queue(), load_module(), load_realtime_queue(), manager_queues_status(), manager_queues_summary(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), queues_data_provider_get(), reload_queue_members(), reload_queues(), reload_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_queue().

struct ast_data_handler queues_data_provider [static]

Initial value:

Definition at line 8332 of file app_queue.c.

struct ast_custom_function queuevar_function [static]

Initial value:

 {
   .name = "QUEUE_VARIABLES",
   .read = queue_function_var,
}

Definition at line 6505 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Initial value:

 {
   .name = "QUEUE_WAITING_COUNT",
   .read = queue_function_queuewaitingcount,
}

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

int shared_lastcall = 1 [static]

queues.conf [general] option

Definition at line 931 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* text

Definition at line 952 of file app_queue.c.

Referenced by festival_exec(), iconv_read(), method_match(), parse_sip_options(), process_sdp(), reqprep(), set_queue_result(), and sip_new().

int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 937 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 922 of file app_queue.c.


Generated on Mon Oct 8 12:39:08 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7